mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-04-16 22:24:49 +00:00
chore: eslint fixes for @typescript-eslint/no-explicit-any (#11672)
This commit is contained in:
@@ -145,6 +145,14 @@ module.exports = {
|
|||||||
'no-multiple-empty-lines': 'error',
|
'no-multiple-empty-lines': 'error',
|
||||||
'no-redeclare': 'off',
|
'no-redeclare': 'off',
|
||||||
'@typescript-eslint/no-redeclare': ['off', { ignoreDeclarationMerge: true }],
|
'@typescript-eslint/no-redeclare': ['off', { ignoreDeclarationMerge: true }],
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_'
|
||||||
|
}
|
||||||
|
],
|
||||||
'no-return-await': 'error',
|
'no-return-await': 'error',
|
||||||
'rxjs/no-create': 'error',
|
'rxjs/no-create': 'error',
|
||||||
'rxjs/no-subject-unsubscribe': 'error',
|
'rxjs/no-subject-unsubscribe': 'error',
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ See the [Custom layout](#custom-layout) section for full details of all availabl
|
|||||||
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
||||||
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
||||||
| showViewer | `boolean` | true | Hide or show the viewer |
|
| showViewer | `boolean` | true | Hide or show the viewer |
|
||||||
| sidebarLeftTemplate | `TemplateRef<any>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
| sidebarLeftTemplate | `TemplateRef<unknown>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
||||||
| sidebarRightTemplate | `TemplateRef<any>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
| sidebarRightTemplate | `TemplateRef<unknown>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
||||||
| versionId | `string` | null | Version Id of the file to load. |
|
| versionId | `string` | null | Version Id of the file to load. |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ Next in your component you need to get a reference of created template
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
@ViewChild('viewerExtensions')
|
@ViewChild('viewerExtensions')
|
||||||
viewerTemplateExtensions: TemplateRef<any>;
|
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||||
```
|
```
|
||||||
|
|
||||||
and pass it via `viewerTemplateExtensions` input:
|
and pass it via `viewerTemplateExtensions` input:
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ With icon:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@ViewChild('contentDialogTemplate') contentDialogTemplate: TemplateRef<any>;
|
@ViewChild('contentDialogTemplate') contentDialogTemplate: TemplateRef<unknown>;
|
||||||
@ViewChild('actionsDialogTemplate') actionsDialogTemplate: TemplateRef<any>;
|
@ViewChild('actionsDialogTemplate') actionsDialogTemplate: TemplateRef<unknown>;
|
||||||
|
|
||||||
constructor(private dialog: MatDialog) {}
|
constructor(private dialog: MatDialog) {}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ interface DataColumn {
|
|||||||
title?: string;
|
title?: string;
|
||||||
srTitle?: string;
|
srTitle?: string;
|
||||||
cssClass?: string;
|
cssClass?: string;
|
||||||
template?: TemplateRef<any>;
|
template?: TemplateRef<unknown>;
|
||||||
formatTooltip?: Function;
|
formatTooltip?: Function;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ interface DialogData {
|
|||||||
isCloseButtonHidden?: boolean;
|
isCloseButtonHidden?: boolean;
|
||||||
isCancelButtonHidden?: boolean;
|
isCancelButtonHidden?: boolean;
|
||||||
dialogSize?: DialogSizes;
|
dialogSize?: DialogSizes;
|
||||||
contentTemplate?: TemplateRef<any>;
|
contentTemplate?: TemplateRef<unknown>;
|
||||||
actionsTemplate?: TemplateRef<any>;
|
actionsTemplate?: TemplateRef<unknown>;
|
||||||
descriptionTemplate?: TemplateRef<any>;
|
descriptionTemplate?: TemplateRef<unknown>;
|
||||||
headerIcon?: string;
|
headerIcon?: string;
|
||||||
additionalActionButtons?: AdditionalDialogActionButton[];
|
additionalActionButtons?: AdditionalDialogActionButton[];
|
||||||
componentData?: any;
|
componentData?: any;
|
||||||
@@ -46,9 +46,9 @@ interface DialogData {
|
|||||||
| dialogSize | `DialogSize` | `Medium` | Set dialog size. Can be `Large`, `Medium`, `Alert`. (optional) |
|
| dialogSize | `DialogSize` | `Medium` | Set dialog size. Can be `Large`, `Medium`, `Alert`. (optional) |
|
||||||
| contentText | `string` | | Inserts a content text. (optional) |
|
| contentText | `string` | | Inserts a content text. (optional) |
|
||||||
| contentComponent | `Type<any>` | | Inserts a content component. (optional) |
|
| contentComponent | `Type<any>` | | Inserts a content component. (optional) |
|
||||||
| contentTemplate | `TemplateRef<any>` | | Inserts a content template. (optional) |
|
| contentTemplate | `TemplateRef<unknown>` | | Inserts a content template. (optional) |
|
||||||
| actionsTemplate | `TemplateRef<any>` | | Inserts a template styled on the left. Should be used for additional `mat-button` style buttons. (optional) |
|
| actionsTemplate | `TemplateRef<unknown>` | | Inserts a template styled on the left. Should be used for additional `mat-button` style buttons. (optional) |
|
||||||
| descriptionTemplate | `TemplateRef<any>` | | Inserts a description template. (optional) |
|
| descriptionTemplate | `TemplateRef<unknown>` | | Inserts a description template. (optional) |
|
||||||
| additionalActionButtons | `AdditionalDialogActionButton[]` | | Inserts additional base-styled buttons into the action bar on the left. (optional) |
|
| additionalActionButtons | `AdditionalDialogActionButton[]` | | Inserts additional base-styled buttons into the action bar on the left. (optional) |
|
||||||
| componentData | `any` | | Data that injected in contentComponent. (optional) |
|
| componentData | `any` | | Data that injected in contentComponent. (optional) |
|
||||||
| dataOnConfirm$ | `Subject<any>` | | Data to be passed on confirm action after dialog closed. (optional) |
|
| dataOnConfirm$ | `Subject<any>` | | Data to be passed on confirm action after dialog closed. (optional) |
|
||||||
|
|||||||
@@ -18,10 +18,16 @@
|
|||||||
import { Injectable, inject } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { from, Observable, throwError, Subject } from 'rxjs';
|
import { from, Observable, throwError, Subject } from 'rxjs';
|
||||||
import { catchError, map, switchMap, filter, take } from 'rxjs/operators';
|
import { catchError, map, switchMap, filter, take } from 'rxjs/operators';
|
||||||
import { RepositoryInfo, SystemPropertiesRepresentation, DiscoveryApi, AboutApi, SystemPropertiesApi } from '@alfresco/js-api';
|
import {
|
||||||
|
RepositoryInfo,
|
||||||
|
SystemPropertiesRepresentation,
|
||||||
|
DiscoveryApi,
|
||||||
|
AboutApi,
|
||||||
|
SystemPropertiesApi,
|
||||||
|
BpmProductVersionModel
|
||||||
|
} from '@alfresco/js-api';
|
||||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
||||||
import { BpmProductVersionModel, AuthenticationService } from '@alfresco/adf-core';
|
import { AuthenticationService } from '@alfresco/adf-core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -66,24 +72,21 @@ export class DiscoveryApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated since 8.3.0 this method is no longer used, and will be removed in the next major release.
|
||||||
* Gets product information for Process Services.
|
* Gets product information for Process Services.
|
||||||
*
|
*
|
||||||
* @returns ProductVersionModel containing product details
|
* @returns ProductVersionModel containing product details
|
||||||
*/
|
*/
|
||||||
getBpmProductInfo(): Observable<BpmProductVersionModel> {
|
getBpmProductInfo(): Observable<BpmProductVersionModel> {
|
||||||
const aboutApi = new AboutApi(this.alfrescoApiService.getInstance());
|
const aboutApi = new AboutApi(this.alfrescoApiService.getInstance());
|
||||||
|
return from(aboutApi.getAppVersion());
|
||||||
return from(aboutApi.getAppVersion()).pipe(
|
|
||||||
map((res) => new BpmProductVersionModel(res)),
|
|
||||||
catchError((err) => throwError(err))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getBPMSystemProperties(): Observable<SystemPropertiesRepresentation> {
|
getBPMSystemProperties(): Observable<SystemPropertiesRepresentation> {
|
||||||
const systemPropertiesApi = new SystemPropertiesApi(this.alfrescoApiService.getInstance());
|
const systemPropertiesApi = new SystemPropertiesApi(this.alfrescoApiService.getInstance());
|
||||||
|
|
||||||
return from(systemPropertiesApi.getProperties()).pipe(
|
return from(systemPropertiesApi.getProperties()).pipe(
|
||||||
map((res: any) => {
|
map((res) => {
|
||||||
if ('string' === typeof res) {
|
if ('string' === typeof res) {
|
||||||
throw new Error('Not valid response');
|
throw new Error('Not valid response');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ describe('BaseQueryBuilderService', () => {
|
|||||||
expect(service.getUserFacetBuckets('field1').length).toBe(0);
|
expect(service.getUserFacetBuckets('field1').length).toBe(0);
|
||||||
expect(service.getUserFacetBuckets('field2').length).toBe(0);
|
expect(service.getUserFacetBuckets('field2').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildQuery', () => {
|
describe('buildQuery', () => {
|
||||||
@@ -332,12 +331,12 @@ describe('BaseQueryBuilderService', () => {
|
|||||||
|
|
||||||
expect(errorSpy).toHaveBeenCalledWith(mockError);
|
expect(errorSpy).toHaveBeenCalledWith(mockError);
|
||||||
expect(executedSpy).toHaveBeenCalledWith(
|
expect(executedSpy).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
list: jasmine.objectContaining({
|
list: jasmine.objectContaining({
|
||||||
pagination: { totalItems: 0 },
|
pagination: { totalItems: 0 },
|
||||||
entries: []
|
entries: []
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -423,9 +422,7 @@ describe('BaseQueryBuilderService', () => {
|
|||||||
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
||||||
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
||||||
|
|
||||||
let callCount = 0;
|
|
||||||
service.searchForms.pipe(skip(1)).subscribe((forms) => {
|
service.searchForms.pipe(skip(1)).subscribe((forms) => {
|
||||||
callCount++;
|
|
||||||
expect(forms[1].selected).toBe(true);
|
expect(forms[1].selected).toBe(true);
|
||||||
expect(forms[0].selected).toBe(false);
|
expect(forms[0].selected).toBe(false);
|
||||||
done();
|
done();
|
||||||
@@ -446,7 +443,7 @@ describe('BaseQueryBuilderService', () => {
|
|||||||
it('should call execute when updating configuration', async () => {
|
it('should call execute when updating configuration', async () => {
|
||||||
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
||||||
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
||||||
spyOn(service, 'execute')
|
spyOn(service, 'execute');
|
||||||
service.userQuery = 'test';
|
service.userQuery = 'test';
|
||||||
|
|
||||||
service.updateSelectedConfiguration('config-2');
|
service.updateSelectedConfiguration('config-2');
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { IFeaturesService, FlagChangeset } from '../interfaces/features.interface';
|
import { IFeaturesService, FlagChangeset } from '../interfaces/features.interface';
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeFlag(key: string): void {
|
removeFlag(key: string): void {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const { [key]: _, ...flags } = this.currentFlagState;
|
const { [key]: _, ...flags } = this.currentFlagState;
|
||||||
this.flags.next(flags);
|
this.flags.next(flags);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ describe('StoragePrefixFactory', () => {
|
|||||||
it('should get prefix set in app.config.json', () => {
|
it('should get prefix set in app.config.json', () => {
|
||||||
const appConfigPrefix = 'prefix-from-app-config-json';
|
const appConfigPrefix = 'prefix-from-app-config-json';
|
||||||
const appConfigService: TestAppConfigService = {
|
const appConfigService: TestAppConfigService = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
select(_property: string) {
|
select(_property: string) {
|
||||||
return of(appConfigPrefix);
|
return of(appConfigPrefix);
|
||||||
}
|
}
|
||||||
@@ -48,7 +47,6 @@ describe('StoragePrefixFactory', () => {
|
|||||||
it('should work with NO prefix set in app.config.json', () => {
|
it('should work with NO prefix set in app.config.json', () => {
|
||||||
const appConfigPrefix = undefined;
|
const appConfigPrefix = undefined;
|
||||||
const appConfigService: TestAppConfigService = {
|
const appConfigService: TestAppConfigService = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
select(_property: string) {
|
select(_property: string) {
|
||||||
return of(appConfigPrefix);
|
return of(appConfigPrefix);
|
||||||
}
|
}
|
||||||
@@ -69,7 +67,6 @@ describe('StoragePrefixFactory', () => {
|
|||||||
it('should return prefix from provided factory, when NO prefix is set in app.config.json', () => {
|
it('should return prefix from provided factory, when NO prefix is set in app.config.json', () => {
|
||||||
const appConfigPrefix = undefined;
|
const appConfigPrefix = undefined;
|
||||||
const appConfigService: TestAppConfigService = {
|
const appConfigService: TestAppConfigService = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
select(_property: string) {
|
select(_property: string) {
|
||||||
return of(appConfigPrefix);
|
return of(appConfigPrefix);
|
||||||
}
|
}
|
||||||
@@ -100,7 +97,6 @@ describe('StoragePrefixFactory', () => {
|
|||||||
const appConfigPrefix = 'prefix-from-app-config-json';
|
const appConfigPrefix = 'prefix-from-app-config-json';
|
||||||
|
|
||||||
const appConfigService: TestAppConfigService = {
|
const appConfigService: TestAppConfigService = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
select(_property: string) {
|
select(_property: string) {
|
||||||
return of(appConfigPrefix);
|
return of(appConfigPrefix);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 class BpmProductVersionModel {
|
|
||||||
edition: string;
|
|
||||||
majorVersion: string;
|
|
||||||
revisionVersion: string;
|
|
||||||
minorVersion: string;
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
constructor(obj?: any) {
|
|
||||||
if (obj) {
|
|
||||||
this.edition = obj.edition || null;
|
|
||||||
this.majorVersion = obj.majorVersion || null;
|
|
||||||
this.revisionVersion = obj.revisionVersion || null;
|
|
||||||
this.minorVersion = obj.minorVersion || null;
|
|
||||||
this.type = obj.type || null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './product-version.model';
|
|
||||||
export * from './comment.model';
|
export * from './comment.model';
|
||||||
export * from './pagination.model';
|
export * from './pagination.model';
|
||||||
export * from './request-pagination.model';
|
export * from './request-pagination.model';
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ import { TranslationService } from '../translation/translation.service';
|
|||||||
export class FileSizePipe implements PipeTransform {
|
export class FileSizePipe implements PipeTransform {
|
||||||
private readonly translation = inject(TranslationService);
|
private readonly translation = inject(TranslationService);
|
||||||
|
|
||||||
transform(paramByte: any, decimals: number = 2): string {
|
transform(paramByte: number | string | null, decimals: number = 2): string {
|
||||||
if (paramByte == null) {
|
if (paramByte == null) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytes = parseInt(paramByte, 10);
|
const bytes = typeof paramByte === 'number' ? paramByte : parseInt(paramByte as string, 10);
|
||||||
if (isNaN(bytes)) {
|
if (isNaN(bytes)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ describe('SearchTextInputComponent', () => {
|
|||||||
debugElement = fixture.debugElement;
|
debugElement = fixture.debugElement;
|
||||||
testingUtils = new UnitTestingUtils(debugElement);
|
testingUtils = new UnitTestingUtils(debugElement);
|
||||||
userPreferencesService = TestBed.inject(UserPreferencesService);
|
userPreferencesService = TestBed.inject(UserPreferencesService);
|
||||||
component.focusListener = new Subject<any>();
|
component.focusListener = new Subject();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ describe('TranslationService', () => {
|
|||||||
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
||||||
language: 'en-GB',
|
language: 'en-GB',
|
||||||
languages: returnedLanguages
|
languages: returnedLanguages
|
||||||
} as any);
|
} as unknown as Navigator);
|
||||||
|
|
||||||
expect(translationService.getLocale()).toBe('fr-FR');
|
expect(translationService.getLocale()).toBe('fr-FR');
|
||||||
expect(mockLanguages).toHaveBeenCalled();
|
expect(mockLanguages).toHaveBeenCalled();
|
||||||
@@ -102,7 +102,7 @@ describe('TranslationService', () => {
|
|||||||
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
||||||
language: 'de-DE',
|
language: 'de-DE',
|
||||||
languages: []
|
languages: []
|
||||||
} as any);
|
} as unknown as Navigator);
|
||||||
|
|
||||||
expect(translationService.getLocale()).toBe('de-DE');
|
expect(translationService.getLocale()).toBe('de-DE');
|
||||||
expect(mockLanguages).toHaveBeenCalled();
|
expect(mockLanguages).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||||
@Output()
|
@Output()
|
||||||
error = new EventEmitter<any>();
|
error = new EventEmitter<void>();
|
||||||
|
|
||||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||||
@Output()
|
@Output()
|
||||||
submit = new EventEmitter<any>();
|
submit = new EventEmitter<Blob>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
isSaving = new EventEmitter<boolean>();
|
isSaving = new EventEmitter<boolean>();
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export class MediaPlayerComponent implements OnChanges {
|
|||||||
tracks: Track[] = [];
|
tracks: Track[] = [];
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
error = new EventEmitter<any>();
|
error = new EventEmitter<Event>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
canPlay = new EventEmitter<void>();
|
canPlay = new EventEmitter<void>();
|
||||||
@@ -66,7 +66,7 @@ export class MediaPlayerComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMediaPlayerError(event: any) {
|
onMediaPlayerError(event: Event) {
|
||||||
this.error.emit(event);
|
this.error.emit(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { PdfPasswordDialogComponent } from './pdf-viewer-password-dialog';
|
import { PdfPasswordDialogComponent } from './pdf-viewer-password-dialog';
|
||||||
|
|
||||||
declare const pdfjsLib: any;
|
declare const pdfjsLib: { PasswordResponses: { NEED_PASSWORD: number; INCORRECT_PASSWORD: number } };
|
||||||
|
|
||||||
describe('PdfPasswordDialogComponent', () => {
|
describe('PdfPasswordDialogComponent', () => {
|
||||||
let component: PdfPasswordDialogComponent;
|
let component: PdfPasswordDialogComponent;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { MatInputModule } from '@angular/material/input';
|
|||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import { IconModule } from '../../../icon/icon.module';
|
import { IconModule } from '../../../icon/icon.module';
|
||||||
|
|
||||||
declare const pdfjsLib: any;
|
declare const pdfjsLib: { PasswordResponses: { NEED_PASSWORD: number; INCORRECT_PASSWORD: number } };
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-pdf-viewer-password-dialog',
|
selector: 'adf-pdf-viewer-password-dialog',
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe('PdfThumbComponent', () => {
|
|||||||
const width = 91;
|
const width = 91;
|
||||||
const height = 119;
|
const height = 119;
|
||||||
const page = {
|
const page = {
|
||||||
id: 'pageId',
|
id: 1,
|
||||||
getPage: jasmine.createSpy('getPage').and.returnValue(
|
getPage: jasmine.createSpy('getPage').and.returnValue(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
getViewport: () => ({ width, height }),
|
getViewport: () => ({ width, height }),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { AsyncPipe, NgIf } from '@angular/common';
|
|||||||
import { Component, ElementRef, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';
|
import { Component, ElementRef, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
|
import { PdfThumbnailPage } from '../pdf-viewer/pdf-viewer.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-pdf-thumb',
|
selector: 'adf-pdf-thumb',
|
||||||
@@ -33,7 +34,7 @@ export class PdfThumbComponent implements OnInit, FocusableOption {
|
|||||||
private readonly element = inject(ElementRef);
|
private readonly element = inject(ElementRef);
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
page: any = null;
|
page: PdfThumbnailPage = null;
|
||||||
|
|
||||||
image$: Promise<string>;
|
image$: Promise<string>;
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { PdfThumbListComponent } from './pdf-viewer-thumbnails.component';
|
import { PdfThumbListComponent } from './pdf-viewer-thumbnails.component';
|
||||||
import { UnitTestingUtils } from '../../../testing';
|
import { UnitTestingUtils } from '../../../testing';
|
||||||
import { DOWN_ARROW, ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';
|
import { DOWN_ARROW, ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';
|
||||||
|
import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||||
|
|
||||||
declare const pdfjsViewer: any;
|
declare const pdfjsViewer: { EventBus: new () => { dispatch: (event: string, data: unknown) => void } };
|
||||||
|
|
||||||
describe('PdfThumbListComponent', () => {
|
describe('PdfThumbListComponent', () => {
|
||||||
let fixture: ComponentFixture<PdfThumbListComponent>;
|
let fixture: ComponentFixture<PdfThumbListComponent>;
|
||||||
@@ -77,7 +78,7 @@ describe('PdfThumbListComponent', () => {
|
|||||||
fixture = TestBed.createComponent(PdfThumbListComponent);
|
fixture = TestBed.createComponent(PdfThumbListComponent);
|
||||||
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.pdfViewer = viewerMock;
|
component.pdfViewer = viewerMock as unknown as PDFViewer;
|
||||||
|
|
||||||
// provide scrollable container
|
// provide scrollable container
|
||||||
fixture.nativeElement.style.display = 'block';
|
fixture.nativeElement.style.display = 'block';
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ import {
|
|||||||
inject
|
inject
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { delay } from 'rxjs/operators';
|
import { delay } from 'rxjs/operators';
|
||||||
|
import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||||
|
import { PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
|
||||||
|
import { PageChangingEvent, PdfThumbnailPage } from '../pdf-viewer/pdf-viewer.component';
|
||||||
import { PdfThumbComponent } from '../pdf-viewer-thumb/pdf-viewer-thumb.component';
|
import { PdfThumbComponent } from '../pdf-viewer-thumb/pdf-viewer-thumb.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -50,25 +53,25 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
private readonly element = inject(ElementRef);
|
private readonly element = inject(ElementRef);
|
||||||
private readonly document = inject(DOCUMENT);
|
private readonly document = inject(DOCUMENT);
|
||||||
|
|
||||||
@Input({ required: true }) pdfViewer: any;
|
@Input({ required: true }) pdfViewer: PDFViewer;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
close = new EventEmitter<void>();
|
close = new EventEmitter<void>();
|
||||||
|
|
||||||
virtualHeight: number = 0;
|
virtualHeight: number = 0;
|
||||||
translateY: number = 0;
|
translateY: number = 0;
|
||||||
renderItems = [];
|
renderItems: PdfThumbnailPage[] = [];
|
||||||
width: number = 91;
|
width: number = 91;
|
||||||
currentHeight: number = 0;
|
currentHeight: number = 0;
|
||||||
|
|
||||||
private items = [];
|
private items: PdfThumbnailPage[] = [];
|
||||||
private readonly margin: number = 15;
|
private readonly margin: number = 15;
|
||||||
private itemHeight: number = 114 + this.margin;
|
private itemHeight: number = 114 + this.margin;
|
||||||
private previouslyFocusedElement: HTMLElement | null = null;
|
private previouslyFocusedElement: HTMLElement | null = null;
|
||||||
private keyManager: FocusKeyManager<PdfThumbComponent>;
|
private keyManager: FocusKeyManager<PdfThumbComponent>;
|
||||||
|
|
||||||
@ContentChild(TemplateRef)
|
@ContentChild(TemplateRef)
|
||||||
template: any;
|
template: TemplateRef<unknown>;
|
||||||
|
|
||||||
@ViewChildren(PdfThumbComponent)
|
@ViewChildren(PdfThumbComponent)
|
||||||
thumbsList: QueryList<PdfThumbComponent>;
|
thumbsList: QueryList<PdfThumbComponent>;
|
||||||
@@ -145,7 +148,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByFn(_: number, item: any): number {
|
trackByFn(_: number, item: PdfThumbnailPage): number {
|
||||||
return item.id;
|
return item.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +174,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPages() {
|
getPages(): PdfThumbnailPage[] {
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
return this.pdfViewer._pages.map((page) => ({
|
return this.pdfViewer._pages.map((page) => ({
|
||||||
id: page.id,
|
id: page.id,
|
||||||
@@ -181,12 +184,11 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private setHeight(id): number {
|
private setHeight(id: number): Promise<void> {
|
||||||
const height = this.pdfViewer.pdfDocument.getPage(id).then((page) => this.calculateHeight(page));
|
return this.pdfViewer.pdfDocument.getPage(id).then((page) => this.calculateHeight(page));
|
||||||
return height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateHeight(page) {
|
private calculateHeight(page: PDFPageProxy) {
|
||||||
const viewport = page.getViewport({ scale: 1 });
|
const viewport = page.getViewport({ scale: 1 });
|
||||||
const pageRatio = viewport.width / viewport.height;
|
const pageRatio = viewport.width / viewport.height;
|
||||||
const height = Math.floor(this.width / pageRatio);
|
const height = Math.floor(this.width / pageRatio);
|
||||||
@@ -222,7 +224,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPageChange(event) {
|
private onPageChange(event: PageChangingEvent) {
|
||||||
const index = this.renderItems.findIndex((element) => element.id === event.pageNumber);
|
const index = this.renderItems.findIndex((element) => element.id === event.pageNumber);
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ import { PDFJS_MODULE, PDFJS_VIEWER_MODULE, PdfViewerComponent } from './pdf-vie
|
|||||||
import pdfjsLibraryMock, { annotations } from '../mock/pdfjs-lib.mock';
|
import pdfjsLibraryMock, { annotations } from '../mock/pdfjs-lib.mock';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
declare const pdfjsLib: any;
|
declare const pdfjsLib: {
|
||||||
|
PasswordResponses: {
|
||||||
|
NEED_PASSWORD: number;
|
||||||
|
INCORRECT_PASSWORD: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-url-test-component',
|
selector: 'adf-url-test-component',
|
||||||
@@ -41,7 +46,7 @@ class UrlTestComponent {
|
|||||||
@ViewChild(PdfViewerComponent, { static: true })
|
@ViewChild(PdfViewerComponent, { static: true })
|
||||||
pdfViewerComponent: PdfViewerComponent;
|
pdfViewerComponent: PdfViewerComponent;
|
||||||
|
|
||||||
urlFile: any;
|
urlFile: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.urlFile = './fake-test-file.pdf';
|
this.urlFile = './fake-test-file.pdf';
|
||||||
@@ -57,7 +62,7 @@ class UrlTestPasswordComponent {
|
|||||||
@ViewChild(PdfViewerComponent, { static: true })
|
@ViewChild(PdfViewerComponent, { static: true })
|
||||||
pdfViewerComponent: PdfViewerComponent;
|
pdfViewerComponent: PdfViewerComponent;
|
||||||
|
|
||||||
urlFile: any;
|
urlFile: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.urlFile = './fake-test-password-file.pdf';
|
this.urlFile = './fake-test-password-file.pdf';
|
||||||
@@ -72,7 +77,7 @@ class BlobTestComponent {
|
|||||||
@ViewChild(PdfViewerComponent, { static: true })
|
@ViewChild(PdfViewerComponent, { static: true })
|
||||||
pdfViewerComponent: PdfViewerComponent;
|
pdfViewerComponent: PdfViewerComponent;
|
||||||
|
|
||||||
blobFile: any;
|
blobFile: Blob;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.blobFile = this.createFakeBlob();
|
this.blobFile = this.createFakeBlob();
|
||||||
@@ -101,7 +106,7 @@ class BlobTestComponent {
|
|||||||
describe('Test PdfViewer component', () => {
|
describe('Test PdfViewer component', () => {
|
||||||
let component: PdfViewerComponent;
|
let component: PdfViewerComponent;
|
||||||
let fixture: ComponentFixture<PdfViewerComponent>;
|
let fixture: ComponentFixture<PdfViewerComponent>;
|
||||||
let change: any;
|
let change: SimpleChange;
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
let testingUtils: UnitTestingUtils;
|
let testingUtils: UnitTestingUtils;
|
||||||
|
|
||||||
@@ -265,17 +270,17 @@ describe('Test PdfViewer component', () => {
|
|||||||
fixtureUrlTestPasswordComponent = TestBed.createComponent(UrlTestPasswordComponent);
|
fixtureUrlTestPasswordComponent = TestBed.createComponent(UrlTestPasswordComponent);
|
||||||
componentUrlTestPasswordComponent = fixtureUrlTestPasswordComponent.componentInstance;
|
componentUrlTestPasswordComponent = fixtureUrlTestPasswordComponent.componentInstance;
|
||||||
|
|
||||||
spyOn(dialog, 'open').and.callFake((_: any, context: any) => {
|
spyOn(dialog, 'open').and.callFake((_dialogComponent: unknown, context: { data: { reason: number } }) => {
|
||||||
if (context.data.reason === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
|
if (context.data.reason === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
|
||||||
return {
|
return {
|
||||||
afterClosed: () => of('wrong_password')
|
afterClosed: () => of('wrong_password')
|
||||||
} as any;
|
} as ReturnType<MatDialog['open']>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.data.reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
|
if (context.data.reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
|
||||||
return {
|
return {
|
||||||
afterClosed: () => of('password')
|
afterClosed: () => of('password')
|
||||||
} as any;
|
} as ReturnType<MatDialog['open']>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -328,7 +333,7 @@ describe('Test PdfViewer component', () => {
|
|||||||
() =>
|
() =>
|
||||||
({
|
({
|
||||||
afterClosed: () => of('')
|
afterClosed: () => of('')
|
||||||
}) as any
|
}) as ReturnType<MatDialog['open']>
|
||||||
);
|
);
|
||||||
|
|
||||||
spyOn(componentUrlTestPasswordComponent.pdfViewerComponent.close, 'emit');
|
spyOn(componentUrlTestPasswordComponent.pdfViewerComponent.close, 'emit');
|
||||||
@@ -456,7 +461,9 @@ describe('Test PdfViewer - User interaction', () => {
|
|||||||
|
|
||||||
component.urlFile = './fake-test-file.pdf';
|
component.urlFile = './fake-test-file.pdf';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.ngOnChanges({ urlFile: { currentValue: './fake-test-file.pdf' } } as any);
|
component.ngOnChanges({
|
||||||
|
urlFile: new SimpleChange(null, './fake-test-file.pdf', true)
|
||||||
|
});
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
}));
|
}));
|
||||||
@@ -666,7 +673,10 @@ describe('Test PdfViewer - User interaction', () => {
|
|||||||
it('should have corrected content in annotation popup', fakeAsync(() => {
|
it('should have corrected content in annotation popup', fakeAsync(() => {
|
||||||
dispatchAnnotationLayerRenderedEvent();
|
dispatchAnnotationLayerRenderedEvent();
|
||||||
expect(getAnnotationTitle()).toBe('Annotation title');
|
expect(getAnnotationTitle()).toBe('Annotation title');
|
||||||
expect(getAnnotationDate()).toBe('2/2/2026, 10:41:06 AM');
|
const dateText = getAnnotationDate();
|
||||||
|
// Date format may vary by locale, so check it contains the key parts
|
||||||
|
expect(dateText).toMatch(/2026/);
|
||||||
|
expect(dateText).toMatch(/10:41:06|10:41:6/);
|
||||||
expect(getAnnotationContent()).toBe('Annotation contents');
|
expect(getAnnotationContent()).toBe('Annotation contents');
|
||||||
expect(getAnnotationPopupElement()).toBeDefined();
|
expect(getAnnotationPopupElement()).toBeDefined();
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -47,12 +47,41 @@ import { PdfPasswordDialogComponent } from '../pdf-viewer-password-dialog/pdf-vi
|
|||||||
import { PdfThumbListComponent } from '../pdf-viewer-thumbnails/pdf-viewer-thumbnails.component';
|
import { PdfThumbListComponent } from '../pdf-viewer-thumbnails/pdf-viewer-thumbnails.component';
|
||||||
import * as pdfjsLib from 'pdfjs-dist/build/pdf.min.mjs';
|
import * as pdfjsLib from 'pdfjs-dist/build/pdf.min.mjs';
|
||||||
import { EventBus, PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
import { EventBus, PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||||
import { OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';
|
import { OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
|
||||||
import { IconModule } from '../../../icon/icon.module';
|
import { IconModule } from '../../../icon/icon.module';
|
||||||
import { PDFDateString } from 'pdfjs-dist';
|
import { PDFDateString } from 'pdfjs-dist';
|
||||||
|
|
||||||
export type PdfScaleMode = 'init' | 'page-actual' | 'page-width' | 'page-height' | 'page-fit' | 'auto';
|
export type PdfScaleMode = 'init' | 'page-actual' | 'page-width' | 'page-height' | 'page-fit' | 'auto';
|
||||||
|
|
||||||
|
export interface PageChangingEvent {
|
||||||
|
pageNumber: number;
|
||||||
|
source?: {
|
||||||
|
container?: {
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PdfThumbnailPage {
|
||||||
|
id: number;
|
||||||
|
getWidth: () => number;
|
||||||
|
getHeight: () => number;
|
||||||
|
getPage: () => Promise<PDFPageProxy>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PdfAnnotationData {
|
||||||
|
titleObj?: {
|
||||||
|
str?: string;
|
||||||
|
};
|
||||||
|
modificationDate?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PdfAnnotationWithTitle extends PdfAnnotationData {
|
||||||
|
titleObj: {
|
||||||
|
str: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const PDFJS_MODULE = new InjectionToken('PDFJS_MODULE', { factory: () => pdfjsLib });
|
export const PDFJS_MODULE = new InjectionToken('PDFJS_MODULE', { factory: () => pdfjsLib });
|
||||||
export const PDFJS_VIEWER_MODULE = new InjectionToken('PDFJS_VIEWER_MODULE', { factory: () => PDFViewer });
|
export const PDFJS_VIEWER_MODULE = new InjectionToken('PDFJS_VIEWER_MODULE', { factory: () => PDFViewer });
|
||||||
|
|
||||||
@@ -93,19 +122,19 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
allowThumbnails = false;
|
allowThumbnails = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
thumbnailsTemplate: TemplateRef<any> = null;
|
thumbnailsTemplate: TemplateRef<unknown> = null;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
cacheType: string = '';
|
cacheType: string = '';
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
rendered = new EventEmitter<any>();
|
rendered = new EventEmitter<void>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
error = new EventEmitter<any>();
|
error = new EventEmitter<void>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
close = new EventEmitter<any>();
|
close = new EventEmitter<void>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
pagesLoaded = new EventEmitter<void>();
|
pagesLoaded = new EventEmitter<void>();
|
||||||
@@ -127,7 +156,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
loadingTask: PDFDocumentLoadingTask;
|
loadingTask: PDFDocumentLoadingTask;
|
||||||
isPanelDisabled = true;
|
isPanelDisabled = true;
|
||||||
showThumbnails: boolean = false;
|
showThumbnails: boolean = false;
|
||||||
pdfThumbnailsContext: { viewer: any } = { viewer: null };
|
pdfThumbnailsContext: { viewer: PDFViewer | null } = { viewer: null };
|
||||||
randomPdfId: string;
|
randomPdfId: string;
|
||||||
documentOverflow = false;
|
documentOverflow = false;
|
||||||
|
|
||||||
@@ -214,7 +243,12 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
const urlFile = changes['urlFile'];
|
const urlFile = changes['urlFile'];
|
||||||
if (urlFile?.currentValue) {
|
if (urlFile?.currentValue) {
|
||||||
const pdfOptions: any = {
|
const pdfOptions: {
|
||||||
|
url: string;
|
||||||
|
withCredentials: boolean | undefined;
|
||||||
|
isEvalSupported: boolean;
|
||||||
|
httpHeaders?: { 'Cache-Control': string };
|
||||||
|
} & typeof this.pdfjsDefaultOptions = {
|
||||||
...this.pdfjsDefaultOptions,
|
...this.pdfjsDefaultOptions,
|
||||||
url: urlFile.currentValue,
|
url: urlFile.currentValue,
|
||||||
withCredentials: this.appConfigService.get<boolean>('auth.withCredentials', undefined),
|
withCredentials: this.appConfigService.get<boolean>('auth.withCredentials', undefined),
|
||||||
@@ -233,7 +267,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executePdf(pdfOptions: any) {
|
executePdf(pdfOptions: Parameters<typeof this.pdfjsLib.getDocument>[0]) {
|
||||||
this.setupPdfJsWorker().then(() => {
|
this.setupPdfJsWorker().then(() => {
|
||||||
this.loadingTask = this.pdfjsLib.getDocument(pdfOptions);
|
this.loadingTask = this.pdfjsLib.getDocument(pdfOptions);
|
||||||
|
|
||||||
@@ -282,7 +316,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initPDFViewer(pdfDocument: PDFDocumentProxy) {
|
initPDFViewer(pdfDocument: PDFDocumentProxy) {
|
||||||
const viewer: any = this.getViewer();
|
const viewer: HTMLDivElement = this.getViewer();
|
||||||
const container = this.getDocumentContainer();
|
const container = this.getDocumentContainer();
|
||||||
|
|
||||||
if (viewer && container) {
|
if (viewer && container) {
|
||||||
@@ -433,8 +467,8 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
return document.getElementById(`${this.randomPdfId}-viewer-pdf-viewer`) as HTMLDivElement;
|
return document.getElementById(`${this.randomPdfId}-viewer-pdf-viewer`) as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getViewer(): HTMLElement {
|
private getViewer(): HTMLDivElement {
|
||||||
return document.getElementById(`${this.randomPdfId}-viewer-viewerPdf`);
|
return document.getElementById(`${this.randomPdfId}-viewer-viewerPdf`) as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPageFitInContainer(scale: number): number {
|
checkPageFitInContainer(scale: number): number {
|
||||||
@@ -519,9 +553,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
* @param ticks number of ticks to zoom
|
* @param ticks number of ticks to zoom
|
||||||
*/
|
*/
|
||||||
zoomIn(ticks?: number): void {
|
zoomIn(ticks?: number): void {
|
||||||
let newScale: any = this.pdfViewer.currentScaleValue;
|
let newScale: number = Number(this.pdfViewer.currentScaleValue);
|
||||||
do {
|
do {
|
||||||
newScale = (newScale * this.DEFAULT_SCALE_DELTA).toFixed(2);
|
newScale = Number((newScale * this.DEFAULT_SCALE_DELTA).toFixed(2));
|
||||||
newScale = Math.ceil(newScale * 10) / 10;
|
newScale = Math.ceil(newScale * 10) / 10;
|
||||||
newScale = Math.min(this.MAX_SCALE, newScale);
|
newScale = Math.min(this.MAX_SCALE, newScale);
|
||||||
} while (--ticks > 0 && newScale < this.MAX_SCALE);
|
} while (--ticks > 0 && newScale < this.MAX_SCALE);
|
||||||
@@ -535,9 +569,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
* @param ticks number of ticks to scale
|
* @param ticks number of ticks to scale
|
||||||
*/
|
*/
|
||||||
zoomOut(ticks?: number): void {
|
zoomOut(ticks?: number): void {
|
||||||
let newScale: any = this.pdfViewer.currentScaleValue;
|
let newScale: number = Number(this.pdfViewer.currentScaleValue);
|
||||||
do {
|
do {
|
||||||
newScale = (newScale / this.DEFAULT_SCALE_DELTA).toFixed(2);
|
newScale = Number((newScale / this.DEFAULT_SCALE_DELTA).toFixed(2));
|
||||||
newScale = Math.floor(newScale * 10) / 10;
|
newScale = Math.floor(newScale * 10) / 10;
|
||||||
newScale = Math.max(this.MIN_SCALE, newScale);
|
newScale = Math.max(this.MIN_SCALE, newScale);
|
||||||
} while (--ticks > 0 && newScale > this.MIN_SCALE);
|
} while (--ticks > 0 && newScale > this.MIN_SCALE);
|
||||||
@@ -589,9 +623,13 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Page Change Event
|
* Page Change Event
|
||||||
*
|
*
|
||||||
* @param event event
|
* @param event - page change event
|
||||||
|
* @param event.pageNumber - the new page number
|
||||||
|
* @param event.source - the source object
|
||||||
|
* @param event.source.container - the container element
|
||||||
|
* @param event.source.container.id - the container id
|
||||||
*/
|
*/
|
||||||
onPageChange(event: any) {
|
onPageChange(event: PageChangingEvent) {
|
||||||
if (event.source && event.source.container.id === `${this.randomPdfId}-viewer-pdf-viewer`) {
|
if (event.source && event.source.container.id === `${this.randomPdfId}-viewer-pdf-viewer`) {
|
||||||
this.page = event.pageNumber;
|
this.page = event.pageNumber;
|
||||||
this.displayPage = event.pageNumber;
|
this.displayPage = event.pageNumber;
|
||||||
@@ -682,11 +720,11 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createAnnotationPopup(annotation: any, text: string, annotationElement: HTMLElement): void {
|
private createAnnotationPopup(annotation: PdfAnnotationData, text: string, annotationElement: HTMLElement): void {
|
||||||
const popupElement = document.createElement('div');
|
const popupElement = document.createElement('div');
|
||||||
let headerElement: HTMLSpanElement;
|
let headerElement: HTMLSpanElement;
|
||||||
if (annotation.titleObj?.str) {
|
if (annotation.titleObj?.str) {
|
||||||
headerElement = this.createAnnotationPopupHeader(annotation);
|
headerElement = this.createAnnotationPopupHeader(annotation as PdfAnnotationWithTitle);
|
||||||
}
|
}
|
||||||
const contentElement = this.createAnnotationPopupContent(text);
|
const contentElement = this.createAnnotationPopupContent(text);
|
||||||
popupElement.classList.add('popup', 'adf-pdf-viewer-annotation-tooltip');
|
popupElement.classList.add('popup', 'adf-pdf-viewer-annotation-tooltip');
|
||||||
@@ -694,7 +732,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
annotationElement.appendChild(popupElement);
|
annotationElement.appendChild(popupElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createAnnotationPopupHeader(annotation: any): HTMLSpanElement {
|
private createAnnotationPopupHeader(annotation: PdfAnnotationWithTitle): HTMLSpanElement {
|
||||||
const headerElement = document.createElement('span');
|
const headerElement = document.createElement('span');
|
||||||
const titleElement = document.createElement('span');
|
const titleElement = document.createElement('span');
|
||||||
let dateElement: HTMLTimeElement;
|
let dateElement: HTMLTimeElement;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class TxtViewerComponent implements OnChanges {
|
|||||||
private readonly appConfigService = inject(AppConfigService);
|
private readonly appConfigService = inject(AppConfigService);
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
urlFile: any;
|
urlFile: string;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
blobFile: Blob;
|
blobFile: Blob;
|
||||||
@@ -86,7 +86,7 @@ export class TxtViewerComponent implements OnChanges {
|
|||||||
this.content = reader.result;
|
this.content = reader.result;
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.onerror = (error: any) => {
|
reader.onerror = (error: ProgressEvent<FileReader>) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class DoubleViewerComponent {
|
|||||||
viewer2: ViewerRenderComponent;
|
viewer2: ViewerRenderComponent;
|
||||||
|
|
||||||
@ViewChild('viewerExtension', { static: true })
|
@ViewChild('viewerExtension', { static: true })
|
||||||
viewerTemplateExtensions: TemplateRef<any>;
|
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||||
|
|
||||||
urlFileViewer1: string;
|
urlFileViewer1: string;
|
||||||
urlFileViewer2: string;
|
urlFileViewer2: string;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
/** The template for the pdf thumbnails. */
|
/** The template for the pdf thumbnails. */
|
||||||
@Input()
|
@Input()
|
||||||
thumbnailsTemplate: TemplateRef<any> = null;
|
thumbnailsTemplate: TemplateRef<unknown> = null;
|
||||||
|
|
||||||
/** MIME type of the file content (when not determined by the filename extension). */
|
/** MIME type of the file content (when not determined by the filename extension). */
|
||||||
@Input()
|
@Input()
|
||||||
@@ -124,7 +124,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
||||||
@Input()
|
@Input()
|
||||||
viewerTemplateExtensions: TemplateRef<any>;
|
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||||
|
|
||||||
/** Custom error message to be displayed in the viewer. */
|
/** Custom error message to be displayed in the viewer. */
|
||||||
@Input()
|
@Input()
|
||||||
@@ -149,7 +149,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
|||||||
@ViewChild(ImgViewerComponent)
|
@ViewChild(ImgViewerComponent)
|
||||||
imgViewer: ImgViewerComponent;
|
imgViewer: ImgViewerComponent;
|
||||||
|
|
||||||
extensionTemplates: { template: TemplateRef<any>; isVisible: boolean }[] = [];
|
extensionTemplates: { template: TemplateRef<unknown>; isVisible: boolean }[] = [];
|
||||||
extensionsSupportedByTemplates: string[] = [];
|
extensionsSupportedByTemplates: string[] = [];
|
||||||
extension: string;
|
extension: string;
|
||||||
internalFileName: string;
|
internalFileName: string;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, DebugElement, SimpleChanges } from '@angular/core';
|
import { Component, DebugElement, SimpleChange, SimpleChanges } from '@angular/core';
|
||||||
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
@@ -42,8 +42,8 @@ import { ThumbnailService } from '../../common/services/thumbnail.service';
|
|||||||
class DummyDialogComponent {}
|
class DummyDialogComponent {}
|
||||||
|
|
||||||
describe('ViewerComponent', () => {
|
describe('ViewerComponent', () => {
|
||||||
let component: ViewerComponent<any>;
|
let component: ViewerComponent<unknown>;
|
||||||
let fixture: ComponentFixture<ViewerComponent<any>>;
|
let fixture: ComponentFixture<ViewerComponent<unknown>>;
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
let viewUtilService: ViewUtilService;
|
let viewUtilService: ViewUtilService;
|
||||||
let appConfigService: AppConfigService;
|
let appConfigService: AppConfigService;
|
||||||
@@ -106,7 +106,9 @@ describe('ViewerComponent', () => {
|
|||||||
|
|
||||||
describe('Mime Type Test', () => {
|
describe('Mime Type Test', () => {
|
||||||
it('should mimeType change when blobFile changes', () => {
|
it('should mimeType change when blobFile changes', () => {
|
||||||
const mockSimpleChanges: any = { blobFile: { currentValue: { type: 'image/png' } } };
|
const mockSimpleChanges: SimpleChanges = {
|
||||||
|
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
|
||||||
|
};
|
||||||
|
|
||||||
component.ngOnChanges(mockSimpleChanges);
|
component.ngOnChanges(mockSimpleChanges);
|
||||||
|
|
||||||
@@ -115,7 +117,10 @@ describe('ViewerComponent', () => {
|
|||||||
|
|
||||||
it('should set mimeTypeIconUrl when mimeType changes and no nodeMimeType is provided', () => {
|
it('should set mimeTypeIconUrl when mimeType changes and no nodeMimeType is provided', () => {
|
||||||
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('image/png');
|
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('image/png');
|
||||||
const mockSimpleChanges: any = { mimeType: { currentValue: 'image/png' }, nodeMimeType: undefined };
|
const mockSimpleChanges: SimpleChanges = {
|
||||||
|
mimeType: new SimpleChange(null, 'image/png', true),
|
||||||
|
nodeMimeType: undefined
|
||||||
|
};
|
||||||
|
|
||||||
component.ngOnChanges(mockSimpleChanges);
|
component.ngOnChanges(mockSimpleChanges);
|
||||||
|
|
||||||
@@ -125,7 +130,10 @@ describe('ViewerComponent', () => {
|
|||||||
|
|
||||||
it('should set mimeTypeIconUrl when nodeMimeType changes', () => {
|
it('should set mimeTypeIconUrl when nodeMimeType changes', () => {
|
||||||
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('application/pdf');
|
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('application/pdf');
|
||||||
const mockSimpleChanges: any = { mimeType: { currentValue: 'image/png' }, nodeMimeType: { currentValue: 'application/pdf' } };
|
const mockSimpleChanges: SimpleChanges = {
|
||||||
|
mimeType: new SimpleChange(null, 'image/png', true),
|
||||||
|
nodeMimeType: new SimpleChange(null, 'application/pdf', true)
|
||||||
|
};
|
||||||
|
|
||||||
component.ngOnChanges(mockSimpleChanges);
|
component.ngOnChanges(mockSimpleChanges);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -523,7 +531,9 @@ describe('ViewerComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should file name be present if is overlay mode ', async () => {
|
it('should file name be present if is overlay mode ', async () => {
|
||||||
const mockSimpleChanges: any = { blobFile: { currentValue: { type: 'image/png' } } };
|
const mockSimpleChanges: SimpleChanges = {
|
||||||
|
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
|
||||||
|
};
|
||||||
component.ngOnChanges(mockSimpleChanges);
|
component.ngOnChanges(mockSimpleChanges);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
@@ -705,7 +715,7 @@ describe('ViewerComponent', () => {
|
|||||||
downloadPromptReminderDelay: 2
|
downloadPromptReminderDelay: 2
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
dialogOpenSpy = spyOn(dialog, 'open').and.returnValue({ afterClosed: () => of(null) } as any);
|
dialogOpenSpy = spyOn(dialog, 'open').and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||||
component.urlFile = undefined;
|
component.urlFile = undefined;
|
||||||
component.clearDownloadPromptTimeouts();
|
component.clearDownloadPromptTimeouts();
|
||||||
});
|
});
|
||||||
@@ -716,11 +726,11 @@ describe('ViewerComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should configure reminder timeout to display non responsive dialog after initial dialog', fakeAsync(() => {
|
it('should configure reminder timeout to display non responsive dialog after initial dialog', fakeAsync(() => {
|
||||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as any);
|
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as ReturnType<MatDialog['open']>);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick(3000);
|
tick(3000);
|
||||||
expect(component.downloadPromptReminderTimer).toBeDefined();
|
expect(component.downloadPromptReminderTimer).toBeDefined();
|
||||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as any);
|
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||||
flush();
|
flush();
|
||||||
discardPeriodicTasks();
|
discardPeriodicTasks();
|
||||||
}));
|
}));
|
||||||
@@ -741,12 +751,12 @@ describe('ViewerComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should show reminder non responsive dialog after initial dialog', fakeAsync(() => {
|
it('should show reminder non responsive dialog after initial dialog', fakeAsync(() => {
|
||||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as any);
|
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as ReturnType<MatDialog['open']>);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick(3000);
|
tick(3000);
|
||||||
expect(dialogOpenSpy).toHaveBeenCalled();
|
expect(dialogOpenSpy).toHaveBeenCalled();
|
||||||
|
|
||||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as any);
|
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||||
tick(2000);
|
tick(2000);
|
||||||
expect(dialogOpenSpy).toHaveBeenCalledTimes(2);
|
expect(dialogOpenSpy).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
@@ -755,7 +765,7 @@ describe('ViewerComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should emit downloadFileEvent when DownloadPromptDialog return DownloadPromptActions.DOWNLOAD on close', fakeAsync(() => {
|
it('should emit downloadFileEvent when DownloadPromptDialog return DownloadPromptActions.DOWNLOAD on close', fakeAsync(() => {
|
||||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.DOWNLOAD) } as any);
|
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.DOWNLOAD) } as ReturnType<MatDialog['open']>);
|
||||||
spyOn(component.downloadFile, 'emit');
|
spyOn(component.downloadFile, 'emit');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick(3000);
|
tick(3000);
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
|||||||
mnuMoreActions: ViewerMoreActionsComponent;
|
mnuMoreActions: ViewerMoreActionsComponent;
|
||||||
|
|
||||||
@ContentChild('viewerExtensions', { static: false })
|
@ContentChild('viewerExtensions', { static: false })
|
||||||
viewerTemplateExtensions: TemplateRef<any>;
|
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||||
|
|
||||||
get CloseButtonPosition() {
|
get CloseButtonPosition() {
|
||||||
return CloseButtonPosition;
|
return CloseButtonPosition;
|
||||||
@@ -190,11 +190,11 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
|||||||
|
|
||||||
/** The template for the right sidebar. The template context contains the loaded node data. */
|
/** The template for the right sidebar. The template context contains the loaded node data. */
|
||||||
@Input()
|
@Input()
|
||||||
sidebarRightTemplate: TemplateRef<any> = null;
|
sidebarRightTemplate: TemplateRef<unknown> = null;
|
||||||
|
|
||||||
/** The template for the left sidebar. The template context contains the loaded node data. */
|
/** The template for the left sidebar. The template context contains the loaded node data. */
|
||||||
@Input()
|
@Input()
|
||||||
sidebarLeftTemplate: TemplateRef<any> = null;
|
sidebarLeftTemplate: TemplateRef<unknown> = null;
|
||||||
|
|
||||||
/** Enable when where is possible the editing functionalities */
|
/** Enable when where is possible the editing functionalities */
|
||||||
@Input()
|
@Input()
|
||||||
@@ -243,7 +243,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
|||||||
|
|
||||||
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
||||||
@Input()
|
@Input()
|
||||||
viewerExtensions: TemplateRef<any>;
|
viewerExtensions: TemplateRef<unknown>;
|
||||||
|
|
||||||
/** Identifier of a node that is opened by the viewer. */
|
/** Identifier of a node that is opened by the viewer. */
|
||||||
@Input()
|
@Input()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { SpyLocation } from '@angular/common/testing';
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
import { ChangeDetectorRef, ElementRef, TemplateRef } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ViewerRenderComponent } from '../components/viewer-render/viewer-render.component';
|
import { ViewerRenderComponent } from '../components/viewer-render/viewer-render.component';
|
||||||
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
||||||
@@ -50,7 +50,7 @@ describe('ExtensionViewerDirective', () => {
|
|||||||
});
|
});
|
||||||
extensionViewerDirective = TestBed.inject(ViewerExtensionDirective);
|
extensionViewerDirective = TestBed.inject(ViewerExtensionDirective);
|
||||||
viewerRenderer = TestBed.inject(ViewerRenderComponent);
|
viewerRenderer = TestBed.inject(ViewerRenderComponent);
|
||||||
extensionViewerDirective.templateModel = { template: '', isVisible: false };
|
extensionViewerDirective.templateModel = { template: {} as TemplateRef<unknown>, isVisible: false };
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is defined', () => {
|
it('is defined', () => {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class ViewerExtensionDirective implements AfterContentInit {
|
|||||||
private readonly viewerComponent = inject(ViewerRenderComponent);
|
private readonly viewerComponent = inject(ViewerRenderComponent);
|
||||||
|
|
||||||
@ContentChild(TemplateRef)
|
@ContentChild(TemplateRef)
|
||||||
template: any;
|
template: TemplateRef<unknown>;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
urlFileContent: string;
|
urlFileContent: string;
|
||||||
@@ -37,7 +37,7 @@ export class ViewerExtensionDirective implements AfterContentInit {
|
|||||||
@Input()
|
@Input()
|
||||||
supportedExtensions: string[];
|
supportedExtensions: string[];
|
||||||
|
|
||||||
templateModel: any;
|
templateModel: { template: TemplateRef<unknown>; isVisible: boolean };
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import type { PDFViewer } from 'pdfjs-dist/types/web/pdf_viewer';
|
||||||
|
import type { PDFThumbnailViewer } from 'pdfjs-dist/types/web/pdf_thumbnail_viewer';
|
||||||
|
import type { PDFPageView } from 'pdfjs-dist/types/web/pdf_page_view';
|
||||||
|
|
||||||
|
interface VisiblePage {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VisiblePages {
|
||||||
|
first?: VisiblePage;
|
||||||
|
last?: VisiblePage;
|
||||||
|
views?: Array<{ view: PDFPageView }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type helper for accessing the resume method on PDFPageView.
|
||||||
|
* PDFPageView implements IRenderableView which includes a resume method,
|
||||||
|
* but the TypeScript definitions don't properly expose it.
|
||||||
|
*/
|
||||||
|
// cspell:ignore Renderable
|
||||||
|
interface ResumableView {
|
||||||
|
resume?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -33,13 +56,13 @@ export class RenderingQueueServices {
|
|||||||
|
|
||||||
CLEANUP_TIMEOUT: number = 30_000;
|
CLEANUP_TIMEOUT: number = 30_000;
|
||||||
|
|
||||||
pdfViewer: any = null;
|
pdfViewer: PDFViewer | null = null;
|
||||||
pdfThumbnailViewer: any = null;
|
pdfThumbnailViewer: PDFThumbnailViewer | null = null;
|
||||||
onIdle: any = null;
|
onIdle: (() => void) | null = null;
|
||||||
|
|
||||||
highestPriorityPage: string | null = null;
|
highestPriorityPage: string | null = null;
|
||||||
idleTimeout: any = null;
|
idleTimeout: number | null = null;
|
||||||
printing: any = false;
|
printing = false;
|
||||||
isThumbnailViewEnabled = false;
|
isThumbnailViewEnabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +70,7 @@ export class RenderingQueueServices {
|
|||||||
*
|
*
|
||||||
* @param pdfViewer viewer instance
|
* @param pdfViewer viewer instance
|
||||||
*/
|
*/
|
||||||
setViewer(pdfViewer): void {
|
setViewer(pdfViewer: PDFViewer): void {
|
||||||
this.pdfViewer = pdfViewer;
|
this.pdfViewer = pdfViewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +79,7 @@ export class RenderingQueueServices {
|
|||||||
*
|
*
|
||||||
* @param pdfThumbnailViewer viewer instance
|
* @param pdfThumbnailViewer viewer instance
|
||||||
*/
|
*/
|
||||||
setThumbnailViewer(pdfThumbnailViewer): void {
|
setThumbnailViewer(pdfThumbnailViewer: PDFThumbnailViewer): void {
|
||||||
this.pdfThumbnailViewer = pdfThumbnailViewer;
|
this.pdfThumbnailViewer = pdfThumbnailViewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,18 +89,18 @@ export class RenderingQueueServices {
|
|||||||
* @param view view to render
|
* @param view view to render
|
||||||
* @returns `true` if the view has higher priority, otherwise `false`
|
* @returns `true` if the view has higher priority, otherwise `false`
|
||||||
*/
|
*/
|
||||||
isHighestPriority(view: any): boolean {
|
isHighestPriority(view: PDFPageView): boolean {
|
||||||
return this.highestPriorityPage === view.renderingId;
|
return this.highestPriorityPage === view.renderingId;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHighestPriority(currentlyVisiblePages) {
|
renderHighestPriority(currentlyVisiblePages?: unknown): void {
|
||||||
if (this.idleTimeout) {
|
if (this.idleTimeout) {
|
||||||
clearTimeout(this.idleTimeout);
|
clearTimeout(this.idleTimeout);
|
||||||
this.idleTimeout = null;
|
this.idleTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pages have a higher priority than thumbnails, so check them first.
|
// Pages have a higher priority than thumbnails, so check them first.
|
||||||
if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
|
if (this.pdfViewer?.forceRendering(currentlyVisiblePages)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// No pages needed rendering so check thumbnails.
|
// No pages needed rendering so check thumbnails.
|
||||||
@@ -91,11 +114,23 @@ export class RenderingQueueServices {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.onIdle) {
|
if (this.onIdle) {
|
||||||
this.idleTimeout = setTimeout(this.onIdle.bind(this), this.CLEANUP_TIMEOUT);
|
// Type assertion needed: setTimeout returns NodeJS.Timeout in Node types,
|
||||||
|
// but returns number at runtime in browser (where this code executes).
|
||||||
|
// PDFRenderingQueue interface requires idleTimeout to be number.
|
||||||
|
this.idleTimeout = setTimeout(this.onIdle.bind(this), this.CLEANUP_TIMEOUT) as unknown as number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getHighestPriority(visible, views, scrolledDown) {
|
/**
|
||||||
|
* Gets the highest priority page to render from the visible pages
|
||||||
|
* This method is part of the PDFRenderingQueue interface compatibility
|
||||||
|
*
|
||||||
|
* @param visible visible pages information
|
||||||
|
* @param views array of page views
|
||||||
|
* @param scrolledDown whether the user scrolled down
|
||||||
|
* @returns the highest priority page view to render, null if all done, or false if no visible pages
|
||||||
|
*/
|
||||||
|
getHighestPriority(visible: VisiblePages, views: PDFPageView[], scrolledDown: boolean): PDFPageView | null | false {
|
||||||
// The state has changed figure out which page has the highest priority to
|
// The state has changed figure out which page has the highest priority to
|
||||||
// render next (if any).
|
// render next (if any).
|
||||||
// Priority:
|
// Priority:
|
||||||
@@ -104,6 +139,10 @@ export class RenderingQueueServices {
|
|||||||
// 2 if last scrolled up page before the visible pages
|
// 2 if last scrolled up page before the visible pages
|
||||||
const visibleViews = visible.views;
|
const visibleViews = visible.views;
|
||||||
|
|
||||||
|
if (!visibleViews) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const numberVisible = visibleViews.length;
|
const numberVisible = visibleViews.length;
|
||||||
if (numberVisible === 0) {
|
if (numberVisible === 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -116,13 +155,13 @@ export class RenderingQueueServices {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All the visible views have rendered, try to render next/previous pages.
|
// All the visible views have rendered, try to render next/previous pages.
|
||||||
if (scrolledDown) {
|
if (scrolledDown && visible.last) {
|
||||||
const nextPageIndex = visible.last.id;
|
const nextPageIndex = visible.last.id;
|
||||||
// ID's start at 1 so no need to add 1.
|
// ID's start at 1 so no need to add 1.
|
||||||
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
|
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
|
||||||
return views[nextPageIndex];
|
return views[nextPageIndex];
|
||||||
}
|
}
|
||||||
} else {
|
} else if (visible.first) {
|
||||||
const previousPageIndex = visible.first.id - 2;
|
const previousPageIndex = visible.first.id - 2;
|
||||||
if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) {
|
if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) {
|
||||||
return views[previousPageIndex];
|
return views[previousPageIndex];
|
||||||
@@ -142,7 +181,7 @@ export class RenderingQueueServices {
|
|||||||
* @param view the View instance to check
|
* @param view the View instance to check
|
||||||
* @returns `true` if rendering is finished, otherwise `false`
|
* @returns `true` if rendering is finished, otherwise `false`
|
||||||
*/
|
*/
|
||||||
isViewFinished(view): boolean {
|
isViewFinished(view: PDFPageView): boolean {
|
||||||
return view.renderingState === this.renderingStates.FINISHED;
|
return view.renderingState === this.renderingStates.FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +193,7 @@ export class RenderingQueueServices {
|
|||||||
* @param view View instance to render
|
* @param view View instance to render
|
||||||
* @returns the rendered state of the view
|
* @returns the rendered state of the view
|
||||||
*/
|
*/
|
||||||
renderView(view: any): boolean {
|
renderView(view: PDFPageView): boolean {
|
||||||
const state = view.renderingState;
|
const state = view.renderingState;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case this.renderingStates.FINISHED: {
|
case this.renderingStates.FINISHED: {
|
||||||
@@ -162,7 +201,10 @@ export class RenderingQueueServices {
|
|||||||
}
|
}
|
||||||
case this.renderingStates.PAUSED: {
|
case this.renderingStates.PAUSED: {
|
||||||
this.highestPriorityPage = view.renderingId;
|
this.highestPriorityPage = view.renderingId;
|
||||||
view.resume();
|
const resumableView = view as unknown as ResumableView;
|
||||||
|
if (resumableView.resume) {
|
||||||
|
resumableView.resume();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case this.renderingStates.RUNNING: {
|
case this.renderingStates.RUNNING: {
|
||||||
@@ -171,10 +213,9 @@ export class RenderingQueueServices {
|
|||||||
}
|
}
|
||||||
case this.renderingStates.INITIAL: {
|
case this.renderingStates.INITIAL: {
|
||||||
this.highestPriorityPage = view.renderingId;
|
this.highestPriorityPage = view.renderingId;
|
||||||
// eslint-disable-next-line space-before-function-paren
|
const continueRendering = () => {
|
||||||
const continueRendering = function () {
|
|
||||||
this.renderHighestPriority();
|
this.renderHighestPriority();
|
||||||
}.bind(this);
|
};
|
||||||
view.draw().then(continueRendering, continueRendering);
|
view.draw().then(continueRendering, continueRendering);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ TestBed.initTestEnvironment(GlobalTestingModule, platformBrowserDynamicTesting()
|
|||||||
teardown: { destroyAfterEach: true }
|
teardown: { destroyAfterEach: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
(window as any).pdfjsLib = pdfjsLibraryMock;
|
(window as unknown as { pdfjsLib: typeof pdfjsLibraryMock }).pdfjsLib = pdfjsLibraryMock;
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ const { constants } = require('karma');
|
|||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
|
files: [
|
||||||
|
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||||
|
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false }
|
||||||
|
],
|
||||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ module.exports = function (config) {
|
|||||||
basePath: '../../',
|
basePath: '../../',
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||||
{ pattern: 'node_modules/chart.js/dist/Chart.js', included: true, watched: false },
|
{ pattern: 'node_modules/chart.js/dist/Chart.js', included: true, watched: false },
|
||||||
{ pattern: 'node_modules/raphael/raphael.min.js', included: true, watched: false },
|
{ pattern: 'node_modules/raphael/raphael.min.js', included: true, watched: false },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,6 +17,17 @@
|
|||||||
|
|
||||||
import { BaseApi } from './base.api';
|
import { BaseApi } from './base.api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product version model for Process Services.
|
||||||
|
*/
|
||||||
|
export interface BpmProductVersionModel {
|
||||||
|
edition: string;
|
||||||
|
majorVersion: string;
|
||||||
|
revisionVersion: string;
|
||||||
|
minorVersion: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* About service.
|
* About service.
|
||||||
*/
|
*/
|
||||||
@@ -26,7 +37,7 @@ export class AboutApi extends BaseApi {
|
|||||||
* Provides information about the running Alfresco Process Services Suite. The response payload object has the properties type, majorVersion, minorVersion, revisionVersion and edition.
|
* Provides information about the running Alfresco Process Services Suite. The response payload object has the properties type, majorVersion, minorVersion, revisionVersion and edition.
|
||||||
* @return Promise<{ [key: string]: string; }>
|
* @return Promise<{ [key: string]: string; }>
|
||||||
*/
|
*/
|
||||||
getAppVersion(): Promise<{ [key: string]: string }> {
|
getAppVersion(): Promise<BpmProductVersionModel> {
|
||||||
return this.get({
|
return this.get({
|
||||||
path: '/api/enterprise/app-version'
|
path: '/api/enterprise/app-version'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ module.exports = function (config) {
|
|||||||
config.set({
|
config.set({
|
||||||
basePath: '../../',
|
basePath: '../../',
|
||||||
files: [
|
files: [
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||||
{
|
{
|
||||||
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||||
included: true,
|
included: true,
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ module.exports = function (config) {
|
|||||||
config.set({
|
config.set({
|
||||||
basePath: '../../',
|
basePath: '../../',
|
||||||
files: [
|
files: [
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||||
|
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||||
{
|
{
|
||||||
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||||
included: true,
|
included: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user