@hmcts/media-viewer
Version:
9,311 lines • 622 kB
JavaScript
import * as i5 from 'rpx-xui-translation';
import { RpxTranslationModule } from 'rpx-xui-translation';
import * as i0 from '@angular/core';
import { Injectable, HostListener, Directive, ViewChild, Component, Input, ViewEncapsulation, Pipe, EventEmitter, Output, ViewChildren, ElementRef, NgModule } from '@angular/core';
import * as i4 from '@angular/common';
import { DatePipe, CommonModule } from '@angular/common';
import * as i2 from '@angular/forms';
import { UntypedFormControl, FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1$1 from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { BehaviorSubject, Subject, filter, debounceTime, of, combineLatest, observeOn, asyncScheduler, take as take$1, delay } from 'rxjs';
import { take, distinctUntilChanged, filter as filter$1, auditTime, tap, debounceTime as debounceTime$1, throttleTime, map, catchError, switchMap, concatMap, exhaustMap, withLatestFrom } from 'rxjs/operators';
import * as i1 from '@ngrx/store';
import { createFeatureSelector, createSelector, select, StoreModule } from '@ngrx/store';
import { v4 } from 'uuid';
import moment from 'moment-timezone';
import 'pdfjs-dist/build/pdf.mjs';
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
import { PDFLinkService } from 'pdfjs-dist/web/pdf_viewer.mjs';
import * as pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/build/pdf.worker';
import { some } from 'lodash';
import * as i4$1 from 'ngx-chips';
import { TagInputModule } from 'ngx-chips';
import * as i3 from '@angular/router';
import { RouterModule } from '@angular/router';
import * as i4$2 from 'mutable-div';
import { MutableDivModule } from 'mutable-div';
import { SelectionModel, ArrayDataSource } from '@angular/cdk/collections';
import * as i3$1 from '@angular/cdk/tree';
import { NestedTreeControl, CdkTreeModule } from '@angular/cdk/tree';
import * as i4$3 from '@angular/cdk/drag-drop';
import { DragDropModule } from '@angular/cdk/drag-drop';
import * as i5$1 from '@angular/cdk/scrolling';
import * as i8 from '@angular/cdk/overlay';
import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
import * as i5$2 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i8$1 from '@swimlane/ngx-datatable';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import * as i1$2 from '@ngrx/effects';
import { createEffect, ofType, EffectsModule } from '@ngrx/effects';
var ResponseType;
(function (ResponseType) {
ResponseType["SUCCESS"] = "SUCCESS";
ResponseType["FAILURE"] = "FAILURE";
ResponseType["UNSUPPORTED"] = "UNSUPPORTED";
})(ResponseType || (ResponseType = {}));
class ViewerException {
constructor(exceptionType, detail) {
this.exceptionType = exceptionType;
this.detail = detail;
}
}
const SET_DOCUMENT_ID = '[Document] Set Document Id';
const POSITION_UPDATED = '[Document] Position Updated';
const ADD_PAGES = '[Document] Add Pages';
const CONVERT = '[Document] Convert';
const CONVERT_SUCCESS = '[Document] Convert Success';
const CONVERT_FAIL = '[Document] Convert Fail';
const CLEAR_CONVERT_DOC_URL = '[Document] Clear Convert Doc Url';
const LOAD_ROTATION = '[Document] Load Rotation';
const LOAD_ROTATION_SUCCESS = '[Document] Load Rotation Success';
const LOAD_ROTATION_FAIL = '[Document] Load Rotation Fail';
const SAVE_ROTATION = '[Document] Save Rotation';
const SAVE_ROTATION_SUCCESS = '[Document] Save Rotation Success';
const SAVE_ROTATION_FAIL = '[Document] Save Rotation Fail';
class SetDocumentId {
constructor(payload) {
this.payload = payload;
this.type = SET_DOCUMENT_ID;
}
}
class AddPages {
constructor(payload) {
this.payload = payload;
this.type = ADD_PAGES;
}
}
class PdfPositionUpdate {
constructor(payload) {
this.payload = payload;
this.type = POSITION_UPDATED;
}
}
class Convert {
constructor(payload) {
this.payload = payload;
this.type = CONVERT;
}
}
class ConvertSuccess {
constructor(payload) {
this.payload = payload;
this.type = CONVERT_SUCCESS;
}
}
class ConvertFailure {
constructor(payload) {
this.payload = payload;
this.type = CONVERT_FAIL;
}
}
class ClearConvertDocUrl {
constructor() {
this.type = CLEAR_CONVERT_DOC_URL;
}
}
class LoadRotation {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ROTATION;
}
}
class LoadRotationSuccess {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ROTATION_SUCCESS;
}
}
class LoadRotationFailure {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ROTATION_FAIL;
}
}
class SaveRotation {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ROTATION;
}
}
class SaveRotationSuccess {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ROTATION_SUCCESS;
}
}
class SaveRotationFailure {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ROTATION_FAIL;
}
}
const UPDATE_TAGS = '[Tags] Update Tags';
const ADD_FILTER_TAGS = '[Tags] Add Filter Tags';
const CLEAR_FILTER_TAGS = '[Tags] Clear Filter Tags';
class UpdateTags {
constructor(payload) {
this.payload = payload;
this.type = UPDATE_TAGS;
}
}
class AddFilterTags {
constructor(payload) {
this.payload = payload;
this.type = ADD_FILTER_TAGS;
}
}
class ClearFilterTags {
constructor() {
this.type = CLEAR_FILTER_TAGS;
}
}
const SET_CASE_ID = '[Icp] Set Case Id';
const LOAD_ICP_SESSION = '[Icp] Load Session';
const LOAD_ICP_SESSION_FAIL = '[Icp] Load Session Failure';
const JOIN_ICP_SOCKET_SESSION = '[Icp] Join Socket Session';
const ICP_SOCKET_SESSION_JOINED = '[Icp] Socket Session Joined';
const LEAVE_ICP_SOCKET_SESSION = '[Icp] Leave Socket Session';
const ICP_PRESENTER_UPDATED = '[Icp] Presenter Updated';
const ICP_PARTICIPANT_LIST_UPDATED = '[Icp] Participant List Updated';
class SetCaseId {
constructor(payload) {
this.payload = payload;
this.type = SET_CASE_ID;
}
}
class LoadIcpSession {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ICP_SESSION;
}
}
class LoadIcpSessionFailure {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ICP_SESSION_FAIL;
}
}
class JoinIcpSocketSession {
constructor(payload) {
this.payload = payload;
this.type = JOIN_ICP_SOCKET_SESSION;
}
}
class IcpSocketSessionJoined {
constructor(payload) {
this.payload = payload;
this.type = ICP_SOCKET_SESSION_JOINED;
}
}
class LeaveIcpSocketSession {
constructor() {
this.type = LEAVE_ICP_SOCKET_SESSION;
}
}
class IcpPresenterUpdated {
constructor(payload) {
this.payload = payload;
this.type = ICP_PRESENTER_UPDATED;
}
}
class IcpParticipantListUpdated {
constructor(payload) {
this.payload = payload;
this.type = ICP_PARTICIPANT_LIST_UPDATED;
}
}
const initialDocumentState = {
convertedDocument: undefined,
documentId: undefined,
pdfPosition: undefined,
pages: {},
hasDifferentPageSize: false,
rotation: undefined,
rotationLoaded: false,
loading: false,
loaded: false
};
function docReducer(state = initialDocumentState, action) {
switch (action.type) {
case CONVERT_SUCCESS: {
const convertedDocument = {
url: action.payload,
error: undefined
};
return {
...state,
convertedDocument
};
}
case CONVERT_FAIL: {
const convertedDocument = {
url: undefined,
error: action.payload
};
return {
...state,
convertedDocument
};
}
case CLEAR_CONVERT_DOC_URL: {
const convertedDocument = undefined;
return {
...state,
convertedDocument
};
}
case LOAD_ROTATION: {
return {
...state,
rotationLoaded: false
};
}
case LOAD_ROTATION_SUCCESS: {
const metadata = action.payload;
const rotation = metadata ? metadata.rotationAngle : 0;
return {
...state,
rotation,
rotationLoaded: true
};
}
case LOAD_ROTATION_FAIL: {
return {
...state,
rotation: 0,
rotationLoaded: true
};
}
case SAVE_ROTATION_SUCCESS: {
const metadata = action.payload;
const rotation = metadata.rotationAngle;
return {
...state,
rotation
};
}
case SET_DOCUMENT_ID: {
return {
...state,
documentId: action.payload
};
}
case ADD_PAGES: {
const payload = action.payload;
let pages = {};
let pageHeight;
let pageWidth;
let hasDifferentPageSize = state.hasDifferentPageSize;
const pageNumberInput = document.getElementById('pageNumber');
const pageIndex = pageNumberInput?.value ? parseInt(pageNumberInput.value, 10) - 1 : 0;
const loadedPage = payload[pageIndex]?.div['attributes']?.style?.value ?? '';
payload.forEach(page => {
const sizingValue = page.div?.['attributes']?.style?.value ?? '';
const widthMatch = sizingValue.match(/width:\s*round\(down,\s*var\(--scale-factor\)\s*\*\s*([\d.]+)([a-z%]+)?,.*var\(--scale-round-x, ([\d.]+)([a-z%]+)?\)\)/);
const heightMatch = sizingValue.match(/height:\s*round\(down,\s*var\(--scale-factor\)\s*\*\s*([\d.]+)([a-z%]+)?,.*var\(--scale-round-y, ([\d.]+)([a-z%]+)?\)\)/);
const scaleRoundXMatch = loadedPage.match(/--scale-round-x:\s*([\d.]+)([a-z%]+)?/);
const scaleRoundYMatch = loadedPage.match(/--scale-round-y:\s*([\d.]+)([a-z%]+)?/);
// You can now use widthUnit, heightUnit, scaleRoundXUnit, scaleRoundYUnit as needed
const scaleFactor = page.viewportScale ?? 1;
const scaleRoundX = scaleRoundXMatch ? parseFloat(scaleRoundXMatch[1]) : 1;
const scaleRoundY = scaleRoundYMatch ? parseFloat(scaleRoundYMatch[1]) : 1;
const baseWidth = widthMatch ? parseFloat(widthMatch[1]) : undefined;
const baseHeight = heightMatch ? parseFloat(heightMatch[1]) : undefined;
function roundDown(value, step) {
return Math.floor(value / step) * step;
}
const computedWidth = baseWidth !== undefined
? roundDown(scaleFactor * baseWidth, scaleRoundX)
: page.div['clientWidth'];
const computedHeight = baseHeight !== undefined
? roundDown(scaleFactor * baseHeight, scaleRoundY)
: page.div['clientHeight'];
if (!hasDifferentPageSize && pageHeight && pageWidth &&
(pageHeight !== computedHeight || pageWidth !== computedWidth)) {
hasDifferentPageSize = true;
}
else {
pageHeight = computedHeight;
pageWidth = computedWidth;
}
const styles = {
left: page.div['offsetLeft'],
height: computedHeight,
width: computedWidth
};
const scaleRotation = {
scale: page.scale,
rotation: page.rotation
};
const p = {
styles,
scaleRotation,
viewportScale: page.viewportScale
};
pages = {
...pages,
[page.id]: p
};
});
return {
...state,
pages,
hasDifferentPageSize
};
}
case POSITION_UPDATED: {
const pdfPosition = action.payload;
return {
...state,
pdfPosition
};
}
}
return state;
}
const getDocPages = (state) => state.pages;
const getDocId = (state) => state.documentId;
const getPdfPos = (state) => state.pdfPosition;
const getHasDifferentPageSizes = (state) => state.hasDifferentPageSize;
const getRotation$1 = (state) => state.rotation;
const rotationLoaded$1 = (state) => state.rotationLoaded;
const getConvertedDocument$1 = (state) => state.convertedDocument;
const LOAD_ANNOTATION_SET = '[Annotations] Load Annotation Set';
const LOAD_ANNOTATION_SET_SUCCESS = '[Annotations] Load Annotation Set Success';
const LOAD_ANNOTATION_SET_FAIL = '[Annotations] Load Annotation Set Fail';
const SAVE_ANNOTATION_SET = '[Annotations] Save Annotation Set';
const SAVE_ANNOTATION_SET_SUCCESS = '[Annotations] Save Annotation Set Success';
const SAVE_ANNOTATION_SET_FAIL = '[Annotations] Save Annotation Set Fail';
const SAVE_ANNOTATION = '[Annotations] Save Annotation';
const SAVE_ANNOTATION_SUCCESS = '[Annotations] Save Annotation Success';
const SAVE_ANNOTATION_FAIL = '[Annotations] Save Annotation Fail';
const ADD_OR_EDIT_COMMENT = '[Annotations] Add or Edit Comment';
const DELETE_ANNOTATION = '[Annotations] Delete Annotation';
const DELETE_ANNOTATION_SUCCESS = '[Annotations] Delete Annotation Success';
const DELETE_ANNOTATION_FAIL = '[Annotations] Delete Annotation Fail';
const SELECT_ANNOTATION = '[Annotations] Select Annotation';
const SEARCH_COMMENT = '[Comments] Search Comments';
const APPLY_COMMENT_SUMMARY_FILTER = '[Comments] Apply Comment Summary Filter';
const CLEAR_COMMENT_SUMMARY_FILTER = '[Comments] Clear Comment Summary Filter';
class LoadAnnotationSet {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ANNOTATION_SET;
}
}
class SaveAnnotationSet {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ANNOTATION_SET;
}
}
class LoadAnnotationSetSucess {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ANNOTATION_SET_SUCCESS;
}
}
class SaveAnnotationSetSuccess {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ANNOTATION_SET_SUCCESS;
}
}
class LoadAnnotationSetFail {
constructor(payload) {
this.payload = payload;
this.type = LOAD_ANNOTATION_SET_FAIL;
}
}
class SaveAnnotationSetFail {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ANNOTATION_SET_FAIL;
}
}
class SaveAnnotation {
constructor(payload, autoSelect) {
this.payload = payload;
this.autoSelect = autoSelect;
this.type = SAVE_ANNOTATION;
}
}
class SaveAnnotationSuccess {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ANNOTATION_SUCCESS;
}
}
class SaveAnnotationFail {
constructor(payload) {
this.payload = payload;
this.type = SAVE_ANNOTATION_FAIL;
}
}
class AddOrEditComment {
constructor(payload) {
this.payload = payload;
this.type = ADD_OR_EDIT_COMMENT;
}
}
class DeleteAnnotation {
constructor(payload) {
this.payload = payload;
this.type = DELETE_ANNOTATION;
}
}
class DeleteAnnotationSuccess {
constructor(payload) {
this.payload = payload;
this.type = DELETE_ANNOTATION_SUCCESS;
}
}
class DeleteAnnotationFail {
constructor(payload) {
this.payload = payload;
this.type = DELETE_ANNOTATION_FAIL;
}
}
class SelectedAnnotation {
constructor(payload) {
this.payload = payload;
this.type = SELECT_ANNOTATION;
}
}
class SearchComment {
constructor(payload) {
this.payload = payload;
this.type = SEARCH_COMMENT;
}
}
class ApplyCommentSymmaryFilter {
constructor(payload) {
this.payload = payload;
this.type = APPLY_COMMENT_SUMMARY_FILTER;
}
}
class ClearCommentSummaryFilters {
constructor() {
this.type = CLEAR_COMMENT_SUMMARY_FILTER;
}
}
/*
@dynamic
marking class as dynamic to stop compiler throwing error for lambda in static function
see https://github.com/angular/angular/issues/19698#issuecomment-338340211
*/
class StoreUtils {
static groupByKeyEntities(annotations, key) {
return annotations.reduce((h, obj) => Object.assign(h, { [obj[key]]: (h[obj[key]] || []).concat(obj) }), {});
}
static generateCommentsEntities(annotations) {
return annotations.reduce((commentEntities, annotation) => {
if (annotation.comments.length) {
const comment = {
...annotation.comments[0] || '',
tags: [...annotation.tags || []]
};
return {
...commentEntities,
[annotation.id]: comment
};
}
return {
...commentEntities
};
}, {});
}
static genTagNameEntities(annotations) {
const filterAnnoWithoutCommentsTags = annotations.filter(a => (a.comments.length && a.tags.length));
const allTags = filterAnnoWithoutCommentsTags.map(anno => this.groupByKeyEntities(anno.tags, 'name'));
const groupedByName = allTags.reduce((tagEntitiy, tagItem) => {
return {
...tagEntitiy,
...tagItem
};
}, {});
return this.genNameEnt(annotations, groupedByName);
}
static genNameEnt(annos, groupedByName) {
return Object.keys(groupedByName).reduce((tagNameEnt, key) => {
const readyAnno = annos.filter(anno => anno.tags.find(tag => tag.name === key))
.map(anno => anno.id)
.reduce((obj, anno) => ({ ...obj, [anno]: anno }), {});
return {
...tagNameEnt,
[key]: readyAnno
};
}, {});
}
static generateAnnotationEntities(anno) {
return anno.reduce((annoEntities, annotation) => {
const annot = {
...annotation,
positionTop: annotation.rectangles[0].y // todo remove this
};
return {
...annoEntities,
[annotation.id]: annot
};
}, {});
}
static generateRedactionEntities(redactions) {
return redactions.reduce((redactEntities, redaction) => {
return {
...redactEntities,
[redaction.redactionId]: redaction
};
}, {});
}
static resetCommentEntSelect(ent) {
return Object.keys(ent).reduce((object, key) => {
object[key] = {
...ent[key],
editable: false,
selected: false
};
return object;
}, {});
}
static filterCommentsSummary(comments, filters) {
if (Object.keys(filters).length) {
const tagFilterApplied = Object.keys(filters.tagFilters)
.filter(key => filters.tagFilters[key] === true).length;
const dateFilterApplied = (filters.dateRangeFrom || filters.dateRangeTo);
const filteredComments = comments.filter(comment => {
let hasTagFilter = false;
let hasDateFilter = false;
// check tags
if (filters.hasOwnProperty('tagFilters')) {
Object.keys(filters.tagFilters).forEach(filter => {
const label = filters.tagFilters[filter];
if (label) {
return comment.tags.forEach(tag => {
if (tag.name === filter && !hasTagFilter) {
hasTagFilter = true;
}
});
}
});
}
// check for dates
if (dateFilterApplied) {
const commentDate = moment(comment.lastModifiedDate);
const dateFrom = filters.dateRangeFrom !== null ? moment(filters.dateRangeFrom) : undefined;
const dateTo = filters.dateRangeTo !== null ? moment(filters.dateRangeTo) : undefined;
if (dateTo && dateFrom) {
if (commentDate > dateFrom && commentDate < dateTo) {
hasDateFilter = true;
}
}
if (dateTo && !dateFrom) {
if (commentDate <= dateTo) {
hasDateFilter = true;
}
}
if (dateFrom && !dateTo) {
if (commentDate > dateFrom) {
hasDateFilter = true;
}
}
}
return (hasTagFilter || hasDateFilter);
});
return (tagFilterApplied || dateFilterApplied) ? filteredComments : comments;
}
else {
return comments;
}
}
}
const initialState = {
annotationSet: {},
annotationEntities: {},
commentEntities: {},
annotationPageEntities: {},
selectedAnnotation: null,
commentSearchQueries: { commentSearch: '' },
commentSummaryFilters: { hasFilter: false, filters: {} },
loading: false,
loaded: false,
};
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ANNOTATION_SET: {
const annotationSet = {
...state.annotationSet,
documentId: action.payload
};
return {
...initialState,
annotationSet,
loading: true
};
}
case LOAD_ANNOTATION_SET_SUCCESS:
case LOAD_ANNOTATION_SET_FAIL: {
const annotationSet = action.payload.status === 200 ? action.payload.body :
{
...state.annotationSet,
annotations: [],
id: v4()
};
const annotationEntities = StoreUtils.generateAnnotationEntities(annotationSet.annotations);
const annotationPageEntities = StoreUtils.groupByKeyEntities(annotationSet.annotations, 'page');
const commentEntities = StoreUtils.generateCommentsEntities(annotationSet.annotations);
return {
...state,
annotationSet,
annotationEntities,
annotationPageEntities,
commentEntities,
loading: false,
loaded: true
};
}
case SAVE_ANNOTATION_SET_SUCCESS: {
const anno = action.payload.annotations;
const annEntities = {
...state.annotationEntities,
...anno
};
const annotArray = Object.keys(annEntities).map(key => annEntities[key]);
const annotationEntities = StoreUtils.generateAnnotationEntities(annotArray);
const annotationPageEntities = StoreUtils.groupByKeyEntities(annotArray, 'page');
const commentEntities = StoreUtils.generateCommentsEntities(annotArray);
const selectedAnnotation = {
...state.selectedAnnotation,
...anno,
editable: false
};
return {
...state,
annotationEntities,
annotationPageEntities,
commentEntities,
selectedAnnotation,
loading: false,
loaded: true
};
}
case SAVE_ANNOTATION_SUCCESS: {
const anno = action.payload;
const annEntities = {
...state.annotationEntities,
[anno.id]: anno
};
const annotArray = Object.keys(annEntities).map(key => annEntities[key]);
const annotationEntities = StoreUtils.generateAnnotationEntities(annotArray);
const annotationPageEntities = StoreUtils.groupByKeyEntities(annotArray, 'page');
const commentEntities = StoreUtils.generateCommentsEntities(annotArray);
const selectedAnnotation = {
...state.selectedAnnotation,
annotationId: anno.id,
editable: false
};
return {
...state,
annotationEntities,
annotationPageEntities,
commentEntities,
selectedAnnotation,
loading: false,
loaded: true
};
}
case DELETE_ANNOTATION_SUCCESS: {
const id = action.payload;
const page = state.annotationEntities[id].page;
const annotationEntities = {
...state.annotationEntities
};
delete annotationEntities[id];
const pageAnnotationsRemoved = [
...state.annotationPageEntities[page].filter(anno => anno.id !== id)
];
const annotationPageEntities = {
...state.annotationPageEntities,
[page]: pageAnnotationsRemoved
};
const commentEntities = {
...state.commentEntities
};
if (state.commentEntities[id]) {
delete commentEntities[id];
}
return {
...state,
annotationEntities,
annotationPageEntities,
commentEntities
};
}
case ADD_OR_EDIT_COMMENT: {
const comment = {
[action.payload.annotationId]: action.payload
};
const comments = {
...state.commentEntities,
...comment
};
return {
...state,
commentEntities: comments
};
}
case SELECT_ANNOTATION: {
const payload = action.payload;
const commentEntity = {
...state.commentEntities[payload.annotationId],
editable: payload.editable,
selected: payload.selected
};
const resetCommentEntSelect = StoreUtils.resetCommentEntSelect({ ...state.commentEntities });
const commentEntities = payload.annotationId && state.commentEntities[payload.annotationId] ? {
...resetCommentEntSelect,
[payload.annotationId]: commentEntity
} : { ...resetCommentEntSelect };
return {
...state,
commentEntities,
selectedAnnotation: action.payload
};
}
case SEARCH_COMMENT: {
const commentSearchQueries = {
...state.commentSearchQueries,
commentSearch: action.payload
};
const commentEntities = StoreUtils.resetCommentEntSelect({ ...state.commentEntities });
return {
...state,
commentEntities,
commentSearchQueries
};
}
case APPLY_COMMENT_SUMMARY_FILTER: {
const payload = action.payload;
const hasTagFilter = () => {
let isFiltered = false;
if (payload.tagFilters) {
Object.keys(payload.tagFilters).map(filter => {
if (payload.tagFilters[filter] && !isFiltered) {
isFiltered = true;
}
});
}
return isFiltered;
};
const hasFilter = (hasTagFilter() || !!payload.dateRangeFrom || !!payload.dateRangeTo);
const commentSummaryFilters = {
hasFilter,
filters: payload
};
return {
...state,
commentSummaryFilters: commentSummaryFilters
};
}
case CLEAR_COMMENT_SUMMARY_FILTER: {
return {
...state,
commentSummaryFilters: { ...initialState.commentSummaryFilters }
};
}
}
return state;
}
const getAnnoSet = (state) => state.annotationSet;
const getCommentEnts = (state) => state.commentEntities;
const getAnnoPageEnt = (state) => state.annotationPageEntities;
const getAnnoEnt = (state) => state.annotationEntities;
const getSelectedAnno = (state) => state.selectedAnnotation;
const commentSearchQ = (state) => state.commentSearchQueries;
const getSummaryFilters = (state) => state.commentSummaryFilters;
const initialTagState = {
tagNameEnt: {},
annotations: [],
filteredPageEntities: {},
filteredComments: {},
formFilterState: {},
filters: []
};
function tagsReducer(state = initialTagState, action) {
switch (action.type) {
case LOAD_ANNOTATION_SET: {
return {
...state,
...initialTagState
};
}
case LOAD_ANNOTATION_SET_SUCCESS: {
const annotations = action.payload.body.annotations;
const tagNameEnt = StoreUtils.genTagNameEntities(annotations);
return {
...state,
tagNameEnt,
annotations
};
}
case SAVE_ANNOTATION_SUCCESS: {
const payload = action.payload;
const anno = [...state.annotations].filter(a => a.id !== payload.id) || [];
const annotations = [...anno, payload];
const tagNameEnt = StoreUtils.genTagNameEntities(annotations);
return {
...state,
annotations,
tagNameEnt
};
}
case DELETE_ANNOTATION_SUCCESS: {
const id = action.payload;
const annotations = [...state.annotations].filter(a => a.id !== id);
const tagNameEnt = StoreUtils.genTagNameEntities(annotations);
const filteredPageEntities = StoreUtils.groupByKeyEntities(annotations, 'page');
const filteredComments = {
...state.filteredComments
};
delete filteredComments[id];
return {
...state,
annotations,
tagNameEnt,
filteredComments,
filteredPageEntities,
filters: []
};
}
case ADD_FILTER_TAGS: {
const formFilterState = action.payload;
const filters = Object.keys(formFilterState).reduce((arr, key) => {
return formFilterState[key] ? [...arr, key] : arr;
}, []);
const filteredComments = filters.reduce((obj, f) => {
return {
...obj,
...state.tagNameEnt[f]
};
}, {});
const annotations = Object.keys(filteredComments).map(key => state.annotations.filter(a => a.id === key)[0]);
const filteredPageEntities = StoreUtils.groupByKeyEntities(annotations, 'page');
return {
...state,
filters,
filteredComments,
filteredPageEntities,
};
}
case CLEAR_FILTER_TAGS: {
return {
...state,
filters: [],
filteredComments: {},
filteredPageEntities: {},
};
}
}
return state;
}
const getTagNameEnt = (state) => state.tagNameEnt;
const getFilters = (state) => state.filters;
const getFilteredComments = (state) => state.filteredComments;
const getFilteredPageEnt = (state) => state.filteredPageEntities;
const LOAD_BOOKMARKS = '[Bookmarks] Load Bookmarks';
const LOAD_BOOKMARKS_SUCCESS = '[Bookmarks] Load Bookmarks Success';
const LOAD_BOOKMARKS_FAIL = '[Bookmarks] Load Bookmarks Failure';
const CREATE_BOOKMARK = '[Bookmarks] Create Bookmark';
const CREATE_BOOKMARK_SUCCESS = '[Bookmarks] Create Bookmark Success';
const CREATE_BOOKMARK_FAIL = '[Bookmarks] Create Bookmark Failure';
const DELETE_BOOKMARK = '[Bookmarks] Delete Bookmark';
const DELETE_BOOKMARK_SUCCESS = '[Bookmarks] Delete Bookmark Success';
const DELETE_BOOKMARK_FAIL = '[Bookmarks] Delete Bookmark Failure';
const MOVE_BOOKMARK = '[Bookmarks] Move Bookmark';
const MOVE_BOOKMARK_SUCCESS = '[Bookmarks] Move Bookmark Success';
const MOVE_BOOKMARK_FAIL = '[Bookmarks] Move Bookmark Failure';
const UPDATE_BOOKMARK = '[Bookmarks] Update Bookmark';
const UPDATE_BOOKMARK_SUCCESS = '[Bookmarks] Update Bookmark Success';
const UPDATE_BOOKMARK_FAIL = '[Bookmarks] Update Bookmark Failure';
const UPDATE_BOOKMARK_SCROLL_TOP = '[Bookmarks] Update Bookmark Scroll Top';
class LoadBookmarks {
constructor() {
this.type = LOAD_BOOKMARKS;
}
}
class LoadBookmarksSuccess {
constructor(payload) {
this.payload = payload;
this.type = LOAD_BOOKMARKS_SUCCESS;
}
}
class LoadBookmarksFailure {
constructor(payload) {
this.payload = payload;
this.type = LOAD_BOOKMARKS_FAIL;
}
}
class CreateBookmark {
constructor(payload) {
this.payload = payload;
this.type = CREATE_BOOKMARK;
}
}
class CreateBookmarkSuccess {
constructor(payload) {
this.payload = payload;
this.type = CREATE_BOOKMARK_SUCCESS;
}
}
class CreateBookmarkFailure {
constructor(payload) {
this.payload = payload;
this.type = CREATE_BOOKMARK_FAIL;
}
}
class DeleteBookmark {
constructor(payload) {
this.payload = payload;
this.type = DELETE_BOOKMARK;
}
}
class DeleteBookmarkSuccess {
constructor(payload) {
this.payload = payload;
this.type = DELETE_BOOKMARK_SUCCESS;
}
}
class DeleteBookmarkFailure {
constructor(payload) {
this.payload = payload;
this.type = DELETE_BOOKMARK_FAIL;
}
}
class MoveBookmark {
constructor(payload) {
this.payload = payload;
this.type = MOVE_BOOKMARK;
}
}
class MoveBookmarkSuccess {
constructor(payload) {
this.payload = payload;
this.type = MOVE_BOOKMARK_SUCCESS;
}
}
class MoveBookmarkFailure {
constructor(payload) {
this.payload = payload;
this.type = MOVE_BOOKMARK_FAIL;
}
}
class UpdateBookmark {
constructor(payload) {
this.payload = payload;
this.type = UPDATE_BOOKMARK;
}
}
class UpdateBookmarkSuccess {
constructor(payload) {
this.payload = payload;
this.type = UPDATE_BOOKMARK_SUCCESS;
}
}
class UpdateBookmarkFailure {
constructor(payload) {
this.payload = payload;
this.type = UPDATE_BOOKMARK_FAIL;
}
}
class UpdateBookmarkScrollTop {
constructor(payload) {
this.payload = payload;
this.type = UPDATE_BOOKMARK_SCROLL_TOP;
}
}
const getBookmarkChildren = (bookmarks) => {
if (bookmarks) {
return bookmarks.reduce((childIds, bookmark) => {
if (bookmark.children && bookmark.children.length > 0) {
return [...childIds, bookmark.id, ...getBookmarkChildren(bookmark.children)];
}
return [...childIds, bookmark.id];
}, []);
}
else {
return [];
}
};
const generateBookmarkEntities = (bookmarks) => {
return bookmarks.reduce((bookmarkEntities, bookmark) => Object.assign(bookmarkEntities, { [bookmark.id]: bookmark }), {});
};
const indexEntities = (entities) => {
const entityIds = Object.keys(entities);
for (let index = 0; entityIds.length > 0; index++) {
let keysToRemove = [];
entityIds.forEach(key => {
if (!entities[key].previous || !entityIds.includes(entities[key].previous.toString())) {
entities[key].index = index;
keysToRemove.push(key);
}
});
keysToRemove.forEach(key => entityIds.splice(entityIds.indexOf(key), 1));
keysToRemove = [];
}
};
const generateBookmarkNodes = (entities) => {
const bookmarkEntities = JSON.parse(JSON.stringify(entities));
indexEntities(bookmarkEntities);
return Object.keys(bookmarkEntities).reduce((nodes, bookmarkId) => {
const bookmarkEntity = bookmarkEntities[bookmarkId];
if (bookmarkEntity.parent) {
const parentEntity = bookmarkEntities[bookmarkEntity.parent];
if (!parentEntity.children) {
parentEntity.children = [];
}
parentEntity.children[bookmarkEntity.index] = bookmarkEntity;
}
else {
nodes[bookmarkEntity.index] = bookmarkEntity;
}
return nodes;
}, []);
};
const initialBookmarksState = {
bookmarks: [],
bookmarkEntities: {},
bookmarkPageEntities: {},
editableBookmark: undefined,
loaded: false,
loading: false,
scrollTop: null,
};
function bookmarksReducer(state = initialBookmarksState, action) {
switch (action.type) {
case LOAD_BOOKMARKS: {
return {
...state,
loading: true
};
}
case LOAD_BOOKMARKS_SUCCESS:
case LOAD_BOOKMARKS_FAIL: {
const bookmarks = action.payload.status === 200 ? action.payload.body : [];
const bookmarkEntities = generateBookmarkEntities(bookmarks);
const bookmarkPageEntities = StoreUtils.groupByKeyEntities(bookmarks, 'pageNumber');
return {
...state,
bookmarks,
bookmarkEntities,
bookmarkPageEntities,
loaded: true
};
}
case CREATE_BOOKMARK_SUCCESS: {
const bookmark = action.payload;
const bookmarkEntities = {
...state.bookmarkEntities,
[bookmark.id]: bookmark,
};
const bookmarkArray = Object.keys(bookmarkEntities).map(key => bookmarkEntities[key]);
const bookmarkPageEntities = StoreUtils.groupByKeyEntities(bookmarkArray, 'pageNumber');
const editableBookmark = bookmark.id;
return {
...state,
bookmarkEntities,
editableBookmark,
bookmarkPageEntities,
loading: false,
loaded: true
};
}
case MOVE_BOOKMARK_SUCCESS: {
const movedBookmarks = generateBookmarkEntities(action.payload);
const bookmarkEntities = {
...state.bookmarkEntities,
...movedBookmarks
};
return {
...state,
bookmarkEntities,
loading: false,
loaded: true
};
}
case DELETE_BOOKMARK_SUCCESS: {
const bookmarkIds = action.payload;
const bookmarkEntities = { ...state.bookmarkEntities };
const bookmarkPageEntities = { ...state.bookmarkPageEntities };
const removeBookmarksByPage = {};
bookmarkIds.forEach(bookmarkId => {
if (removeBookmarksByPage[bookmarkEntities[bookmarkId].pageNumber] !== undefined &&
removeBookmarksByPage[bookmarkEntities[bookmarkId].pageNumber].length > 0) {
removeBookmarksByPage[bookmarkEntities[bookmarkId].pageNumber].push(bookmarkId);
}
else {
removeBookmarksByPage[bookmarkEntities[bookmarkId].pageNumber] = [bookmarkId];
}
delete bookmarkEntities[bookmarkId];
});
Object.entries(removeBookmarksByPage).forEach(([pageNumber, bmrkIds]) => {
bookmarkPageEntities[pageNumber]
= bookmarkPageEntities[pageNumber].filter(bookmark => !bmrkIds.includes(bookmark.id));
});
return {
...state,
bookmarkEntities,
bookmarkPageEntities,
loading: false,
loaded: true
};
}
case UPDATE_BOOKMARK_SUCCESS: {
const bookmark = action.payload;
const bookmarkEntities = {
...state.bookmarkEntities,
[bookmark.id]: { ...bookmark }
};
const bookmarkArray = Object.keys(bookmarkEntities).map(key => bookmarkEntities[key]);
const bookmarkPageEntities = StoreUtils.groupByKeyEntities(bookmarkArray, 'pageNumber');
const editableBookmark = undefined;
return {
...state,
bookmarkEntities,
editableBookmark,
bookmarkPageEntities,
loading: false,
loaded: true
};
}
case UPDATE_BOOKMARK_SCROLL_TOP: {
const scrollTop = action.payload;
return {
...state,
scrollTop: scrollTop
};
}
}
return state;
}
const getBookmarks = (state) => state.bookmarks;
const getBookmarkEnts = (state) => state.bookmarkEntities;
const getBookmarkPageEnt = (state) => state.bookmarkPageEntities;
const getEditBookmark = (state) => state.editableBookmark;
const getScrollTop$1 = (state) => state.scrollTop;
const LOAD_REDACTIONS = '[Redaction] Load Redaction';
const LOAD_REDACTION_SUCCESS = '[Redaction] Load Redaction Success';
const LOAD_REDACTION_FAIL = '[Redaction] Load Redaction Fail';
const SAVE_REDACTION = '[Redaction] Save Redaction';
const SAVE_REDACTION_SUCCESS = '[Redaction] Save Redaction Success';
const SAVE_REDACTION_FAIL = '[Redaction] Save Redaction Fail';
const SAVE_BULK_REDACTION = '[Redaction] Save bulk Redaction';
const SAVE_BULK_REDACTION_SUCCESS = '[Redaction] Save bulk Redaction Success';
const SAVE_BULK_REDACTION_FAIL = '[Redaction] Save bulk Redaction Fail';
const DELETE_REDACTION = '[Redaction] Delete Redaction';
const DELETE_REDACTION_SUCCESS = '[Redaction] Delete Redaction Success';
const DELETE_REDACTION_FAIL = '[Redaction] Delete Redaction Fail';
const SELECT_REDACTION = '[Redaction] Select Redaction';
const REDACT = '[Redaction] Redact';
const REDACT_SUCCESS = '[Redaction] Redact Success';
const REDACT_FAIL = '[Redaction] Redact Fail';
const RESET_REDACTED_DOCUMENT = '[Redaction] Reset Redacted Document';
const UNMARK_ALL = '[Redaction] Unmark All';
const UNMARK_ALL_SUCCESS = '[Redaction] Unmark All Success';
class LoadRedactions {
constructor(payload) {
this.payload = payload;
this.type = LOAD_REDACTIONS;
}
}
class LoadRedactionSuccess {
constructor(payload) {
this.payload = payload;
this.type = LOAD_REDACTION_SUCCESS;
}
}
class LoadRedactionFailure {
constructor(payload) {
this.payload = payload;
this.type = LOAD_REDACTION_FAIL;
}
}
class SaveRedaction {
constructor(payload) {
this.payload = payload;
this.type = SAVE_REDACTION;
}
}
class SaveRedactionSuccess {
constructor(payload) {
this.payload = payload;
this.type = SAVE_REDACTION_SUCCESS;
}
}
class SaveRedactionFailure {
constructor(payload) {
this.payload = payload;
this.type = SAVE_REDACTION_FAIL;
}
}
class SaveBulkRedactionFailure {
constructor(payload) {
this.payload = payload;
this.type = SAVE_BULK_REDACTION_FAIL;
}
}
class SaveBulkRedaction {
constructor(payload) {
this.payload = payload;
this.type = SAVE_BULK_REDACTION;
}
}
class SaveBulkRedactionSuccess {
constructor(payload) {
this.payload = payload;
this.type = SAVE_BULK_REDACTION_SUCCESS;
}
}
class DeleteRedaction {
constructor(payload) {
this.payload = payload;
this.type = DELETE_REDACTION;
}
}
class DeleteRedactionSuccess {
constructor(payload) {
this.payload = payload;
this.type = DELETE_REDACTION_SUCCESS;
}
}
class DeleteRedactionFailure {
constructor(payload) {
this.payload = payload;
this.type = DELETE_REDACTION_FAIL;
}
}
class SelectRedaction {
constructor(payload) {
this.payload = payload;
this.type = SELECT_REDACTION;
}
}
class Redact {
constructor(payload) {
this.payload = payload;
this.type = REDACT;
}
}
class RedactSuccess {
constructor(payload) {
this.payload = payload;
this.type = REDACT_SUCCESS;
}
}
class RedactFailure {
constructor(payload) {
this.payload = payload;
this.type = REDACT_FAIL;
}
}
class ResetRedactedDocument {
constructor() {
this.type = RESET_REDACTED_DOCUMENT;
}
}
class UnmarkAll {
constructor(payload) {
this.payload = payload;
this.type = UNMARK_ALL;
}
}
class UnmarkAllSuccess {
constructor() {
this.type = UNMARK_ALL_SUCCESS;
}
}
const initialRedactionState = {
redactionEntities: {},
redactionPageEntities: {},
selectedRedaction: {},
redactedDocumentInfo: undefined,
documentId: undefined
};
function redactionReducer(state = initialRedactionState, action) {
switch (action.type) {
case LOAD_REDACTIONS: {
return {
...state,
...initialRedactionState
};
}
case LOAD_REDACTION_SUCCESS: {
const payload = action.payload;
if (payload) {
const redactionEntities = StoreUtils.generateRedactionEntities(payload);
const redactionPageEntities = StoreUtils.groupByKeyEntities(payload, 'page');
return {
...state,
redactionEntities,
redactionPageEntities
};
}
return {
...state
};
}
case SAVE_REDACTION_SUCCESS: {
const { payload } = action;
const redactionEntities = {
...state.redactionEntities,
[payload.redactionId]: payload
};
const redactionArray = Object.keys(redactionEntities).map(key => redactionEntities[key]);
const redactionPageEntities = StoreUtils.groupByKeyEntities(redactionArray, 'page');
return {
...state,
redactionEntities,
redactionPageEntities
};
}
case SAVE_BULK_REDACTION_SUCCESS: {
const payloadResult = Object.assign({}, ...action.payload.searchRedactions.map((x) => ({ [x.redactionId]: x })));
const redactionEntities = {
...state.redactionEntities,
...payloadResult
};
const redactionArray = Object.keys(redactionEntities).map(key => redactionEntities[key]);
const redactionPageEntities = StoreUtils.groupByKeyEntities(redactionArray, 'page');
return {
...state,
redactionEntities,
redactionPageEntities
};
}
case SELECT_REDACTION:
case SELECT_ANNOTATION: {
return {
...state,
selectedRedaction: action.payload
};
}
case DELETE_REDACTION_SUCCESS: {
const page = action.payload.page;
const id = action.payload.redactionId;
const redactionEntities = {
...state.redactionEntities
};
delete redactionEntities[id];
const pageRedactionRemoved = [
...state.redactionPageEntities[page].filter(redaction => redaction.redactionId !== id)
];
const redactionPageEntities = {
...state.redactionPageEntities,
[page]: pageRedactionRemoved
};
return {
...state,
redactionPageEntities,
redactionEntities,
};
}
case REDACT_SUCCESS: {
const redactedDocumentInfo = action.payload;
return {
...state,
...initialRedactionState,
redactedDocumentInfo
};
}
case RESET_REDACTED_DOCUMENT: {
return {
...state,
redactedDocumentInfo: undefined
};
}
case UNMARK_ALL_SUCCESS: {
return {
...state,
...initialRedactionState
};
}
}
return state;
}
const getRedactionEnt$1 = (state) => state.redactionEntities;
const getPageEnt = (state) => state.redactionPageEntities;
const getSelectedRedaction = (state) => state.selectedRedaction;
const getRedactedDocInfo = (state) => state.redactedDocumentInfo;
const initialIcpSessionState = {
session: null,
presenter: null,
client: null,
participants: []
};
function icpReducer(state = initialIcpSessionState, action) {
switch (action.type) {
case SET_CASE_ID: {
const caseId = action.payload;
const session = { ...state.session, caseId };
return {
...state,
session
};
}
case ICP_SOCKET_SESSION_JOINED: {
const session = action.payload.session;
const participantInfo = action.payload.participantInfo;
return {
...state,
session,
client: participantInfo.client,
presenter: participantInfo.presenter,
};
}
case ICP_PARTICIPANT_LIST_UPDATED: {
const updatedParticipants = action.payload;
const participants = Object.keys(updatedParticipants)
.map(id => ({ id: id, username: updatedParticipants[id] }));
return {
...state,
participants
};
}
case ICP_PRESENTER_UPDATED: {
const presenter = action.payload;
return {
...state, presenter
};
}
case LEAVE_ICP_SOCKET_SESSION: {
return {
...state,
...initialIcpSessionState
};
}
}
return state;
}
const getIcpSession$1 = (state) => state.session;
const getPresenter$1 = (state) => state.presenter;
const getClient$1 = (state) => state.client;
const getParticipants$1 = (state) => state.participants;
const reducers = {
document: docReducer,
annotations: reducer,
tags: tagsReducer,
bookmarks: bookmarksReducer,
redactions: redactionReducer,
icp: icpReducer,
};
const getMVState = createFeatureSelector('media-viewer');
const getDocumentState = createSelector(getMVState, (state) => state.document);
const getPages = createSelector(getDocumentState, getDocPages);
const getPageList = createSelector(getPages, (pages) => Object.values(pages));
const getDocumentId = createSelector(getDocumentState, getDocId);
const getPdfPosition = createSelector(getDocumentState, getPdfPos);
const getPageDifference = createSelector(getDocumentState, getHasDifferentPageSizes);
const getConvertedDocument = createSelector(getDocumentState, getConvertedDocument$1);
const getRotation = createSelector(getDocumentState, getRotation$1);
const rotationLoaded = createSelector(getDocumentState, rotationLoaded$1);
/**
* Helper Class
* Used for dynamic templates manipulation
* */
class HtmlTemplatesHelper {
static setDescribedBy(errorMessage, config) {
if (!errorMessage) {
return config.hint ? `${config.id}-hint` : null;
}
else if (errorMessage && errorMessage.isInvalid) {
return config.hint ? `${config.id}-hint ${config.id}-error` : `${config.id}-error`;
}
else {
return config.hint ? `${config.id}-hint` : null;
}
}
static getAdjustedBoundingRect(element, log = true) {
const viewportX = window.visualViewport.offsetLeft;
const viewportY = window.visualViewport.offsetTop;
const viewportScale = window.visualViewport.scale;
const viewportPageX = window.visualViewport.pageLeft;
const viewportPageY = window.visualViewport.pageTop;
if (log && viewportX || viewportY || (viewportScale != 1) || viewportPageX || viewportPageY) {
console.log(`Element: ${element.id} Viewport X: ${viewportX}, Y: ${viewportY}, Scale: ${viewportScale}, PageX: ${viewportPageX}, PageY: ${viewportPageY}`);
}
return element.getBoundingClientRect();
}
}
class IcpEventService {
constructor() {
this.enabled = new BehaviorSubject(false);
this.sessionLaunch = new Subject();
this.becomingPresenter = new Subject();
this.stoppingPresenting = new Subject();
this.leavingSession = new BehaviorSubject(false);
this.sessionExitConfirmed = new Subject();
this.participantsListVisible = new BehaviorSubject(false);
this.launchSession = () => {
this.sessionLaunch.next();
};
this.enable = () => {
this.enabled.next(true);
this.launchSession();
};
this.becomePresenter = () => {
this.becomingPresenter.next();
};
this.stopPresenting = () => {
this.stoppingPresenting.next();
};
this.leaveSession = () => {
this.leavingSession.next(true);
};
this.confirmExit = () => {
this.sessionExitConfirmed.next();
this.participantsListVisible.next(false);
this.enabled.next(false);
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEventService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
var SearchType;
(function (SearchType) {
SearchType["Redact"] = "Redact";
SearchType["Highlight"] = "Highlight";
})(SearchType || (SearchType = {}));
class ToolbarEventService {
constructor(icpEventService) {
this.icpEventService = icpEventService;
this.highlightModeSubject = new BehaviorSubject(false);
this.highlightToolbarSubject = new BehaviorSubject(false);
this.drawModeSubject = new BehaviorSubject(false);
this.rotateSubject = new Subject();
this.searchSubject = new Subject();
this.searchResultsCountSubject = new Subject();
this.zoomSubject = new Subject();
this.stepZoomSubject = new Subject();
this.zoomValueSubject = new BehaviorSubject(1);
this.pageCountSubject = new Subject();
this.printSubject = new Subject();
this.downloadSubject = new Subject();
this.setCurrentPageSubject = new Subject();
this.setCurrentPageInputValueSubject = new BehaviorSubject(1);
this.changePageByDeltaSubject = new Subject();
this.showCommentSummary = new BehaviorSubject(false);
this.grabNDrag = new BehaviorSubject(false);
this.saveRotationSubject = new Subject();
this.redactionMode = new BehaviorSubject(false);
this.redactionPreview = new Subject();
this.applyRedactToDocument = new Subject();
this.clearAllRedactMarkers = new Subject();
this.redactWholePage = new Subject();
this.redactionSerachSubject = new Subject();
this.redactAllInProgressSubject = new BehaviorSubject(false);
this.openRedactionSearch = new BehaviorSubject(null);
this.sidebarOpen = new BehaviorSubject(false);
this.sidebarOutlineView = new BehaviorSubject(true);
this.searchBarHidden = new BehaviorSubject(true);
this.commentsPanelVisible = new BehaviorSubject(false);
}
/**
* Reset the stateful behaviour subjects
*/
reset() {
this.setCurrentPageSubject.next(1);
this.setCurrentPageInputValueSubject.next(1);
this.zoomValueSubject.next(1);
this.highlightModeSubject.next(false);
this.highlightToolbarSubject.next(false);
this.drawModeSubject.next(false);
this.showCommentSummary.next(false);
this.grabNDrag.next(false);
}
// Function to inform Observers that highlightMode has been enabled
toggleHighlightMode() {
// Highlight and Draw states are mutually exclusive
if (this.highlightModeSubject.getValue() === false) {
this.drawModeSubject.next(false);
this.grabNDrag.next(false);
this.highlightModeSubject.next(true);
}
else {
this.highlightModeSubject.next(false);
}
}
// Function to inform Observers that ToggleMode has been enabled
toggleDrawMode() {
if (this.drawModeSubject.getValue() === false) {
this.highlightModeSubject.next(false);
this.grabNDrag.next(false);
this.drawModeSubject.next(true);
}
else {
this.drawModeSubject.next(false);
}
}
toggleHighlightToolbar() {
const isOpening = !this.highlightToolbarSubject.getValue();
this.highlightToolbarSubject.next(isOpening);
if (!isOpening) {
this.highlightModeSubject.next(false);
this.drawModeSubject.next(false);
}
}
rotate(angle) {
this.rotateSubject.next(angle);
}
search(phrase) {
this.searchSubject.next(phrase);
}
getSearchResultsCount() {
return this.searchResultsCountSubject.asObservable();
}
zoom(value) {
this.zoomSubject.next(value);
}
stepZoom(value) {
this.stepZoomSubject.next(value);
}
getZoomValue() {
return this.zoomValueSubject.asObservable();
}
getPageCount() {
return this.pageCountSubject.asObservable();
}
print() {
this.printSubject.next();
}
download() {
this.downloadSubject.next();
}
setPage(value) {
this.setCurrentPageSubject.next(value);
}
incrementPage(value) {
this.changePageByDeltaSubject.next(value);
}
getCurrentPageNumber() {
return this.setCurrentPageInputValueSubject.asObservable();
}
getShowCommentSummary() {
return this.showCommentSummary.asObservable();
}
toggleCommentsSummary(value) {
this.showCommentSummary.next(value);
}
saveRotation() {
this.saveRotationSubject.next();
}
toggleGrabNDrag() {
this.grabNDrag.next(!this.grabNDrag.getValue());
}
toggleSideBar(toggle) {
this.sidebarOpen.next(toggle);
}
toggleSideBarView(toggle) {
this.sidebarOutlineView.next(toggle);
}
toggleRedactionMode() {
if (this.redactionMode.getValue() === false) {
this.highlightToolbarSubject.next(false);
this.highlightModeSubject.next(false);
this.drawModeSubject.next(false);
this.grabNDrag.next(false);
this.redactionMode.next(true);
}
else {
this.highlightModeSubject.next(false);
this.drawModeSubject.next(false);
this.redactionMode.next(false);
}
this.openRedactionSearch.next({ modeType: SearchType.Redact, isOpen: false });
}
toggleRedactionPreview(viewMode) {
this.redactionPreview.next(viewMode);
}
unmarkAll() {
this.clearAllRedactMarkers.next();
}
applyRedactionToDocument() {
this.applyRedactToDocument.next();
}
redactPage() {
this.redactWholePage.next();
}
toggleCommentsPanel(isVisible) {
if (isVisible) {
this.toggleParticipantsList(!isVisible);
}
this.commentsPanelVisible.next(isVisible);
}
toggleParticipantsList(isVisible) {
if (isVisible) {
this.toggleCommentsPanel(!isVisible);
}
this.icpEventService.participantsListVisible.next(isVisible);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarEventService, deps: [{ token: IcpEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarEventService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: IcpEventService }] });
class ViewerEventService {
constructor() {
this.textHighlight = new Subject();
this.boxHighlight = new Subject();
this.ctxToolbarCleared = new Subject();
this.navigationEvent = new Subject();
this.navigationEventICP = new Subject();
}
textSelected(selectionData) {
this.textHighlight.next(selectionData);
}
boxSelected(selectionData) {
this.boxHighlight.next(selectionData);
}
clearCtxToolbar() {
this.ctxToolbarCleared.next();
}
goToDestination(destination) {
this.navigationEvent.next(destination);
}
goToDestinationICP(destination) {
this.navigationEventICP.next(destination);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerEventService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
const getTagsRootState = createSelector(getMVState, (state) => state.tags);
const getTagEntities = createSelector(getTagsRootState, getTagNameEnt);
const getTagFilters = createSelector(getTagsRootState, getFilters);
const getTagFiltered = createSelector(getTagsRootState, getFilteredComments);
const getFilteredPageEntities = createSelector(getTagsRootState, getFilteredPageEnt);
const getAllTagsArr = createSelector(getTagEntities, (tagEnt) => Object.keys(tagEnt).map(key => {
return {
key,
length: Object.keys(tagEnt[key]).length
};
}));
const getAnnotationsSetState = createSelector(getMVState, (state) => state.annotations);
const getAnnotationEntities = createSelector(getAnnotationsSetState, getAnnoEnt);
const getSet = createSelector(getAnnotationsSetState, getAnnoSet);
const getDocumentIdSetId = createSelector(getSet, (annoSet) => {
return {
documentId: annoSet.documentId,
annotationSetId: annoSet.id
};
});
const getAnnotationSet = createSelector(getAnnotationEntities, getSet, (entities, set) => {
return {
...set,
annotations: Object.keys(entities).map(key => entities[key])
};
});
const getSelectedAnnotation = createSelector(getAnnotationsSetState, getSelectedAnno);
const getCommentEntities = createSelector(getAnnotationsSetState, getCommentEnts);
const getPageEntities = createSelector(getAnnotationsSetState, getAnnoPageEnt);
const getComponentSearchQueries = createSelector(getAnnotationsSetState, commentSearchQ);
const getComponentSearchText = createSelector(getComponentSearchQueries, (queries) => queries.commentSearch);
const getCommentSummaryFilters = createSelector(getAnnotationsSetState, getSummaryFilters);
const getAnnoPerPage = createSelector(getPages, getPageEntities, getFilteredPageEntities, (pages, pageEnt, filteredPageEnt) => {
const isFiltered = !!Object.keys(filteredPageEnt).length;
const entities = isFiltered ? filteredPageEnt : pageEnt;
if (pages) {
const arr = [];
Object.keys(pages).forEach(key => {
arr.push({
anno: entities[key] ? entities[key] : [],
styles: pages[key].styles
});
});
return arr;
}
});
const getCommentsArray = createSelector(getCommentEntities, getPages, getAnnotationEntities, getTagFiltered, (commentEnts, pages, annoEnts, filtered) => {
if (commentEnts && annoEnts && pages[1]) {
const isFiltered = !!Object.keys(filtered).length;
const com = isFiltered ? filtered : commentEnts;
return Object.keys(com).map(key => {
const page = annoEnts[key].page;
return {
...commentEnts[key],
page,
pages
};
});
}
});
const getCommentSummary = createSelector(getCommentsArray, getCommentSummaryFilters, (commentSummary = [], filters) => {
const comments = StoreUtils.filterCommentsSummary(commentSummary, filters.filters);
return comments
.filter(comment => comment.createdByDetails !== undefined)
.map(comment => ({
page: comment.page,
user: `${comment.createdByDetails.forename} ${comment.createdByDetails.surname}`,
date: moment(comment.lastModifiedDate).format('D MMMM YYYY'),
tags: comment.tags,
comment: comment.content
}));
});
const getFilteredAnnotations = createSelector(getAnnotationEntities, getTagFiltered, (annoEnt, filters) => {
const isFiltered = !!Object.keys(filters).length;
const anno = isFiltered ? filters : annoEnt;
return Object.keys(anno).map(key => annoEnt[key])
.filter(annotation => annotation.comments && annotation.comments.length > 0);
});
class HighlightCreateService {
constructor(toolBarEvents, store) {
this.toolBarEvents = toolBarEvents;
this.store = store;
}
saveAnnotation(rectangles, page, annotationId) {
this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
const anno = {
id: annotationId || v4(),
color: 'FFFF00',
comments: [],
page: page,
rectangles: rectangles,
type: 'highlight',
...anoSetDocId,
createdBy: '',
createdByDetails: undefined,
createdDate: moment.utc().tz('Europe/London').toISOString(),
lastModifiedBy: '',
lastModifiedByDetails: undefined,
lastModifiedDate: '',
tags: [],
};
this.store.dispatch(new SaveAnnotation(anno, !!annotationId));
});
}
saveAnnotationSet(searchRectangles) {
this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
const annoSet = searchRectangles.map(x => {
return {
id: v4(),
color: 'FFFF00',
comments: [],
page: x.page,
rectangles: x.rectangles,
type: 'highlight',
...anoSetDocId,
createdBy: '',
createdByDetails: undefined,
createdDate: moment.utc().tz('Europe/London').toISOString(),
lastModifiedBy: '',
lastModifiedByDetails: undefined,
lastModifiedDate: '',
tags: [],
};
});
this.store.dispatch(new SaveAnnotationSet({
id: anoSetDocId.annotationSetId, annotations: annoSet, documentId: anoSetDocId.documentId
}));
});
}
applyRotation(pageHeight, pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, rotate, zoom) {
const { x, y, width, height } = {
x: +(offsetLeft / zoom).toFixed(2),
y: +(offsetTop / zoom).toFixed(2),
width: +(offsetWidth / zoom).toFixed(2),
height: +(offsetHeight / zoom).toFixed(2)
};
const rectangle = { x, y, width, height };
switch (rotate) {
case 90:
rectangle.width = height;
rectangle.height = width;
rectangle.x = y;
rectangle.y = +(pageWidth / zoom - x - width).toFixed(2);
break;
case 180:
rectangle.x = +(pageWidth / zoom - x - width).toFixed(2);
rectangle.y = +(pageHeight / zoom - y - height).toFixed(2);
break;
case 270:
rectangle.width = height;
rectangle.height = width;
rectangle.x = +(pageHeight / zoom - y - height).toFixed(2);
rectangle.y = x;
break;
}
return rectangle;
}
resetHighlight() {
window.getSelection().removeAllRanges();
this.toolBarEvents.highlightModeSubject.next(false);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightCreateService, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightCreateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightCreateService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: i1.Store }] });
class HighlightCreateDirective {
constructor(element, toolbarEvents, viewerEvents, highlightService, store) {
this.element = element;
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.highlightService = highlightService;
this.store = store;
this.$subscriptions = [];
}
ngOnInit() {
this.$subscriptions.push(this.store.select(getPages).subscribe((pages) => {
if (pages[1]) {
this.allPages = pages;
}
}));
this.$subscriptions.push(this.toolbarEvents.highlightModeSubject.pipe(filter(enabled => enabled && !!this.element.nativeElement), debounceTime(100)).subscribe(() => {
this.focusWithoutScrolling(this.element.nativeElement);
}));
}
ngOnDestroy() {
this.$subscriptions.forEach(sub => sub.unsubscribe());
}
focusWithoutScrolling(element) {
try {
element.focus({ preventScroll: true });
}
catch {
element.focus();
}
}
onMouseUp(mouseEvent) {
let page;
let currentElement = mouseEvent.target;
while (currentElement.offsetParent) {
currentElement = currentElement.offsetParent;
if (currentElement.getAttribute) {
page = parseInt(currentElement.getAttribute('data-page-number'), 10);
if (page) {
break;
}
}
}
if (this.toolbarEvents.highlightModeSubject.getValue()) {
const rectangles = this.getRectangles(mouseEvent, page);
this.viewerEvents.textSelected({ page, rectangles });
}
}
onKeyboardSelectionConfirmed() {
if (this.toolbarEvents.highlightModeSubject.getValue()) {
const selection = window.getSelection();
if (selection && selection.rangeCount && !selection.isCollapsed) {
const page = this.getCurrentPageFromSelection(selection);
const rectangles = this.getRectanglesFromSelection(selection, page);
if (rectangles && rectangles.length > 0) {
this.viewerEvents.textSelected({ page, rectangles });
}
}
}
}
getCurrentPageFromSelection(selection) {
const range = selection.getRangeAt(0);
let currentElement = range.startContainer;
if (currentElement.nodeType === Node.TEXT_NODE) {
currentElement = currentElement.parentElement;
}
while (currentElement && currentElement.offsetParent) {
currentElement = currentElement.offsetParent;
if (currentElement.getAttribute) {
const page = parseInt(currentElement.getAttribute('data-page-number'), 10);
if (page) {
return page;
}
}
}
return 1;
}
getRectanglesFromSelection(selection, page) {
if (!this.allPages || !this.allPages[page]) {
return [];
}
this.setPageProperties(page);
const range = selection.getRangeAt(0).cloneRange();
const clientRects = range.getClientRects();
if (!clientRects || clientRects.length === 0) {
return [];
}
let textLayerElement = range.startContainer;
if (textLayerElement.nodeType === Node.TEXT_NODE) {
textLayerElement = textLayerElement.parentElement;
}
const textLayer = textLayerElement.closest('.textLayer');
if (!textLayer) {
return [];
}
this.removeEnhancedTextModeStyling(textLayerElement);
return this.processClientRects(clientRects, textLayer);
}
onPdfViewerClick(event) {
this.store.dispatch(new SelectedAnnotation({
annotationId: '',
selected: false,
editable: false,
}));
this.viewerEvents.clearCtxToolbar();
}
getRectangles(event, page) {
this.setPageProperties(page);
const selection = window.getSelection();
if (selection) {
const localElement = event.target;
this.removeEnhancedTextModeStyling(localElement);
if (selection.rangeCount && !selection.isCollapsed) {
const range = selection.getRangeAt(0).cloneRange();
const clientRects = range.getClientRects();
if (clientRects) {
const textLayer = localElement.closest(".textLayer");
return this.processClientRects(clientRects, textLayer);
}
}
}
}
createTextRectangle(rect, parentRect) {
const height = rect.bottom - rect.top;
const width = rect.right - rect.left;
const top = rect.top - parentRect.top;
const left = rect.left - parentRect.left;
let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
rectangle = { id: v4(), ...rectangle };
return rectangle;
}
removeEnhancedTextModeStyling(element) {
if (element.parentElement.children) {
for (let i = 0; i < element.parentElement.children.length; i++) {
const child = element.parentElement.children[i];
child.style.padding = '0';
// regex will be targeting the translate style in string
// e.g. scaleX(0.969918) translateX(-110.684px) translateY(-105.274px) will become scaleX(0.969918)
const translateCSSRegex = /translate[XYZ]\(-?\d*(\.\d+)?(px)?\)/g;
child.style.transform = child.style.transform.replace(translateCSSRegex, '').trim();
}
}
}
setPageProperties(page) {
this.pageHeight = this.allPages[page].styles.height;
this.pageWidth = this.allPages[page].styles.width;
this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
}
processClientRects(clientRects, textLayer) {
const parentRect = HtmlTemplatesHelper.getAdjustedBoundingRect(textLayer);
const selectionRectangles = [];
for (let i = 0; i < clientRects.length; i++) {
const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
const findSelectionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
if (!findSelectionRectangle) {
selectionRectangles.push(selectionRectangle);
}
}
return selectionRectangles;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightCreateDirective, deps: [{ token: i0.ElementRef }, { token: ToolbarEventService }, { token: ViewerEventService }, { token: HighlightCreateService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: HighlightCreateDirective, isStandalone: false, selector: "[mvCreateTextHighlight]", host: { listeners: { "mouseup": "onMouseUp($event)", "mousedown": "onPdfViewerClick($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightCreateDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvCreateTextHighlight]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: ToolbarEventService }, { type: ViewerEventService }, { type: HighlightCreateService }, { type: i1.Store }], propDecorators: { onMouseUp: [{
type: HostListener,
args: ['mouseup', ['$event']]
}], onPdfViewerClick: [{
type: HostListener,
args: ['mousedown', ['$event']]
}] } });
pdfjs.GlobalWorkerOptions.workerSrc = '/assets/build/pdf.worker.min.mjs';
/**
* Values of the state field returned by the find events
*/
var FindState;
(function (FindState) {
FindState[FindState["FOUND"] = 0] = "FOUND";
FindState[FindState["NOT_FOUND"] = 1] = "NOT_FOUND";
FindState[FindState["WRAPPED"] = 2] = "WRAPPED";
FindState[FindState["PENDING"] = 3] = "PENDING";
})(FindState || (FindState = {}));
class PdfJsWrapper {
constructor(pdfViewer, downloadManager, toolbarEvents, documentLoadInit, documentLoadProgress, documentLoaded, outlineLoaded, documentLoadFailed, pageRendered, positionUpdated) {
this.pdfViewer = pdfViewer;
this.downloadManager = downloadManager;
this.toolbarEvents = toolbarEvents;
this.documentLoadInit = documentLoadInit;
this.documentLoadProgress = documentLoadProgress;
this.documentLoaded = documentLoaded;
this.outlineLoaded = outlineLoaded;
this.documentLoadFailed = documentLoadFailed;
this.pageRendered = pageRendered;
this.positionUpdated = positionUpdated;
this.pdfViewer.eventBus.on('updateviewarea', e => positionUpdated.next(e));
this.pdfViewer.eventBus.on('pagechanging', e => this.toolbarEvents.setCurrentPageInputValueSubject.next(e.pageNumber));
this.pdfViewer.eventBus.on('pagechanging', e => this.drawMissingPages(e));
this.pdfViewer.eventBus.on('pagesinit', () => this.pdfViewer.currentScaleValue = '1');
this.pdfViewer.eventBus.on('pagerendered', e => { }); // not used left for future convenience
this.pdfViewer.eventBus.on('pagesloaded', (e) => this.emitDocumentInfo(e));
this.pdfViewer.eventBus.on('scalechanging', (e) => this.emitDocumentInfo(e));
this.pdfViewer.eventBus.on('rotationchanging', (e) => this.emitDocumentInfo(e));
this.pdfViewer.eventBus.on('updatefindcontrolstate', event => {
this.sendSearchDetails(event);
});
this.pdfViewer.eventBus.on('updatefindmatchescount', event => {
const result = { ...event.matchesCount, isPrevious: event?.source?.state?.findPrevious };
this.toolbarEvents.searchResultsCountSubject.next(result);
});
this.zoomValue = 1;
this.redactionPages = [];
// Subscribe to redactionMode changes and run a function when it changes
this.toolbarEvents.redactionMode.subscribe((redactionModeValue) => {
this.onRedactionModeChanged(redactionModeValue);
});
}
sendSearchDetails(event) {
if (event.state !== FindState.PENDING) {
const result = { ...event.matchesCount, isPrevious: event?.source?.state?.findPrevious };
this.toolbarEvents.searchResultsCountSubject.next(result);
if (event?.source?.selected?.pageIdx !== -1 && event.matchesCount.total > 0) {
this.toolbarEvents.redactionSerachSubject.next({
page: event?.source?.selected?.pageIdx,
matchedIndex: event?.source?.selected?.matchIdx,
matchesCount: event.matchesCount.total
});
}
}
}
// in the event a user fast scrolls or navigates to a specific page,
// we need to render the missing pages so redaction box is in right place
drawMissingPages(e) {
const redactionMode = this.toolbarEvents.redactionMode.getValue();
const { pageNumber, previous: previousPageNumber } = e;
if (!previousPageNumber || pageNumber < previousPageNumber || Math.abs(pageNumber - previousPageNumber) <= 1) {
return;
}
const start = previousPageNumber + 1;
const end = pageNumber;
// if the redaction mode is on render the pages now
if (redactionMode) {
for (let i = start; i < end; i++) {
const page = this.pdfViewer._pages[i - 1];
if (page && !page.renderingState) {
page.draw();
}
}
}
else {
// keep track of pages as if a user skips to a page we need to render all the missing pages
console.log('Adding redaction pages to queue:', start, end);
this.redactionPages.push({ start, end });
}
}
onRedactionModeChanged(redactionMode) {
if (redactionMode && this.redactionPages.length) {
this.redactionPages.forEach(({ start, end }) => {
console.log('Drawing redaction pages from queue:', start, end);
for (let i = start; i < end; i++) {
const page = this.pdfViewer._pages[i - 1];
if (page && !page.renderingState) {
page.draw();
}
}
});
this.redactionPages = [];
}
}
emitDocumentInfo(e) {
const allPages = [...this.pdfViewer._pages].map(page => {
return {
div: page.div,
scale: page.scale,
rotation: page.rotation,
id: page.id,
viewportScale: page.viewport.scale
};
});
this.pageRendered.next(allPages);
}
async loadDocument(documentUrl) {
const loadingTask = this.createLoadingTask(documentUrl);
loadingTask.onProgress = ({ loaded, total }) => {
this.documentLoadProgress.next({ loaded, total });
};
this.documentLoadInit.next(documentUrl);
try {
const pdfDocument = await loadingTask.promise;
this.documentLoaded.next(pdfDocument);
this.toolbarEvents.pageCountSubject.next(pdfDocument.numPages);
this.pdfViewer.setDocument(pdfDocument);
if (this.pdfViewer.linkService instanceof PDFLinkService) {
const linkservice = this.pdfViewer.linkService;
linkservice.setDocument(pdfDocument, null);
}
const outlineNode = await pdfDocument.getOutline();
const outline = outlineNode ? outlineNode.map(x => {
return {
bold: x.bold,
color: x.color,
count: x.count,
dest: x.dest,
italic: x.italic,
items: x.items,
newWindow: x.newWindow,
title: x.title,
unsafeUrl: x.unsafeUrl,
url: x.url
};
}) : null;
if (outline) {
await this.setOutlinePageNumbers(pdfDocument, outline);
}
this.documentOutline = outline;
this.outlineLoaded.next(this.documentOutline);
const pdfMetaData = await pdfDocument.getMetadata();
this.setCurrentPDFTitle(pdfMetaData.info?.Title);
}
catch (e) {
this.documentLoadFailed.next(e);
}
}
createLoadingTask(documentUrl) {
return pdfjs.getDocument({
url: documentUrl,
cMapUrl: 'assets/minified/cmaps',
cMapPacked: true,
withCredentials: true,
isEvalSupported: false
});
}
async setOutlinePageNumbers(pdfDocument, outlineArray) {
outlineArray.forEach(async (outline) => {
await this.setNestedOutlinePageNumbers(pdfDocument, outline);
});
}
async setNestedOutlinePageNumbers(pdfDocument, outline) {
outline.pageNumber = await this.getOutlinePageNumber(pdfDocument, outline);
outline.items.forEach(async (outlineItem) => {
outlineItem.pageNumber = await this.getOutlinePageNumber(pdfDocument, outlineItem);
this.setNestedOutlinePageNumbers(pdfDocument, outlineItem);
});
}
async getOutlinePageNumber(pdfDocument, outline) {
const dest = outline.dest;
const pageIndex = await pdfDocument.getPageIndex(dest[0]);
return Number(pageIndex) + 1;
}
downloadFile(url, filename) {
this.downloadManager.download(null, url, filename);
}
setPageNumber(pageNumber) {
this.pdfViewer.currentPageNumber = pageNumber;
}
getPageNumber() {
return this.pdfViewer.currentPageNumber;
}
changePageNumber(numPages) {
this.pdfViewer.currentPageNumber += numPages;
}
search(operation) {
const command = operation.reset ? '' : 'again';
const data = {
source: this.pdfViewer,
type: command,
query: operation.searchTerm,
phraseSearch: true,
caseSensitive: operation.matchCase,
entireWord: operation.wholeWord,
highlightAll: operation.highlightAll,
findPrevious: operation.previous,
};
this.pdfViewer.eventBus.dispatch('find', data);
}
clearSearch() {
this.pdfViewer.eventBus.dispatch('findbarclose', {});
}
navigateTo(destination) {
if (destination instanceof Object) {
if (!destination[1].name.includes('XYZ')) {
destination[1] = { name: 'XYZ' };
destination[2] = destination[2] || null;
destination[3] = destination[3] || null;
}
destination[4] = this.zoomValue;
}
this.nativeNavigate(destination);
}
nativeNavigate(destination) {
this.pdfViewer.linkService.goToDestination(destination);
}
setZoom(zoomValue) {
this.pdfViewer.currentScaleValue = this.getZoomValue(zoomValue).toString();
this.zoomValue = +this.pdfViewer.currentScaleValue;
this.toolbarEvents.zoomValueSubject.next(this.zoomValue);
}
stepZoom(zoomValue) {
this.pdfViewer.currentScaleValue = this.getZoomValue((+this.pdfViewer.currentScaleValue) + zoomValue).toString();
this.zoomValue = +this.pdfViewer.currentScaleValue;
this.toolbarEvents.zoomValueSubject.next(this.zoomValue);
}
getZoomValue(zoomValue) {
if (isNaN(zoomValue)) {
return this.zoomValue;
}
if (zoomValue > 5) {
return 5;
}
if (zoomValue < 0.1) {
return 0.1;
}
return +zoomValue.toFixed(2);
}
rotate(rotation) {
return this.pdfViewer.pagesRotation = (this.pdfViewer.pagesRotation + rotation) % 360;
}
resetRotation(rotation) {
return this.pdfViewer.pagesRotation = rotation;
}
getNormalisedPagesRotation() {
return this.pdfViewer.pagesRotation;
}
getCurrentPDFZoomValue() {
return +this.pdfViewer.currentScaleValue;
}
setCurrentPDFTitle(title) {
this.documentTitle = title;
}
getCurrentPDFTitle() {
return this.documentTitle;
}
}
class PdfJsWrapperFactory {
constructor(toolbarEvents) {
this.toolbarEvents = toolbarEvents;
this.linkService = new pdfjsViewer.PDFLinkService();
this.eventBus = new pdfjsViewer.EventBus();
}
create(container) {
const pdfFindController = new pdfjsViewer.PDFFindController({
linkService: this.linkService,
eventBus: this.eventBus
});
const pdfViewer = new pdfjsViewer.PDFViewer({
container: container.nativeElement,
linkService: this.linkService,
findController: pdfFindController,
eventBus: this.eventBus,
imageResourcesPath: '/assets/images/',
});
this.linkService.setViewer(pdfViewer);
this.pdfJsWrapper = new PdfJsWrapper(pdfViewer, new pdfjsViewer.DownloadManager(), this.toolbarEvents, new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject());
return this.pdfJsWrapper;
}
pdfWrapper() {
return this.pdfJsWrapper;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PdfJsWrapperFactory, deps: [{ token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PdfJsWrapperFactory, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PdfJsWrapperFactory, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }] });
class PrintService {
printDocumentNatively(url) {
const printWindow = window.open(url);
printWindow.focus();
setTimeout((printer) => {
printer.print();
}, 3000, printWindow);
}
printElementNatively(element, width, height) {
const printWindow = window.open('', '', `left=0,top=0,width=${width},height=${height},toolbar=0,scrollbars=0,status=0`);
const documentHead = document.head;
printWindow.document.body.appendChild(documentHead.cloneNode(true));
printWindow.document.body.appendChild(element.cloneNode(true));
printWindow.document.close();
printWindow.focus();
printWindow.print();
setTimeout((printer) => {
printer.close();
}, 1000, printWindow);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PrintService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PrintService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PrintService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
const getIcpState = createSelector(getMVState, (state) => state.icp);
const getIcpSession = createSelector(getIcpState, getIcpSession$1);
const getCaseId = createSelector(getIcpSession, session => session === null ? null : session.caseId);
const getPresenter = createSelector(getIcpState, getPresenter$1);
const getPresenterName = createSelector(getPresenter, presenter => presenter === null ? null : presenter.username);
const getClient = createSelector(getIcpState, getClient$1);
const isPresenter = createSelector(getPresenter, getClient, (presenter, client) => presenter === null ? undefined : presenter.id === client.id // isPresenter is called when = false.
);
const getParticipants = createSelector(getIcpState, getParticipants$1);
var IcpEvents;
(function (IcpEvents) {
IcpEvents["SESSION_JOINED"] = "IcpClientJoinedSession";
IcpEvents["CLIENT_DISCONNECTED"] = "IcpClientDisconnectedFromSession";
IcpEvents["NEW_PARTICIPANT_JOINED"] = "IcpNewParticipantJoinedSession";
IcpEvents["REMOVE_PARTICIPANT"] = "IcpRemoveParticipantFromList";
IcpEvents["PARTICIPANTS_UPDATED"] = "IcpParticipantsListUpdated";
IcpEvents["UPDATE_PRESENTER"] = "IcpNewPresenterStartsPresenting";
IcpEvents["PRESENTER_UPDATED"] = "IcpPresenterUpdated";
IcpEvents["UPDATE_SCREEN"] = "IcpUpdateScreen";
IcpEvents["SCREEN_UPDATED"] = "IcpScreenUpdated";
IcpEvents["SESSION_JOIN"] = "IcpClientJoinSession";
IcpEvents["SESSION_LEAVE"] = "IcpClientLeaveSession";
})(IcpEvents || (IcpEvents = {}));
class SocketService {
constructor() {
this.connected$ = new BehaviorSubject(false);
this.sessionJoined$ = new Subject();
this.presenterUpdated$ = new Subject();
this.clientDisconnected$ = new Subject();
this.participantUpdated$ = new Subject();
this.newParticipantJoined$ = new Subject();
this.screenUpdated$ = new Subject();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
connect(url, session) {
const socketUrl = new URL(url);
socketUrl.searchParams.append('sessionId', `${session.sessionId}`);
socketUrl.searchParams.append('caseId', `${session.caseId}`);
socketUrl.searchParams.append('documentId', `${session.documentId}`);
this.subscription = this.getSocketClient(socketUrl.toString()).subscribe((socket) => {
socket.onopen = (event) => {
this.connected$.next(true);
};
socket.onmessage = (event) => {
console.log('onmessage');
const eventData = JSON.parse(event.data);
if (eventData.data && eventData.data.eventName) {
this.messageEventHandller(eventData.data.eventName, eventData.data.data);
}
};
socket.onerror = (event) => {
console.log('onerror');
};
socket.onclose = (event) => {
console.log('onclose');
};
});
}
connected() {
return this.connected$.asObservable();
}
join(session) {
this.emit(IcpEvents.SESSION_JOIN, session);
}
leave(session) {
this.emit(IcpEvents.SESSION_LEAVE, session);
this.subscription.unsubscribe();
}
emit(event, data) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({
type: 'event',
event,
data
}));
}
else {
console.warn('WebSocket is not open. Ready state is:', this.socket ? this.socket.readyState : 'no socket');
}
}
listen(event) {
switch (event) {
case IcpEvents.SESSION_JOINED: {
return this.sessionJoined$.asObservable();
}
case IcpEvents.PRESENTER_UPDATED: {
return this.presenterUpdated$.asObservable();
}
case IcpEvents.CLIENT_DISCONNECTED: {
return this.clientDisconnected$.asObservable();
}
case IcpEvents.PARTICIPANTS_UPDATED: {
return this.participantUpdated$.asObservable();
}
case IcpEvents.NEW_PARTICIPANT_JOINED: {
return this.newParticipantJoined$.asObservable();
}
case IcpEvents.SCREEN_UPDATED: {
return this.screenUpdated$.asObservable();
}
default: {
break;
}
}
}
messageEventHandller(eventName, data) {
switch (eventName) {
case IcpEvents.SESSION_JOINED: {
this.sessionJoined$.next(data);
break;
}
case IcpEvents.PRESENTER_UPDATED: {
this.presenterUpdated$.next(data);
break;
}
case IcpEvents.CLIENT_DISCONNECTED: {
this.clientDisconnected$.next();
break;
}
case IcpEvents.PARTICIPANTS_UPDATED: {
this.participantUpdated$.next(data);
break;
}
case IcpEvents.NEW_PARTICIPANT_JOINED: {
this.newParticipantJoined$.next();
break;
}
case IcpEvents.SCREEN_UPDATED: {
this.screenUpdated$.next(data);
break;
}
}
}
getSocketClient(url) {
this.socket = new WebSocket(url, 'json.webpubsub.azure.v1');
return of(this.socket);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SocketService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SocketService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SocketService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
class IcpUpdateService {
constructor(socket) {
this.socket = socket;
}
joinSession(username, session, token) {
this.session = session;
this.socket.connect(`${session.connectionUrl}?access_token=${token}`, session);
if (this.joinSessionSubscription) {
this.joinSessionSubscription.unsubscribe();
}
this.joinSessionSubscription = this.socket.connected().subscribe(isConnected => {
if (isConnected) {
this.socket.join({ ...this.session, username });
}
});
return this.socket.listen(IcpEvents.SESSION_JOINED);
}
leaveSession() {
if (this.joinSessionSubscription) {
this.joinSessionSubscription.unsubscribe();
this.joinSessionSubscription = null;
}
this.socket.leave(this.session);
}
newParticipantJoined() {
return this.socket.listen(IcpEvents.NEW_PARTICIPANT_JOINED);
}
clientDisconnected() {
return this.socket.listen(IcpEvents.CLIENT_DISCONNECTED);
}
removeParticipant(participantId) {
this.socket.emit(IcpEvents.REMOVE_PARTICIPANT, {
participantId: participantId,
caseId: this.session.caseId,
documentId: this.session.documentId
});
}
participantListUpdated() {
return this.socket.listen(IcpEvents.PARTICIPANTS_UPDATED);
}
updatePresenter(presenter) {
this.socket.emit(IcpEvents.UPDATE_PRESENTER, {
...this.session,
presenterId: presenter.id,
presenterName: presenter.username
});
}
presenterUpdated() {
return this.socket.listen(IcpEvents.PRESENTER_UPDATED);
}
updateScreen(screen) {
const update = {
body: screen,
caseId: this.session.caseId,
documentId: this.session.documentId
};
this.socket.emit(IcpEvents.UPDATE_SCREEN, update);
}
screenUpdated() {
return this.socket.listen(IcpEvents.SCREEN_UPDATED);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpUpdateService, deps: [{ token: SocketService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpUpdateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpUpdateService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: SocketService }] });
class IcpPresenterService {
constructor(toolbarEvents, socketService, store) {
this.toolbarEvents = toolbarEvents;
this.socketService = socketService;
this.store = store;
}
update(isPresenter) {
if (isPresenter) {
this.subscribe();
}
else {
this.unsubscribe();
}
}
subscribe() {
if (!this.$subscription) {
this.$subscription = this.store.pipe(select(getPdfPosition)).subscribe(pdfPosition => {
this.pdfPosition = pdfPosition;
this.onPositionUpdate(pdfPosition);
});
this.$subscription.add(this.store.pipe(select(getPresenter)).subscribe(presenter => {
this.presenter = presenter;
}));
this.$subscription.add(this.socketService.newParticipantJoined().subscribe(() => this.onNewParticipantJoined()));
}
}
unsubscribe() {
if (this.$subscription) {
this.$subscription.unsubscribe();
this.$subscription = undefined;
}
}
onPositionUpdate(pdfPosition) {
const screen = { pdfPosition, document: undefined };
this.socketService.updateScreen(screen);
}
onNewParticipantJoined() {
this.onPositionUpdate(this.pdfPosition);
this.socketService.updatePresenter(this.presenter);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpPresenterService, deps: [{ token: ToolbarEventService }, { token: IcpUpdateService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpPresenterService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpPresenterService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: IcpUpdateService }, { type: i1.Store }] });
class IcpFollowerService {
constructor(toolbarEvents, viewerEvents, socketService, store) {
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.socketService = socketService;
this.store = store;
this.previousRotation = null;
}
update(isFollower) {
if (isFollower) {
this.subscribe();
}
else {
this.unsubscribe();
}
}
subscribe() {
if (!this.$subscription) {
this.$subscription = this.socketService.screenUpdated()
.subscribe(screen => this.followScreenUpdate(screen));
}
}
unsubscribe() {
if (this.$subscription) {
this.$subscription.unsubscribe();
this.$subscription = undefined;
}
}
followScreenUpdate({ pdfPosition }) {
if (pdfPosition) {
this.viewerEvents.goToDestinationICP([
pdfPosition.pageNumber - 1,
{ 'name': 'XYZ' },
pdfPosition.left,
pdfPosition.top
]);
}
this.store.pipe(select(getPdfPosition), take(1), distinctUntilChanged(undefined, a => a.rotation))
.subscribe(position => {
if (this.previousRotation === pdfPosition.rotation) {
return;
}
const rotationDelta = (pdfPosition.rotation - position.rotation) % 360;
if (rotationDelta && rotationDelta !== 0) {
this.toolbarEvents.rotate(rotationDelta);
}
});
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpFollowerService, deps: [{ token: ToolbarEventService }, { token: ViewerEventService }, { token: IcpUpdateService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpFollowerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpFollowerService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ViewerEventService }, { type: IcpUpdateService }, { type: i1.Store }] });
class IcpService {
constructor(toolbarEvents, viewerEvents, socketService, presenterSubscriptions, followerSubscriptions, store, icpEventService) {
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.socketService = socketService;
this.presenterSubscriptions = presenterSubscriptions;
this.followerSubscriptions = followerSubscriptions;
this.store = store;
this.icpEventService = icpEventService;
this.subscription = this.store.pipe(select(getCaseId), filter$1(value => !!value)).subscribe(caseId => {
this.caseId = caseId;
});
this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
this.subscription.add(this.icpEventService.sessionLaunch.subscribe(() => {
if (this.caseId && this.documentId) {
this.launchSession();
}
}));
}
ngOnDestroy() {
this.unsubscribeSession();
this.subscription.unsubscribe();
}
launchSession() {
this.store.dispatch(new LoadIcpSession({ caseId: this.caseId, documentId: this.documentId }));
this.subscription.add(this.store.pipe(select(getIcpSession), filter$1(value => !!value && Object.keys(value).length > 1), take(1)).subscribe(() => { this.setUpSessionSubscriptions(); }));
}
setUpSessionSubscriptions() {
this.sessionSubscription = this.icpEventService.becomingPresenter.subscribe(() => this.becomePresenter());
this.sessionSubscription.add(this.icpEventService.stoppingPresenting.subscribe(() => this.stopPresenting()));
this.sessionSubscription.add(this.icpEventService.sessionExitConfirmed.subscribe(() => this.leavePresentation()));
this.sessionSubscription.add(this.store.pipe(select(getPresenter)).subscribe(presenter => this.presenter = presenter));
this.sessionSubscription.add(this.store.pipe(select(getClient)).subscribe(client => this.client = client));
this.sessionSubscription.add(this.store.pipe(select(isPresenter)).subscribe(isPresenter => {
this.isPresenter = isPresenter;
this.presenterSubscriptions.update(isPresenter);
this.followerSubscriptions.update(!isPresenter);
}));
this.sessionSubscription.add(this.socketService.clientDisconnected().subscribe(cli => this.clientDisconnected(cli)));
this.sessionSubscription.add(this.socketService.presenterUpdated().subscribe(pres => {
this.store.dispatch(new IcpPresenterUpdated(pres));
}));
this.sessionSubscription.add(this.socketService.participantListUpdated().subscribe(participants => {
this.store.dispatch(new IcpParticipantListUpdated(participants));
}));
}
unsubscribeSession() {
this.presenterSubscriptions.update(false);
this.followerSubscriptions.update(false);
this.sessionSubscription.unsubscribe();
}
leavePresentation() {
if (this.isPresenter) {
this.stopPresenting();
}
this.removeParticipant(this.client.id);
this.socketService.leaveSession();
this.store.dispatch(new LeaveIcpSocketSession());
this.unsubscribeSession();
}
stopPresenting() {
const presenter = { username: '', id: '' };
this.socketService.updatePresenter(presenter);
}
becomePresenter() {
this.socketService.updatePresenter(this.client);
}
clientDisconnected(client) {
if (client === this.presenter.id) {
this.stopPresenting();
}
this.removeParticipant(client);
}
removeParticipant(participantId) {
this.socketService.removeParticipant(participantId);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpService, deps: [{ token: ToolbarEventService }, { token: ViewerEventService }, { token: IcpUpdateService }, { token: IcpPresenterService }, { token: IcpFollowerService }, { token: i1.Store }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ViewerEventService }, { type: IcpUpdateService }, { type: IcpPresenterService }, { type: IcpFollowerService }, { type: i1.Store }, { type: IcpEventService }] });
class ToolbarButtonVisibilityService {
constructor() {
this.showPrint = false;
this.showDownload = false;
this.showNavigation = false;
this.showZoom = false;
this.showRotate = false;
this.showPresentationMode = false;
this.showRedact = false;
this.showOpenFile = false;
this.showBookmark = false;
this.showHighlightButton = false;
this.showDrawButton = false;
this.showSearchBar = false;
this.showSidebar = false;
this.showCommentSummary = false;
this.showGrabNDragButton = false;
this.showSaveRotationButton = false;
}
/**
* Reset the visibility of all buttons to false then set the value from the options
*/
setup(options) {
this.showPrint = false;
this.showDownload = false;
this.showNavigation = false;
this.showZoom = false;
this.showRotate = false;
this.showPresentationMode = false;
this.showRedact = false;
this.showOpenFile = false;
this.showBookmark = false;
this.showHighlightButton = false;
this.showDrawButton = false;
this.showSearchBar = false;
this.showSidebar = false;
this.showCommentSummary = false;
this.showGrabNDragButton = false;
this.showSaveRotationButton = false;
for (const key in options) {
this[key] = options[key];
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarButtonVisibilityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarButtonVisibilityService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarButtonVisibilityService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
/**
* Default toolbar state for the PDF viewer
*/
const defaultPdfOptions = {
showPrint: true,
showDownload: true,
showNavigation: true,
showZoom: true,
showRotate: true,
showSearchBar: true,
showSidebar: true,
showGrabNDragButton: true,
showCommentSummary: true,
showPresentationMode: true,
showRedact: true
};
/**
* Default toolbar state for the image viewer
*/
const defaultImageOptions = {
showPrint: true,
showDownload: true,
showZoom: true,
showRotate: true,
showGrabNDragButton: true,
showCommentSummary: true,
showRedact: true
};
/**
* Default toolbar state for multimedia
*/
const defaultMultimediaOptions = {
showDownload: true
};
/**
* Default toolbar state for unsupported media
*/
const defaultUnsupportedOptions = {
showDownload: true,
showPrint: true
};
class RedactionSearchBarComponent {
constructor(store, toolbarButtons, toolbarEvents, highlightService) {
this.store = store;
this.toolbarButtons = toolbarButtons;
this.toolbarEvents = toolbarEvents;
this.highlightService = highlightService;
this.highlightAll = true;
this.matchCase = false;
this.wholeWord = false;
this.resultsText = '';
this.searchText = '';
this.resultCount = 0;
this.redactElements = [];
this.advancedSearchVisible = false;
}
ngOnInit() {
this.subscription = this.toolbarEvents.redactionSerachSubject.subscribe((results) => this.redactAllSearched(results));
this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
this.subscription.add(this.store.pipe(select(getPages)).subscribe((pages) => {
if (pages[1]) {
this.allPages = pages;
}
}));
this.subscription.add(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
this.subscription.add(this.toolbarEvents.openRedactionSearch.subscribe((searchMode) => {
if (searchMode) {
this.openSearchModal = searchMode.isOpen;
this.searchType = searchMode.modeType;
this.modeText();
}
}));
this.subscription.add(this.toolbarEvents.redactAllInProgressSubject
.subscribe(inProgress => this.redactAllInProgress = inProgress));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
onWindowKeyDown(e) {
if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
e.preventDefault();
this.toolbarEvents.searchBarHidden.next(false);
setTimeout(() => this.findInput.nativeElement.focus(), 200);
}
}
search(reset = true) {
this.redactAll = !reset;
if (this.redactAll) {
this.toolbarEvents.redactAllInProgressSubject.next(true);
}
if (reset) {
this.redactElements = [];
}
this.toolbarEvents.search({
searchTerm: this.searchText,
highlightAll: this.highlightAll,
matchCase: this.matchCase,
wholeWord: this.wholeWord,
previous: false,
reset
});
}
modeText() {
if (this.searchType === SearchType.Highlight) {
this.inProgressText = 'Highlighting';
this.titleText = 'Highlight';
}
else if (this.searchType === SearchType.Redact) {
this.inProgressText = 'Redacting';
this.titleText = 'Redact';
}
}
saveRedaction(redactRectangle) {
const redaction = redactRectangle.map(ele => {
return { page: ele.page, rectangles: ele.rectangles, redactionId: v4(), documentId: this.documentId };
});
this.store.dispatch(new SaveBulkRedaction({ searchRedactions: redaction }));
}
existInRedactElements(pageNumber, matechedIndex, rectangles) {
if (this.redactElements && this.redactElements.length > 0) {
const pagesFound = this.redactElements.find(re => re.page === pageNumber && re.matchedIndex === matechedIndex);
const pageRectangles = pagesFound?.rectangles;
if (!pageRectangles || pageRectangles.length <= 0) {
return false;
}
let matchesRectangles = 0;
for (let rectIndx = 0; rectIndx < pageRectangles.length; rectIndx++) {
const rectangle = pageRectangles[rectIndx];
const foundRectangle = rectangles.find(re => re.width === rectangle.width &&
re.height === rectangle.height && re.x === rectangle.x && re.y === rectangle.y);
if (foundRectangle) {
matchesRectangles++;
}
}
return pageRectangles.length === matchesRectangles;
}
return false;
}
onCloseSearchModal() {
this.toolbarEvents.openRedactionSearch.next({ isOpen: false, modeType: this.searchType });
}
setSearchResultsCount(results) {
this.resultCount = results.total;
this.resultsText = this.resultCount > 0
? `${results.total} results founds`
: 'No results found';
}
redactAllSearched(results) {
const $this = this;
const intervalId = setInterval(() => {
const highlightElement = document.getElementsByClassName('highlight selected');
if (highlightElement && highlightElement.length > 0) {
clearInterval(intervalId);
$this.redactAllSearchedTick(results);
}
}, 100);
}
redactAllSearchedTick(results) {
const highlightElement = document.getElementsByClassName('highlight selected');
if (highlightElement && highlightElement.length > 0) {
this.resultCount = results.matchesCount;
const pageNumber = results.page + 1;
const rectangles = this.getRectangles(pageNumber);
if (rectangles && this.redactElements.length <= this.resultCount
&& !this.existInRedactElements(pageNumber, results.matchedIndex, rectangles)) {
this.redactElements.push({ page: pageNumber, matchedIndex: results?.matchedIndex, rectangles });
this.CreateRedactAllText();
}
if (this.redactAll && this.resultCount && this.resultCount > 0
&& rectangles && this.redactElements.length < this.resultCount) {
this.search(false);
}
if (this.redactAll && this.resultCount && this.redactElements.length === this.resultCount) {
this.redactAll = false;
this.redactAllText = null;
if (this.searchType === SearchType.Redact) {
this.saveRedaction(this.redactElements);
}
else if (this.searchType === SearchType.Highlight) {
this.highlightService.saveAnnotationSet(this.redactElements);
this.cleanUpPostSave();
}
}
}
}
cleanUpPostSave() {
this.toolbarEvents.redactAllInProgressSubject.next(false);
this.searchText = '';
this.search();
}
CreateRedactAllText() {
this.redactAllText = `${this.redactElements.length} of ${this.resultCount}`;
}
onEscapeKeyPress(e) {
this.toolbarEvents.searchBarHidden.next(true);
}
onEnterKeyPress(e) {
this.search();
}
toggleSearchBar() {
this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
}
getRectangles(page) {
this.pageHeight = this.allPages[page].styles.height;
this.pageWidth = this.allPages[page].styles.width;
this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
const selectedHighLightedElements = document.getElementsByClassName('highlight selected');
if (selectedHighLightedElements && selectedHighLightedElements.length > 0) {
const docRange = document.createRange();
if (some(selectedHighLightedElements, element => element.className === 'highlight begin selected' || element.className === 'highlight end selected')) {
docRange.setStart(selectedHighLightedElements[0], 0);
const endNode = selectedHighLightedElements[selectedHighLightedElements.length - 1];
docRange.setEnd(endNode, endNode.childNodes.length);
}
else {
docRange.selectNodeContents(selectedHighLightedElements[0]);
}
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(docRange);
if (selection.rangeCount && !selection.isCollapsed) {
const range = selection.getRangeAt(0).cloneRange();
const clientRects = range.getClientRects();
if (clientRects) {
const parentRect = HtmlTemplatesHelper.getAdjustedBoundingRect(selectedHighLightedElements[0]?.parentElement?.parentElement);
const selectionRectangles = [];
for (let i = 0; i < clientRects.length; i++) {
const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
const findSelecttionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
if (!findSelecttionRectangle) {
selectionRectangles.push(selectionRectangle);
}
}
return selectionRectangles;
}
}
}
}
createTextRectangle(rect, parentRect) {
const height = rect.bottom - rect.top;
const width = rect.right - rect.left;
const top = rect.top - parentRect.top;
const left = rect.left - parentRect.left;
let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
rectangle = { id: v4(), ...rectangle };
return rectangle;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionSearchBarComponent, deps: [{ token: i1.Store }, { token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: RedactionSearchBarComponent, isStandalone: false, selector: "mv-redaction-search-bar", host: { listeners: { "window:keydown": "onWindowKeyDown($event)" } }, viewQueries: [{ propertyName: "findInput", first: true, predicate: ["findInput"], descendants: true, static: true }], ngImport: i0, template: "<div\n *ngIf=\"openSearchModal\"\n class=\"searchbar redaction-search-bar govuk-!-padding-3\"\n>\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full govuk-!-padding-right-0\">\n <div class=\"redaction-search-buttons-area\">\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button searchbar-button--close\"\n title=\"Close Search\"\n [attr.aria-label]=\"'Close Search' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onCloseSearchModal()\"\n ></button>\n </div>\n <input\n id=\"search_input\"\n class=\"govuk-input govuk-!-width-three-quarters govuk-!-display-inline-block govuk-!-margin-top-5\"\n type=\"text\"\n aria-label=\"Redact from search\"\n #findInput\n title=\"Redact from search\"\n placeholder=\"{{ titleText + ' from search...' | rpxTranslate }}\"\n data-l10n-id=\"search_input\"\n [ngModel]=\"searchText\"\n (ngModelChange)=\"searchText = $event\"\n (keydown.escape)=\"onEscapeKeyPress($event)\"\n (keydown.enter)=\"onEnterKeyPress($event)\"\n />\n <div class=\"redaction-search-buttons-area\">\n <button\n id=\"mvSearchAllBtn\"\n class=\"govuk-button govuk-!-margin-bottom-0\"\n data-module=\"govuk-button\"\n (click)=\"search()\"\n [disabled]=\"redactAllInProgress\"\n >\n {{ \"Search\" | rpxTranslate }}\n </button>\n <button\n id=\"mvRedactAllBtn\"\n class=\"govuk-button govuk-!-margin-left-2 govuk-!-margin-bottom-0\"\n data-module=\"govuk-button\"\n (click)=\"search(false)\"\n [disabled]=\"redactAllInProgress || resultCount <= 0\"\n >\n {{ titleText + \" all\" | rpxTranslate }}\n </button>\n </div>\n <div\n class=\"govuk-grid-column-three-quarters govuk-!-padding-left-0 govuk-!-padding-top-2 redaction-status-text\"\n >\n <span\n id=\"findRedactResultsCount\"\n class=\"govuk-grid-column-full govuk-!-margin-right-4 govuk-!-margin-left-0 govuk-!-padding-left-0\"\n ><h4\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n *ngIf=\"redactAllInProgress\"\n >\n {{ inProgressText + \" in progress\" | rpxTranslate }}\n </h4>\n {{ redactAllInProgress ? \"\" : resultsText }}</span\n >\n <span>\n <h5\n id=\"redactAllInProgressCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n >\n {{ redactAllInProgress ? redactAllText : \"\" }}\n </h5>\n </span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".redaction-search-buttons-area{display:flex;flex-direction:row;padding-top:.5rem}.redaction-search-bar{width:20rem;left:73%;top:2%}.redaction-status-text{height:2.6rem}\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redaction-search-bar', standalone: false, template: "<div\n *ngIf=\"openSearchModal\"\n class=\"searchbar redaction-search-bar govuk-!-padding-3\"\n>\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full govuk-!-padding-right-0\">\n <div class=\"redaction-search-buttons-area\">\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button searchbar-button--close\"\n title=\"Close Search\"\n [attr.aria-label]=\"'Close Search' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onCloseSearchModal()\"\n ></button>\n </div>\n <input\n id=\"search_input\"\n class=\"govuk-input govuk-!-width-three-quarters govuk-!-display-inline-block govuk-!-margin-top-5\"\n type=\"text\"\n aria-label=\"Redact from search\"\n #findInput\n title=\"Redact from search\"\n placeholder=\"{{ titleText + ' from search...' | rpxTranslate }}\"\n data-l10n-id=\"search_input\"\n [ngModel]=\"searchText\"\n (ngModelChange)=\"searchText = $event\"\n (keydown.escape)=\"onEscapeKeyPress($event)\"\n (keydown.enter)=\"onEnterKeyPress($event)\"\n />\n <div class=\"redaction-search-buttons-area\">\n <button\n id=\"mvSearchAllBtn\"\n class=\"govuk-button govuk-!-margin-bottom-0\"\n data-module=\"govuk-button\"\n (click)=\"search()\"\n [disabled]=\"redactAllInProgress\"\n >\n {{ \"Search\" | rpxTranslate }}\n </button>\n <button\n id=\"mvRedactAllBtn\"\n class=\"govuk-button govuk-!-margin-left-2 govuk-!-margin-bottom-0\"\n data-module=\"govuk-button\"\n (click)=\"search(false)\"\n [disabled]=\"redactAllInProgress || resultCount <= 0\"\n >\n {{ titleText + \" all\" | rpxTranslate }}\n </button>\n </div>\n <div\n class=\"govuk-grid-column-three-quarters govuk-!-padding-left-0 govuk-!-padding-top-2 redaction-status-text\"\n >\n <span\n id=\"findRedactResultsCount\"\n class=\"govuk-grid-column-full govuk-!-margin-right-4 govuk-!-margin-left-0 govuk-!-padding-left-0\"\n ><h4\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n *ngIf=\"redactAllInProgress\"\n >\n {{ inProgressText + \" in progress\" | rpxTranslate }}\n </h4>\n {{ redactAllInProgress ? \"\" : resultsText }}</span\n >\n <span>\n <h5\n id=\"redactAllInProgressCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n >\n {{ redactAllInProgress ? redactAllText : \"\" }}\n </h5>\n </span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".redaction-search-buttons-area{display:flex;flex-direction:row;padding-top:.5rem}.redaction-search-bar{width:20rem;left:73%;top:2%}.redaction-status-text{height:2.6rem}\n"] }]
}], ctorParameters: () => [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: HighlightCreateService }], propDecorators: { findInput: [{
type: ViewChild,
args: ['findInput', { static: true }]
}], onWindowKeyDown: [{
type: HostListener,
args: ['window:keydown', ['$event']]
}] } });
// TODO: replace by NgRx
class CommentService {
constructor() {
this.unsavedChanges = new Subject();
this.marginToCommentEmitter = new BehaviorSubject(false);
}
setCommentSet(commentSetComponent) {
this.commentSetComponent = commentSetComponent;
}
onCommentChange(changes) {
this.unsavedChanges.next(changes);
}
getUnsavedChanges() {
return this.unsavedChanges.asObservable();
}
hasUnsavedComments(annotation) {
if (annotation.comments.length > 0) {
const comment = this.getComment(annotation);
return comment.hasUnsavedChanges;
}
return false;
}
updateUnsavedCommentsStatus(annotation, hasUnsavedChanges) {
const comment = this.getComment(annotation);
comment.hasUnsavedChanges = hasUnsavedChanges;
this.allCommentsSaved();
}
getComment(annotation) {
return this.commentSetComponent.commentComponents
.find(c => c.comment.annotationId === annotation.comments[0].annotationId);
}
resetCommentSet() {
this.commentSetComponent = null;
}
allCommentsSaved() {
this.onCommentChange(this.commentSetComponent.commentComponents.some(comment => comment.hasUnsavedChanges === true));
}
createMarginToCommentEvent(margin) {
this.marginToCommentEmitter.next(margin);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentService, decorators: [{
type: Injectable
}] });
class CommentSetRenderService {
redrawComponents(commentComponents, pageHeights, rotate, zoom) {
let prevComment;
this.sortComponents(commentComponents, pageHeights, rotate, zoom).forEach((comment) => {
this.adjustIfOverlapping(comment, prevComment, zoom);
prevComment = comment;
});
}
sortComponents(commentComponents, pageHeights, rotate, zoom) {
return commentComponents.sort((a, b) => {
a.rectTop = this.top(a._rectangle, pageHeights[a.page - 1], rotate, zoom);
b.rectTop = this.top(b._rectangle, pageHeights[b.page - 1], rotate, zoom);
return this.processSort(a, b);
});
}
adjustIfOverlapping(comment, prevComment, zoom) {
if (prevComment) {
const endOfPrevComment = prevComment.commentTop + this.height(prevComment);
if (comment.commentTop <= endOfPrevComment) {
comment.rectTop = (endOfPrevComment - comment.totalPrevPagesHeight) / zoom;
}
}
}
processSort(a, b) {
if (this.onSameLine(a, b)) {
return a.rectLeft >= b.rectLeft ? 1 : -1;
}
return a.commentTop >= b.commentTop ? 1 : -1;
}
onSameLine(a, b) {
return this.difference(a.commentTop, b.commentTop) === 0;
}
top(rectangle, height, rotate, zoom) {
const actualHeight = height / zoom;
switch (rotate) {
case 90: return rectangle.x;
case 180: return actualHeight - (rectangle.y + rectangle.height);
case 270: return actualHeight - (rectangle.x + rectangle.width);
default: return rectangle.y;
}
}
height(element) {
return HtmlTemplatesHelper.getAdjustedBoundingRect(element.form.nativeElement)?.height | 0;
}
difference(a, b) { return Math.abs(a - b); }
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetRenderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetRenderService, decorators: [{
type: Injectable
}] });
class TagsServices {
constructor(http) {
this.http = http;
this.snakeCase = string => {
// transform string_to_snake_case
return string.replace(/ +/g, ' ') // find space
.split(/ |\B(?=[A-Z])/) // split it into array
.map(word => word.toLowerCase()) // transform to lover case
.join('_'); // trun array into sting using _
};
}
getAllTags(createdBy) {
const url = `/em-anno/tags/${createdBy}`;
return this.http.get(url);
}
// @TODO: Move everything below this to NgRx store
getNewTags(annoid) {
return this.tagItems ? this.tagItems[annoid] : [];
}
updateTagItems(items, annoId) {
const snakeCased = items.map(item => {
return {
...item,
name: this.snakeCase(item.name)
};
});
this.tagItems = {
...this.tagItems,
[annoId]: snakeCased
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TagsServices, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TagsServices }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TagsServices, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class TextHighlightDirective {
constructor(element) {
this.element = element;
}
ngAfterViewChecked() {
if (this.textToHighlight) {
this.highlightInputText(this.textToHighlight);
}
}
highlightInputText(textToHighlight) {
this.resetHighlight();
this.textToHighlight = textToHighlight;
const searchPattern = new RegExp(textToHighlight, 'gi');
const hostElement = this.element.nativeElement;
if (hostElement.innerHTML.match(searchPattern)) {
hostElement.innerHTML = hostElement.innerHTML
.replace(searchPattern, this.highlightPattern('$&'));
}
this.textToHighlight = undefined;
}
resetHighlight() {
const hostElement = this.element.nativeElement;
const searchPattern = new RegExp(this.highlightPattern('(.*?)'), 'gi');
while (hostElement.innerHTML.match(searchPattern)) {
const matchGroups = searchPattern.exec(hostElement.innerHTML);
if (matchGroups) {
hostElement.innerHTML = hostElement.innerHTML.replace(matchGroups[0], matchGroups[1]);
}
}
}
highlightPattern(dynamicText) {
return '<span class="mvTextHighlight">' + dynamicText + '</span>';
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextHighlightDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: TextHighlightDirective, isStandalone: false, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextHighlightDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvTextHighlight]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { textToHighlight: [{
type: Input
}] } });
class TextareaAutoExpandDirective {
constructor(el) {
this.el = el;
}
ngAfterContentChecked() {
this.adjustHeight();
}
onMouseDown() {
this.adjustHeight();
}
adjustHeight() {
const nativeElement = this.el.nativeElement;
nativeElement.style.overflow = 'hidden';
nativeElement.style.height = 'auto';
nativeElement.style.height = nativeElement.scrollHeight - 5 + 'px';
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextareaAutoExpandDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: TextareaAutoExpandDirective, isStandalone: false, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvTextAreaAutoExpand]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onMouseDown: [{
type: HostListener,
args: ['input']
}] } });
class TagsComponent {
constructor(tagsServices) {
this.tagsServices = tagsServices;
this.validators = [this.minLength, this.maxLength20];
this.errorMessages = {
'minLength': 'Minimum of 2 characters',
'maxLength20': 'Maximum of 20 characters'
};
this.requestAutocompleteItems = (text) => {
return this.tagsServices.getAllTags(this.userId);
};
}
onUpdateTags(value) {
this.tagsServices.updateTagItems(value, this.annoId);
}
minLength(control) {
if (control.value.length < 2) {
return {
'minLength': true
};
}
return null;
}
maxLength20(control) {
if (control.value.length >= 20) {
return {
'maxLength20': true
};
}
return null;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: TagsComponent, isStandalone: false, selector: "mv-tags", inputs: { tagItems: "tagItems", userId: "userId", editable: "editable", annoId: "annoId" }, ngImport: i0, template: "<div class=\"tags\">\n <tag-input\n *ngIf=\"editable\" class=\"tags__edit\"\n [maxItems]=\"10\"\n [ngModel]=\"tagItems\"\n (ngModelChange)=\"tagItems = $event; onUpdateTags($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n [placeholder]=\"'Add tag'\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [onTextChangeDebounce]=\"250\"\n [secondaryPlaceholder]=\"'Search or add tags'\"\n [validators]=\"validators\"\n [errorMessages]=\"errorMessages\"\n [onlyFromAutocomplete]=\"false\">\n <tag-input-dropdown\n [autocompleteObservable]=\"requestAutocompleteItems\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [zIndex]=\"10000\"\n [minimumTextLength]=\"2\">\n <ng-template let-item=\"item\" let-index=\"index\">\n {{ item.label }}\n </ng-template>\n </tag-input-dropdown>\n </tag-input>\n\n <div class=\"tags__renderer\" *ngIf=\"!editable && (tagItems && tagItems.length)\">\n <span *ngFor=\"let tag of tagItems\" class=\"tags__item hmcts-badge\">\n {{tag.label}}\n </span>\n </div>\n</div>\n\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$1.TagInputComponent, selector: "tag-input", inputs: ["separatorKeys", "separatorKeyCodes", "placeholder", "secondaryPlaceholder", "maxItems", "validators", "asyncValidators", "onlyFromAutocomplete", "errorMessages", "theme", "onTextChangeDebounce", "inputId", "inputClass", "clearOnBlur", "hideForm", "addOnBlur", "addOnPaste", "pasteSplitPattern", "blinkIfDupe", "removable", "editable", "allowDupes", "modelAsStrings", "trimTags", "inputText", "ripple", "tabindex", "disable", "dragZone", "onRemoving", "onAdding", "animationDuration"], outputs: ["onAdd", "onRemove", "onSelect", "onFocus", "onBlur", "onTextChange", "onPaste", "onValidationError", "onTagEdited", "inputTextChange"] }, { kind: "component", type: i4$1.TagInputDropdown, selector: "tag-input-dropdown", inputs: ["offset", "focusFirstElement", "showDropdownIfEmpty", "autocompleteObservable", "minimumTextLength", "limitItemsTo", "displayBy", "identifyBy", "matchingFn", "appendToBody", "keepOpen", "dynamicUpdate", "zIndex", "autocompleteItems"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TagsComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-tags', encapsulation: ViewEncapsulation.None, standalone: false, template: "<div class=\"tags\">\n <tag-input\n *ngIf=\"editable\" class=\"tags__edit\"\n [maxItems]=\"10\"\n [ngModel]=\"tagItems\"\n (ngModelChange)=\"tagItems = $event; onUpdateTags($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n [placeholder]=\"'Add tag'\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [onTextChangeDebounce]=\"250\"\n [secondaryPlaceholder]=\"'Search or add tags'\"\n [validators]=\"validators\"\n [errorMessages]=\"errorMessages\"\n [onlyFromAutocomplete]=\"false\">\n <tag-input-dropdown\n [autocompleteObservable]=\"requestAutocompleteItems\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [zIndex]=\"10000\"\n [minimumTextLength]=\"2\">\n <ng-template let-item=\"item\" let-index=\"index\">\n {{ item.label }}\n </ng-template>\n </tag-input-dropdown>\n </tag-input>\n\n <div class=\"tags__renderer\" *ngIf=\"!editable && (tagItems && tagItems.length)\">\n <span *ngFor=\"let tag of tagItems\" class=\"tags__item hmcts-badge\">\n {{tag.label}}\n </span>\n </div>\n</div>\n\n" }]
}], ctorParameters: () => [{ type: TagsServices }], propDecorators: { tagItems: [{
type: Input
}], userId: [{
type: Input
}], editable: [{
type: Input
}], annoId: [{
type: Input
}] } });
/**
* A moment timezone pipe to support parsing based on time zone abbreviations
* covers all cases of offset variation due to daylight saving.
*
* Same API as DatePipe with additional timezone abbreviation support
* Official date pipe dropped support for abbreviations names from Angular V5
*/
class MomentDatePipe extends DatePipe {
transform(value, format = 'mediumDate', timezone = 'Europe/London') {
const timezoneOffset = moment(value).tz(timezone).format('Z');
return super.transform(value, format, timezoneOffset);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: MomentDatePipe, isStandalone: false, name: "momentDate" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MomentDatePipe, decorators: [{
type: Pipe,
args: [{
name: 'momentDate',
standalone: false
}]
}] });
class CommentComponent {
constructor(store, commentService, tagsServices) {
this.store = store;
this.commentService = commentService;
this.tagsServices = tagsServices;
this.CHAR_LIMIT = 5000;
this.totalPrevPagesHeight = 0;
this.hasUnsavedChanges = false;
this.commentClick = new EventEmitter();
this.renderComments = new EventEmitter();
this.delete = new EventEmitter();
this.updated = new EventEmitter();
this.changes = new EventEmitter();
this.rotate = 0;
this.zoom = 1;
}
ngOnInit() {
this.subscriptions = this.store.select(getComponentSearchText)
.pipe(distinctUntilChanged()).subscribe(searchString => this.searchString = searchString);
this.reRenderComments();
this.marginToComment$ = this.commentService.marginToCommentEmitter.asObservable();
}
ngAfterContentInit() {
if (this.tagItems && this.tagItems.length) {
this.tagsServices.updateTagItems(this.tagItems, this._comment.annotationId);
}
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
set comment(comment) {
this._comment = { ...comment };
this.page = this._comment.page;
this.lastUpdate = comment.lastModifiedDate ? comment.lastModifiedDate : comment.createdDate;
this.author = comment.createdByDetails;
this.createdBy = comment.createdBy;
this.editor = comment.lastModifiedByDetails;
this.originalComment = comment.content;
this.fullComment = this.originalComment;
this.selected = this._comment.selected;
this._editable = this._comment.editable;
this.tagItems = this._comment.tags;
const pageMarginBottom = 10;
this.totalPrevPagesHeight = 0;
for (let i = 0; i < this.page - 1; i++) {
const height = this._comment.pages[i + 1] ? this._comment.pages[i + 1].styles.height : undefined;
if (height) {
this.totalPrevPagesHeight += height + pageMarginBottom;
}
}
}
get comment() {
return this._comment;
}
set annotation(annotation) {
this._rectangle = annotation.rectangles
.reduce((prev, current) => prev.y < current.y ? prev : current);
const actualHeight = this._comment.pages[this.page].styles.height / this.zoom;
switch (this.rotate) {
case 90:
this.rectTop = this._rectangle.x;
break;
case 180:
this.rectTop = actualHeight - (this._rectangle.y + this._rectangle.height);
break;
case 270:
this.rectTop = actualHeight - (this._rectangle.x + this._rectangle.width);
break;
default: this.rectTop = this._rectangle.y;
}
this.rectLeft = this._rectangle.x;
}
get editable() {
return this._editable;
}
onCommentChange(updatedComment) {
this.hasUnsavedChanges =
this.originalComment.substring(0, this.CHAR_LIMIT) !== updatedComment.substring(0, this.CHAR_LIMIT);
this.commentService.onCommentChange(this.hasUnsavedChanges);
}
deleteOrCancel() {
if (!this.editable) {
this.delete.emit(this._comment);
}
else {
this.hasUnsavedChanges = false;
this._editable = false;
this.fullComment = this.originalComment;
this.changes.emit(false);
if (!this.author && !this.fullComment) {
this.delete.emit(this._comment);
}
}
}
editOrSave() {
if (!this.editable) {
this._editable = true;
}
else {
this._comment.content = this.fullComment.substring(0, this.CHAR_LIMIT);
const tags = this.tagsServices.getNewTags(this._comment.annotationId);
const payload = {
comment: this._comment,
tags
};
this.updated.emit(payload);
this.hasUnsavedChanges = false;
this._editable = false;
this.changes.emit(false);
}
}
onCommentClick() {
if (!this.selected) {
this.selected = true;
this._editable = false;
this.commentClick.emit({ annotationId: this._comment.annotationId, editable: this._editable, selected: true });
}
}
reRenderComments() {
this.renderComments.emit(this._comment);
}
get commentTop() {
return this.totalPrevPagesHeight + (this.rectTop * this.zoom);
}
get height() {
return HtmlTemplatesHelper.getAdjustedBoundingRect(this.form.nativeElement).height / this.zoom;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: TagsServices }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentComponent, isStandalone: false, selector: "mv-anno-comment", inputs: { rotate: "rotate", zoom: "zoom", index: "index", page: "page", comment: "comment", annotation: "annotation" }, outputs: { commentClick: "commentClick", renderComments: "renderComments", delete: "delete", updated: "updated", changes: "changes" }, viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true }, { propertyName: "editableComment", first: true, predicate: ["editableComment"], descendants: true }], ngImport: i0, template: "<div\n #form\n (click)=\"onCommentClick()\"\n class=\"aui-comment\"\n [ngClass]=\"{ stylestoggle: (marginToComment$ | async) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\"\n>\n <div id=\"detailsWrapper {{ index }}\" class=\"aui-comment__header\">\n <span *ngIf=\"author && !editor\" class=\"aui-comment__author\">\n {{ author.forename }} {{ author.surname }}\n </span>\n <span *ngIf=\"editor\" class=\"aui-comment__author\">\n {{ editor.forename }} {{ editor.surname }}\n </span>\n <time [hidden]=\"!selected && !this.editable\" class=\"aui-comment__meta\">\n {{ lastUpdate | momentDate : \"d MMMM y h:mm a\" }}\n </time>\n </div>\n <mv-tags\n [tagItems]=\"tagItems\"\n [userId]=\"createdBy\"\n [editable]=\"editable\"\n [annoId]=\"_comment.annotationId\"\n >\n </mv-tags>\n <textarea\n *ngIf=\"selected && editable\"\n #editableComment\n mvTextAreaAutoExpand\n type=\"text\"\n required\n name=\"content\"\n [maxlength]=\"CHAR_LIMIT\"\n class=\"aui-comment__content form-control mimic-focus edit-mode expanded\"\n [ngModel]=\"fullComment\"\n (ngModelChange)=\"fullComment = $event; reRenderComments(); onCommentChange($event)\"\n aria-label=\"comment\"\n >\n </textarea>\n <p\n *ngIf=\"!editable\"\n mvTextHighlight\n class=\"commentText\"\n [textToHighlight]=\"searchString\"\n >\n {{ fullComment }}\n </p>\n <div\n *ngIf=\"\n selected ||\n this.editable ||\n (!fullComment.length && tagItems && !tagItems.length)\n \"\n class=\"aui-comment__footer commentBtns\"\n >\n <button\n class=\"govuk-button\"\n type=\"button\"\n role=\"button\"\n (click)=\"editOrSave()\"\n >\n {{ !editable ? (\"Edit\" | rpxTranslate) : (\"Save\" | rpxTranslate) }}\n </button>\n <button\n type=\"button\"\n role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\"\n >\n {{ !editable ? (\"Delete\" | rpxTranslate) : (\"Cancel\" | rpxTranslate) }}\n </button>\n </div>\n <span class=\"aui-comment__private\">{{ \"private\" | rpxTranslate }}</span>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: ["textToHighlight"] }, { kind: "directive", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]" }, { kind: "component", type: TagsComponent, selector: "mv-tags", inputs: ["tagItems", "userId", "editable", "annoId"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }, { kind: "pipe", type: MomentDatePipe, name: "momentDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-anno-comment', standalone: false, template: "<div\n #form\n (click)=\"onCommentClick()\"\n class=\"aui-comment\"\n [ngClass]=\"{ stylestoggle: (marginToComment$ | async) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\"\n>\n <div id=\"detailsWrapper {{ index }}\" class=\"aui-comment__header\">\n <span *ngIf=\"author && !editor\" class=\"aui-comment__author\">\n {{ author.forename }} {{ author.surname }}\n </span>\n <span *ngIf=\"editor\" class=\"aui-comment__author\">\n {{ editor.forename }} {{ editor.surname }}\n </span>\n <time [hidden]=\"!selected && !this.editable\" class=\"aui-comment__meta\">\n {{ lastUpdate | momentDate : \"d MMMM y h:mm a\" }}\n </time>\n </div>\n <mv-tags\n [tagItems]=\"tagItems\"\n [userId]=\"createdBy\"\n [editable]=\"editable\"\n [annoId]=\"_comment.annotationId\"\n >\n </mv-tags>\n <textarea\n *ngIf=\"selected && editable\"\n #editableComment\n mvTextAreaAutoExpand\n type=\"text\"\n required\n name=\"content\"\n [maxlength]=\"CHAR_LIMIT\"\n class=\"aui-comment__content form-control mimic-focus edit-mode expanded\"\n [ngModel]=\"fullComment\"\n (ngModelChange)=\"fullComment = $event; reRenderComments(); onCommentChange($event)\"\n aria-label=\"comment\"\n >\n </textarea>\n <p\n *ngIf=\"!editable\"\n mvTextHighlight\n class=\"commentText\"\n [textToHighlight]=\"searchString\"\n >\n {{ fullComment }}\n </p>\n <div\n *ngIf=\"\n selected ||\n this.editable ||\n (!fullComment.length && tagItems && !tagItems.length)\n \"\n class=\"aui-comment__footer commentBtns\"\n >\n <button\n class=\"govuk-button\"\n type=\"button\"\n role=\"button\"\n (click)=\"editOrSave()\"\n >\n {{ !editable ? (\"Edit\" | rpxTranslate) : (\"Save\" | rpxTranslate) }}\n </button>\n <button\n type=\"button\"\n role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\"\n >\n {{ !editable ? (\"Delete\" | rpxTranslate) : (\"Cancel\" | rpxTranslate) }}\n </button>\n </div>\n <span class=\"aui-comment__private\">{{ \"private\" | rpxTranslate }}</span>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: CommentService }, { type: TagsServices }], propDecorators: { commentClick: [{
type: Output
}], renderComments: [{
type: Output
}], delete: [{
type: Output
}], updated: [{
type: Output
}], changes: [{
type: Output
}], rotate: [{
type: Input
}], zoom: [{
type: Input
}], index: [{
type: Input
}], page: [{
type: Input
}], form: [{
type: ViewChild,
args: ['form', { static: false }]
}], editableComment: [{
type: ViewChild,
args: ['editableComment', { static: false }]
}], comment: [{
type: Input
}], annotation: [{
type: Input
}] } });
class CommentSetComponent {
constructor(store, commentService, renderService, toolbarEvents) {
this.store = store;
this.commentService = commentService;
this.renderService = renderService;
this.toolbarEvents = toolbarEvents;
this.pageHeights = [];
this.subscriptions = [];
this.clearSelection();
}
ngOnInit() {
this.comments$ = this.store.pipe(select(getCommentsArray));
this.annoEntities$ = this.store.pipe(select(getAnnotationEntities));
this.subscriptions.push(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
this.redrawComments();
this.showCommentsPanel = toggle;
}));
this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument()));
}
ngOnChanges(changes) {
if (changes.annotationSet) {
this.commentService.setCommentSet(this);
}
if (changes.contentScrollTop) {
if (this.container) {
this.container.nativeElement.scrollTo(0, this.contentScrollTop);
}
}
}
ngOnDestroy() {
if (this.subscriptions.length > 0) {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
}
onSelect(annotationId) {
this.store.dispatch(new SelectedAnnotation(annotationId));
}
onCommentDelete(comment) {
const annotation = this.annotationSet.annotations.find(anno => anno.id === comment.annotationId);
const comments = [];
const annot = {
...annotation,
comments
};
this.onAnnotationUpdate(annot);
this.redrawComments();
}
redrawComments() {
setTimeout(() => {
const componentList = this.commentComponents.map(comment => comment);
this.renderService.redrawComponents(componentList, this.pageHeights, this.rotate, this.zoom);
}, 0);
}
rotateDocument() {
if (this.panel) {
this.panel.nativeElement.style.height = '0';
}
}
onCommentUpdate(payload) {
const annotation = this.annotationSet.annotations.find(anno => anno.id === payload.comment.annotationId);
const comments = [payload.comment];
const tags = payload.tags;
const annot = {
...annotation,
comments,
tags
};
this.onAnnotationUpdate(annot);
}
onAnnotationUpdate(annotation) {
this.store.dispatch(new SaveAnnotation(annotation));
}
onContainerClick(e) {
if (e.path && e.path[0] === this.panel.nativeElement) {
this.clearSelection();
}
}
clearSelection() {
this.store.dispatch(new SelectedAnnotation({ annotationId: '', editable: false, selected: false }));
}
allCommentsSaved() {
this.commentService.allCommentsSaved();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: CommentSetRenderService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentSetComponent, isStandalone: false, selector: "mv-comment-set", inputs: { annotationSet: "annotationSet", zoom: "zoom", rotate: "rotate", height: "height", pageHeights: "pageHeights", contentScrollTop: "contentScrollTop" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true }, { propertyName: "commentComponents", predicate: ["commentComponent"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #container [ngClass]=\"{'comments': showCommentsPanel}\">\n <div #panel [ngClass]=\"{ 'comments-panel comment-container': true, 'expanded': showCommentsPanel }\"\n [style.height.px]=\"height\"\n (click)=\"onContainerClick($event)\">\n <ng-container *ngFor=\"let comment of (comments$ | async); let i = index;\">\n <mv-anno-comment\n [ngStyle]=\"showCommentsPanel ? {} : {'display':'none'}\"\n #commentComponent\n (commentClick)=\"onSelect($event)\"\n (delete)=\"onCommentDelete($event)\"\n (updated)=\"onCommentUpdate($event)\"\n (changes)=\"allCommentsSaved()\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [index]=\"i\"\n [page]=\"comment.page\"\n [comment]=\"comment\"\n [annotation]=\"(annoEntities$ | async)[comment.annotationId]\"\n (renderComments)=\"redrawComments()\">\n </mv-anno-comment>\n </ng-container>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: CommentComponent, selector: "mv-anno-comment", inputs: ["rotate", "zoom", "index", "page", "comment", "annotation"], outputs: ["commentClick", "renderComments", "delete", "updated", "changes"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-set', standalone: false, template: "<div #container [ngClass]=\"{'comments': showCommentsPanel}\">\n <div #panel [ngClass]=\"{ 'comments-panel comment-container': true, 'expanded': showCommentsPanel }\"\n [style.height.px]=\"height\"\n (click)=\"onContainerClick($event)\">\n <ng-container *ngFor=\"let comment of (comments$ | async); let i = index;\">\n <mv-anno-comment\n [ngStyle]=\"showCommentsPanel ? {} : {'display':'none'}\"\n #commentComponent\n (commentClick)=\"onSelect($event)\"\n (delete)=\"onCommentDelete($event)\"\n (updated)=\"onCommentUpdate($event)\"\n (changes)=\"allCommentsSaved()\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [index]=\"i\"\n [page]=\"comment.page\"\n [comment]=\"comment\"\n [annotation]=\"(annoEntities$ | async)[comment.annotationId]\"\n (renderComments)=\"redrawComments()\">\n </mv-anno-comment>\n </ng-container>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: CommentService }, { type: CommentSetRenderService }, { type: ToolbarEventService }], propDecorators: { annotationSet: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], height: [{
type: Input
}], pageHeights: [{
type: Input
}], contentScrollTop: [{
type: Input
}], container: [{
type: ViewChild,
args: ['container', { static: false }]
}], panel: [{
type: ViewChild,
args: ['panel', { static: false }]
}], commentComponents: [{
type: ViewChildren,
args: ['commentComponent']
}] } });
class CommentsNavigateComponent {
constructor(store, toolbarEvents) {
this.store = store;
this.toolbarEvents = toolbarEvents;
this.autoSelect = false;
this.navigationList = [];
this.index = 0;
}
ngOnChanges(changes) {
if (changes.annotationList) {
this.initNavigationList();
}
}
initNavigationList() {
this.index = 0;
this.navigationList = [...this.annotationList || []]
.map(annotation => ({
content: annotation.comments[0].content,
annotationId: annotation.id,
page: annotation.page,
rectangle: this.upperRectangle(annotation.rectangles),
}))
.sort(this.sortComments);
if (this.autoSelect) {
this.toolbarEvents.setPage(Number.parseInt(this.navigationList[0].page, 0));
this.store.dispatch(new SelectedAnnotation({
annotationId: this.navigationList[0].annotationId,
editable: false,
selected: true
}));
}
}
sortComments(mappedCommentA, mappedCommentB) {
if (mappedCommentA.page !== mappedCommentB.page) {
return mappedCommentA.page - mappedCommentB.page;
}
else {
const rectA = mappedCommentA.rectangle;
const rectB = mappedCommentB.rectangle;
if (rectA.y !== rectB.y) {
return rectA.y - rectB.y;
}
else {
return rectA.x - rectB.x;
}
}
}
nextItem() {
this.index += 1;
if (this.index === this.annotationList.length) {
this.index = 0;
}
this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
this.store.dispatch(new SelectedAnnotation({
annotationId: this.navigationList[this.index].annotationId, editable: false, selected: true
}));
}
prevItem() {
this.index -= 1;
if (this.index < 0) {
this.index = this.navigationList.length - 1;
}
this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
this.store.dispatch(new SelectedAnnotation({
annotationId: this.navigationList[this.index].annotationId,
editable: false,
selected: true
}));
}
upperRectangle(rectangles) {
[...rectangles].sort((rect1, rect2) => rect1.y - rect2.y);
return { x: rectangles[0].x, y: rectangles[0].y };
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentsNavigateComponent, deps: [{ token: i1.Store }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentsNavigateComponent, isStandalone: false, selector: "mv-comments-navigate", inputs: { annotationList: "annotationList", autoSelect: "autoSelect" }, usesOnChanges: true, ngImport: i0, template: "<p class=\"comment-search\">\n <span class=\"comment-search__item\">\n Showing {{ index + 1 }} of {{ navigationList.length }}\n </span>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Previous comment\"\n [attr.aria-label]=\"'Previous comment' | rpxTranslate\"\n (click)=\"prevItem()\">Prev</a>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Next comment'\"\n [attr.aria-label]=\"'Next comment' | rpxTranslate\"\n (click)=\"nextItem()\">Next</a>\n</p>\n", dependencies: [{ kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comments-navigate', encapsulation: ViewEncapsulation.None, standalone: false, template: "<p class=\"comment-search\">\n <span class=\"comment-search__item\">\n Showing {{ index + 1 }} of {{ navigationList.length }}\n </span>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Previous comment\"\n [attr.aria-label]=\"'Previous comment' | rpxTranslate\"\n (click)=\"prevItem()\">Prev</a>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Next comment'\"\n [attr.aria-label]=\"'Next comment' | rpxTranslate\"\n (click)=\"nextItem()\">Next</a>\n</p>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: ToolbarEventService }], propDecorators: { annotationList: [{
type: Input
}], autoSelect: [{
type: Input
}] } });
class CommentSearchComponent {
constructor(store) {
this.store = store;
this.searchResults = [];
this.searchIndex = 0;
}
ngAfterViewInit() {
if (this.searchInput) {
this.searchInput.nativeElement.focus();
}
}
ngOnDestroy() {
// TODO workaround for tab error
setTimeout(() => { this.store.dispatch(new SearchComment('')); }, 250);
}
searchComments(searchText) {
this.clearSearch();
if (searchText.length > 2) {
this.searchString = searchText;
this.searchResults = this.annotations
.filter(annotation => annotation.comments.length > 0)
.filter(annotation => annotation.comments[0].content.toLowerCase().includes(this.searchString.toLowerCase()));
if (this.searchResults.length > 0) {
this.store.dispatch(new SearchComment(searchText));
}
}
}
clearSearch() {
this.searchString = undefined;
this.searchResults = [];
this.searchIndex = 0;
this.store.dispatch(new SearchComment(''));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSearchComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentSearchComponent, isStandalone: false, selector: "mv-comment-search", inputs: { annotations: "annotations" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], ngImport: i0, template: "<input\n #searchInput\n type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\"\n name=\"searchString\"\n aria-label=\"search comments input\"\n [ngModel]=\"searchString\"\n (ngModelChange)=\"searchString = $event\"\n/>\n<button\n type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\"\n>\n {{ \"Search\" | rpxTranslate }}\n</button>\n<ng-container *ngIf=\"searchResults.length > 0\">\n <mv-comments-navigate [annotationList]=\"searchResults\"></mv-comments-navigate>\n</ng-container>\n<ng-container *ngIf=\"searchString && searchResults?.length === 0\">\n <p class=\"comment-search__item\">\n {{ \"No matches have been found\" | rpxTranslate }}\n </p>\n</ng-container>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSearchComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-search', encapsulation: ViewEncapsulation.None, standalone: false, template: "<input\n #searchInput\n type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\"\n name=\"searchString\"\n aria-label=\"search comments input\"\n [ngModel]=\"searchString\"\n (ngModelChange)=\"searchString = $event\"\n/>\n<button\n type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\"\n>\n {{ \"Search\" | rpxTranslate }}\n</button>\n<ng-container *ngIf=\"searchResults.length > 0\">\n <mv-comments-navigate [annotationList]=\"searchResults\"></mv-comments-navigate>\n</ng-container>\n<ng-container *ngIf=\"searchString && searchResults?.length === 0\">\n <p class=\"comment-search__item\">\n {{ \"No matches have been found\" | rpxTranslate }}\n </p>\n</ng-container>\n" }]
}], ctorParameters: () => [{ type: i1.Store }], propDecorators: { annotations: [{
type: Input
}], searchInput: [{
type: ViewChild,
args: ['searchInput', { static: false }]
}] } });
class FilterPipe {
transform(items, searchText, fieldName) {
if (!items) {
return [];
}
if (!searchText) {
return items;
}
return items.filter(item => {
if (item) {
if (item[fieldName]) {
return item[fieldName].toLowerCase().includes(searchText.toLowerCase());
}
else {
return item.toLowerCase().includes(searchText.toLowerCase());
}
}
return false;
});
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: FilterPipe, isStandalone: false, name: "filter" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: FilterPipe, decorators: [{
type: Pipe,
args: [{
name: 'filter',
standalone: false
}]
}] });
class UnsnakePipe {
transform(items) {
return items.split('_').join(' ');
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: UnsnakePipe, isStandalone: false, name: "unsnake" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: UnsnakePipe, decorators: [{
type: Pipe,
args: [{
name: 'unsnake',
standalone: false
}]
}] });
class CommentFilterComponent {
constructor(store, fb) {
this.store = store;
this.fb = fb;
this.isPreview = false;
}
ngOnInit() {
this.tagGroup = this.fb.group({
'tagFilters': this.fb.group({}),
});
this.filter$ = this.store.pipe(select(getTagFilters));
this.$subscriptions = this.tagGroup.valueChanges.pipe(auditTime(5)).subscribe(value => {
const tagFilters = value['tagFilters'];
this.store.dispatch(new AddFilterTags(tagFilters));
});
this.buildFrom();
}
buildFrom() {
const checkboxes = this.tagGroup.get('tagFilters');
this.allTags$ = this.store.pipe(select(getAllTagsArr)).pipe(tap(tags => {
this.tagGroup.reset();
tags.forEach((value) => {
checkboxes.addControl(value.key, new UntypedFormControl(false));
});
}));
}
onClearFilters() {
this.tagGroup.reset();
this.store.dispatch(new ClearFilterTags());
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
onRemoveFilter(tagName) {
const checkboxes = this.tagGroup.get('tagFilters');
checkboxes.controls[tagName].setValue(false);
}
onToggleFilterView() {
this.isPreview = !this.isPreview;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentFilterComponent, deps: [{ token: i1.Store }, { token: i2.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentFilterComponent, isStandalone: false, selector: "mv-comment-filter", ngImport: i0, template: "<div class=\"comment-filter\">\n <div class=\"hmcts-filter__content\">\n <div class=\"hmcts-filter__toggle-filters\">\n <p\n class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{ 'govuk-accordion__section--expanded': isPreview }\"\n (click)=\"onToggleFilterView()\"\n >\n <strong>{{ \"Filter options\" | rpxTranslate }}</strong\n ><span class=\"govuk-accordion__icon\"></span>\n </p>\n </div>\n\n <ng-container *ngIf=\"filter$ | async as filters\">\n <div class=\"hmcts-filter__selected-heading\" *ngIf=\"filters.length\">\n <div class=\"hmcts-filter__heading-action\">\n <p>\n <a\n (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\"\n >{{ \"Clear filters\" | rpxTranslate }}</a\n >\n </p>\n </div>\n </div>\n\n <h4\n class=\"govuk-heading-s govuk-!-margin-bottom-0\"\n *ngIf=\"filters.length\"\n >\n {{ \"Tag filters\" | rpxTranslate }}\n </h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a\n class=\"hmcts-filter__tag\"\n [routerLink]=\"[]\"\n (click)=\"onRemoveFilter(tagName)\"\n >\n <span class=\"govuk-visually-hidden\">{{\n \"Remove this filter\" | rpxTranslate\n }}</span\n >{{ tagName | unsnake }}</a\n >\n </li>\n </ul>\n </ng-container>\n\n <div class=\"hmcts-filter__options\" [ngClass]=\"{ isVisible: isPreview }\">\n <div class=\"govuk-form-group\">\n <label class=\"govuk-label govuk-label--s\" for=\"keywords\">\n {{ \"Search Tags\" | rpxTranslate }}\n </label>\n <input\n class=\"govuk-input\"\n id=\"keywords\"\n name=\"keywords\"\n type=\"text\"\n [ngModel]=\"searchValue\"\n (ngModelChange)=\"searchValue = $event\"\n />\n </div>\n\n <div class=\"govuk-form-group\">\n <div [formGroup]=\"tagGroup\">\n <fieldset class=\"govuk-fieldset\" formGroupName=\"tagFilters\">\n <legend class=\"govuk-fieldset__legend govuk-fieldset__legend--s\">\n Tags\n </legend>\n <div class=\"govuk-checkboxes--scroll\">\n <div class=\"govuk-checkboxes govuk-checkboxes--small\">\n <div\n class=\"govuk-checkboxes__item\"\n *ngFor=\"\n let item of allTags$ | async | filter : searchValue : 'key'\n \"\n >\n <input\n [formControlName]=\"item.key\"\n [id]=\"item.key\"\n class=\"govuk-checkboxes__input\"\n [value]=\"false\"\n [attr.aria-describedby]=\"item.key\"\n type=\"checkbox\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n [for]=\"item.key\"\n >\n {{ item.key | unsnake }} ({{ item.length }})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }, { kind: "pipe", type: FilterPipe, name: "filter" }, { kind: "pipe", type: UnsnakePipe, name: "unsnake" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-filter', encapsulation: ViewEncapsulation.None, standalone: false, template: "<div class=\"comment-filter\">\n <div class=\"hmcts-filter__content\">\n <div class=\"hmcts-filter__toggle-filters\">\n <p\n class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{ 'govuk-accordion__section--expanded': isPreview }\"\n (click)=\"onToggleFilterView()\"\n >\n <strong>{{ \"Filter options\" | rpxTranslate }}</strong\n ><span class=\"govuk-accordion__icon\"></span>\n </p>\n </div>\n\n <ng-container *ngIf=\"filter$ | async as filters\">\n <div class=\"hmcts-filter__selected-heading\" *ngIf=\"filters.length\">\n <div class=\"hmcts-filter__heading-action\">\n <p>\n <a\n (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\"\n >{{ \"Clear filters\" | rpxTranslate }}</a\n >\n </p>\n </div>\n </div>\n\n <h4\n class=\"govuk-heading-s govuk-!-margin-bottom-0\"\n *ngIf=\"filters.length\"\n >\n {{ \"Tag filters\" | rpxTranslate }}\n </h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a\n class=\"hmcts-filter__tag\"\n [routerLink]=\"[]\"\n (click)=\"onRemoveFilter(tagName)\"\n >\n <span class=\"govuk-visually-hidden\">{{\n \"Remove this filter\" | rpxTranslate\n }}</span\n >{{ tagName | unsnake }}</a\n >\n </li>\n </ul>\n </ng-container>\n\n <div class=\"hmcts-filter__options\" [ngClass]=\"{ isVisible: isPreview }\">\n <div class=\"govuk-form-group\">\n <label class=\"govuk-label govuk-label--s\" for=\"keywords\">\n {{ \"Search Tags\" | rpxTranslate }}\n </label>\n <input\n class=\"govuk-input\"\n id=\"keywords\"\n name=\"keywords\"\n type=\"text\"\n [ngModel]=\"searchValue\"\n (ngModelChange)=\"searchValue = $event\"\n />\n </div>\n\n <div class=\"govuk-form-group\">\n <div [formGroup]=\"tagGroup\">\n <fieldset class=\"govuk-fieldset\" formGroupName=\"tagFilters\">\n <legend class=\"govuk-fieldset__legend govuk-fieldset__legend--s\">\n Tags\n </legend>\n <div class=\"govuk-checkboxes--scroll\">\n <div class=\"govuk-checkboxes govuk-checkboxes--small\">\n <div\n class=\"govuk-checkboxes__item\"\n *ngFor=\"\n let item of allTags$ | async | filter : searchValue : 'key'\n \"\n >\n <input\n [formControlName]=\"item.key\"\n [id]=\"item.key\"\n class=\"govuk-checkboxes__input\"\n [value]=\"false\"\n [attr.aria-describedby]=\"item.key\"\n type=\"checkbox\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n [for]=\"item.key\"\n >\n {{ item.key | unsnake }} ({{ item.length }})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: i2.UntypedFormBuilder }] });
class CommentSetHeaderComponent {
constructor(store, commentService, toolbarEvents, icpEventService) {
this.store = store;
this.commentService = commentService;
this.toolbarEvents = toolbarEvents;
this.icpEventService = icpEventService;
this.showCommentSummaryDialog = new EventEmitter();
this.tabs = [];
this.tabSelected = '';
}
ngOnInit() {
const tagFilter$ = this.store.pipe(select(getTagFilters));
const filteredAnnotation$ = this.store.pipe(select(getFilteredAnnotations));
this.$subscriptions = combineLatest([tagFilter$, filteredAnnotation$]).subscribe(([formData, filteredAnno]) => {
this.navigationList = filteredAnno;
this.tabs = this.navigationList.length > 0 ?
[{ label: 'comments' }, { label: 'filter' }, { label: 'search' }] : [{ label: 'comments' }];
this.isFiltered = !formData.length;
this.tabs = [...this.tabs].map((tab) => {
return !this.isFiltered && tab.label === 'filter' ? { ...tab, isFiltered: true } : { ...tab, isFiltered: false };
});
});
}
toggleCommentsSummary() {
this.showCommentSummaryDialog.emit();
}
selectTab(tab) {
this.tabSelected = tab !== this.tabSelected ? tab : undefined;
if (this.tabSelected) {
this.marginToComment = true;
this.commentService.createMarginToCommentEvent(this.marginToComment);
}
else {
this.marginToComment = false;
this.commentService.createMarginToCommentEvent(this.marginToComment);
}
}
toggleCommentsPanel() {
this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetHeaderComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: ToolbarEventService }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentSetHeaderComponent, isStandalone: false, selector: "mv-comment-set-header", inputs: { showCommentSummary: "showCommentSummary", isHidden: "isHidden" }, outputs: { showCommentSummaryDialog: "showCommentSummaryDialog" }, ngImport: i0, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': icpEventService.enabled | async}\" data-module=\"govuk-tabs\">\n <ul class=\"govuk-tabs__list\">\n <li *ngFor=\"let tab of tabs; let i = index\" class=\"govuk-tabs__list-item govuk-tabs__list-item\"\n [ngClass]=\"{'govuk-tabs__list-item--selected': tabSelected === tab.label}\">\n <a id=\"commentSubPane{{ i }}\" (click)=\"selectTab(tab.label)\" [routerLink]=\"[]\" [ngClass]=\"{'govuk-tabs__list-item--filtered': tab.isFiltered}\"\n class=\"govuk-tabs__tab\" [hidden]=\"isHidden\">\n {{ tab.label | titlecase }}\n </a>\n </li>\n <li>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button commentSummaryHeader-button--close\"\n title=\"Close Comments\" [attr.aria-label]=\"'Close Comments' | rpxTranslate\"\n (click)=\"toggleCommentsPanel()\" [hidden]=\"isHidden\">\n </button>\n </li>\n </ul>\n <div class=\"govuk-tabs__panel\" [hidden]=\"!tabSelected\">\n <ng-container *ngIf=\"tabSelected === 'comments'\">\n <div style=\"width: 100%\">\n <div class=\"hmcts-banner\" *ngIf=\"navigationList?.length === 0\">\n <svg alt=\"\" class=\"hmcts-banner__icon\" fill=\"currentColor\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 25 25\" height=\"25\" width=\"25\">\n <path d=\"M13.7,18.5h-2.4v-2.4h2.4V18.5z M12.5,13.7c-0.7,0-1.2-0.5-1.2-1.2V7.7c0-0.7,0.5-1.2,1.2-1.2s1.2,0.5,1.2,1.2v4.8\nC13.7,13.2,13.2,13.7,12.5,13.7z M12.5,0.5c-6.6,0-12,5.4-12,12s5.4,12,12,12s12-5.4,12-12S19.1,0.5,12.5,0.5z\" /></svg>\n <div class=\"hmcts-banner__message\">\n <span class=\"hmcts-banner__assistive\">information</span>\n Select text to add a comment or highlight.\n </div>\n </div>\n <button type=\"button\" class=\"govuk-button\"\n id=\"commentSummary\" tabindex=\"0\"\n data-l10n-id=\"commentSummary\"\n title=\"Open collate summary\"\n [attr.aria-label]=\"'Open collate summary' | rpxTranslate\"\n (click)=\"toggleCommentsSummary()\">Collate comments</button>\n </div>\n <ng-container *ngIf=\"navigationList?.length > 0\">\n <mv-comments-navigate\n [annotationList]=\"navigationList\">\n </mv-comments-navigate>\n </ng-container>\n <p class=\"aui-comment__private-text\">\n <span class=\"aui-comment__private\">private</span>\n All comments can only be seen by you\n </p>\n </ng-container>\n <div [hidden]=\"tabSelected !== 'filter'\">\n <div class=\"govuk-tabs__panel--container\">\n <mv-comment-filter></mv-comment-filter>\n <mv-comments-navigate\n *ngIf=\"navigationList?.length > 0\"\n [annotationList]=\"navigationList\">\n </mv-comments-navigate>\n </div>\n </div>\n\n <div [hidden]=\"tabSelected !== 'search'\">\n <mv-comment-search [annotations]=\"navigationList\"></mv-comment-search>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: CommentSearchComponent, selector: "mv-comment-search", inputs: ["annotations"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }, { kind: "component", type: CommentFilterComponent, selector: "mv-comment-filter" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-set-header', encapsulation: ViewEncapsulation.None, standalone: false, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': icpEventService.enabled | async}\" data-module=\"govuk-tabs\">\n <ul class=\"govuk-tabs__list\">\n <li *ngFor=\"let tab of tabs; let i = index\" class=\"govuk-tabs__list-item govuk-tabs__list-item\"\n [ngClass]=\"{'govuk-tabs__list-item--selected': tabSelected === tab.label}\">\n <a id=\"commentSubPane{{ i }}\" (click)=\"selectTab(tab.label)\" [routerLink]=\"[]\" [ngClass]=\"{'govuk-tabs__list-item--filtered': tab.isFiltered}\"\n class=\"govuk-tabs__tab\" [hidden]=\"isHidden\">\n {{ tab.label | titlecase }}\n </a>\n </li>\n <li>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button commentSummaryHeader-button--close\"\n title=\"Close Comments\" [attr.aria-label]=\"'Close Comments' | rpxTranslate\"\n (click)=\"toggleCommentsPanel()\" [hidden]=\"isHidden\">\n </button>\n </li>\n </ul>\n <div class=\"govuk-tabs__panel\" [hidden]=\"!tabSelected\">\n <ng-container *ngIf=\"tabSelected === 'comments'\">\n <div style=\"width: 100%\">\n <div class=\"hmcts-banner\" *ngIf=\"navigationList?.length === 0\">\n <svg alt=\"\" class=\"hmcts-banner__icon\" fill=\"currentColor\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 25 25\" height=\"25\" width=\"25\">\n <path d=\"M13.7,18.5h-2.4v-2.4h2.4V18.5z M12.5,13.7c-0.7,0-1.2-0.5-1.2-1.2V7.7c0-0.7,0.5-1.2,1.2-1.2s1.2,0.5,1.2,1.2v4.8\nC13.7,13.2,13.2,13.7,12.5,13.7z M12.5,0.5c-6.6,0-12,5.4-12,12s5.4,12,12,12s12-5.4,12-12S19.1,0.5,12.5,0.5z\" /></svg>\n <div class=\"hmcts-banner__message\">\n <span class=\"hmcts-banner__assistive\">information</span>\n Select text to add a comment or highlight.\n </div>\n </div>\n <button type=\"button\" class=\"govuk-button\"\n id=\"commentSummary\" tabindex=\"0\"\n data-l10n-id=\"commentSummary\"\n title=\"Open collate summary\"\n [attr.aria-label]=\"'Open collate summary' | rpxTranslate\"\n (click)=\"toggleCommentsSummary()\">Collate comments</button>\n </div>\n <ng-container *ngIf=\"navigationList?.length > 0\">\n <mv-comments-navigate\n [annotationList]=\"navigationList\">\n </mv-comments-navigate>\n </ng-container>\n <p class=\"aui-comment__private-text\">\n <span class=\"aui-comment__private\">private</span>\n All comments can only be seen by you\n </p>\n </ng-container>\n <div [hidden]=\"tabSelected !== 'filter'\">\n <div class=\"govuk-tabs__panel--container\">\n <mv-comment-filter></mv-comment-filter>\n <mv-comments-navigate\n *ngIf=\"navigationList?.length > 0\"\n [annotationList]=\"navigationList\">\n </mv-comments-navigate>\n </div>\n </div>\n\n <div [hidden]=\"tabSelected !== 'search'\">\n <mv-comment-search [annotations]=\"navigationList\"></mv-comment-search>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: CommentService }, { type: ToolbarEventService }, { type: IcpEventService }], propDecorators: { showCommentSummary: [{
type: Input
}], isHidden: [{
type: Input
}], showCommentSummaryDialog: [{
type: Output
}] } });
const getBookmarkState = createSelector(getMVState, (state) => state.bookmarks);
const getBookmarkPages = createSelector(getBookmarkState, getBookmarkPageEnt);
const getBookmarkEntities = createSelector(getBookmarkState, getBookmarkEnts);
const getBookmarkNodes = createSelector(getBookmarkEntities, (entities) => generateBookmarkNodes(entities));
const getEditableBookmark = createSelector(getBookmarkState, getEditBookmark);
const getScrollTop = createSelector(getBookmarkState, getScrollTop$1);
const getBookmarkInfo = createSelector(getBookmarkNodes, getDocumentId, getPdfPosition, getPages, (bookmarkNodes, docId, pdfPosition, pages) => {
return {
pageNumber: pdfPosition.pageNumber - 1,
xCoordinate: pdfPosition.left,
yCoordinate: ((pages[pdfPosition.pageNumber].styles.height)
- (pdfPosition.top * (pages[pdfPosition.pageNumber].viewportScale)))
/ parseFloat(pages[pdfPosition.pageNumber].scaleRotation.scale),
previous: bookmarkNodes.length > 0 ? bookmarkNodes.sort((a, b) => a.index - b.index)[bookmarkNodes.length - 1].id : undefined,
documentId: docId
};
});
const getBookmarksPerPage = createSelector(getPages, getBookmarkPages, (pages, pageEnt) => {
if (pages && pageEnt) {
const arr = [];
Object.keys(pages).forEach(key => {
const pageIdx = Number(key) - 1; // -1 as the thisPages array is 0 indexed
const thisPage = pageEnt[pageIdx];
arr.push({
bookmark: thisPage ? thisPage : [],
styles: pages[key].styles
});
});
return arr;
}
});
class CtxToolbarComponent {
constructor() {
this.createHighlightEvent = new EventEmitter();
this.deleteHighlightEvent = new EventEmitter();
this.addOrEditCommentEvent = new EventEmitter();
this.createBookmarkEvent = new EventEmitter();
this.cancelEvent = new EventEmitter();
this.defaultHeight = 70;
this.defaultWidth = 300;
}
ngOnChanges(changes) {
this.setRectangle();
this.top = this.popupTop();
this.left = this.popupLeft();
}
onEscapeKey(event) {
if (this.rectangle) {
event.stopPropagation();
event.preventDefault();
this.rectangle = undefined;
this.cancelEvent.emit();
}
}
set rectangles(rectangles) {
if (rectangles) {
this._rectangles = rectangles;
this.setRectangle();
}
}
get rectangles() {
return this._rectangles;
}
createHighlight() {
this.createHighlightEvent.emit();
this.rectangle = undefined;
}
deleteHighlight() {
this.deleteHighlightEvent.emit();
}
addOrEditComment() {
this.addOrEditCommentEvent.emit();
setTimeout(() => {
// This hack to wait until the element is rendered seems to be unreliable,
if (!location.hash) {
document.getElementById('viewerContainer')?.scrollBy(0, 1);
}
}, 20);
}
createBookmark() {
this.createBookmarkEvent.emit(this.rectangle);
this.rectangle = undefined;
}
setRectangle() {
const rectangle = this.rectangles
.reduce((prev, current) => prev.y < current.y ? prev : current);
this.rectangle = { ...rectangle };
switch (this.rotate) {
case 90:
this.rectangle.width = rectangle.height;
this.rectangle.height = rectangle.width;
this.rectangle.x = (this.pageWidth / this.zoom) - rectangle.y - rectangle.height;
this.rectangle.y = rectangle.x;
break;
case 180:
this.rectangle.x = (this.pageWidth / this.zoom) - rectangle.x - rectangle.width;
this.rectangle.y = (this.pageHeight / this.zoom) - rectangle.y - rectangle.height;
break;
case 270:
this.rectangle.width = rectangle.height;
this.rectangle.height = rectangle.width;
this.rectangle.x = rectangle.y;
this.rectangle.y = (this.pageHeight / this.zoom) - rectangle.x - rectangle.width;
break;
}
}
popupTop() {
const popupTop = this.rectangle.y * this.zoom - this.defaultHeight;
return popupTop <= 0 ? this.defaultHeight : popupTop;
}
popupLeft() {
const popupLeft = (this.rectangle.x + (this.rectangle.width / 2)) * this.zoom - (this.defaultWidth / 2);
if (popupLeft <= 0) {
return 0;
}
else if (popupLeft >= this.pageWidth - this.defaultWidth) {
return this.pageWidth - this.defaultWidth;
}
else {
return popupLeft;
}
}
focusToolbar() {
if (this.toolbarContainer?.nativeElement) {
const firstButton = this.toolbarContainer.nativeElement.querySelector('button');
if (firstButton) {
setTimeout(() => firstButton.focus(), 0);
return;
}
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CtxToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CtxToolbarComponent, isStandalone: false, selector: "mv-ctx-toolbar", inputs: { zoom: "zoom", rotate: "rotate", pageHeight: "pageHeight", pageWidth: "pageWidth", canHighlight: "canHighlight", canBookmark: "canBookmark", canComment: "canComment", canDelete: "canDelete", rectangles: "rectangles" }, outputs: { createHighlightEvent: "createHighlightEvent", deleteHighlightEvent: "deleteHighlightEvent", addOrEditCommentEvent: "addOrEditCommentEvent", createBookmarkEvent: "createBookmarkEvent", cancelEvent: "cancelEvent" }, host: { listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, viewQueries: [{ propertyName: "toolbarContainer", first: true, predicate: ["toolbarContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #toolbarContainer class=\"toolbar\" *ngIf=\"rectangle\"\n [style.top.px]=\"top\"\n [style.left.px]=\"left\">\n <button *ngIf=\"canHighlight\"\n type=\"button\"\n title=\"Highlight\"\n [attr.aria-label]=\"'Highlight' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton aui__toolbar-button-highlight\"\n (mousedown)=\"createHighlight()\"\n (keydown.enter)=\"createHighlight()\">\n <span aria-hidden=\"true\">Highlight</span>\n </button>\n <button *ngIf=\"canDelete\"\n type=\"button\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton\"\n (mousedown)=\"deleteHighlight()\"\n (keydown.enter)=\"deleteHighlight()\">\n <span aria-hidden=\"true\">Remove</span>\n </button>\n <button *ngIf=\"canComment\"\n type=\"button\"\n title=\"Comment\"\n [attr.aria-label]=\"'Comment' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-comment\"\n (mousedown)=\"addOrEditComment()\"\n (keydown.enter)=\"addOrEditComment()\">\n <span aria-hidden=\"true\">Comment</span></button>\n <button *ngIf=\"canBookmark\"\n type=\"button\"\n title=\"Bookmark\"\n [attr.aria-label]=\"'Bookmark' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-bookmark\"\n id=\"bookmarkButton\"\n (mousedown)=\"createBookmark()\"\n (keydown.enter)=\"createBookmark()\">\n <span aria-hidden=\"true\">Bookmark</span></button>\n <div class=\"arrow-down\">\n <div class=\"inner-triangle\"></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CtxToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-ctx-toolbar', standalone: false, template: "<div #toolbarContainer class=\"toolbar\" *ngIf=\"rectangle\"\n [style.top.px]=\"top\"\n [style.left.px]=\"left\">\n <button *ngIf=\"canHighlight\"\n type=\"button\"\n title=\"Highlight\"\n [attr.aria-label]=\"'Highlight' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton aui__toolbar-button-highlight\"\n (mousedown)=\"createHighlight()\"\n (keydown.enter)=\"createHighlight()\">\n <span aria-hidden=\"true\">Highlight</span>\n </button>\n <button *ngIf=\"canDelete\"\n type=\"button\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton\"\n (mousedown)=\"deleteHighlight()\"\n (keydown.enter)=\"deleteHighlight()\">\n <span aria-hidden=\"true\">Remove</span>\n </button>\n <button *ngIf=\"canComment\"\n type=\"button\"\n title=\"Comment\"\n [attr.aria-label]=\"'Comment' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-comment\"\n (mousedown)=\"addOrEditComment()\"\n (keydown.enter)=\"addOrEditComment()\">\n <span aria-hidden=\"true\">Comment</span></button>\n <button *ngIf=\"canBookmark\"\n type=\"button\"\n title=\"Bookmark\"\n [attr.aria-label]=\"'Bookmark' | rpxTranslate\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-bookmark\"\n id=\"bookmarkButton\"\n (mousedown)=\"createBookmark()\"\n (keydown.enter)=\"createBookmark()\">\n <span aria-hidden=\"true\">Bookmark</span></button>\n <div class=\"arrow-down\">\n <div class=\"inner-triangle\"></div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { toolbarContainer: [{
type: ViewChild,
args: ['toolbarContainer', { static: false }]
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], pageHeight: [{
type: Input
}], pageWidth: [{
type: Input
}], canHighlight: [{
type: Input
}], canBookmark: [{
type: Input
}], canComment: [{
type: Input
}], canDelete: [{
type: Input
}], createHighlightEvent: [{
type: Output
}], deleteHighlightEvent: [{
type: Output
}], addOrEditCommentEvent: [{
type: Output
}], createBookmarkEvent: [{
type: Output
}], cancelEvent: [{
type: Output
}], onEscapeKey: [{
type: HostListener,
args: ['document:keydown.escape', ['$event']]
}], rectangles: [{
type: Input
}] } });
class KeyboardBoxDrawDirective {
set enabled(value) {
const wasEnabled = this._enabled;
this._enabled = value;
if (!value && wasEnabled) {
this.reset();
return;
}
if (value && !wasEnabled && KeyboardBoxDrawDirective.lastInteractionWasKeyboard && !this.showCursor) {
if (typeof document !== 'undefined' && this.elementRef.nativeElement === document.activeElement) {
this.initializeCursorForKeyboard();
}
}
}
get enabled() {
return this._enabled;
}
static { this.lastInteractionWasKeyboard = false; }
constructor(elementRef) {
this.elementRef = elementRef;
this._enabled = false;
this.minBoxSize = 10;
this.incrementSmall = 1;
this.incrementMedium = 5;
this.incrementLarge = 10;
this.drawingStarted = new EventEmitter();
this.drawingUpdated = new EventEmitter();
this.drawingConfirmed = new EventEmitter();
this.drawingCancelled = new EventEmitter();
this.cursorPositionChanged = new EventEmitter();
this.isDrawing = false;
this.showCursor = false;
if (typeof window !== 'undefined') {
window.addEventListener('keydown', KeyboardBoxDrawDirective.onGlobalKeyDown, { capture: true });
window.addEventListener('mousedown', KeyboardBoxDrawDirective.onGlobalMouseDown, { capture: true });
}
}
static onGlobalKeyDown() {
KeyboardBoxDrawDirective.lastInteractionWasKeyboard = true;
}
static onGlobalMouseDown() {
KeyboardBoxDrawDirective.lastInteractionWasKeyboard = false;
}
ngOnDestroy() {
this.cleanup();
}
initializeCursorForKeyboard() {
if (this.enabled && !this.showCursor && !this.isDrawing) {
const rect = this.elementRef.nativeElement.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
this.cursorX = rect.width / 2;
this.cursorY = rect.height / 2;
this.showCursor = true;
this.emitCursorPosition();
}
}
}
onKeyDown(event) {
if (!this.enabled) {
return;
}
if (event.key === 'Tab') {
if (this.isDrawing) {
this.cancelDrawing();
}
else {
if (this.showCursor) {
this.hideCursor();
}
this.drawingCancelled.emit();
}
return;
}
if (['Shift', 'Alt', 'Control'].includes(event.key)) {
return;
}
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
if (!this.isDrawing) {
this.startDrawing();
}
else {
this.confirmDrawing();
}
return;
}
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
if (this.isDrawing) {
this.cancelDrawing();
}
else {
if (this.showCursor) {
this.hideCursor();
}
this.drawingCancelled.emit();
}
return;
}
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
event.preventDefault();
event.stopPropagation();
if (this.isDrawing) {
this.resizeBox(event);
}
else {
this.moveCursor(event);
}
return;
}
event.preventDefault();
event.stopPropagation();
}
onBlur() {
if (this.showCursor && !this.isDrawing) {
this.hideCursor();
}
}
onFocus() {
if (!this.enabled || !KeyboardBoxDrawDirective.lastInteractionWasKeyboard) {
return;
}
this.initializeCursorForKeyboard();
}
moveCursor(event) {
const increment = event.shiftKey ? this.incrementLarge : this.incrementMedium;
if (!this.showCursor) {
const rect = this.elementRef.nativeElement.getBoundingClientRect();
this.cursorX = rect.width / 2;
this.cursorY = rect.height / 2;
this.showCursor = true;
this.emitCursorPosition();
return;
}
const rect = this.elementRef.nativeElement.getBoundingClientRect();
switch (event.key) {
case 'ArrowUp':
this.cursorY = Math.max(0, this.cursorY - increment);
break;
case 'ArrowDown':
this.cursorY = Math.min(rect.height, this.cursorY + increment);
break;
case 'ArrowLeft':
this.cursorX = Math.max(0, this.cursorX - increment);
break;
case 'ArrowRight':
this.cursorX = Math.min(rect.width, this.cursorX + increment);
break;
}
this.emitCursorPosition();
}
startDrawing() {
let startX;
let startY;
if (this.showCursor) {
startX = this.cursorX;
startY = this.cursorY;
this.showCursor = false;
this.emitCursorPosition();
}
else {
// default to center
const rect = this.elementRef.nativeElement.getBoundingClientRect();
startX = rect.width / 2;
startY = rect.height / 2;
}
this.isDrawing = true;
this.drawStartX = startX;
this.drawStartY = startY;
this.currentWidth = this.minBoxSize;
this.currentHeight = this.minBoxSize;
this.emitDrawingStarted();
}
resizeBox(event) {
const increment = event.shiftKey ? this.incrementLarge : this.incrementSmall;
const rect = this.elementRef.nativeElement.getBoundingClientRect();
switch (event.key) {
case 'ArrowRight':
const maxWidth = rect.width - this.drawStartX;
this.currentWidth = Math.min(maxWidth, this.currentWidth + increment);
break;
case 'ArrowLeft':
this.currentWidth = Math.max(this.minBoxSize, this.currentWidth - increment);
break;
case 'ArrowDown':
const maxHeight = rect.height - this.drawStartY;
this.currentHeight = Math.min(maxHeight, this.currentHeight + increment);
break;
case 'ArrowUp':
this.currentHeight = Math.max(this.minBoxSize, this.currentHeight - increment);
break;
}
this.emitDrawingUpdated();
}
confirmDrawing() {
this.emitDrawingConfirmed();
this.cleanup();
}
cancelDrawing() {
this.drawingCancelled.emit();
this.cleanup();
}
hideCursor() {
this.showCursor = false;
this.emitCursorPosition();
}
cleanup() {
this.isDrawing = false;
this.showCursor = false;
this.cursorX = undefined;
this.cursorY = undefined;
this.drawStartX = undefined;
this.drawStartY = undefined;
this.currentWidth = undefined;
this.currentHeight = undefined;
}
emitCursorPosition() {
this.cursorPositionChanged.emit({
x: this.cursorX,
y: this.cursorY,
visible: this.showCursor
});
}
emitDrawingStarted() {
this.drawingStarted.emit({
startX: this.drawStartX,
startY: this.drawStartY,
width: this.currentWidth,
height: this.currentHeight
});
}
emitDrawingUpdated() {
this.drawingUpdated.emit({
startX: this.drawStartX,
startY: this.drawStartY,
width: this.currentWidth,
height: this.currentHeight
});
}
emitDrawingConfirmed() {
this.drawingConfirmed.emit({
startX: this.drawStartX,
startY: this.drawStartY,
width: this.currentWidth,
height: this.currentHeight
});
}
reset() {
this.cleanup();
this.emitCursorPosition();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardBoxDrawDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: KeyboardBoxDrawDirective, isStandalone: false, selector: "[mvKeyboardBoxDraw]", inputs: { enabled: "enabled", minBoxSize: "minBoxSize", incrementSmall: "incrementSmall", incrementMedium: "incrementMedium", incrementLarge: "incrementLarge" }, outputs: { drawingStarted: "drawingStarted", drawingUpdated: "drawingUpdated", drawingConfirmed: "drawingConfirmed", drawingCancelled: "drawingCancelled", cursorPositionChanged: "cursorPositionChanged" }, host: { listeners: { "keydown": "onKeyDown($event)", "blur": "onBlur()", "focus": "onFocus()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardBoxDrawDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvKeyboardBoxDraw]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { enabled: [{
type: Input
}], minBoxSize: [{
type: Input
}], incrementSmall: [{
type: Input
}], incrementMedium: [{
type: Input
}], incrementLarge: [{
type: Input
}], drawingStarted: [{
type: Output
}], drawingUpdated: [{
type: Output
}], drawingConfirmed: [{
type: Output
}], drawingCancelled: [{
type: Output
}], cursorPositionChanged: [{
type: Output
}], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}], onBlur: [{
type: HostListener,
args: ['blur']
}], onFocus: [{
type: HostListener,
args: ['focus']
}] } });
class BoxHighlightCreateComponent {
constructor(toolbarEvents, highlightService) {
this.toolbarEvents = toolbarEvents;
this.highlightService = highlightService;
this.saveSelection = new EventEmitter();
this.drawStartX = -1;
this.drawStartY = -1;
this.backgroundColor = 'none';
this.isCurrentPage = false;
this.keyboardDrawingMode = false;
this.showCursor = false;
this.subscriptions = [];
}
ngOnInit() {
this.subscriptions = [
this.toolbarEvents.drawModeSubject.subscribe(drawMode => {
this.defaultHeight = drawMode ? '100%' : '0px';
this.defaultWidth = drawMode ? '100%' : '0px';
this.drawMode = drawMode;
if (drawMode && this.isCurrentPage) {
this.focusDrawingContainer();
}
}),
this.toolbarEvents.redactWholePage.subscribe(() => {
this.wholePage = true;
}),
this.toolbarEvents.setCurrentPageInputValueSubject.subscribe(pageNumber => {
this.isCurrentPage = this.page === pageNumber;
if (this.drawMode && this.isCurrentPage) {
this.focusDrawingContainer();
}
})
];
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
subscription.unsubscribe();
});
}
isElementInViewport(el) {
const rect = el.getBoundingClientRect();
const windowHeight = (window.innerHeight || document.documentElement.clientHeight);
const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
const verticallyVisible = rect.bottom > 0 && rect.top < windowHeight;
const horizontallyVisible = rect.right > 0 && rect.left < windowWidth;
return verticallyVisible && horizontallyVisible;
}
focusDrawingContainer() {
setTimeout(() => {
if (this.drawingContainer?.nativeElement && this.isElementInViewport(this.drawingContainer.nativeElement)) {
try {
this.drawingContainer.nativeElement.focus({ preventScroll: true });
}
catch {
this.drawingContainer.nativeElement.focus();
}
}
}, 100);
}
initHighlight(event) {
if (this.wholePage) {
this.highlightPage();
return;
}
const rect = HtmlTemplatesHelper.getAdjustedBoundingRect(event.target), offsetX = event.clientX - rect.left, offsetY = event.clientY - rect.top;
this.position = 'absolute';
this.backgroundColor = 'yellow';
this.drawStartX = offsetX;
this.drawStartY = offsetY;
this.display = 'block';
this.height = 50;
this.width = 50;
this.top = this.drawStartY;
this.left = this.drawStartX;
this.adjustForRotation();
}
updateHighlight(event) {
const rect = HtmlTemplatesHelper.getAdjustedBoundingRect(event.target, false), offsetX = event.clientX - rect.left, offsetY = event.clientY - rect.top;
if (this.drawStartX > 0 && this.drawStartY > 0) {
this.height = Math.abs(offsetY - this.drawStartY);
this.width = Math.abs(offsetX - this.drawStartX);
this.top = Math.min(offsetY, this.drawStartY);
this.left = Math.min(offsetX, this.drawStartX);
}
}
createHighlight() {
if (this.height / this.zoom > 5 || this.width / this.zoom > 5) {
let rectangle = this.highlightService
.applyRotation(this.pageHeight, this.pageWidth, this.height, this.width, this.top, this.left, this.rotate, this.zoom);
const annotationId = v4();
rectangle = { id: annotationId, ...rectangle };
this.saveSelection.emit({ rectangles: [rectangle], page: this.page, annotationId });
this.resetHighlight();
}
}
onCursorPositionChanged(position) {
this.cursorX = position.x;
this.cursorY = position.y;
this.showCursor = position.visible;
}
onDrawingStarted(event) {
this.keyboardDrawingMode = true;
this.position = 'absolute';
this.backgroundColor = 'yellow';
this.display = 'block';
this.drawStartX = event.startX;
this.drawStartY = event.startY;
this.width = event.width;
this.height = event.height;
this.top = this.drawStartY;
this.left = this.drawStartX;
this.adjustForRotation();
}
onDrawingUpdated(event) {
this.width = event.width;
this.height = event.height;
}
onDrawingConfirmed(event) {
this.keyboardDrawingMode = false;
this.createHighlight();
}
onDrawingCancelled() {
this.keyboardDrawingMode = false;
this.resetHighlight();
this.toolbarEvents.drawModeSubject.next(false);
}
adjustForRotation() {
switch (this.rotate) {
case 90:
this.top = this.drawStartY - this.height;
break;
case 180:
this.top = this.drawStartY - this.height;
this.left = this.drawStartX - this.width;
break;
case 270:
this.left = this.drawStartX - this.width;
break;
}
}
resetHighlight() {
this.drawStartX = -1;
this.drawStartY = -1;
this.display = 'none';
this.width = 0;
this.height = 0;
this.backgroundColor = 'none';
this.position = 'initial';
this.wholePage = false;
}
highlightPage() {
this.height = this.pageHeight;
this.width = this.pageWidth;
this.top = 0;
this.left = 0;
this.createHighlight();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BoxHighlightCreateComponent, deps: [{ token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: BoxHighlightCreateComponent, isStandalone: false, selector: "mv-box-highlight-create", inputs: { page: "page", pageHeight: "pageHeight", pageWidth: "pageWidth", rotate: "rotate", zoom: "zoom", container: "container" }, outputs: { saveSelection: "saveSelection" }, viewQueries: [{ propertyName: "highlight", first: true, predicate: ["boxHighlight"], descendants: true }, { propertyName: "drawingContainer", first: true, predicate: ["drawingContainer"], descendants: true }], ngImport: i0, template: "<div #drawingContainer\n [style.height]=\"defaultHeight\"\n [style.width]=\"defaultWidth\"\n [tabindex]=\"drawMode && isCurrentPage ? 0 : -1\"\n mvKeyboardBoxDraw\n [enabled]=\"drawMode && isCurrentPage\"\n (cursorPositionChanged)=\"onCursorPositionChanged($event)\"\n (drawingStarted)=\"onDrawingStarted($event)\"\n (drawingUpdated)=\"onDrawingUpdated($event)\"\n (drawingConfirmed)=\"onDrawingConfirmed($event)\"\n (drawingCancelled)=\"onDrawingCancelled()\"\n (mousedown)=\"initHighlight($event)\"\n (mousemove)=\"updateHighlight($event)\"\n (mouseup)=\"createHighlight()\">\n\n <div *ngIf=\"showCursor\"\n class=\"drawing-cursor\"\n [style.position]=\"'absolute'\"\n [style.top]=\"cursorY + 'px'\"\n [style.left]=\"cursorX + 'px'\">\n </div>\n\n <div #boxHighlight\n class=\"box-highlight\"\n [style.display]=\"display\"\n [style.position]=\"position\"\n [style.background-color]=\"backgroundColor\"\n [style.height]=\"height ? height + 'px' : '100%'\"\n [style.width]=\"width ? width + 'px' : '100%'\"\n [style.top]=\"top + 'px'\"\n [style.left]=\"left + 'px'\">\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: KeyboardBoxDrawDirective, selector: "[mvKeyboardBoxDraw]", inputs: ["enabled", "minBoxSize", "incrementSmall", "incrementMedium", "incrementLarge"], outputs: ["drawingStarted", "drawingUpdated", "drawingConfirmed", "drawingCancelled", "cursorPositionChanged"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BoxHighlightCreateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-box-highlight-create', standalone: false, template: "<div #drawingContainer\n [style.height]=\"defaultHeight\"\n [style.width]=\"defaultWidth\"\n [tabindex]=\"drawMode && isCurrentPage ? 0 : -1\"\n mvKeyboardBoxDraw\n [enabled]=\"drawMode && isCurrentPage\"\n (cursorPositionChanged)=\"onCursorPositionChanged($event)\"\n (drawingStarted)=\"onDrawingStarted($event)\"\n (drawingUpdated)=\"onDrawingUpdated($event)\"\n (drawingConfirmed)=\"onDrawingConfirmed($event)\"\n (drawingCancelled)=\"onDrawingCancelled()\"\n (mousedown)=\"initHighlight($event)\"\n (mousemove)=\"updateHighlight($event)\"\n (mouseup)=\"createHighlight()\">\n\n <div *ngIf=\"showCursor\"\n class=\"drawing-cursor\"\n [style.position]=\"'absolute'\"\n [style.top]=\"cursorY + 'px'\"\n [style.left]=\"cursorX + 'px'\">\n </div>\n\n <div #boxHighlight\n class=\"box-highlight\"\n [style.display]=\"display\"\n [style.position]=\"position\"\n [style.background-color]=\"backgroundColor\"\n [style.height]=\"height ? height + 'px' : '100%'\"\n [style.width]=\"width ? width + 'px' : '100%'\"\n [style.top]=\"top + 'px'\"\n [style.left]=\"left + 'px'\">\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: HighlightCreateService }], propDecorators: { page: [{
type: Input
}], pageHeight: [{
type: Input
}], pageWidth: [{
type: Input
}], rotate: [{
type: Input
}], zoom: [{
type: Input
}], container: [{
type: Input
}], saveSelection: [{
type: Output
}], highlight: [{
type: ViewChild,
args: ['boxHighlight', { static: false }]
}], drawingContainer: [{
type: ViewChild,
args: ['drawingContainer', { static: false }]
}] } });
class KeyboardBoxMoveDirective {
constructor(elementRef) {
this.elementRef = elementRef;
this.enabled = true;
this.incrementSmall = 1;
this.incrementLarge = 10;
this.keyboardMovingChange = new EventEmitter();
this.boxDelete = new EventEmitter();
this.moveSubject = new Subject();
this.isMoving = false;
this.moveSubscription = this.moveSubject
.pipe(debounceTime$1(500))
.subscribe(() => {
this.setMoving(false);
this.emitStoppedEvent();
});
}
ngOnDestroy() {
if (this.moveSubscription) {
this.moveSubscription.unsubscribe();
}
this.moveSubject.complete();
}
onKeyDown(event) {
if (event.key === 'Delete' || event.key === 'Backspace') {
event.preventDefault();
event.stopPropagation();
this.boxDelete.emit();
return;
}
if (!this.enabled) {
return;
}
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
event.preventDefault();
event.stopPropagation();
this.moveBox(event);
}
}
moveBox(event) {
const element = this.elementRef.nativeElement;
const increment = event.shiftKey ? this.incrementLarge : this.incrementSmall;
if (!this.isMoving) {
this.setMoving(true);
}
const currentTop = parseFloat(element.style.top) || 0;
const currentLeft = parseFloat(element.style.left) || 0;
const currentWidth = element.offsetWidth;
const currentHeight = element.offsetHeight;
let newTop = currentTop;
let newLeft = currentLeft;
switch (event.key) {
case 'ArrowUp':
newTop = currentTop - increment;
break;
case 'ArrowDown':
newTop = currentTop + increment;
break;
case 'ArrowLeft':
newLeft = currentLeft - increment;
break;
case 'ArrowRight':
newLeft = currentLeft + increment;
break;
}
if (this.movementBounds) {
newTop = Math.max(0, Math.min(newTop, this.movementBounds.containerHeight - currentHeight));
newLeft = Math.max(0, Math.min(newLeft, this.movementBounds.containerWidth - currentWidth));
}
element.style.top = `${newTop}px`;
element.style.left = `${newLeft}px`;
this.moveSubject.next();
}
setMoving(moving) {
if (this.isMoving !== moving) {
this.isMoving = moving;
this.keyboardMovingChange.emit(moving);
}
}
emitStoppedEvent() {
const element = this.elementRef.nativeElement;
const wasFocused = document.activeElement === element;
const stoppedEvent = new CustomEvent('stopped', {
detail: element,
bubbles: true
});
element.dispatchEvent(stoppedEvent);
if (wasFocused) {
setTimeout(() => {
element.focus();
}, 50);
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardBoxMoveDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: KeyboardBoxMoveDirective, isStandalone: false, selector: "[mvKeyboardBoxMove]", inputs: { enabled: "enabled", incrementSmall: "incrementSmall", incrementLarge: "incrementLarge", movementBounds: "movementBounds" }, outputs: { keyboardMovingChange: "keyboardMovingChange", boxDelete: "boxDelete" }, host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardBoxMoveDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvKeyboardBoxMove]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { enabled: [{
type: Input
}], incrementSmall: [{
type: Input
}], incrementLarge: [{
type: Input
}], movementBounds: [{
type: Input
}], keyboardMovingChange: [{
type: Output
}], boxDelete: [{
type: Output
}], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
class RectangleComponent {
set annoRect(annoRect) {
this._annoRect = { ...annoRect };
this.height = +(annoRect.height * this.zoom).toFixed(2);
this.width = +(annoRect.width * this.zoom).toFixed(2);
this.left = +(annoRect.x * this.zoom).toFixed(2);
this.top = +(annoRect.y * this.zoom).toFixed(2);
}
get annoRect() {
return this._annoRect;
}
constructor(toolbarEvents, highlightService) {
this.toolbarEvents = toolbarEvents;
this.highlightService = highlightService;
this.rectangleTabIndex = 0;
this.selectEvent = new EventEmitter();
this.updateEvent = new EventEmitter();
this.deleteEvent = new EventEmitter();
this.keyboardMovingChange = new EventEmitter();
this.tabToToolbar = new EventEmitter();
this.subscriptions = [];
this.enableGrabNDrag = false;
}
ngOnChanges(changes) {
if (changes.rotate) {
this.adjustForRotation(this.rotate);
}
if (changes.pageHeight || changes.pageWidth) {
this.updateMovementBounds();
}
}
ngAfterViewInit() {
this.subscriptions.push(this.toolbarEvents.grabNDrag.subscribe(grabNDrag => this.enableGrabNDrag = grabNDrag));
setTimeout(() => this.updateMovementBounds(), 0);
if (this._selected && this.viewRect) {
this.viewRect.nativeElement.focus();
}
}
ngOnDestroy() {
this.subscriptions.filter(subscription => !subscription.closed)
.forEach(subscription => subscription.unsubscribe());
}
set selected(selected) {
this._selected = selected;
if (this._selected && this.viewRect) {
this.viewRect.nativeElement.focus();
}
}
get selected() {
return this._selected;
}
onClick() {
this.selectEvent.emit(this.annoRect);
}
onFocus() {
this.selectEvent.emit(this.annoRect);
}
onUpdate(viewRect) {
const { offsetHeight, offsetWidth, offsetTop, offsetLeft } = viewRect;
if (this.hasRectangleChanged(viewRect)) {
let rectangle = this.highlightService
.applyRotation(this.pageHeight, this.pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, this.rotate, this.zoom);
rectangle = { ...this.annoRect, ...rectangle };
this.updateEvent.emit({ ...rectangle });
}
}
adjustForRotation(rotation) {
const { top, left, height, width } = this;
switch (rotation) {
case 90:
this.width = height;
this.height = width;
this.left = this.pageWidth - top - height;
this.top = left;
break;
case 180:
this.left = this.pageWidth - left - width;
this.top = this.pageHeight - top - height;
break;
case 270:
this.width = height;
this.height = width;
this.left = top;
this.top = this.pageHeight - left - width;
break;
}
}
hasRectangleChanged(viewRect) {
return Math.round(this.left) !== viewRect.offsetLeft ||
Math.round(this.top) !== viewRect.offsetTop ||
Math.round(this.width) !== viewRect.offsetWidth ||
Math.round(this.height) !== viewRect.offsetHeight;
}
updateMovementBounds() {
if (this.pageHeight && this.pageWidth) {
this.movementBounds = {
containerHeight: this.pageHeight,
containerWidth: this.pageWidth
};
}
}
onKeyboardMovingChange(isMoving) {
this.keyboardMovingChange.emit(isMoving);
}
onDelete() {
if (this._selected) {
this.deleteEvent.emit(this.annoRect);
}
}
onTab(event) {
if (!this._selected) {
return;
}
this.tabToToolbar.emit(event);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RectangleComponent, deps: [{ token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: RectangleComponent, isStandalone: false, selector: "mv-anno-rectangle", inputs: { color: "color", zoom: "zoom", rotate: "rotate", editable: "editable", pageHeight: "pageHeight", pageWidth: "pageWidth", rectangleTabIndex: "rectangleTabIndex", annoRect: "annoRect", selected: "selected" }, outputs: { selectEvent: "selectEvent", updateEvent: "updateEvent", deleteEvent: "deleteEvent", keyboardMovingChange: "keyboardMovingChange", tabToToolbar: "tabToToolbar" }, viewQueries: [{ propertyName: "viewRect", first: true, predicate: ["rectElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"editable; else nonEditable\">\n <div #rectElement\n class=\"rectangle\"\n draggable\n resizable\n mvKeyboardBoxMove\n [tabindex]=\"rectangleTabIndex\"\n [ngClass]=\"{ 'selected': selected, 'grabNDrag': enableGrabNDrag }\"\n [style.backgroundColor]=\"'#' + color\"\n [style.width]=\"width + 'px'\"\n [style.height]=\"height + 'px'\"\n [style.left]=\"left + 'px'\"\n [style.top]=\"top + 'px'\"\n [rotate]=\"0\"\n [selected]=\"selected\"\n [movementBounds]=\"movementBounds\"\n (click)=\"onClick()\"\n (focus)=\"onFocus()\"\n (keydown.tab)=\"onTab($event)\"\n (stopped)=\"onUpdate(rectElement)\"\n (keyboardMovingChange)=\"onKeyboardMovingChange($event)\"\n (boxDelete)=\"onDelete()\">\n </div>\n</div>\n<ng-template #nonEditable>\n <div #rectElement\n class=\"rectangle\"\n mvKeyboardBoxMove\n [tabindex]=\"rectangleTabIndex\"\n [ngClass]=\"{ 'selected': selected }\"\n [style.backgroundColor]=\"'#' + color\"\n [style.width]=\"width + 'px'\"\n [style.height]=\"height+ 'px'\"\n [style.left]=\"left + 'px'\"\n [style.top]=\"top + 'px'\"\n [enabled]=\"false\"\n (click)=\"onClick()\"\n (focus)=\"onFocus()\"\n (keydown.tab)=\"onTab($event)\"\n (boxDelete)=\"onDelete()\">\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4$2.DraggableElementDirective, selector: "[draggable]", inputs: ["rotate"], outputs: ["coordinates", "stopped"] }, { kind: "directive", type: i4$2.ResizableElementDirective, selector: "[resizable]", inputs: ["rotate", "selected"], outputs: ["stopped"] }, { kind: "directive", type: KeyboardBoxMoveDirective, selector: "[mvKeyboardBoxMove]", inputs: ["enabled", "incrementSmall", "incrementLarge", "movementBounds"], outputs: ["keyboardMovingChange", "boxDelete"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RectangleComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-anno-rectangle', standalone: false, template: "<div *ngIf=\"editable; else nonEditable\">\n <div #rectElement\n class=\"rectangle\"\n draggable\n resizable\n mvKeyboardBoxMove\n [tabindex]=\"rectangleTabIndex\"\n [ngClass]=\"{ 'selected': selected, 'grabNDrag': enableGrabNDrag }\"\n [style.backgroundColor]=\"'#' + color\"\n [style.width]=\"width + 'px'\"\n [style.height]=\"height + 'px'\"\n [style.left]=\"left + 'px'\"\n [style.top]=\"top + 'px'\"\n [rotate]=\"0\"\n [selected]=\"selected\"\n [movementBounds]=\"movementBounds\"\n (click)=\"onClick()\"\n (focus)=\"onFocus()\"\n (keydown.tab)=\"onTab($event)\"\n (stopped)=\"onUpdate(rectElement)\"\n (keyboardMovingChange)=\"onKeyboardMovingChange($event)\"\n (boxDelete)=\"onDelete()\">\n </div>\n</div>\n<ng-template #nonEditable>\n <div #rectElement\n class=\"rectangle\"\n mvKeyboardBoxMove\n [tabindex]=\"rectangleTabIndex\"\n [ngClass]=\"{ 'selected': selected }\"\n [style.backgroundColor]=\"'#' + color\"\n [style.width]=\"width + 'px'\"\n [style.height]=\"height+ 'px'\"\n [style.left]=\"left + 'px'\"\n [style.top]=\"top + 'px'\"\n [enabled]=\"false\"\n (click)=\"onClick()\"\n (focus)=\"onFocus()\"\n (keydown.tab)=\"onTab($event)\"\n (boxDelete)=\"onDelete()\">\n </div>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: HighlightCreateService }], propDecorators: { color: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], editable: [{
type: Input
}], pageHeight: [{
type: Input
}], pageWidth: [{
type: Input
}], rectangleTabIndex: [{
type: Input
}], selectEvent: [{
type: Output
}], updateEvent: [{
type: Output
}], deleteEvent: [{
type: Output
}], keyboardMovingChange: [{
type: Output
}], tabToToolbar: [{
type: Output
}], viewRect: [{
type: ViewChild,
args: ['rectElement', { static: false }]
}], annoRect: [{
type: Input
}], selected: [{
type: Input
}] } });
const FOCUSABLE_SELECTOR = [
'a[href]',
'button:not([disabled])',
'textarea:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
'[contenteditable="true"]'
].join(',');
class AnnotationViewComponent {
set annotation(value) {
this.anno = { ...value };
}
set selectedAnnoId(selectedId) {
if (selectedId) {
const id = this.anno.id || this.anno.redactionId; // todo make it unique
this.selected = selectedId.annotationId ? (selectedId.annotationId === id) : false;
if (this.selected && !this.isKeyboardMoving) {
this.showToolbarSubject.next(true);
}
else {
this.showToolbarSubject.next(false);
}
}
}
constructor(toolbarEvents, store) {
this.toolbarEvents = toolbarEvents;
this.store = store;
this.isKeyboardMoving = false;
this.showToolbar = false;
this.showToolbarSubject = new Subject();
this.update = new EventEmitter();
this.delete = new EventEmitter();
this.annotationClick = new EventEmitter();
this.lastFocusedRectangle = null;
this.nextTabTarget = null;
this.showToolbarSubject
.pipe(observeOn(asyncScheduler))
.subscribe(show => {
this.showToolbar = show;
});
}
ngOnDestroy() {
this.showToolbarSubject.complete();
}
setShowToolbar(show) {
this.showToolbarSubject.next(show);
}
onSelect() {
const annotationId = this.anno.id || this.anno.redactionId;
this.annotationClick.emit({ annotationId, editable: false, selected: true });
if (!this.isKeyboardMoving) {
this.setShowToolbar(true);
}
}
onRectangleUpdate(rectangle) {
const annotation = { ...this.anno };
annotation.rectangles = annotation.rectangles.filter(r => r.id !== rectangle.id);
annotation.rectangles.push(rectangle);
this.update.emit(annotation);
}
deleteHighlight() {
this.delete.emit(this.anno);
}
addOrEditComment() {
if (this.anno.comments.length === 0) {
const comment = {
annotationId: this.anno.id,
content: '',
createdBy: this.anno.createdBy,
createdByDetails: undefined,
createdDate: moment.utc().tz('Europe/London').toISOString(),
id: v4(),
lastModifiedBy: '',
lastModifiedByDetails: undefined,
lastModifiedDate: '',
tags: [],
page: null,
pages: {},
pageHeight: null,
selected: false,
editable: false
};
this.store.dispatch(new AddOrEditComment(comment));
}
this.selected = true;
this.annotationClick.emit({ annotationId: this.anno.id, editable: true, selected: true });
this.toolbarEvents.toggleCommentsPanel(true);
}
onKeyboardMovingChange(isMoving) {
this.isKeyboardMoving = isMoving;
if (isMoving) {
this.showToolbar = false;
this.showToolbarSubject.next(false);
}
else if (this.selected) {
this.setShowToolbar(true);
}
}
onTabToToolbar(event) {
if (event.shiftKey) {
return;
}
const toolbarElement = this.ctxToolbar?.nativeElement;
if (!toolbarElement) {
return;
}
const firstButton = toolbarElement.querySelector('button');
if (!firstButton) {
return;
}
this.lastFocusedRectangle = event.target;
this.nextTabTarget = this.getNextTabTarget(this.lastFocusedRectangle);
event.preventDefault();
event.stopPropagation();
firstButton.focus();
}
onToolbarKeydown(event) {
const toolbarElement = this.ctxToolbar?.nativeElement;
if (!toolbarElement) {
return;
}
const buttons = Array.from(toolbarElement.querySelectorAll('button'));
if (buttons.length === 0) {
return;
}
if (event.key === 'Tab' && event.shiftKey && this.lastFocusedRectangle && event.target === buttons[0]) {
event.preventDefault();
event.stopPropagation();
this.lastFocusedRectangle.focus();
return;
}
if (event.key !== 'Tab' || event.shiftKey) {
return;
}
const lastButton = buttons[buttons.length - 1];
if (event.target !== lastButton) {
return;
}
if (!this.nextTabTarget) {
return;
}
event.preventDefault();
event.stopPropagation();
this.nextTabTarget.focus();
this.nextTabTarget = null;
}
getNextTabTarget(current) {
if (!current || typeof document === 'undefined') {
return null;
}
const ordered = Array.from(document.querySelectorAll(FOCUSABLE_SELECTOR))
.filter(el => !el.closest('mv-ctx-toolbar'))
.filter(el => el.getClientRects().length > 0)
.filter(el => el.tabIndex >= 0);
ordered.sort((a, b) => {
const aPositive = a.tabIndex > 0;
const bPositive = b.tabIndex > 0;
if (aPositive && bPositive) {
return (a.tabIndex - b.tabIndex) || this.compareDocumentPosition(a, b);
}
if (aPositive) {
return -1;
}
if (bPositive) {
return 1;
}
return this.compareDocumentPosition(a, b);
});
const index = ordered.indexOf(current);
if (index === -1) {
return null;
}
return ordered[index + 1] ?? null;
}
compareDocumentPosition(a, b) {
if (a === b) {
return 0;
}
const position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
return -1;
}
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
return 1;
}
return 0;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationViewComponent, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: AnnotationViewComponent, isStandalone: false, selector: "mv-annotation", inputs: { annotation: "annotation", zoom: "zoom", rotate: "rotate", selectedAnnoId: "selectedAnnoId", pageHeight: "pageHeight", pageWidth: "pageWidth" }, outputs: { update: "update", delete: "delete", annotationClick: "annotationClick" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }, { propertyName: "ctxToolbar", first: true, predicate: ["ctxToolbar"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div #container [tabindex]=\"-1\">\n <mv-ctx-toolbar #ctxToolbar *ngIf=\"selected && showToolbar\"\n [canDelete]=\"true\" [canComment]=\"true\"\n [rectangles]=\"anno.rectangles\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n (deleteHighlightEvent)=\"deleteHighlight()\"\n (addOrEditCommentEvent)=\"addOrEditComment()\"\n (keydown)=\"onToolbarKeydown($event)\">\n </mv-ctx-toolbar>\n <ng-container *ngFor=\"let rectangle of anno.rectangles; let i = index\">\n <mv-anno-rectangle\n (selectEvent)=\"onSelect()\"\n (updateEvent)=\"onRectangleUpdate($event)\"\n (keyboardMovingChange)=\"onKeyboardMovingChange($event)\"\n (deleteEvent)=\"deleteHighlight()\"\n (tabToToolbar)=\"onTabToToolbar($event)\"\n [selected]=\"selected\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n [editable]=\"anno.rectangles.length <= 1\"\n [annoRect]=\"rectangle\"\n [rectangleTabIndex]=\"i + 1\"\n [color]=\"anno.color\">\n </mv-anno-rectangle>\n </ng-container>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: RectangleComponent, selector: "mv-anno-rectangle", inputs: ["color", "zoom", "rotate", "editable", "pageHeight", "pageWidth", "rectangleTabIndex", "annoRect", "selected"], outputs: ["selectEvent", "updateEvent", "deleteEvent", "keyboardMovingChange", "tabToToolbar"] }, { kind: "component", type: CtxToolbarComponent, selector: "mv-ctx-toolbar", inputs: ["zoom", "rotate", "pageHeight", "pageWidth", "canHighlight", "canBookmark", "canComment", "canDelete", "rectangles"], outputs: ["createHighlightEvent", "deleteHighlightEvent", "addOrEditCommentEvent", "createBookmarkEvent", "cancelEvent"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationViewComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-annotation', standalone: false, template: "<div #container [tabindex]=\"-1\">\n <mv-ctx-toolbar #ctxToolbar *ngIf=\"selected && showToolbar\"\n [canDelete]=\"true\" [canComment]=\"true\"\n [rectangles]=\"anno.rectangles\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n (deleteHighlightEvent)=\"deleteHighlight()\"\n (addOrEditCommentEvent)=\"addOrEditComment()\"\n (keydown)=\"onToolbarKeydown($event)\">\n </mv-ctx-toolbar>\n <ng-container *ngFor=\"let rectangle of anno.rectangles; let i = index\">\n <mv-anno-rectangle\n (selectEvent)=\"onSelect()\"\n (updateEvent)=\"onRectangleUpdate($event)\"\n (keyboardMovingChange)=\"onKeyboardMovingChange($event)\"\n (deleteEvent)=\"deleteHighlight()\"\n (tabToToolbar)=\"onTabToToolbar($event)\"\n [selected]=\"selected\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n [editable]=\"anno.rectangles.length <= 1\"\n [annoRect]=\"rectangle\"\n [rectangleTabIndex]=\"i + 1\"\n [color]=\"anno.color\">\n </mv-anno-rectangle>\n </ng-container>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: i1.Store }], propDecorators: { annotation: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], selectedAnnoId: [{
type: Input
}], pageHeight: [{
type: Input
}], pageWidth: [{
type: Input
}], update: [{
type: Output
}], delete: [{
type: Output
}], annotationClick: [{
type: Output
}], container: [{
type: ViewChild,
args: ['container', { static: false }]
}], ctxToolbar: [{
type: ViewChild,
args: ['ctxToolbar', { static: false, read: ElementRef }]
}] } });
class AnnotationSetComponent {
constructor(store, commentService) {
this.store = store;
this.commentService = commentService;
this.annotations = [];
}
ngOnInit() {
this.selectedAnnotation$ = this.store.pipe(select(getSelectedAnnotation));
}
onAnnotationUpdate(annotation) {
this.store.dispatch(new SaveAnnotation(annotation));
}
onAnnotationDelete(annotation) {
if (annotation.comments.length > 0) {
this.commentService.updateUnsavedCommentsStatus(annotation, false);
}
this.store.dispatch(new DeleteAnnotation(annotation.id));
}
selectAnnotation(selectedAnnotation) {
this.store.dispatch(new SelectedAnnotation(selectedAnnotation));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationSetComponent, deps: [{ token: i1.Store }, { token: CommentService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: AnnotationSetComponent, isStandalone: false, selector: "mv-annotation-set", inputs: { page: "page", annotations: "annotations", zoom: "zoom", rotate: "rotate", pageHeight: "pageHeight", pageWidth: "pageWidth" }, ngImport: i0, template: "<ng-container *ngFor=\"let annotation of annotations\">\n <mv-annotation [annotation]=\"annotation\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [selectedAnnoId]=\"selectedAnnotation$ | async\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n (update)=\"onAnnotationUpdate($event)\"\n (delete)=\"onAnnotationDelete($event)\"\n (annotationClick)=\"selectAnnotation($event)\">\n </mv-annotation>\n</ng-container>\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: AnnotationViewComponent, selector: "mv-annotation", inputs: ["annotation", "zoom", "rotate", "selectedAnnoId", "pageHeight", "pageWidth"], outputs: ["update", "delete", "annotationClick"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationSetComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-annotation-set', standalone: false, template: "<ng-container *ngFor=\"let annotation of annotations\">\n <mv-annotation [annotation]=\"annotation\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [selectedAnnoId]=\"selectedAnnotation$ | async\"\n [pageHeight]=\"pageHeight\"\n [pageWidth]=\"pageWidth\"\n (update)=\"onAnnotationUpdate($event)\"\n (delete)=\"onAnnotationDelete($event)\"\n (annotationClick)=\"selectAnnotation($event)\">\n </mv-annotation>\n</ng-container>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: CommentService }], propDecorators: { page: [{
type: Input
}], annotations: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], pageHeight: [{
type: Input
}], pageWidth: [{
type: Input
}] } });
class MetadataLayerComponent {
constructor(store, highlightService, toolbarEvents, viewerEvents) {
this.store = store;
this.highlightService = highlightService;
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.pages = []; // todo add type
this.drawMode = false;
}
ngOnInit() {
this.$subscriptions = this.store.pipe(select(getPages))
.subscribe(pages => this.pages = Object.values(pages));
this.annoPages$ = this.store.pipe(select(getPageEntities));
this.$subscriptions.add(this.toolbarEvents.drawModeSubject.subscribe(drawMode => this.drawMode = drawMode));
this.$subscriptions.add(this.viewerEvents.textHighlight.subscribe(highlight => this.showContextToolbar(highlight)));
this.$subscriptions.add(this.viewerEvents.ctxToolbarCleared.subscribe(() => this.clearContextToolbar()));
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
showContextToolbar(highlight) {
this.highlightPage = highlight.page;
this.rectangles = highlight.rectangles;
if (this.rectangles) {
this.toolbarEvents.highlightModeSubject.next(false);
setTimeout(() => this.ctxToolbar.focusToolbar(), 0);
}
}
clearContextToolbar() {
this.rectangles = undefined;
}
cancelContextToolbar() {
const selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
}
this.rectangles = undefined;
this.toolbarEvents.highlightModeSubject.next(true);
}
createHighlight() {
this.highlightService.saveAnnotation(this.rectangles, this.highlightPage);
this.highlightService.resetHighlight();
this.rectangles = undefined;
}
createBookmark(rectangle) {
this.store.pipe(select(getBookmarkInfo), take(1))
.subscribe((bookmarkInfo) => {
const selection = window.getSelection().toString();
this.store.dispatch(new CreateBookmark({
...bookmarkInfo,
name: selection.length > 0 ? selection.substr(0, 30) : 'new bookmark',
id: v4(),
pageNumber: this.highlightPage - 1,
xCoordinate: rectangle.x,
yCoordinate: rectangle.y
}));
this.toolbarEvents.toggleSideBar(true);
this.toolbarEvents.toggleSideBarView(false);
this.highlightService.resetHighlight();
this.rectangles = undefined;
});
}
saveAnnotation({ rectangles, page, annotationId }) {
this.highlightService.saveAnnotation(rectangles, page, annotationId);
this.toolbarEvents.drawModeSubject.next(false);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MetadataLayerComponent, deps: [{ token: i1.Store }, { token: HighlightCreateService }, { token: ToolbarEventService }, { token: ViewerEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: MetadataLayerComponent, isStandalone: false, selector: "mv-metadata-layer", inputs: { zoom: "zoom", rotate: "rotate" }, viewQueries: [{ propertyName: "ctxToolbar", first: true, predicate: CtxToolbarComponent, descendants: true }], ngImport: i0, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let page of pages; index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': page.styles.width,\n 'height.px': page.styles.height\n }\"\n [ngClass]=\"{ 'pageContainer__page--draw' : drawMode }\">\n <mv-box-highlight-create\n [page]=\"i + 1\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n [rotate]=\"rotate\"\n [zoom]=\"zoom\"\n (saveSelection)=\"saveAnnotation($event)\">\n </mv-box-highlight-create>\n <mv-ctx-toolbar *ngIf=\"rectangles && highlightPage === (i + 1)\"\n [rectangles]=\"rectangles\"\n [canBookmark]=\"true\" [canHighlight]=\"true\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n (createBookmarkEvent)=\"createBookmark($event)\"\n (createHighlightEvent)=\"createHighlight()\"\n (cancelEvent)=\"cancelContextToolbar()\">\n </mv-ctx-toolbar>\n <div class=\"pageContainer__page-item\">\n <mv-annotation-set *ngIf=\"(annoPages$ | async) as annoPages\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n [page]=\"i\" [annotations]=\"annoPages[i + 1]\"></mv-annotation-set>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: BoxHighlightCreateComponent, selector: "mv-box-highlight-create", inputs: ["page", "pageHeight", "pageWidth", "rotate", "zoom", "container"], outputs: ["saveSelection"] }, { kind: "component", type: AnnotationSetComponent, selector: "mv-annotation-set", inputs: ["page", "annotations", "zoom", "rotate", "pageHeight", "pageWidth"] }, { kind: "component", type: CtxToolbarComponent, selector: "mv-ctx-toolbar", inputs: ["zoom", "rotate", "pageHeight", "pageWidth", "canHighlight", "canBookmark", "canComment", "canDelete", "rectangles"], outputs: ["createHighlightEvent", "deleteHighlightEvent", "addOrEditCommentEvent", "createBookmarkEvent", "cancelEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MetadataLayerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-metadata-layer', standalone: false, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let page of pages; index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': page.styles.width,\n 'height.px': page.styles.height\n }\"\n [ngClass]=\"{ 'pageContainer__page--draw' : drawMode }\">\n <mv-box-highlight-create\n [page]=\"i + 1\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n [rotate]=\"rotate\"\n [zoom]=\"zoom\"\n (saveSelection)=\"saveAnnotation($event)\">\n </mv-box-highlight-create>\n <mv-ctx-toolbar *ngIf=\"rectangles && highlightPage === (i + 1)\"\n [rectangles]=\"rectangles\"\n [canBookmark]=\"true\" [canHighlight]=\"true\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n (createBookmarkEvent)=\"createBookmark($event)\"\n (createHighlightEvent)=\"createHighlight()\"\n (cancelEvent)=\"cancelContextToolbar()\">\n </mv-ctx-toolbar>\n <div class=\"pageContainer__page-item\">\n <mv-annotation-set *ngIf=\"(annoPages$ | async) as annoPages\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [pageHeight]=\"page.styles.height\"\n [pageWidth]=\"page.styles.width\"\n [page]=\"i\" [annotations]=\"annoPages[i + 1]\"></mv-annotation-set>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: HighlightCreateService }, { type: ToolbarEventService }, { type: ViewerEventService }], propDecorators: { zoom: [{
type: Input
}], rotate: [{
type: Input
}], ctxToolbar: [{
type: ViewChild,
args: [CtxToolbarComponent, { static: false }]
}] } });
class BookmarksComponent {
set bookmarkNodes(value) {
this.rebuildTreeForData(value);
if (this._bookmarkNodes && this.sortMode !== this.customSort) {
this.sortBookmarks();
}
}
;
get bookmarkNodes() {
return this._bookmarkNodes;
}
;
constructor(store) {
this.store = store;
this.goToDestination = new EventEmitter();
this.treeHasChanged = new EventEmitter();
this._bookmarkNodes = [];
// expansion model tracks expansion state
this.expansionModel = new SelectionModel(true);
this.isDraggingOn = false;
this.isUserdragging = false;
this.expandDelay = 1000;
this.pageLookup = {};
this.BOOKMARK_CHAR_LIMIT = 30;
this.dragNodeExpandOverWaitTimeMs = 300;
this.options = {
allowDrag: true,
allowDrop: true
};
this._customSort = 'CUSTOM';
this._positionSort = 'POSITION';
this.hasChild = (_, node) => {
return node.children && node.children.length > 0;
};
}
ngOnInit() {
this.sortMode = this.customSort;
this.$subscription = this.store.pipe(select(getEditableBookmark))
.subscribe(editableId => this.editableBookmark = editableId);
this.$subscription.add(this.store.select(getPages)
.subscribe(pages => {
Object.keys(pages).map(key => {
this.pageLookup[key] = pages[key];
});
}));
}
ngOnChanges(changes) {
if (changes?.bookmarkNodes) {
setTimeout(() => {
this.store.dispatch(new UpdateBookmarkScrollTop(this.parentScrollTop));
}, 200);
}
}
ngOnDestroy() {
this.$subscription.unsubscribe();
}
editBookmark(id) {
this.editableBookmark = id;
}
onAddBookmarkClick() {
this.store.pipe(select(getBookmarkInfo), take(1))
.subscribe((bookmarkInfo) => {
this.store.dispatch(new CreateBookmark({
...bookmarkInfo, name: '', id: v4()
}));
});
}
deleteBookmark2(node) {
this.customSortBookmarks();
let next;
const changedData = JSON.parse(JSON.stringify(this.bookmarkNodes));
const siblings = this.findNodeSiblings(changedData, node.id);
if (siblings && siblings.length > node.index + 1) {
next = siblings[node.index + 1];
next.previous = node.previous;
}
const toDelete = [node.id, ...getBookmarkChildren(node.children)];
this.store.dispatch(new DeleteBookmark({
deleted: toDelete, updated: next
}));
}
updateBookmark(bookmark, name) {
const editedBookmark = {
...bookmark,
name
};
if (name) {
this.store.dispatch(new UpdateBookmark(editedBookmark));
this.editableBookmark = undefined;
}
}
goToBookmark(bookmark) {
const thisPage = this.pageLookup[bookmark.pageNumber + 1];
const defaultHeight = thisPage.styles.height;
const defaultScaleY = this.scaledY(bookmark.yCoordinate, defaultHeight, thisPage);
let top = 0, left = 0;
switch (this.rotate) {
case 90:
left = -defaultScaleY;
break;
case 180:
top = this.scaledY(bookmark.yCoordinate, (defaultHeight - (24 * this.zoom)), thisPage);
break;
case 270:
left = defaultScaleY;
break;
default:
top = defaultScaleY;
}
this.goToDestination.emit([
bookmark.pageNumber,
{ 'name': 'XYZ' },
left,
top
]);
}
get customSort() {
return this._customSort;
}
get positionSort() {
return this._positionSort;
}
sort(mode) {
this.sortMode = mode;
this.sortBookmarks();
}
sortBookmarks() {
switch (this.sortMode) {
case this.customSort: {
this.customSortBookmarks();
break;
}
case this.positionSort: {
this.positionSortBookmarks();
break;
}
default: {
this.customSortBookmarks();
break;
}
}
}
positionSortBookmarks() {
this.bookmarkNodes.sort((a, b) => a.pageNumber === b.pageNumber ? a.yCoordinate - b.yCoordinate : a.pageNumber - b.pageNumber);
this.isDraggingOn = false;
this.rebuildTreeForData(this.bookmarkNodes);
}
customSortBookmarks() {
if (this.bookmarkNodes.length > 1) {
this.bookmarkNodes.sort((a, b) => a.index - b.index);
}
this.isDraggingOn = true;
this.rebuildTreeForData(this.bookmarkNodes);
}
scaledY(yCoordinate, height, page) {
const viewportScale = page.viewportScale / this.zoom;
return ((height / this.zoom) - yCoordinate) / viewportScale;
}
getNode(bookmarks, parentId) {
if (typeof bookmarks !== 'undefined') {
for (let i = 0; i < bookmarks.length; i++) {
if (bookmarks[i].id === parentId) {
return [bookmarks[i]];
}
const a = this.getNode(bookmarks[i].children, parentId);
if (a !== null) {
return a;
}
}
}
return null;
}
drop(event) {
if (!event.isPointerOverContainer || (event.previousIndex === event.currentIndex))
return;
const hasMovedUpTheTree = event.previousIndex > event.currentIndex;
const bookmarkNodesString = JSON.stringify(this._bookmarkNodes);
const changedData = JSON.parse(bookmarkNodesString);
const visibleNodes = this.visibleNodes(this._bookmarkNodes);
const toNode = visibleNodes[event.currentIndex];
const toNodeSiblings = this.findNodeSiblings(changedData, toNode.id);
const toNodeParent = toNodeSiblings[0].parent;
const toIndex = toNodeSiblings.findIndex(s => s.id === toNode.id);
const fromNode = event.item.data;
const fromNodeSiblings = this.findNodeSiblings(changedData, fromNode.id);
const fromIndex = fromNodeSiblings.findIndex(n => n.id === fromNode.id);
if (this.isToNodeChildOfFromNode(fromNode?.children, toNode))
return;
if (this.dragNodeInsertToParent) {
const indexOfParent = toNodeSiblings.findIndex(element => element.id === toNode.id);
const parentNode = toNodeSiblings[indexOfParent];
const firstChild = parentNode?.children && parentNode?.children.length > 0 ? parentNode.children[0] : null;
let movedBookmarksWithParent = [{ ...fromNode, parent: parentNode.id, previous: null }];
if (firstChild) {
movedBookmarksWithParent = [...movedBookmarksWithParent, { ...firstChild, previous: fromNode.id }];
}
let fromNodeSibling = this.getSiblingFromAllSibliings(fromNodeSiblings, fromIndex + 1);
const nextFromindexforParent = fromIndex + 1;
fromNodeSibling = fromNodeSibling && fromNodeSibling.id === fromNode.previous ? this.getSiblingFromAllSibliings(fromNodeSiblings, nextFromindexforParent + 1) : fromNodeSibling;
if (fromNodeSibling && fromNodeSibling.id !== parentNode.id) {
movedBookmarksWithParent = [...movedBookmarksWithParent, { ...fromNodeSibling, previous: fromNode.previous }];
}
let previousSiblingForParentIndex = toIndex - 1;
let parentNodeSibling = this.getSiblingFromAllSibliings(toNodeSiblings, previousSiblingForParentIndex);
if (parentNodeSibling && parentNodeSibling.id == fromNode.id) {
previousSiblingForParentIndex = previousSiblingForParentIndex - 1;
if (previousSiblingForParentIndex >= 0) {
parentNodeSibling = this.getSiblingFromAllSibliings(toNodeSiblings, previousSiblingForParentIndex);
}
else {
parentNodeSibling = null;
}
}
movedBookmarksWithParent = [...movedBookmarksWithParent, { ...parentNode, previous: parentNodeSibling?.id }];
const hasParentDups = movedBookmarksWithParent.map(x => x.id).some(function (value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
});
if (hasParentDups || movedBookmarksWithParent && movedBookmarksWithParent.length <= 1)
return;
this.store.dispatch(new MoveBookmark(movedBookmarksWithParent));
return;
}
if (!toNodeSiblings)
return;
const fromNodePrevious = hasMovedUpTheTree ? toNode.previous : toNode.id;
let movedBookmarks = [{
...fromNode,
previous: toNode.index > 0 ? fromNodePrevious : undefined,
parent: toNodeParent
}];
let fromNodeSibling = this.getSiblingFromAllSibliings(fromNodeSiblings, fromIndex + 1);
const nextFromindex = fromIndex + 1;
fromNodeSibling = fromNodeSibling && fromNodeSibling.id === fromNode.previous ? this.getSiblingFromAllSibliings(fromNodeSiblings, nextFromindex + 1) : fromNodeSibling;
if (fromNodeSibling) {
movedBookmarks = [...movedBookmarks, { ...fromNodeSibling, previous: fromNode.previous }];
}
let toNodeSiblingIndex = hasMovedUpTheTree ? toIndex : toIndex + 1;
let toNodeSibling = this.getSiblingFromAllSibliings(toNodeSiblings, toNodeSiblingIndex);
if (toNodeSibling) {
movedBookmarks = [...movedBookmarks, { ...toNodeSibling, previous: fromNode.id }];
}
const hasDups = movedBookmarks.map(x => x.id).some(function (value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
});
if (hasDups || movedBookmarks && movedBookmarks.length <= 1)
return;
this.store.dispatch(new MoveBookmark(movedBookmarks));
}
getSiblingFromAllSibliings(sibling, index) {
return sibling.length > index ? sibling[index] : undefined;
}
dragStart() {
this.dragNodeInsertToParent = false;
this.hoveredNode = null;
this.isUserdragging = true;
}
dragEnd() {
this.isUserdragging = false;
if (this.hoverHtmlElement?.style) {
this.hoverHtmlElement.style.borderRight = '';
}
}
dragHover(event, node) {
if (this.isUserdragging) {
const newEvent = event;
const percentageX = newEvent.offsetX / newEvent.target.clientWidth;
if (percentageX > .55) {
this.hoveredNode = node;
if (this.hoverHtmlElement?.style) {
this.hoverHtmlElement.style.borderRight = '';
}
this.hoverHtmlElement = event.currentTarget;
this.hoverHtmlElement.style.borderRight = '5px solid #007bff';
this.dragNodeInsertToParent = true;
}
else {
this.hoveredNode = null;
if (this.hoverHtmlElement?.style) {
this.hoverHtmlElement.style.borderRight = '';
}
this.dragNodeInsertToParent = false;
}
}
}
dragHoverEnd(event, node) {
if (this.isUserdragging) {
if (!node || this.hoveredNode?.id !== node.id) {
this.dragNodeInsertToParent = false;
if (this.hoverHtmlElement?.style) {
this.hoverHtmlElement.style.borderRight = '';
}
this.hoveredNode = null;
}
}
}
onNodeExpand(node) {
const isExpanded = this.treeControl.isExpanded(node);
return isExpanded ? "toggle-children-wrapper-expanded" : "toggle-children-wrapper-collapsed";
}
visibleNodes(bookmarks) {
const result = [];
function addExpandedChildren(node, expanded) {
result.push(node);
if (expanded.some(bookmark => bookmark && bookmark.id === node.id) && node.children) {
node.children.map((child) => addExpandedChildren(child, expanded));
}
}
bookmarks.forEach((node) => {
addExpandedChildren(node, this.treeControl.expansionModel.selected);
});
return result;
}
findNodeSiblings(arr, id) {
let result, subResult;
arr.forEach((item, i) => {
if (item.id === id) {
result = arr;
}
else if (item?.children) {
subResult = this.findNodeSiblings(item.children, id);
if (subResult)
result = subResult;
}
});
return result;
}
rebuildTreeForData(data) {
this._bookmarkNodes = data ?? [];
this.datasource = new ArrayDataSource(this._bookmarkNodes);
this.treeControl = new NestedTreeControl(node => node.children);
this.treeControl.dataNodes = this._bookmarkNodes;
this.expansionModel.selected.forEach((bookmark) => {
const node = this.treeControl.dataNodes.find((n) => n.id === bookmark.id);
this.treeControl.expand(node);
});
this.treeHasChanged.emit(true);
}
isToNodeChildOfFromNode(fromNodeChildren, toNode) {
if (!fromNodeChildren) {
return false;
}
const result = this.getNode(fromNodeChildren, toNode.id);
return result ? true : false;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: BookmarksComponent, isStandalone: false, selector: "mv-bookmarks", inputs: { bookmarkNodes: "bookmarkNodes", zoom: "zoom", rotate: "rotate", parentScrollTop: "parentScrollTop" }, outputs: { goToDestination: "goToDestination", treeHasChanged: "treeHasChanged" }, usesOnChanges: true, ngImport: i0, template: "<a *ngIf=\"bookmarkNodes?.length === 0\" class=\"highlightedOutlineItem\"\n >No bookmarks created yet</a\n>\n<cdk-tree\n cdkDropList\n [cdkDropListDisabled]=\"!isDraggingOn\"\n class=\"bookmarks-tree\"\n *ngIf=\"datasource\"\n [dataSource]=\"datasource\"\n [treeControl]=\"treeControl\"\n (cdkDropListDropped)=\"drop($event)\"\n>\n <!-- This is the tree node template for leaf nodes -->\n <cdk-nested-tree-node class=\"node-wrapper\" *cdkTreeNodeDef=\"let node\">\n <div *ngIf=\"node.id !== editableBookmark; else inputBookmark\">\n <div\n class=\"node-content-wrapper nested-tree-node nest-tree-node-padding\"\n cdkDrag\n cdkDragBoundary=\".bookmarks-tree\"\n [cdkDragData]=\"node\"\n (mouseenter)=\"dragHover($event, node)\"\n (mouseleave)=\"dragHoverEnd($event, node)\"\n (cdkDragStarted)=\"dragStart()\"\n (cdkDragReleased)=\"dragEnd()\"\n >\n <div class=\"outlineItem\">\n <a (click)=\"goToBookmark(node)\">\n {{ node.name }}\n </a>\n </div>\n <button\n class=\"bookmark__rename\"\n (click)=\"editBookmark(node.id)\"\n ></button>\n <button\n class=\"bookmark__delete\"\n (click)=\"deleteBookmark2(node)\"\n ></button>\n </div>\n <!-- <div class=\"node-drop-slot\"></div> -->\n </div>\n <ng-template #inputBookmark>\n <input\n #bookmarkName\n class=\"bookmark__input\"\n [value]=\"node.name\"\n [maxLength]=\"BOOKMARK_CHAR_LIMIT\"\n />\n <button\n class=\"bookmark__save\"\n (click)=\"updateBookmark(node, bookmarkName.value)\"\n ></button>\n </ng-template>\n </cdk-nested-tree-node>\n <!-- This is the tree node template for expandable nodes -->\n <cdk-nested-tree-node\n class=\"node-wrapper\"\n *cdkTreeNodeDef=\"let node; when: hasChild\"\n >\n <div *ngIf=\"node.id !== editableBookmark; else inputBookmark2\">\n <div\n class=\"node-content-wrapper nested-tree-node\"\n cdkDrag\n [cdkDragData]=\"node\"\n (mouseenter)=\"dragHover($event, node)\"\n (mouseleave)=\"dragHoverEnd($event, node)\"\n (cdkDragStarted)=\"dragStart()\"\n (cdkDragReleased)=\"dragEnd()\"\n >\n <div class=\"outlineItem\">\n <span\n cdkTreeNodeToggle\n class=\"toggle-children-wrapper\"\n [ngClass]=\"onNodeExpand(node)\"\n ><span class=\"toggle-children\"></span\n ></span>\n <a (click)=\"goToBookmark(node)\">\n {{ node.name }}\n </a>\n </div>\n <button\n class=\"bookmark__rename\"\n (click)=\"editBookmark(node.id)\"\n ></button>\n <button\n class=\"bookmark__delete\"\n (click)=\"deleteBookmark2(node)\"\n ></button>\n </div>\n <!-- <div class=\"node-drop-slot\"></div> -->\n </div>\n <ng-template #inputBookmark2>\n <input\n #bookmarkName\n class=\"bookmark__input\"\n [value]=\"node.name\"\n [maxLength]=\"BOOKMARK_CHAR_LIMIT\"\n />\n <button\n class=\"bookmark__save\"\n (click)=\"updateBookmark(node, bookmarkName.value)\"\n ></button>\n </ng-template>\n <div class=\"nested-tree-node-children\" *ngIf=\"treeControl.isExpanded(node)\">\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n</cdk-tree>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3$1.CdkNestedTreeNode, selector: "cdk-nested-tree-node", exportAs: ["cdkNestedTreeNode"] }, { kind: "directive", type: i3$1.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: i3$1.CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "component", type: i3$1.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "trackBy"], exportAs: ["cdkTree"] }, { kind: "directive", type: i3$1.CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }, { kind: "directive", type: i4$3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i4$3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-bookmarks', standalone: false, template: "<a *ngIf=\"bookmarkNodes?.length === 0\" class=\"highlightedOutlineItem\"\n >No bookmarks created yet</a\n>\n<cdk-tree\n cdkDropList\n [cdkDropListDisabled]=\"!isDraggingOn\"\n class=\"bookmarks-tree\"\n *ngIf=\"datasource\"\n [dataSource]=\"datasource\"\n [treeControl]=\"treeControl\"\n (cdkDropListDropped)=\"drop($event)\"\n>\n <!-- This is the tree node template for leaf nodes -->\n <cdk-nested-tree-node class=\"node-wrapper\" *cdkTreeNodeDef=\"let node\">\n <div *ngIf=\"node.id !== editableBookmark; else inputBookmark\">\n <div\n class=\"node-content-wrapper nested-tree-node nest-tree-node-padding\"\n cdkDrag\n cdkDragBoundary=\".bookmarks-tree\"\n [cdkDragData]=\"node\"\n (mouseenter)=\"dragHover($event, node)\"\n (mouseleave)=\"dragHoverEnd($event, node)\"\n (cdkDragStarted)=\"dragStart()\"\n (cdkDragReleased)=\"dragEnd()\"\n >\n <div class=\"outlineItem\">\n <a (click)=\"goToBookmark(node)\">\n {{ node.name }}\n </a>\n </div>\n <button\n class=\"bookmark__rename\"\n (click)=\"editBookmark(node.id)\"\n ></button>\n <button\n class=\"bookmark__delete\"\n (click)=\"deleteBookmark2(node)\"\n ></button>\n </div>\n <!-- <div class=\"node-drop-slot\"></div> -->\n </div>\n <ng-template #inputBookmark>\n <input\n #bookmarkName\n class=\"bookmark__input\"\n [value]=\"node.name\"\n [maxLength]=\"BOOKMARK_CHAR_LIMIT\"\n />\n <button\n class=\"bookmark__save\"\n (click)=\"updateBookmark(node, bookmarkName.value)\"\n ></button>\n </ng-template>\n </cdk-nested-tree-node>\n <!-- This is the tree node template for expandable nodes -->\n <cdk-nested-tree-node\n class=\"node-wrapper\"\n *cdkTreeNodeDef=\"let node; when: hasChild\"\n >\n <div *ngIf=\"node.id !== editableBookmark; else inputBookmark2\">\n <div\n class=\"node-content-wrapper nested-tree-node\"\n cdkDrag\n [cdkDragData]=\"node\"\n (mouseenter)=\"dragHover($event, node)\"\n (mouseleave)=\"dragHoverEnd($event, node)\"\n (cdkDragStarted)=\"dragStart()\"\n (cdkDragReleased)=\"dragEnd()\"\n >\n <div class=\"outlineItem\">\n <span\n cdkTreeNodeToggle\n class=\"toggle-children-wrapper\"\n [ngClass]=\"onNodeExpand(node)\"\n ><span class=\"toggle-children\"></span\n ></span>\n <a (click)=\"goToBookmark(node)\">\n {{ node.name }}\n </a>\n </div>\n <button\n class=\"bookmark__rename\"\n (click)=\"editBookmark(node.id)\"\n ></button>\n <button\n class=\"bookmark__delete\"\n (click)=\"deleteBookmark2(node)\"\n ></button>\n </div>\n <!-- <div class=\"node-drop-slot\"></div> -->\n </div>\n <ng-template #inputBookmark2>\n <input\n #bookmarkName\n class=\"bookmark__input\"\n [value]=\"node.name\"\n [maxLength]=\"BOOKMARK_CHAR_LIMIT\"\n />\n <button\n class=\"bookmark__save\"\n (click)=\"updateBookmark(node, bookmarkName.value)\"\n ></button>\n </ng-template>\n <div class=\"nested-tree-node-children\" *ngIf=\"treeControl.isExpanded(node)\">\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n</cdk-tree>\n" }]
}], ctorParameters: () => [{ type: i1.Store }], propDecorators: { bookmarkNodes: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], parentScrollTop: [{
type: Input
}], goToDestination: [{
type: Output
}], treeHasChanged: [{
type: Output
}] } });
class OutlineItemComponent {
constructor() {
this.navigationEvent = new EventEmitter();
}
ngOnInit() {
this.showOutlineItems = true;
}
goToDestination(destination) {
if (destination) {
this.navigationEvent.emit(destination);
}
}
toggleOutline() {
this.showOutlineItems = !this.showOutlineItems;
}
isViewedItem(current, next) {
if (current.pageNumber === this.currentPageNumber) {
return true;
}
return next === undefined ? current.pageNumber <= this.currentPageNumber && this.endPage > this.currentPageNumber :
current.pageNumber <= this.currentPageNumber && (next.pageNumber > this.currentPageNumber);
}
findEndPage(next) {
return next === undefined ? this.endPage : next.pageNumber;
}
showHighlightOutlineCss() {
return this.isCurrentSection ? 'highlightedOutlineItem' : 'outlineItem';
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: OutlineItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: OutlineItemComponent, isStandalone: false, selector: "mv-outline-item", inputs: { outline: "outline", currentPageNumber: "currentPageNumber", isCurrentSection: "isCurrentSection", endPage: "endPage" }, outputs: { navigationEvent: "navigationEvent" }, ngImport: i0, template: "<div class=\"outlineItem\">\n <div *ngIf=\"outline.items.length > 0\"\n [ngClass]=\"{ 'outlineItemToggler': true, 'outlineItemsHidden': !showOutlineItems }\"\n (click)=\"toggleOutline()\"\n (keyup.enter)=\"toggleOutline()\" tabindex=\"0\"></div>\n <a (click)=\"goToDestination(outline?.dest)\"\n (keyup.enter)=\"goToDestination(outline?.dest)\"\n [style.font-weight]=\"outline.bold ? 'bold' : ''\"\n [style.font-style]=\"outline.italic ? 'italic' : ''\"\n [ngClass]=\"showHighlightOutlineCss()\"\n tabindex=\"0\">\n <div style=\"text-align:left;\" [ngClass]=\"showHighlightOutlineCss()\">\n {{ outline.title }}\n <span style=\"float:right;\">\n {{ outline.pageNumber }}\n </span>\n </div>\n </a>\n \n <div *ngIf=\"outline.items.length > 0\" class=\"outlineItems\">\n <div *ngFor=\"let outlineItem of outline.items; index as i\">\n <mv-outline-item\n [outline]=\"outlineItem\" \n [currentPageNumber]= \"currentPageNumber\" \n [isCurrentSection]= \"isViewedItem(outlineItem, outline.items[i+1])\"\n [endPage]=\"findEndPage(outline.items[i+1])\"\n (navigationEvent)=\"goToDestination($event)\">\n </mv-outline-item>\n </div>\n </div>\n</div>\n\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: OutlineItemComponent, selector: "mv-outline-item", inputs: ["outline", "currentPageNumber", "isCurrentSection", "endPage"], outputs: ["navigationEvent"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: OutlineItemComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-outline-item', standalone: false, template: "<div class=\"outlineItem\">\n <div *ngIf=\"outline.items.length > 0\"\n [ngClass]=\"{ 'outlineItemToggler': true, 'outlineItemsHidden': !showOutlineItems }\"\n (click)=\"toggleOutline()\"\n (keyup.enter)=\"toggleOutline()\" tabindex=\"0\"></div>\n <a (click)=\"goToDestination(outline?.dest)\"\n (keyup.enter)=\"goToDestination(outline?.dest)\"\n [style.font-weight]=\"outline.bold ? 'bold' : ''\"\n [style.font-style]=\"outline.italic ? 'italic' : ''\"\n [ngClass]=\"showHighlightOutlineCss()\"\n tabindex=\"0\">\n <div style=\"text-align:left;\" [ngClass]=\"showHighlightOutlineCss()\">\n {{ outline.title }}\n <span style=\"float:right;\">\n {{ outline.pageNumber }}\n </span>\n </div>\n </a>\n \n <div *ngIf=\"outline.items.length > 0\" class=\"outlineItems\">\n <div *ngFor=\"let outlineItem of outline.items; index as i\">\n <mv-outline-item\n [outline]=\"outlineItem\" \n [currentPageNumber]= \"currentPageNumber\" \n [isCurrentSection]= \"isViewedItem(outlineItem, outline.items[i+1])\"\n [endPage]=\"findEndPage(outline.items[i+1])\"\n (navigationEvent)=\"goToDestination($event)\">\n </mv-outline-item>\n </div>\n </div>\n</div>\n\n" }]
}], propDecorators: { outline: [{
type: Input
}], currentPageNumber: [{
type: Input
}], isCurrentSection: [{
type: Input
}], endPage: [{
type: Input
}], navigationEvent: [{
type: Output
}] } });
class SideBarComponent {
constructor(viewerEvents, store, toolbarEvents) {
this.viewerEvents = viewerEvents;
this.store = store;
this.toolbarEvents = toolbarEvents;
this.treeChanged = false;
this.selectedView = 'outline';
this.subscriptions = [];
}
ngOnInit() {
this.bookmarkNodes$ = this.store.pipe(select(getBookmarkNodes));
this.subscriptions.push(this.store.pipe(select(getEditableBookmark))
.subscribe(editable => this.selectedView = editable ? 'bookmarks' : this.selectedView));
this.subscriptions.push(this.toolbarEvents.sidebarOutlineView.subscribe(toggle => {
this.selectedView = toggle ? 'outline' : 'bookmarks';
}), this.toolbarEvents.sidebarOpen.subscribe(toggle => {
this.sidebarOpen = toggle;
}));
this.subscriptions.push(this.store.pipe(select(getScrollTop)).subscribe(scrollTopValue => {
if (this.sidebarDiv?.nativeElement) {
this.sidebarDiv.nativeElement.scrollTop = scrollTopValue;
}
}));
}
ngOnChanges(changes) {
if (changes.url && this.url) {
this.store.dispatch(new LoadBookmarks());
}
}
ngOnDestroy() {
if (this.subscriptions.length > 0) {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
}
onScroll(event) {
if (!this.treeChanged) {
this.scrollTop = event.srcElement.scrollTop;
}
this.treeChanged = false;
}
goToDestination(destination) {
this.viewerEvents.goToDestination(destination);
}
hasTreeChanged(value) {
this.treeChanged = value;
}
toggleSidebarView(sidebarView) {
this.selectedView = sidebarView;
}
isViewedItem(current, next) {
if (current.pageNumber === this.currentPageNumber) {
return true;
}
return next === undefined ? current.pageNumber <= this.currentPageNumber :
current.pageNumber <= this.currentPageNumber && (next.pageNumber > this.currentPageNumber);
}
findEndPage(next) {
return next === undefined ? Number.MAX_SAFE_INTEGER : next.pageNumber;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SideBarComponent, deps: [{ token: ViewerEventService }, { token: i1.Store }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: SideBarComponent, isStandalone: false, selector: "mv-side-bar", inputs: { annotationsEnabled: "annotationsEnabled", outline: "outline", url: "url", zoom: "zoom", rotate: "rotate", currentPageNumber: "currentPageNumber" }, viewQueries: [{ propertyName: "bookmarks", first: true, predicate: BookmarksComponent, descendants: true }, { propertyName: "sidebarDiv", first: true, predicate: ["sidebar"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div id=\"toolbarSidebar\">\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n class=\"splitToolbarButton toggled\"\n >\n <button\n id=\"sortBookmarkPosition\"\n class=\"splitToolbarButton left bookmark__sort__position\"\n (click)=\"bookmarks.sort(bookmarks.positionSort)\"\n [attr.aria-label]=\"'Order by place in document' | rpxTranslate\"\n aria-pressed=\"true\"\n ></button>\n <button\n id=\"sortBookmarkCustom\"\n class=\"splitToolbarButton left bookmark__sort__custom\"\n (click)=\"bookmarks.sort(bookmarks.customSort)\"\n [attr.aria-label]=\"'Order manually' | rpxTranslate\"\n aria-pressed=\"true\"\n ></button>\n </div>\n <div class=\"splitToolbarButtonSeparator\"></div>\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n class=\"splitToolbarButton right\"\n >\n <button\n id=\"addBookmark\"\n class=\"toolbarButton addBookmark\"\n (click)=\"bookmarks.onAddBookmarkClick()\"\n [attr.aria-label]=\"'Add bookmark' | rpxTranslate\"\n tabindex=\"3\"\n data-l10n-id=\"addBookmark\"\n >\n <span data-l10n-id=\"addBookmark_label\" aria-hidden=\"true\">{{\n \"Add bookmark\" | rpxTranslate\n }}</span>\n </button>\n </div>\n</div>\n<div #sidebar id=\"sidebarContent\" cdkScrollable (scroll)=\"onScroll($event)\" [hidden]=\"!sidebarOpen\">\n <div id=\"outlineView\" class=\"outlineWithDeepNesting\">\n <div class=\"outlineItem\" *ngIf=\"selectedView === 'outline'\">\n <mv-outline-item\n *ngFor=\"let outlineItem of outline; index as i\"\n [outline]=\"outlineItem\"\n [currentPageNumber]=\"currentPageNumber\"\n [isCurrentSection]=\"isViewedItem(outlineItem, outline[i + 1])\"\n [endPage]=\"findEndPage(outline[i + 1])\"\n (navigationEvent)=\"goToDestination($event)\"\n ></mv-outline-item>\n </div>\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n id=\"bookmarkContainer\"\n >\n <mv-bookmarks\n [bookmarkNodes]=\"bookmarkNodes$ | async\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [parentScrollTop]=\"scrollTop\"\n (goToDestination)=\"goToDestination($event)\"\n (treeHasChanged)=\"hasTreeChanged($event)\"\n ></mv-bookmarks>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5$1.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "component", type: BookmarksComponent, selector: "mv-bookmarks", inputs: ["bookmarkNodes", "zoom", "rotate", "parentScrollTop"], outputs: ["goToDestination", "treeHasChanged"] }, { kind: "component", type: OutlineItemComponent, selector: "mv-outline-item", inputs: ["outline", "currentPageNumber", "isCurrentSection", "endPage"], outputs: ["navigationEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SideBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-side-bar', standalone: false, template: "<div id=\"toolbarSidebar\">\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n class=\"splitToolbarButton toggled\"\n >\n <button\n id=\"sortBookmarkPosition\"\n class=\"splitToolbarButton left bookmark__sort__position\"\n (click)=\"bookmarks.sort(bookmarks.positionSort)\"\n [attr.aria-label]=\"'Order by place in document' | rpxTranslate\"\n aria-pressed=\"true\"\n ></button>\n <button\n id=\"sortBookmarkCustom\"\n class=\"splitToolbarButton left bookmark__sort__custom\"\n (click)=\"bookmarks.sort(bookmarks.customSort)\"\n [attr.aria-label]=\"'Order manually' | rpxTranslate\"\n aria-pressed=\"true\"\n ></button>\n </div>\n <div class=\"splitToolbarButtonSeparator\"></div>\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n class=\"splitToolbarButton right\"\n >\n <button\n id=\"addBookmark\"\n class=\"toolbarButton addBookmark\"\n (click)=\"bookmarks.onAddBookmarkClick()\"\n [attr.aria-label]=\"'Add bookmark' | rpxTranslate\"\n tabindex=\"3\"\n data-l10n-id=\"addBookmark\"\n >\n <span data-l10n-id=\"addBookmark_label\" aria-hidden=\"true\">{{\n \"Add bookmark\" | rpxTranslate\n }}</span>\n </button>\n </div>\n</div>\n<div #sidebar id=\"sidebarContent\" cdkScrollable (scroll)=\"onScroll($event)\" [hidden]=\"!sidebarOpen\">\n <div id=\"outlineView\" class=\"outlineWithDeepNesting\">\n <div class=\"outlineItem\" *ngIf=\"selectedView === 'outline'\">\n <mv-outline-item\n *ngFor=\"let outlineItem of outline; index as i\"\n [outline]=\"outlineItem\"\n [currentPageNumber]=\"currentPageNumber\"\n [isCurrentSection]=\"isViewedItem(outlineItem, outline[i + 1])\"\n [endPage]=\"findEndPage(outline[i + 1])\"\n (navigationEvent)=\"goToDestination($event)\"\n ></mv-outline-item>\n </div>\n <div\n *ngIf=\"annotationsEnabled && selectedView === 'bookmarks'\"\n id=\"bookmarkContainer\"\n >\n <mv-bookmarks\n [bookmarkNodes]=\"bookmarkNodes$ | async\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [parentScrollTop]=\"scrollTop\"\n (goToDestination)=\"goToDestination($event)\"\n (treeHasChanged)=\"hasTreeChanged($event)\"\n ></mv-bookmarks>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ViewerEventService }, { type: i1.Store }, { type: ToolbarEventService }], propDecorators: { annotationsEnabled: [{
type: Input
}], outline: [{
type: Input
}], url: [{
type: Input
}], zoom: [{
type: Input
}], rotate: [{
type: Input
}], currentPageNumber: [{
type: Input
}], bookmarks: [{
type: ViewChild,
args: [BookmarksComponent]
}], sidebarDiv: [{
type: ViewChild,
args: ['sidebar']
}] } });
class GrabNDragDirective {
constructor(el) {
this.el = el;
this.pointerDown = false;
this.dragEnabled = false;
}
onPointerDown(event) {
if (this.dragEnabled) {
event.preventDefault();
this.pointerDown = true;
this.originalPosition = {
left: event.clientX + this.el.nativeElement.scrollLeft,
top: event.clientY + this.el.nativeElement.scrollTop,
};
}
}
onPointerMove(event) {
if (this.pointerDown && this.dragEnabled) {
event.preventDefault();
const scrollDiff = {
left: this.originalPosition.left - (event.clientX + this.el.nativeElement.scrollLeft),
top: this.originalPosition.top - (event.clientY + this.el.nativeElement.scrollTop)
};
if (this.dragEnabled) {
this.dragX.scrollLeft += scrollDiff.left;
this.el.nativeElement.scrollTop += scrollDiff.top;
}
}
}
onWindowPointerUp() {
this.pointerDown = false;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GrabNDragDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: GrabNDragDirective, isStandalone: false, selector: "[mvGrabNDrag]", inputs: { dragEnabled: "dragEnabled", dragX: "dragX" }, host: { listeners: { "pointerdown": "onPointerDown($event)", "window:pointermove": "onPointerMove($event)", "window:pointerup": "onWindowPointerUp()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GrabNDragDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvGrabNDrag]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { dragEnabled: [{
type: Input
}], dragX: [{
type: Input
}], onPointerDown: [{
type: HostListener,
args: ['pointerdown', ['$event']]
}], onPointerMove: [{
type: HostListener,
args: ['window:pointermove', ['$event']]
}], onWindowPointerUp: [{
type: HostListener,
args: ['window:pointerup']
}] } });
class KeyboardTextHighlightDirective {
set enabled(value) {
const wasEnabled = this._enabled;
this._enabled = value;
}
get enabled() {
return this._enabled;
}
static { this.lastInteractionWasKeyboard = false; }
constructor(elementRef) {
this.elementRef = elementRef;
this._enabled = false;
this.incrementSmall = 5;
this.incrementMedium = 10;
this.incrementLarge = 20;
this.selectionStarted = new EventEmitter();
this.selectionUpdated = new EventEmitter();
this.selectionConfirmed = new EventEmitter();
this.selectionCancelled = new EventEmitter();
this.cursorPositionChanged = new EventEmitter();
this.selectionCursorPositionChanged = new EventEmitter();
this.isSelecting = false;
this.showCursor = false;
this.lastValidEndNode = null;
this.lastValidEndOffset = 0;
if (typeof window !== 'undefined') {
window.addEventListener('keydown', KeyboardTextHighlightDirective.onGlobalKeyDown, { capture: true });
window.addEventListener('mousedown', KeyboardTextHighlightDirective.onGlobalMouseDown, { capture: true });
}
}
static onGlobalKeyDown() {
KeyboardTextHighlightDirective.lastInteractionWasKeyboard = true;
}
static onGlobalMouseDown() {
KeyboardTextHighlightDirective.lastInteractionWasKeyboard = false;
}
ngOnDestroy() {
this.cleanup();
}
onKeyDown(event) {
if (!this.enabled) {
return;
}
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
if (!this.isSelecting) {
this.startTextSelection();
}
else {
this.confirmTextSelection();
}
return;
}
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
if (this.isSelecting) {
this.cancelTextSelection();
}
else if (this.showCursor) {
this.hideCursor();
}
return;
}
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
event.preventDefault();
event.stopPropagation();
if (this.isSelecting) {
this.expandTextSelection(event);
}
else {
this.moveCursor(event);
}
}
}
onBlur() {
if (this.showCursor && !this.isSelecting) {
this.hideCursor();
}
}
moveCursor(event) {
const increment = event.shiftKey ? this.incrementLarge : this.incrementMedium;
if (!this.showCursor) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
this.cursorX = viewportWidth / 2;
this.cursorY = viewportHeight / 2;
this.showCursor = true;
this.emitCursorPosition();
return;
}
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
switch (event.key) {
case 'ArrowUp':
this.cursorY = Math.max(0, this.cursorY - increment);
break;
case 'ArrowDown':
this.cursorY = Math.min(viewportHeight, this.cursorY + increment);
break;
case 'ArrowLeft':
this.cursorX = Math.max(0, this.cursorX - increment);
break;
case 'ArrowRight':
this.cursorX = Math.min(viewportWidth, this.cursorX + increment);
break;
}
this.emitCursorPosition();
}
startTextSelection() {
if (this.showCursor) {
this.selectionStartX = this.cursorX;
this.selectionStartY = this.cursorY;
this.selectionEndX = this.cursorX;
this.selectionEndY = this.cursorY;
this.showCursor = false;
this.emitCursorPosition();
}
else {
// default to center
this.selectionStartX = window.innerWidth / 2;
this.selectionStartY = window.innerHeight / 2;
this.selectionEndX = this.selectionStartX;
this.selectionEndY = this.selectionStartY;
}
this.isSelecting = true;
this.currentPage = this.getCurrentPageNumber();
this.lastValidEndNode = null;
this.lastValidEndOffset = 0;
this.selectionCursorPositionChanged.emit({
x: this.selectionEndX,
y: this.selectionEndY,
visible: true
});
this.createTextSelectionAtPoint(this.selectionStartX, this.selectionStartY);
this.selectionStarted.emit();
}
expandTextSelection(event) {
const increment = event.shiftKey ? this.incrementLarge : this.incrementSmall;
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
switch (event.key) {
case 'ArrowRight':
this.selectionEndX = Math.min(viewportWidth, this.selectionEndX + increment);
break;
case 'ArrowLeft':
this.selectionEndX = Math.max(0, this.selectionEndX - increment);
break;
case 'ArrowDown':
this.selectionEndY = Math.min(viewportHeight, this.selectionEndY + increment);
break;
case 'ArrowUp':
this.selectionEndY = Math.max(0, this.selectionEndY - increment);
break;
}
this.selectionCursorPositionChanged.emit({
x: this.selectionEndX,
y: this.selectionEndY,
visible: true
});
this.updateTextSelection();
this.emitSelectionUpdated();
}
createTextSelectionAtPoint(viewportX, viewportY) {
const selection = window.getSelection();
// get precise caret position at the coordinates
// caretPositionFromPoint is standard but not supported in all browsers
// caretRangeFromPoint is older and supported in more browsers
let range = null;
if (document.caretPositionFromPoint) {
const caretPosition = document.caretPositionFromPoint(viewportX, viewportY);
if (caretPosition) {
range = document.createRange();
range.setStart(caretPosition.offsetNode, caretPosition.offset);
range.collapse(true);
}
}
else if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(viewportX, viewportY);
}
if (range) {
selection.removeAllRanges();
selection.addRange(range);
}
}
updateTextSelection() {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) {
return;
}
const range = selection.getRangeAt(0);
const startNode = range.startContainer;
const startOffset = range.startOffset;
let endNode = null;
let endOffset = 0;
if (document.caretPositionFromPoint) {
const caretPosition = document.caretPositionFromPoint(this.selectionEndX, this.selectionEndY);
if (caretPosition) {
endNode = caretPosition.offsetNode;
endOffset = caretPosition.offset;
}
}
else if (document.caretRangeFromPoint) {
const caretRange = document.caretRangeFromPoint(this.selectionEndX, this.selectionEndY);
if (caretRange) {
endNode = caretRange.startContainer;
endOffset = caretRange.startOffset;
}
}
if (endNode) {
const range = document.createRange();
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
const isBackward = range.collapsed && (startNode !== endNode || endOffset < startOffset);
const comparison = startNode.compareDocumentPosition(endNode);
const endBeforeStart = (comparison & Node.DOCUMENT_POSITION_PRECEDING) !== 0;
if (isBackward || endBeforeStart) {
if (this.lastValidEndNode) {
endNode = this.lastValidEndNode;
endOffset = this.lastValidEndOffset;
}
else {
endNode = startNode;
endOffset = startOffset;
}
}
else {
this.lastValidEndNode = endNode;
this.lastValidEndOffset = endOffset;
}
selection.setBaseAndExtent(startNode, startOffset, endNode, endOffset);
}
}
confirmTextSelection() {
this.selectionConfirmed.emit();
this.selectionCursorPositionChanged.emit({
x: this.selectionEndX,
y: this.selectionEndY,
visible: false
});
this.cleanup();
}
cancelTextSelection() {
const selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
}
this.selectionCancelled.emit();
this.selectionCursorPositionChanged.emit({
x: this.selectionEndX,
y: this.selectionEndY,
visible: false
});
this.cleanup();
}
hideCursor() {
this.showCursor = false;
this.emitCursorPosition();
}
cleanup() {
this.isSelecting = false;
this.showCursor = false;
this.cursorX = undefined;
this.cursorY = undefined;
this.selectionStartX = undefined;
this.selectionStartY = undefined;
this.selectionEndX = undefined;
this.selectionEndY = undefined;
this.currentPage = undefined;
this.lastValidEndNode = null;
this.lastValidEndOffset = 0;
}
getCurrentPageNumber() {
let currentElement = this.elementRef.nativeElement;
while (currentElement && currentElement.offsetParent) {
currentElement = currentElement.offsetParent;
if (currentElement.getAttribute) {
const page = parseInt(currentElement.getAttribute('data-page-number'), 10);
if (page) {
return page;
}
}
}
return 1;
}
emitCursorPosition() {
const position = {
x: this.cursorX,
y: this.cursorY,
visible: this.showCursor
};
this.cursorPositionChanged.emit(position);
}
emitSelectionUpdated() {
this.selectionUpdated.emit({
page: this.currentPage,
startX: this.selectionStartX,
startY: this.selectionStartY,
endX: this.selectionEndX,
endY: this.selectionEndY
});
}
reset() {
this.cleanup();
this.emitCursorPosition();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardTextHighlightDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: KeyboardTextHighlightDirective, isStandalone: false, selector: "[mvKeyboardTextHighlight]", inputs: { enabled: "enabled", incrementSmall: "incrementSmall", incrementMedium: "incrementMedium", incrementLarge: "incrementLarge" }, outputs: { selectionStarted: "selectionStarted", selectionUpdated: "selectionUpdated", selectionConfirmed: "selectionConfirmed", selectionCancelled: "selectionCancelled", cursorPositionChanged: "cursorPositionChanged", selectionCursorPositionChanged: "selectionCursorPositionChanged" }, host: { listeners: { "keydown": "onKeyDown($event)", "blur": "onBlur()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardTextHighlightDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvKeyboardTextHighlight]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { enabled: [{
type: Input
}], incrementSmall: [{
type: Input
}], incrementMedium: [{
type: Input
}], incrementLarge: [{
type: Input
}], selectionStarted: [{
type: Output
}], selectionUpdated: [{
type: Output
}], selectionConfirmed: [{
type: Output
}], selectionCancelled: [{
type: Output
}], cursorPositionChanged: [{
type: Output
}], selectionCursorPositionChanged: [{
type: Output
}], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}], onBlur: [{
type: HostListener,
args: ['blur']
}] } });
const getRedactionState = createSelector(getMVState, (state) => state.redactions);
const getRedactionPages = createSelector(getRedactionState, getPageEnt);
const getSelected = createSelector(getRedactionState, getSelectedRedaction);
const getRedactedDocumentInfo = createSelector(getRedactionState, getRedactedDocInfo);
const getRedactionEnt = createSelector(getRedactionState, getRedactionEnt$1);
const getRedactionArray = createSelector(getRedactionEnt, getDocumentId, (ent, documentId) => {
const redactions = Object.keys(ent).map(key => ent[key]);
return { redactions, documentId };
});
const getRedactionsPerPage = createSelector(getPages, getRedactionPages, (pages, pageEnt) => {
if (pages && pageEnt) {
const arr = [];
Object.keys(pages).forEach(key => {
arr.push({
anno: pageEnt[key] ? pageEnt[key] : [],
styles: pages[key].styles
});
});
return arr;
}
});
class RedactionComponent {
constructor(store, viewerEvents, toolbarEvents) {
this.store = store;
this.viewerEvents = viewerEvents;
this.toolbarEvents = toolbarEvents;
}
ngOnInit() {
this.redactionsPerPage$ = this.store.pipe(select(getRedactionsPerPage));
this.selectedRedaction$ = this.store.pipe(select(getSelected));
this.$subscription = this.toolbarEvents.drawModeSubject.subscribe(drawMode => this.drawMode = drawMode);
this.$subscription.add(this.store.pipe(select(getRedactedDocumentInfo), filter$1(value => !!value))
.subscribe(redactedDocInfo => this.downloadDocument(redactedDocInfo)));
this.$subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
this.$subscription.add(this.viewerEvents.textHighlight.subscribe(highlight => this.markTextRedaction(highlight)));
this.toolbarEvents.applyRedactToDocument.subscribe(() => {
this.store.pipe(select(getRedactionArray), take(1)).subscribe(redactions => {
this.store.dispatch(new Redact(redactions));
});
});
this.toolbarEvents.clearAllRedactMarkers.subscribe(() => {
this.store.dispatch(new UnmarkAll(this.documentId));
});
}
ngOnDestroy() {
this.$subscription.unsubscribe();
}
markTextRedaction(highlight) {
const redactionHighlight = highlight.rectangles;
if (redactionHighlight && redactionHighlight.length) {
this.saveRedaction(highlight.page, [...redactionHighlight]);
}
this.toolbarEvents.highlightModeSubject.next(false);
}
markBoxRedaction({ rectangles, page }) {
this.saveRedaction(page, rectangles);
this.toolbarEvents.drawModeSubject.next(false);
}
saveRedaction(page, rectangles) {
const redaction = { page, rectangles, redactionId: v4(), documentId: this.documentId };
this.store.dispatch(new SaveRedaction(redaction));
}
onMarkerDelete(event) {
this.store.dispatch(new DeleteRedaction(event));
}
selectRedaction(event) {
this.store.dispatch(new SelectRedaction(event));
}
onMarkerUpdate(redaction) {
this.store.dispatch(new SaveRedaction(redaction));
}
downloadDocument({ blob, filename }) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = filename;
a.click();
a.remove();
URL.revokeObjectURL(url);
this.store.dispatch(new ResetRedactedDocument());
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionComponent, deps: [{ token: i1.Store }, { token: ViewerEventService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: RedactionComponent, isStandalone: false, selector: "mv-redactions", inputs: { zoom: "zoom", rotate: "rotate" }, ngImport: i0, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let redaction of (redactionsPerPage$ | async); index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': redaction.styles.width,\n 'height.px': redaction.styles.height\n }\"\n [attr.redaction-page-num]=\"i+1\"\n [ngClass]=\"{ 'pageContainer__page--draw' : drawMode }\">\n <mv-box-highlight-create\n [page]=\"i + 1\"\n [pageHeight]=\"redaction.styles.height\"\n [pageWidth]=\"redaction.styles.width\"\n [rotate]=\"rotate\"\n [zoom]=\"zoom\"\n (saveSelection)=\"markBoxRedaction($event)\">\n </mv-box-highlight-create>\n <div class=\"pageContainer__page-item\">\n <ng-container *ngFor=\"let anno of redaction.anno\">\n <!-- TODO rename this to selection -->\n <mv-annotation [annotation]=\"anno\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [selectedAnnoId]=\"selectedRedaction$ | async\"\n [pageHeight]=\"redaction.styles.height\"\n [pageWidth]=\"redaction.styles.width\"\n (update)=\"onMarkerUpdate($event)\"\n (delete)=\"onMarkerDelete($event)\"\n (annotationClick)=\"selectRedaction($event)\">\n </mv-annotation>\n </ng-container>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: AnnotationViewComponent, selector: "mv-annotation", inputs: ["annotation", "zoom", "rotate", "selectedAnnoId", "pageHeight", "pageWidth"], outputs: ["update", "delete", "annotationClick"] }, { kind: "component", type: BoxHighlightCreateComponent, selector: "mv-box-highlight-create", inputs: ["page", "pageHeight", "pageWidth", "rotate", "zoom", "container"], outputs: ["saveSelection"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redactions', standalone: false, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let redaction of (redactionsPerPage$ | async); index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': redaction.styles.width,\n 'height.px': redaction.styles.height\n }\"\n [attr.redaction-page-num]=\"i+1\"\n [ngClass]=\"{ 'pageContainer__page--draw' : drawMode }\">\n <mv-box-highlight-create\n [page]=\"i + 1\"\n [pageHeight]=\"redaction.styles.height\"\n [pageWidth]=\"redaction.styles.width\"\n [rotate]=\"rotate\"\n [zoom]=\"zoom\"\n (saveSelection)=\"markBoxRedaction($event)\">\n </mv-box-highlight-create>\n <div class=\"pageContainer__page-item\">\n <ng-container *ngFor=\"let anno of redaction.anno\">\n <!-- TODO rename this to selection -->\n <mv-annotation [annotation]=\"anno\"\n [zoom]=\"zoom\"\n [rotate]=\"rotate\"\n [selectedAnnoId]=\"selectedRedaction$ | async\"\n [pageHeight]=\"redaction.styles.height\"\n [pageWidth]=\"redaction.styles.width\"\n (update)=\"onMarkerUpdate($event)\"\n (delete)=\"onMarkerDelete($event)\"\n (annotationClick)=\"selectRedaction($event)\">\n </mv-annotation>\n </ng-container>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: ViewerEventService }, { type: ToolbarEventService }], propDecorators: { zoom: [{
type: Input
}], rotate: [{
type: Input
}] } });
class BookmarkIconsComponent {
constructor(store) {
this.store = store;
}
ngOnInit() {
this.bookmarksPerPage$ = this.store.pipe(select(getBookmarksPerPage));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarkIconsComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: BookmarkIconsComponent, isStandalone: false, selector: "mv-bookmark-icons", inputs: { zoom: "zoom", rotate: "rotate" }, ngImport: i0, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let bookmarks of (bookmarksPerPage$ | async); index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': bookmarks.styles.width,\n 'height.px': bookmarks.styles.height\n }\">\n <div class=\"pageContainer__page-item\">\n <ng-container *ngFor=\"let bMrk of bookmarks.bookmark\">\n <div class=\"bookmark__here\"\n [style.width]=\"16 * zoom + 'px'\"\n [style.height]=\"24 * zoom + 'px'\"\n [style.top]=\"bMrk.yCoordinate * zoom + 'px'\"\n [title]=\"bMrk.name\">\n </div>\n </ng-container>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarkIconsComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-bookmark-icons', standalone: false, template: "<div class=\"pageContainer\">\n <div *ngFor=\"let bookmarks of (bookmarksPerPage$ | async); index as i\"\n class=\"pageContainer__page\"\n [ngStyle]=\"{\n 'width.px': bookmarks.styles.width,\n 'height.px': bookmarks.styles.height\n }\">\n <div class=\"pageContainer__page-item\">\n <ng-container *ngFor=\"let bMrk of bookmarks.bookmark\">\n <div class=\"bookmark__here\"\n [style.width]=\"16 * zoom + 'px'\"\n [style.height]=\"24 * zoom + 'px'\"\n [style.top]=\"bMrk.yCoordinate * zoom + 'px'\"\n [title]=\"bMrk.name\">\n </div>\n </ng-container>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }], propDecorators: { zoom: [{
type: Input
}], rotate: [{
type: Input
}] } });
class ParticipantsListComponent {
constructor(toolbarEvents, store, icpEventService) {
this.toolbarEvents = toolbarEvents;
this.store = store;
this.icpEventService = icpEventService;
this.showParticipantsList = false;
}
ngOnInit() {
this.participants$ = this.store.pipe(select(getParticipants));
this.presenter$ = this.store.pipe(select(getPresenter));
this.isPresenter$ = this.store.pipe(select(isPresenter));
this.subscription = this.icpEventService.participantsListVisible.subscribe(isVisible => this.showParticipantsList = isVisible);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ParticipantsListComponent, deps: [{ token: ToolbarEventService }, { token: i1.Store }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: ParticipantsListComponent, isStandalone: false, selector: "mv-participants-list", ngImport: i0, template: "<div class=\"comments-panel comment-container icp-mode\" [ngClass]=\"{'expanded': showParticipantsList }\">\n\n <div class=\"participant__card no-shadow\">\n <p class=\"participant__name\">All Participants</p>\n </div>\n\n <div *ngIf=\"(presenter$ | async)?.username !== ''\" class=\"participant__card\">\n <div class=\"participant__header\"> Presenting </div>\n <p class=\"participant__name\">{{(presenter$ | async)?.username}}</p>\n </div>\n\n <div *ngIf=\"(participants$ | async).length > 1\" class=\"participant__card\">\n <div class=\"participant__header\">Following</div>\n <ng-container *ngFor=\"let participant of (participants$ | async)\">\n <p *ngIf=\"participant.id !== (presenter$ | async)?.id\" class=\"participant__name\">{{participant.username}}</p>\n </ng-container>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ParticipantsListComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-participants-list', standalone: false, template: "<div class=\"comments-panel comment-container icp-mode\" [ngClass]=\"{'expanded': showParticipantsList }\">\n\n <div class=\"participant__card no-shadow\">\n <p class=\"participant__name\">All Participants</p>\n </div>\n\n <div *ngIf=\"(presenter$ | async)?.username !== ''\" class=\"participant__card\">\n <div class=\"participant__header\"> Presenting </div>\n <p class=\"participant__name\">{{(presenter$ | async)?.username}}</p>\n </div>\n\n <div *ngIf=\"(participants$ | async).length > 1\" class=\"participant__card\">\n <div class=\"participant__header\">Following</div>\n <ng-container *ngFor=\"let participant of (participants$ | async)\">\n <p *ngIf=\"participant.id !== (presenter$ | async)?.id\" class=\"participant__name\">{{participant.username}}</p>\n </ng-container>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: i1.Store }, { type: IcpEventService }] });
class PdfViewerComponent {
constructor(store, icpStore, pdfJsWrapperFactory, printService, toolbarEvents, viewerEvents, icpService, toolbarButtons, icpEventService) {
this.store = store;
this.icpStore = icpStore;
this.pdfJsWrapperFactory = pdfJsWrapperFactory;
this.printService = printService;
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.icpService = icpService;
this.toolbarButtons = toolbarButtons;
this.icpEventService = icpEventService;
this.mediaLoadStatus = new EventEmitter();
this.pdfViewerException = new EventEmitter();
this.documentTitle = new EventEmitter();
this.pageHeights = [];
this.rotation = 0;
this.zoom = 1;
this.loadingDocument = false;
this.hasDifferentPageSize = false;
this.enableGrabNDrag = false;
this.showSelectionStartCursor = false;
this.showSelectionEndCursor = false;
this.highlightMode = toolbarEvents.highlightModeSubject.pipe(tap(() => {
this.store.dispatch(new ClearFilterTags());
}));
this.drawMode = toolbarEvents.drawModeSubject;
}
async ngAfterContentInit() {
this.pdfWrapper.documentLoadProgress.subscribe(v => this.onDocumentLoadProgress(v));
this.pdfWrapper.documentLoaded.subscribe(() => this.onDocumentLoaded());
this.pdfWrapper.documentLoadFailed.subscribe((error) => this.onDocumentLoadFailed(error));
this.pdfWrapper.outlineLoaded.subscribe(outline => this.documentOutline = outline);
this.pdfWrapper.pageRendered.subscribe((event) => this.updatePages(event));
this.$subscription = this.toolbarEvents.printSubject
.subscribe(() => this.printService.printDocumentNatively(this.url));
this.$subscription.add(this.toolbarEvents.downloadSubject.subscribe(() => this.pdfWrapper.downloadFile(this.downloadUrl || this.url, this.downloadFileName)));
this.$subscription.add(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument(rotate)));
this.$subscription.add(this.toolbarEvents.zoomSubject.subscribe(zoom => this.setZoom(zoom)));
this.$subscription.add(this.toolbarEvents.stepZoomSubject.subscribe(zoom => this.stepZoom(zoom)));
this.$subscription.add(this.toolbarEvents.searchSubject.subscribe(search => this.pdfWrapper.search(search)));
this.$subscription.add(this.toolbarEvents.setCurrentPageSubject.subscribe(pageNumber => this.pdfWrapper.setPageNumber(pageNumber)));
this.$subscription.add(this.toolbarEvents.changePageByDeltaSubject.subscribe(pageNumber => this.pdfWrapper.changePageNumber(pageNumber)));
this.$subscription.add(this.toolbarEvents.grabNDrag.subscribe(grabNDrag => this.enableGrabNDrag = grabNDrag));
this.$subscription.add(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
this.showCommentsPanel = toggle;
}));
this.$subscription.add(this.viewerEvents.navigationEvent.subscribe(dest => this.goToDestination(dest)));
this.$subscription.add(this.viewerEvents.navigationEventICP.subscribe(destination => this.goToDestinationICP(destination)));
this.$subscription.add(this.icpEventService.participantsListVisible.subscribe(toggle => this.showIcpParticipantsList = toggle));
this.$subscription.add(this.pdfWrapper.positionUpdated.asObservable()
.pipe(throttleTime(500, asyncScheduler, { leading: true, trailing: true }))
.subscribe(event => this.store.dispatch(new PdfPositionUpdate(event.location))));
}
async ngOnChanges(changes) {
if (!this.pdfWrapper) {
this.pdfWrapper = this.pdfJsWrapperFactory.create(this.viewerContainer);
}
if (changes.url && this.pdfWrapper) {
await this.loadDocument();
if (this.enableRedactions) {
this.toolbarEvents.redactionMode.pipe(filter$1(value => !!value))
.subscribe(() => this.resetRotation());
}
}
if (changes.caseId) {
this.icpStore.dispatch(new SetCaseId(this.caseId));
}
}
resetRotation() {
if (this.hasDifferentPageSize) {
this.pdfWrapper.resetRotation(0);
this.rotation = 0;
}
}
ngOnDestroy() {
this.$subscription.unsubscribe();
}
async loadDocument() {
this.initDocumentProgress();
await this.pdfWrapper.loadDocument(this.url);
this.documentTitle.emit(this.pdfWrapper.getCurrentPDFTitle());
this.setPageHeights();
this.$subscription.add(this.store.pipe(select(getPageDifference))
.subscribe(hasDifferentPageSie => this.hasDifferentPageSize = hasDifferentPageSie));
}
onDocumentLoadProgress(progress) {
if (progress.total) {
this.loadingDocumentProgress = Math.min(100, Math.ceil(progress.loaded / progress.total * 100));
}
}
onDocumentLoaded() {
this.loadingDocument = false;
this.mediaLoadStatus.emit(ResponseType.SUCCESS);
}
onDocumentLoadFailed(error) {
this.loadingDocument = false;
this.viewerException = new ViewerException(error.name, { message: error.message });
this.errorMessage = `Could not load the document "${this.url}"`;
this.mediaLoadStatus.emit(ResponseType.FAILURE);
this.pdfViewerException.emit(this.viewerException);
}
updatePages(event) {
if (this.enableAnnotations) {
this.store.dispatch(new AddPages(event));
}
}
set searchBarHidden(hidden) {
if (this.pdfWrapper && hidden) {
this.pdfWrapper.clearSearch();
}
}
toggleCommentsSummary() {
this.toolbarEvents.toggleCommentsSummary(!this.toolbarEvents.showCommentSummary.getValue());
}
rotateDocument(rotation) {
const pageNumber = this.pdfWrapper.getPageNumber();
this.pdfWrapper.rotate(rotation);
this.pdfWrapper.setPageNumber(pageNumber);
this.rotation = (this.rotation + rotation) % 360;
this.setPageHeights();
}
initDocumentProgress() {
this.loadingDocument = true;
this.loadingDocumentProgress = null;
this.errorMessage = null;
}
setZoom(zoomFactor) {
if (!isNaN(zoomFactor)) {
this.pdfWrapper.setZoom(zoomFactor);
this.zoom = this.calculateZoomValue(zoomFactor);
this.setPageHeights();
}
}
stepZoom(zoomFactor) {
if (!isNaN(zoomFactor)) {
this.pdfWrapper.stepZoom(zoomFactor);
this.zoom = this.calculateZoomValue(this.zoom, zoomFactor);
this.setPageHeights();
}
}
setPageHeights() {
this.pageHeights = [];
const pdfViewerChildren = this.pdfViewer.nativeElement.children;
for (let i = 0; i < pdfViewerChildren.length; i++) {
this.pageHeights.push(pdfViewerChildren[i].clientHeight);
}
}
calculateZoomValue(zoomValue, increment = 0) {
const newZoomValue = zoomValue + increment;
if (newZoomValue > 5) {
return 5;
}
if (newZoomValue < 0.1) {
return 0.1;
}
return +newZoomValue.toFixed(2);
}
goToDestination(destination) {
const currentRotation = this.rotation;
this.rotateDocument(360 - this.rotation);
this.pdfWrapper.navigateTo(destination);
setTimeout(() => {
this.rotateDocument(currentRotation);
}, 10);
}
goToDestinationICP(destination) {
this.pdfWrapper.nativeNavigate(destination);
}
getCurrentPageNumber() {
return this.pdfWrapper.getPageNumber();
}
onKeyboardTextSelectionConfirmed() {
if (this.highlightCreateDirective) {
this.highlightCreateDirective.onKeyboardSelectionConfirmed();
}
}
onKeyboardTextSelectionCancelled() {
const selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
}
}
onSelectionStartCursorChanged(position) {
if (position.visible) {
this.selectionStartCursorX = position.x;
this.selectionStartCursorY = position.y;
this.showSelectionStartCursor = true;
}
else {
this.showSelectionStartCursor = false;
}
}
onSelectionEndCursorChanged(position) {
if (position.visible) {
this.selectionEndCursorX = position.x;
this.selectionEndCursorY = position.y;
this.showSelectionEndCursor = true;
}
else {
this.showSelectionEndCursor = false;
}
}
onViewerContainerFocusIn(event) {
const elementFrom = event.relatedTarget;
const elementTo = event.target;
const comingFromSidebar = !!(elementFrom?.closest('#sidebarContainer') ||
elementFrom?.closest('#sidebarContent'));
const goingToViewer = !!elementTo?.closest('#viewerContainer');
if (!comingFromSidebar || !goingToViewer) {
return;
}
const isTextLayer = elementTo?.classList.contains('textLayer');
if (isTextLayer) {
const focusedPageElement = elementTo.closest('[data-page-number]');
const focusedPageNumber = focusedPageElement ? parseInt(focusedPageElement.getAttribute('data-page-number')) : null;
const currentPage = this.getCurrentPageNumber();
if (focusedPageNumber !== null && focusedPageNumber !== currentPage) {
const currentPageElement = this.viewerContainer.nativeElement.querySelector(`[data-page-number='${currentPage}']`);
if (currentPageElement) {
const currentPageTextLayer = currentPageElement.querySelector('.textLayer');
if (currentPageTextLayer) {
currentPageTextLayer.focus();
}
}
}
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PdfViewerComponent, deps: [{ token: i1.Store }, { token: i1.Store }, { token: PdfJsWrapperFactory }, { token: PrintService }, { token: ToolbarEventService }, { token: ViewerEventService }, { token: IcpService }, { token: ToolbarButtonVisibilityService }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: PdfViewerComponent, isStandalone: false, selector: "mv-pdf-viewer", inputs: { downloadUrl: "downloadUrl", url: "url", downloadFileName: "downloadFileName", enableAnnotations: "enableAnnotations", enableRedactions: "enableRedactions", enableICP: "enableICP", annotationSet: "annotationSet", enableRedactSearch: "enableRedactSearch", height: "height", caseId: "caseId", searchBarHidden: "searchBarHidden" }, outputs: { mediaLoadStatus: "mediaLoadStatus", pdfViewerException: "pdfViewerException", documentTitle: "documentTitle" }, viewQueries: [{ propertyName: "viewerContainer", first: true, predicate: ["viewerContainer"], descendants: true, static: true }, { propertyName: "pdfViewer", first: true, predicate: ["pdfViewer"], descendants: true }, { propertyName: "highlightCreateDirective", first: true, predicate: HighlightCreateDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<mv-side-bar\n *ngIf=\"toolbarEvents.sidebarOpen\"\n id=\"sidebarContainer\"\n [url]=\"url\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [outline]=\"documentOutline\"\n [annotationsEnabled]=\"enableAnnotations\"\n [currentPageNumber]=\"getCurrentPageNumber()\"\n>\n</mv-side-bar>\n<mv-comment-set-header\n *ngIf=\"enableAnnotations\"\n [ngClass]=\"{ 'show-comments-panel': showCommentsPanel }\"\n [isHidden]=\"!showCommentsPanel\"\n [showCommentSummary]=\"toolbarButtons.showCommentSummary\"\n (showCommentSummaryDialog)=\"toggleCommentsSummary()\"\n>\n</mv-comment-set-header>\n<mv-participants-list></mv-participants-list>\n<div\n class=\"pdfContainer\"\n [ngStyle]=\"{ height: height }\"\n [ngClass]=\"{ pdfContainer: true, hidden: errorMessage }\"\n>\n <mv-redaction-search-bar></mv-redaction-search-bar>\n <div\n #viewerContainer\n mvGrabNDrag\n [dragX]=\"viewerContainer\"\n [dragEnabled]=\"enableGrabNDrag\"\n id=\"viewerContainer\"\n class=\"viewer-container\"\n [class.annotations]=\"enableAnnotations\"\n [class.show-comments-panel]=\"\n (showCommentsPanel || showIcpParticipantsList) &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [class.grabNDrag]=\"enableGrabNDrag\"\n (focusin)=\"onViewerContainerFocusIn($event)\"\n >\n <div\n #pdfViewer\n class=\"pdfViewer\"\n mvCreateTextHighlight\n mvKeyboardTextHighlight\n [enabled]=\"highlightMode | async\"\n [tabindex]=\"(highlightMode | async) ? 0 : -1\"\n (selectionConfirmed)=\"onKeyboardTextSelectionConfirmed()\"\n (selectionCancelled)=\"onKeyboardTextSelectionCancelled()\"\n (cursorPositionChanged)=\"onSelectionStartCursorChanged($event)\"\n (selectionCursorPositionChanged)=\"onSelectionEndCursorChanged($event)\"\n [ngClass]=\"{\n hidden: false,\n highlightMode: highlightMode | async,\n drawMode: drawMode | async\n }\"\n ></div>\n <div *ngIf=\"showSelectionStartCursor\"\n class=\"selection-start-cursor\"\n [style.position]=\"'fixed'\"\n [style.top]=\"selectionStartCursorY + 'px'\"\n [style.left]=\"selectionStartCursorX + 'px'\">\n </div>\n <div *ngIf=\"showSelectionEndCursor\"\n class=\"selection-end-cursor\"\n [style.position]=\"'fixed'\"\n [style.top]=\"selectionEndCursorY + 'px'\"\n [style.left]=\"selectionEndCursorX + 'px'\">\n </div>\n <mv-redactions\n *ngIf=\"toolbarEvents.redactionMode | async; else annotationTemplate\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n ></mv-redactions>\n <ng-template #annotationTemplate>\n <mv-metadata-layer\n *ngIf=\"enableAnnotations && annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n >\n </mv-metadata-layer>\n <mv-bookmark-icons [zoom]=\"zoom\" [rotate]=\"rotation\"> </mv-bookmark-icons>\n </ng-template>\n </div>\n <mv-comment-set\n [contentScrollTop]=\"viewerContainer.scrollTop\"\n *ngIf=\"\n enableAnnotations &&\n annotationSet &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [annotationSet]=\"annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [height]=\"pdfViewer.offsetHeight\"\n [pageHeights]=\"pageHeights\"\n >\n </mv-comment-set>\n <div class=\"loadingMessage\" *ngIf=\"loadingDocument\">\n <h3 class=\"govuk-heading-m\">\n {{ \"Loading...\" | rpxTranslate}}{{\n loadingDocumentProgress ? loadingDocumentProgress + \"%\" : \"\"\n }}\n </h3>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: RedactionSearchBarComponent, selector: "mv-redaction-search-bar" }, { kind: "component", type: CommentSetComponent, selector: "mv-comment-set", inputs: ["annotationSet", "zoom", "rotate", "height", "pageHeights", "contentScrollTop"] }, { kind: "component", type: CommentSetHeaderComponent, selector: "mv-comment-set-header", inputs: ["showCommentSummary", "isHidden"], outputs: ["showCommentSummaryDialog"] }, { kind: "component", type: MetadataLayerComponent, selector: "mv-metadata-layer", inputs: ["zoom", "rotate"] }, { kind: "component", type: SideBarComponent, selector: "mv-side-bar", inputs: ["annotationsEnabled", "outline", "url", "zoom", "rotate", "currentPageNumber"] }, { kind: "directive", type: GrabNDragDirective, selector: "[mvGrabNDrag]", inputs: ["dragEnabled", "dragX"] }, { kind: "directive", type: HighlightCreateDirective, selector: "[mvCreateTextHighlight]" }, { kind: "directive", type: KeyboardTextHighlightDirective, selector: "[mvKeyboardTextHighlight]", inputs: ["enabled", "incrementSmall", "incrementMedium", "incrementLarge"], outputs: ["selectionStarted", "selectionUpdated", "selectionConfirmed", "selectionCancelled", "cursorPositionChanged", "selectionCursorPositionChanged"] }, { kind: "component", type: RedactionComponent, selector: "mv-redactions", inputs: ["zoom", "rotate"] }, { kind: "component", type: BookmarkIconsComponent, selector: "mv-bookmark-icons", inputs: ["zoom", "rotate"] }, { kind: "component", type: ParticipantsListComponent, selector: "mv-participants-list" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: PdfViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-pdf-viewer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<mv-side-bar\n *ngIf=\"toolbarEvents.sidebarOpen\"\n id=\"sidebarContainer\"\n [url]=\"url\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [outline]=\"documentOutline\"\n [annotationsEnabled]=\"enableAnnotations\"\n [currentPageNumber]=\"getCurrentPageNumber()\"\n>\n</mv-side-bar>\n<mv-comment-set-header\n *ngIf=\"enableAnnotations\"\n [ngClass]=\"{ 'show-comments-panel': showCommentsPanel }\"\n [isHidden]=\"!showCommentsPanel\"\n [showCommentSummary]=\"toolbarButtons.showCommentSummary\"\n (showCommentSummaryDialog)=\"toggleCommentsSummary()\"\n>\n</mv-comment-set-header>\n<mv-participants-list></mv-participants-list>\n<div\n class=\"pdfContainer\"\n [ngStyle]=\"{ height: height }\"\n [ngClass]=\"{ pdfContainer: true, hidden: errorMessage }\"\n>\n <mv-redaction-search-bar></mv-redaction-search-bar>\n <div\n #viewerContainer\n mvGrabNDrag\n [dragX]=\"viewerContainer\"\n [dragEnabled]=\"enableGrabNDrag\"\n id=\"viewerContainer\"\n class=\"viewer-container\"\n [class.annotations]=\"enableAnnotations\"\n [class.show-comments-panel]=\"\n (showCommentsPanel || showIcpParticipantsList) &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [class.grabNDrag]=\"enableGrabNDrag\"\n (focusin)=\"onViewerContainerFocusIn($event)\"\n >\n <div\n #pdfViewer\n class=\"pdfViewer\"\n mvCreateTextHighlight\n mvKeyboardTextHighlight\n [enabled]=\"highlightMode | async\"\n [tabindex]=\"(highlightMode | async) ? 0 : -1\"\n (selectionConfirmed)=\"onKeyboardTextSelectionConfirmed()\"\n (selectionCancelled)=\"onKeyboardTextSelectionCancelled()\"\n (cursorPositionChanged)=\"onSelectionStartCursorChanged($event)\"\n (selectionCursorPositionChanged)=\"onSelectionEndCursorChanged($event)\"\n [ngClass]=\"{\n hidden: false,\n highlightMode: highlightMode | async,\n drawMode: drawMode | async\n }\"\n ></div>\n <div *ngIf=\"showSelectionStartCursor\"\n class=\"selection-start-cursor\"\n [style.position]=\"'fixed'\"\n [style.top]=\"selectionStartCursorY + 'px'\"\n [style.left]=\"selectionStartCursorX + 'px'\">\n </div>\n <div *ngIf=\"showSelectionEndCursor\"\n class=\"selection-end-cursor\"\n [style.position]=\"'fixed'\"\n [style.top]=\"selectionEndCursorY + 'px'\"\n [style.left]=\"selectionEndCursorX + 'px'\">\n </div>\n <mv-redactions\n *ngIf=\"toolbarEvents.redactionMode | async; else annotationTemplate\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n ></mv-redactions>\n <ng-template #annotationTemplate>\n <mv-metadata-layer\n *ngIf=\"enableAnnotations && annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n >\n </mv-metadata-layer>\n <mv-bookmark-icons [zoom]=\"zoom\" [rotate]=\"rotation\"> </mv-bookmark-icons>\n </ng-template>\n </div>\n <mv-comment-set\n [contentScrollTop]=\"viewerContainer.scrollTop\"\n *ngIf=\"\n enableAnnotations &&\n annotationSet &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [annotationSet]=\"annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [height]=\"pdfViewer.offsetHeight\"\n [pageHeights]=\"pageHeights\"\n >\n </mv-comment-set>\n <div class=\"loadingMessage\" *ngIf=\"loadingDocument\">\n <h3 class=\"govuk-heading-m\">\n {{ \"Loading...\" | rpxTranslate}}{{\n loadingDocumentProgress ? loadingDocumentProgress + \"%\" : \"\"\n }}\n </h3>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: i1.Store }, { type: PdfJsWrapperFactory }, { type: PrintService }, { type: ToolbarEventService }, { type: ViewerEventService }, { type: IcpService }, { type: ToolbarButtonVisibilityService }, { type: IcpEventService }], propDecorators: { mediaLoadStatus: [{
type: Output
}], pdfViewerException: [{
type: Output
}], documentTitle: [{
type: Output
}], downloadUrl: [{
type: Input
}], url: [{
type: Input
}], downloadFileName: [{
type: Input
}], enableAnnotations: [{
type: Input
}], enableRedactions: [{
type: Input
}], enableICP: [{
type: Input
}], annotationSet: [{
type: Input
}], enableRedactSearch: [{
type: Input
}], height: [{
type: Input
}], caseId: [{
type: Input
}], viewerContainer: [{
type: ViewChild,
args: ['viewerContainer', { static: true }]
}], pdfViewer: [{
type: ViewChild,
args: ['pdfViewer', { static: false }]
}], highlightCreateDirective: [{
type: ViewChild,
args: [HighlightCreateDirective, { static: false }]
}], searchBarHidden: [{
type: Input
}] } });
class ViewerUtilService {
constructor(http) {
this.http = http;
}
validateFile(url) {
return this.http.head(url);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerUtilService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerUtilService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ViewerUtilService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class ImageViewerComponent {
constructor(store, printService, viewerUtilService, toolbarEvents, toolbarButtons, viewerEvents) {
this.store = store;
this.printService = printService;
this.viewerUtilService = viewerUtilService;
this.toolbarEvents = toolbarEvents;
this.toolbarButtons = toolbarButtons;
this.viewerEvents = viewerEvents;
this.mediaLoadStatus = new EventEmitter();
this.imageViewerException = new EventEmitter();
this.rotation = 0;
this.zoom = 1;
this.subscriptions = [];
this.enableGrabNDrag = false;
}
ngOnInit() {
this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotation => this.rotateImage(rotation)), this.toolbarEvents.zoomSubject.subscribe(zoom => this.setZoom(zoom)), this.toolbarEvents.stepZoomSubject.subscribe(zoom => this.stepZoom(zoom)), this.toolbarEvents.printSubject.subscribe(() => this.printService.printDocumentNatively(this.url)), this.toolbarEvents.downloadSubject.subscribe(() => this.download()), this.toolbarEvents.grabNDrag.subscribe(grabNDrag => this.enableGrabNDrag = grabNDrag), this.toolbarEvents.commentsPanelVisible.subscribe(toggle => this.showCommentsPanel = toggle));
}
onImageViewerClick(event) {
const classNme = event.target.className;
if (classNme.startsWith('pageContainer')) {
this.store.dispatch(new SelectedAnnotation({
annotationId: '',
selected: false,
editable: false,
}));
this.viewerEvents.clearCtxToolbar();
}
}
ngOnDestroy() {
this.subscriptions.filter(subscription => !subscription.closed)
.forEach(subscription => subscription.unsubscribe());
if (this.response) {
this.response.unsubscribe();
}
}
ngOnChanges(changes) {
if (changes.url) {
this.errorMessage = null;
this.toolbarEvents.reset();
}
}
rotateImage(rotation) {
this.rotation = (this.rotation + rotation) % 360;
this.initAnnoPage(this.img.nativeElement);
}
async setZoom(zoomFactor) {
if (!isNaN(zoomFactor)) {
await this.setZoomValue(this.calculateZoomValue(zoomFactor));
this.img.nativeElement.width = this.img.nativeElement.naturalWidth * this.zoom;
this.initAnnoPage(this.img.nativeElement);
}
}
async stepZoom(zoomFactor) {
if (!isNaN(zoomFactor)) {
await this.setZoomValue(this.calculateZoomValue(this.zoom, zoomFactor));
this.img.nativeElement.width = this.img.nativeElement.naturalWidth * this.zoom;
this.initAnnoPage(this.img.nativeElement);
}
}
download() {
const a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = this.url;
a.download = this.downloadFileName;
a.click();
a.remove();
}
// the returned promise is a work-around
setZoomValue(zoomValue) {
return new Promise((resolve) => {
this.zoom = zoomValue;
this.toolbarEvents.zoomValueSubject.next(zoomValue);
resolve(true);
});
}
calculateZoomValue(zoomValue, increment = 0) {
const newZoomValue = zoomValue + increment;
if (newZoomValue > 5) {
return 5;
}
if (newZoomValue < 0.1) {
return 0.1;
}
return +newZoomValue.toFixed(2);
}
onLoadError(url) {
this.response = this.viewerUtilService.validateFile(url)
.subscribe(next => next, error => {
this.viewerException = new ViewerException(error.name, { httpResponseCode: error.status, message: error.message });
});
this.errorMessage = `Could not load the image "${this.url}"`;
this.mediaLoadStatus.emit(ResponseType.FAILURE);
this.imageViewerException.emit(this.viewerException);
}
onLoad(img) {
this.mediaLoadStatus.emit(ResponseType.SUCCESS);
this.initAnnoPage(img);
}
initAnnoPage(img) {
this.imageHeight = this.rotation % 180 !== 0 ? img.offsetWidth : img.offsetHeight;
this.imageWidth = this.rotation % 180 !== 0 ? img.offsetHeight : img.offsetWidth;
this.imageLeft = this.rotation % 180 !== 0 ? img.offsetTop : img.offsetLeft;
this.imageTop = this.rotation % 180 !== 0 ? img.offsetLeft : img.offsetTop;
const payload = [{
div: {
scrollHeight: this.imageHeight,
scrollWidth: this.imageWidth,
offsetLeft: this.imageLeft,
},
pageNumber: 1,
scale: this.zoom,
rotation: this.rotation,
id: 1
}];
this.store.dispatch(new AddPages(payload));
}
toggleCommentsSummary() {
this.toolbarEvents.toggleCommentsSummary(!this.toolbarEvents.showCommentSummary.getValue());
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ImageViewerComponent, deps: [{ token: i1.Store }, { token: PrintService }, { token: ViewerUtilService }, { token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: ViewerEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: ImageViewerComponent, isStandalone: false, selector: "mv-image-viewer", inputs: { url: "url", downloadFileName: "downloadFileName", enableAnnotations: "enableAnnotations", annotationSet: "annotationSet", height: "height" }, outputs: { mediaLoadStatus: "mediaLoadStatus", imageViewerException: "imageViewerException" }, host: { listeners: { "mousedown": "onImageViewerClick($event)" } }, viewQueries: [{ propertyName: "img", first: true, predicate: ["img"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<mv-comment-set-header\n [ngClass]=\"{ 'show-comments-panel': showCommentsPanel }\"\n [isHidden]=\"!showCommentsPanel\"\n [showCommentSummary]=\"toolbarButtons.showCommentSummary\"\n (showCommentSummaryDialog)=\"toggleCommentsSummary()\"\n>\n</mv-comment-set-header>\n<div\n id=\"viewer-wrapper\"\n [ngStyle]=\"{ height: height }\"\n [ngClass]=\"{ grabNDrag: enableGrabNDrag }\"\n *ngIf=\"url && !errorMessage\"\n>\n <div\n #imageContainer\n id=\"image-container\"\n mvGrabNDrag\n [dragX]=\"imageContainer\"\n [dragEnabled]=\"enableGrabNDrag\"\n (scroll)=\"($event)\"\n [ngClass]=\"{\n 'image-container': true,\n annotations: enableAnnotations,\n 'show-comments-panel': showCommentsPanel\n }\"\n >\n <img\n #img\n alt=\"\"\n [src]=\"url\"\n [ngClass]=\"'rot' + rotation\"\n (error)=\"onLoadError(url)\"\n (load)=\"onLoad(img)\"\n />\n <mv-redactions\n *ngIf=\"toolbarEvents.redactionMode | async; else annotationTemplate\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n ></mv-redactions>\n <ng-template #annotationTemplate>\n <mv-metadata-layer\n *ngIf=\"enableAnnotations && annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n >\n </mv-metadata-layer>\n </ng-template>\n </div>\n\n <mv-comment-set\n [contentScrollTop]=\"imageContainer.scrollTop\"\n *ngIf=\"\n enableAnnotations &&\n annotationSet &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [annotationSet]=\"annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [height]=\"imageHeight\"\n >\n </mv-comment-set>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: CommentSetComponent, selector: "mv-comment-set", inputs: ["annotationSet", "zoom", "rotate", "height", "pageHeights", "contentScrollTop"] }, { kind: "component", type: CommentSetHeaderComponent, selector: "mv-comment-set-header", inputs: ["showCommentSummary", "isHidden"], outputs: ["showCommentSummaryDialog"] }, { kind: "component", type: MetadataLayerComponent, selector: "mv-metadata-layer", inputs: ["zoom", "rotate"] }, { kind: "directive", type: GrabNDragDirective, selector: "[mvGrabNDrag]", inputs: ["dragEnabled", "dragX"] }, { kind: "component", type: RedactionComponent, selector: "mv-redactions", inputs: ["zoom", "rotate"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ImageViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-image-viewer', standalone: false, template: "<mv-comment-set-header\n [ngClass]=\"{ 'show-comments-panel': showCommentsPanel }\"\n [isHidden]=\"!showCommentsPanel\"\n [showCommentSummary]=\"toolbarButtons.showCommentSummary\"\n (showCommentSummaryDialog)=\"toggleCommentsSummary()\"\n>\n</mv-comment-set-header>\n<div\n id=\"viewer-wrapper\"\n [ngStyle]=\"{ height: height }\"\n [ngClass]=\"{ grabNDrag: enableGrabNDrag }\"\n *ngIf=\"url && !errorMessage\"\n>\n <div\n #imageContainer\n id=\"image-container\"\n mvGrabNDrag\n [dragX]=\"imageContainer\"\n [dragEnabled]=\"enableGrabNDrag\"\n (scroll)=\"($event)\"\n [ngClass]=\"{\n 'image-container': true,\n annotations: enableAnnotations,\n 'show-comments-panel': showCommentsPanel\n }\"\n >\n <img\n #img\n alt=\"\"\n [src]=\"url\"\n [ngClass]=\"'rot' + rotation\"\n (error)=\"onLoadError(url)\"\n (load)=\"onLoad(img)\"\n />\n <mv-redactions\n *ngIf=\"toolbarEvents.redactionMode | async; else annotationTemplate\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n ></mv-redactions>\n <ng-template #annotationTemplate>\n <mv-metadata-layer\n *ngIf=\"enableAnnotations && annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n >\n </mv-metadata-layer>\n </ng-template>\n </div>\n\n <mv-comment-set\n [contentScrollTop]=\"imageContainer.scrollTop\"\n *ngIf=\"\n enableAnnotations &&\n annotationSet &&\n (toolbarEvents.redactionMode | async) !== true\n \"\n [annotationSet]=\"annotationSet\"\n [zoom]=\"zoom\"\n [rotate]=\"rotation\"\n [height]=\"imageHeight\"\n >\n </mv-comment-set>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: PrintService }, { type: ViewerUtilService }, { type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: ViewerEventService }], propDecorators: { url: [{
type: Input
}], downloadFileName: [{
type: Input
}], enableAnnotations: [{
type: Input
}], annotationSet: [{
type: Input
}], height: [{
type: Input
}], mediaLoadStatus: [{
type: Output
}], imageViewerException: [{
type: Output
}], img: [{
type: ViewChild,
args: ['img', { static: false }]
}], onImageViewerClick: [{
type: HostListener,
args: ['mousedown', ['$event']]
}] } });
class UnsupportedViewerComponent {
constructor(toolbarEvents, viewerUtilService) {
this.toolbarEvents = toolbarEvents;
this.viewerUtilService = viewerUtilService;
this.loadStatus = new EventEmitter();
this.unsupportedViewerException = new EventEmitter();
this.subscriptions = [];
}
ngOnInit() {
this.subscriptions.push(this.toolbarEvents.downloadSubject.subscribe(() => this.downloadLink.nativeElement.click()), this.viewerUtilService.validateFile(this.url).subscribe(next => next, error => {
this.viewerException = new ViewerException(error.name, { httpResponseCode: error.status, message: error.message });
}));
this.loadStatus.emit(ResponseType.UNSUPPORTED);
if (!this.typeException) {
this.unsupportedViewerException.emit(this.viewerException);
}
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: UnsupportedViewerComponent, deps: [{ token: ToolbarEventService }, { token: ViewerUtilService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: UnsupportedViewerComponent, isStandalone: false, selector: "mv-unsupported-viewer", inputs: { url: "url", downloadFileName: "downloadFileName", typeException: "typeException" }, outputs: { loadStatus: "loadStatus", unsupportedViewerException: "unsupportedViewerException" }, viewQueries: [{ propertyName: "downloadLink", first: true, predicate: ["downloadLink"], descendants: true }], ngImport: i0, template: "<div class=\"govuk-width-container\">\n <div class=\"govuk-notification-banner__content\">\n <p>\n <span *ngIf=\"typeException, else unsupported\">The document could not be displayed, or the file type is unsupported.</span>\n Please <a #downloadLink [href]=\"url\" [download]=\"downloadFileName\">click here to download</a>.\n </p>\n <ng-template #unsupported>\n <span>This file type is not supported by this viewer.</span>\n </ng-template>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: UnsupportedViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-unsupported-viewer', standalone: false, template: "<div class=\"govuk-width-container\">\n <div class=\"govuk-notification-banner__content\">\n <p>\n <span *ngIf=\"typeException, else unsupported\">The document could not be displayed, or the file type is unsupported.</span>\n Please <a #downloadLink [href]=\"url\" [download]=\"downloadFileName\">click here to download</a>.\n </p>\n <ng-template #unsupported>\n <span>This file type is not supported by this viewer.</span>\n </ng-template>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ViewerUtilService }], propDecorators: { url: [{
type: Input
}], downloadFileName: [{
type: Input
}], typeException: [{
type: Input
}], loadStatus: [{
type: Output
}], unsupportedViewerException: [{
type: Output
}], downloadLink: [{
type: ViewChild,
args: ['downloadLink', { static: false }]
}] } });
class AnnotationApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.annotationApiUrl = '/em-anno';
this.annotationSetBaseUrl = '/annotation-sets';
this.annotationBaseUrl = '/annotations';
}
getAnnotationSet(documentId) {
const fixedUrl = `${this.annotationSetsFullUrl}/filter?documentId=${documentId}`;
return this.httpClient
.get(fixedUrl, { observe: 'response', withCredentials: true });
}
postAnnotationSet(body) {
return this.httpClient
.post(this.annotationSetsFullUrl, body, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body), catchError(() => []));
}
getComments(annotationSet) {
return annotationSet
.pipe(map(this.sortAnnotations), map(this.extractComments));
}
/**
* Sort the annotations in the response by page and then y position of their first rectangle
*/
sortAnnotations(r) {
return r.annotations.sort((a, b) => a.page !== b.page ? a.page - b.page : a.rectangles[0].y - b.rectangles[0].y);
}
extractComments(annotations) {
return [].concat(...annotations.map(a => a.comments));
}
deleteAnnotation(annotationId) {
const url = `${this.annotationFullsUrl}/${annotationId}`;
return this.httpClient
.delete(url, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
postAnnotation(annotation) {
return this.httpClient
.post(this.annotationFullsUrl, annotation, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
get annotationSetsFullUrl() {
return this.annotationApiUrl + this.annotationSetBaseUrl;
}
get annotationFullsUrl() {
return this.annotationApiUrl + this.annotationBaseUrl;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
/**
* Number Helper Service
* */
class NumberHelperService {
constructor() { }
isNumber(value) {
return (value !== null
&& value !== undefined
&& value !== ''
&& !isNaN(Number(value.toString())));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: NumberHelperService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: NumberHelperService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
class ToolbarFocusService {
focusToolbarButton(toolbarSelector, buttonId, delay = 0) {
setTimeout(() => {
const element = document.querySelector(toolbarSelector);
if (!element) {
return;
}
if (buttonId) {
const button = document.querySelector(`#${buttonId}`);
if (button) {
this.focusWithoutScrolling(button);
return;
}
}
if (element.tagName === 'BUTTON') {
this.focusWithoutScrolling(element);
return;
}
const tabbableButton = element.querySelector('button[tabindex="0"]');
if (tabbableButton) {
this.focusWithoutScrolling(tabbableButton);
}
}, delay);
}
focusWithoutScrolling(element) {
try {
element.focus({ preventScroll: true });
}
catch {
element.focus();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarFocusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarFocusService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarFocusService, decorators: [{
type: Injectable
}] });
class KeyboardNavDirective {
constructor(elementRef) {
this.elementRef = elementRef;
this.orientation = 'horizontal';
this.itemFocused = new EventEmitter();
this.itemActivated = new EventEmitter();
this.focusableItems = [];
this.currentFocusIndex = -1;
this.isUsingArrowKeys = false;
}
ngOnInit() {
this.updateFocusableItems();
this.setupMutationObserver();
}
ngOnDestroy() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
}
setupMutationObserver() {
this.mutationObserver = new MutationObserver(() => {
this.updateFocusableItems();
});
this.mutationObserver.observe(this.elementRef.nativeElement, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'disabled', 'hidden']
});
}
updateFocusableItems() {
const allItems = Array.from(this.elementRef.nativeElement.querySelectorAll('button:not([disabled])'));
this.focusableItems = allItems.filter(item => this.isVisible(item));
if (this.isUsingArrowKeys) {
this.applyArrowKeyTabindex();
}
else {
this.focusableItems.forEach(item => {
if (!item.hasAttribute('tabindex') || item.getAttribute('tabindex') === '-1') {
item.setAttribute('tabindex', '0');
}
});
}
}
isVisible(element) {
if (!element) {
return false;
}
if (element.hasAttribute('disabled')) {
return false;
}
const computedStyle = window.getComputedStyle(element);
if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden') {
return false;
}
// check if any parent element is hidden (up to the directive's host element)
let parent = element.parentElement;
while (parent && parent !== this.elementRef.nativeElement) {
const parentStyle = window.getComputedStyle(parent);
if (parentStyle.display === 'none' || parentStyle.visibility === 'hidden') {
return false;
}
parent = parent.parentElement;
}
return true;
}
applyArrowKeyTabindex() {
if (this.focusableItems.length === 0) {
return;
}
let indexToMakeTabbable = 0;
if (this.currentFocusIndex >= 0 && this.currentFocusIndex < this.focusableItems.length) {
indexToMakeTabbable = this.currentFocusIndex;
}
else {
const focusedElement = document.activeElement;
const focusedIndex = this.focusableItems.indexOf(focusedElement);
if (focusedIndex !== -1) {
indexToMakeTabbable = focusedIndex;
this.currentFocusIndex = focusedIndex;
}
}
this.focusableItems.forEach((item, index) => {
item.setAttribute('tabindex', index === indexToMakeTabbable ? '0' : '-1');
});
}
onKeyDown(event) {
const target = event.target;
const currentIndex = this.focusableItems.indexOf(target);
if (currentIndex === -1) {
return;
}
let handled = false;
let isArrowKey = false;
switch (event.key) {
case 'Tab':
if (this.isUsingArrowKeys) {
this.isUsingArrowKeys = false;
this.focusableItems.forEach(item => {
item.setAttribute('tabindex', '0');
});
}
return;
case 'ArrowRight':
if (this.orientation === 'horizontal') {
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusNext(currentIndex);
handled = true;
}
break;
case 'ArrowLeft':
if (this.orientation === 'horizontal') {
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusPrevious(currentIndex);
handled = true;
}
break;
case 'ArrowDown':
if (this.orientation === 'vertical') {
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusNext(currentIndex);
handled = true;
}
break;
case 'ArrowUp':
if (this.orientation === 'vertical') {
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusPrevious(currentIndex);
handled = true;
}
break;
case 'Home':
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusFirst();
handled = true;
break;
case 'End':
isArrowKey = true;
if (!this.isUsingArrowKeys) {
this.isUsingArrowKeys = true;
this.applyArrowKeyTabindex();
}
this.focusLast();
handled = true;
break;
case 'Enter':
case ' ':
this.activateItem(target);
handled = true;
break;
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
}
focusNext(currentIndex) {
if (this.focusableItems.length === 0) {
return;
}
let nextIndex = currentIndex + 1;
if (nextIndex >= this.focusableItems.length) {
nextIndex = 0;
}
this.focusItemAtIndex(nextIndex);
}
focusPrevious(currentIndex) {
if (this.focusableItems.length === 0) {
return;
}
let previousIndex = currentIndex - 1;
if (previousIndex < 0) {
previousIndex = this.focusableItems.length - 1;
}
this.focusItemAtIndex(previousIndex);
}
focusFirst() {
if (this.focusableItems.length > 0) {
this.focusItemAtIndex(0);
}
}
focusLast() {
if (this.focusableItems.length > 0) {
this.focusItemAtIndex(this.focusableItems.length - 1);
}
}
focusItemAtIndex(index) {
if (index < 0 || index >= this.focusableItems.length) {
return;
}
this.currentFocusIndex = index;
this.focusableItems.forEach((item, i) => {
item.setAttribute('tabindex', i === index ? '0' : '-1');
});
this.focusableItems[index].focus();
this.itemFocused.emit(this.focusableItems[index]);
}
activateItem(item) {
item.click();
this.itemActivated.emit(item);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardNavDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: KeyboardNavDirective, isStandalone: false, selector: "[mvKeyboardNav]", inputs: { orientation: ["mvKeyboardNav", "orientation"] }, outputs: { itemFocused: "itemFocused", itemActivated: "itemActivated" }, host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: KeyboardNavDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvKeyboardNav]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { orientation: [{
type: Input,
args: ['mvKeyboardNav']
}], itemFocused: [{
type: Output
}], itemActivated: [{
type: Output
}], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
class SearchBarComponent {
constructor(toolbarButtons, toolbarEvents) {
this.toolbarButtons = toolbarButtons;
this.toolbarEvents = toolbarEvents;
this.highlightAll = true;
this.matchCase = false;
this.wholeWord = false;
this.resultsText = '';
this.searchText = '';
this.resultCount = 0;
this.subscriptions = [];
this.advancedSearchVisible = false;
}
ngOnInit() {
this.subscriptions.push(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
}
ngAfterViewInit() {
this.findInput.nativeElement.focus();
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
onWindowKeyDown(e) {
if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
e.preventDefault();
this.toolbarEvents.searchBarHidden.next(false);
setTimeout(() => this.findInput.nativeElement.focus(), 200);
}
}
searchNext() {
this.toolbarEvents.search({
searchTerm: this.searchText,
highlightAll: this.highlightAll,
matchCase: this.matchCase,
wholeWord: this.wholeWord,
previous: false,
reset: false
});
}
searchPrev() {
this.toolbarEvents.search({
searchTerm: this.searchText,
highlightAll: this.highlightAll,
matchCase: this.matchCase,
wholeWord: this.wholeWord,
previous: true,
reset: false
});
}
search() {
this.toolbarEvents.search({
searchTerm: this.searchText,
highlightAll: this.highlightAll,
matchCase: this.matchCase,
wholeWord: this.wholeWord,
previous: false,
reset: true
});
}
setSearchResultsCount(results) {
this.resultCount = results.total;
this.resultsText = this.resultCount > 0
? `Found ${results.current} of ${results.total}`
: 'No results found';
if (this.resultCount && this.resultCount > 0 && !results.isPrevious) {
setTimeout(() => {
this.findNext.nativeElement.focus();
}, 1000);
}
}
onEscapeKeyPress(e) {
this.toolbarEvents.searchBarHidden.next(true);
}
onEnterKeyPress(e) {
this.search();
}
toggleAdvancedSearch() {
this.advancedSearchVisible = !this.advancedSearchVisible;
}
toggleSearchBar() {
this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SearchBarComponent, deps: [{ token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: SearchBarComponent, isStandalone: false, selector: "mv-search-bar", host: { listeners: { "window:keydown": "onWindowKeyDown($event)" } }, viewQueries: [{ propertyName: "findInput", first: true, predicate: ["findInput"], descendants: true, static: true }, { propertyName: "findNext", first: true, predicate: ["findNext"], descendants: true }], ngImport: i0, template: "<div\n class=\"searchbar govuk-!-padding-3\"\n [hidden]=\"toolbarEvents.searchBarHidden | async\"\n>\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input\n class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\"\n aria-label=\"Search document\"\n #findInput\n [ngModel]=\"searchText\"\n (ngModelChange)=\"searchText = $event\"\n (keydown.escape)=\"onEscapeKeyPress($event)\"\n (keydown.enter)=\"onEnterKeyPress($event)\"\n title=\"Search document\"\n placeholder=\"Search document for\u2026\"\n tabindex=\"0\"\n data-l10n-id=\"find_input\"\n />\n <button\n class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\"\n data-module=\"govuk-button\"\n (click)=\"search()\"\n style=\"position: absolute; top: 40px; right: 10px\"\n >\n Search\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span\n id=\"findResultsCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-right-4\"\n role=\"status\"\n >{{ resultsText }}</span\n >\n <a\n *ngIf=\"resultCount > 0\"\n id=\"findPrevious\"\n [routerLink]=\"[]\"\n role=\"button\"\n class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\"\n title=\"Find the previous occurrence of the phrase\"\n [attr.aria-label]=\"'Find the previous occurrence of the phrase' | rpxTranslate\"\n data-l10n-id=\"find_previous\"\n >{{ \"Prev\" | rpxTranslate }}</a\n >\n <a\n *ngIf=\"resultCount > 0\"\n id=\"findNext\"\n #findNext\n [routerLink]=\"[]\"\n role=\"button\"\n class=\"govuk-link govuk-link--no-visited-state\"\n (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\"\n [attr.aria-label]=\"'Find the next occurrence of the phrase' | rpxTranslate\"\n data-l10n-id=\"find_next\"\n >{{ \"Next\" | rpxTranslate }}</a\n >\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a\n [routerLink]=\"[]\"\n class=\"govuk-link govuk-link--no-visited-state\"\n role=\"button\"\n (click)=\"toggleAdvancedSearch()\"\n title=\"Advanced\"\n [attr.aria-label]=\"'Advanced search options' | rpxTranslate\"\n [attr.aria-expanded]=\"advancedSearchVisible\"\n aria-controls=\"advancedSearchVisible\"\n data-l10n-id=\"find_advanced\"\n style=\"position: absolute; top: 95px; right: 15px\"\n >{{ \"Advanced\" | rpxTranslate }}</a\n >\n </div>\n </div>\n\n <div\n id=\"advancedSearchVisible\"\n class=\"govuk-grid-row\"\n *ngIf=\"advancedSearchVisible\"\n >\n <div class=\"govuk-grid-column-full\">\n <div\n class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\"\n >\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findHighlightAll\"\n name=\"findHighlightAll\"\n type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\"\n [checked]=\"highlightAll\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findHighlightAll\"\n data-l10n-id=\"find_highlight\"\n >\n {{ \"Highlight all\" | rpxTranslate }}\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findMatchCase\"\n name=\"findMatchCase\"\n type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\"\n [checked]=\"matchCase\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\"\n >\n {{ \"Match text (exact case)\" | rpxTranslate }}\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findEntireWord\"\n name=\"findMatchCase\"\n type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\"\n [checked]=\"wholeWord\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\"\n >\n {{ \"Match whole words or sentences\" | rpxTranslate }}\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n <div>\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button searchbar-button--close\"\n title=\"Close Search\"\n [attr.aria-label]=\"'Close Search' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\"\n ></button>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SearchBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-search-bar', standalone: false, template: "<div\n class=\"searchbar govuk-!-padding-3\"\n [hidden]=\"toolbarEvents.searchBarHidden | async\"\n>\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input\n class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\"\n aria-label=\"Search document\"\n #findInput\n [ngModel]=\"searchText\"\n (ngModelChange)=\"searchText = $event\"\n (keydown.escape)=\"onEscapeKeyPress($event)\"\n (keydown.enter)=\"onEnterKeyPress($event)\"\n title=\"Search document\"\n placeholder=\"Search document for\u2026\"\n tabindex=\"0\"\n data-l10n-id=\"find_input\"\n />\n <button\n class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\"\n data-module=\"govuk-button\"\n (click)=\"search()\"\n style=\"position: absolute; top: 40px; right: 10px\"\n >\n Search\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span\n id=\"findResultsCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-right-4\"\n role=\"status\"\n >{{ resultsText }}</span\n >\n <a\n *ngIf=\"resultCount > 0\"\n id=\"findPrevious\"\n [routerLink]=\"[]\"\n role=\"button\"\n class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\"\n title=\"Find the previous occurrence of the phrase\"\n [attr.aria-label]=\"'Find the previous occurrence of the phrase' | rpxTranslate\"\n data-l10n-id=\"find_previous\"\n >{{ \"Prev\" | rpxTranslate }}</a\n >\n <a\n *ngIf=\"resultCount > 0\"\n id=\"findNext\"\n #findNext\n [routerLink]=\"[]\"\n role=\"button\"\n class=\"govuk-link govuk-link--no-visited-state\"\n (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\"\n [attr.aria-label]=\"'Find the next occurrence of the phrase' | rpxTranslate\"\n data-l10n-id=\"find_next\"\n >{{ \"Next\" | rpxTranslate }}</a\n >\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a\n [routerLink]=\"[]\"\n class=\"govuk-link govuk-link--no-visited-state\"\n role=\"button\"\n (click)=\"toggleAdvancedSearch()\"\n title=\"Advanced\"\n [attr.aria-label]=\"'Advanced search options' | rpxTranslate\"\n [attr.aria-expanded]=\"advancedSearchVisible\"\n aria-controls=\"advancedSearchVisible\"\n data-l10n-id=\"find_advanced\"\n style=\"position: absolute; top: 95px; right: 15px\"\n >{{ \"Advanced\" | rpxTranslate }}</a\n >\n </div>\n </div>\n\n <div\n id=\"advancedSearchVisible\"\n class=\"govuk-grid-row\"\n *ngIf=\"advancedSearchVisible\"\n >\n <div class=\"govuk-grid-column-full\">\n <div\n class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\"\n >\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findHighlightAll\"\n name=\"findHighlightAll\"\n type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\"\n [checked]=\"highlightAll\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findHighlightAll\"\n data-l10n-id=\"find_highlight\"\n >\n {{ \"Highlight all\" | rpxTranslate }}\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findMatchCase\"\n name=\"findMatchCase\"\n type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\"\n [checked]=\"matchCase\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\"\n >\n {{ \"Match text (exact case)\" | rpxTranslate }}\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input\n class=\"govuk-checkboxes__input\"\n id=\"findEntireWord\"\n name=\"findMatchCase\"\n type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\"\n [checked]=\"wholeWord\"\n />\n <label\n class=\"govuk-label govuk-checkboxes__label\"\n for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\"\n >\n {{ \"Match whole words or sentences\" | rpxTranslate }}\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n <div>\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button searchbar-button--close\"\n title=\"Close Search\"\n [attr.aria-label]=\"'Close Search' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\"\n ></button>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }], propDecorators: { findInput: [{
type: ViewChild,
args: ['findInput', { static: true }]
}], findNext: [{
type: ViewChild,
args: ['findNext', { static: false }]
}], onWindowKeyDown: [{
type: HostListener,
args: ['window:keydown', ['$event']]
}] } });
class TooltipDismissDirective {
constructor(el) {
this.el = el;
}
onEscapeDismissTooltip() {
const element = this.el.nativeElement;
element.setAttribute('data-tooltip-dismissed', 'true');
}
onShowTooltip() {
const element = this.el.nativeElement;
element.removeAttribute('data-tooltip-dismissed');
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TooltipDismissDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: TooltipDismissDirective, isStandalone: false, selector: ".mv-tooltip, [mvTooltipDismiss]", host: { listeners: { "document:keydown.escape": "onEscapeDismissTooltip($event)", "mouseenter": "onShowTooltip()", "focus": "onShowTooltip()", "focusin": "onShowTooltip()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TooltipDismissDirective, decorators: [{
type: Directive,
args: [{
selector: '.mv-tooltip, [mvTooltipDismiss]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onEscapeDismissTooltip: [{
type: HostListener,
args: ['document:keydown.escape', ['$event']]
}], onShowTooltip: [{
type: HostListener,
args: ['mouseenter']
}, {
type: HostListener,
args: ['focus']
}, {
type: HostListener,
args: ['focusin']
}] } });
class MainToolbarComponent {
constructor(toolbarEvents, toolbarButtons, cdr, numberHelper, icpEventService, toolbarFocusService) {
this.toolbarEvents = toolbarEvents;
this.toolbarButtons = toolbarButtons;
this.cdr = cdr;
this.numberHelper = numberHelper;
this.icpEventService = icpEventService;
this.toolbarFocusService = toolbarFocusService;
this.enableAnnotations = false;
this.enableRedactions = false;
this.enableICP = false;
this.contentType = null;
this.subscriptions = [];
this.icpEnabled = false;
this.redactionEnabled = false;
this.pageNumber = 1;
this.pageCount = 0;
this.isDropdownMenuOpen = false;
this.isBookmarksOpen = false;
this.isIndexOpen = false;
this.isRedactOpen = false;
this.isCommentsOpen = false;
this.isHighlightOpen = false;
this.dropdownMenuPositions = [
new ConnectionPositionPair({
originX: 'end',
originY: 'bottom'
}, {
overlayX: 'end',
overlayY: 'top'
}, 0, 3)
];
this.zoomScales = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2.5, 3, 5];
this.allButtonsWidth = 0;
this.widthRequiredForBtn = {};
}
ngOnInit() {
this.subscriptions.push(this.toolbarEvents.setCurrentPageSubject.subscribe(pageNumber => this.setCurrentPage(pageNumber)), this.toolbarEvents.setCurrentPageInputValueSubject.subscribe(pageNumber => this.pageNumber = pageNumber), this.toolbarEvents.getPageCount().subscribe(count => this.pageCount = count), this.icpEventService.enabled.subscribe(enabled => {
this.icpEnabled = enabled;
if (this.icpEnabled) {
this.toolbarEvents.toggleCommentsPanel(!enabled);
this.toolbarEvents.sidebarOpen.next(false);
}
}), this.toolbarEvents.redactionMode.subscribe(enabled => {
this.redactionEnabled = enabled;
this.isRedactOpen = enabled;
}), this.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
this.redactAllInProgress = disable;
}), this.toolbarEvents.highlightToolbarSubject.subscribe(isOpen => {
this.isHighlightOpen = isOpen;
}));
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
ngAfterViewInit() {
Array.from(this.mvToolbarMain.nativeElement.children).forEach((button) => {
this.allButtonsWidth += HtmlTemplatesHelper.getAdjustedBoundingRect(button).width;
this.widthRequiredForBtn[button.id] = this.allButtonsWidth;
});
this.cdr.detectChanges();
}
onResize() {
this.cdr.detectChanges();
}
onControlPrint(event) {
event.preventDefault();
this.printFile();
}
onEscapeKey(event) {
const activeElement = document.activeElement;
const targetElement = event.target ?? activeElement;
const targetId = targetElement?.id || activeElement?.id;
const stopEvent = () => {
event.preventDefault();
event.stopPropagation();
};
const isInDropdown = this.isEventWithin(event, '.dropdown-menu');
const isInSearchBar = this.isEventWithin(event, 'mv-search-bar');
const isInSidebar = this.isEventWithin(event, '#sidebarContainer');
if (this.isDropdownMenuOpen && (isInDropdown || targetId === 'mvMoreOptionsBtn')) {
stopEvent();
this.isDropdownMenuOpen = false;
this.toolbarFocusService.focusToolbarButton('#mvToolbarMain', 'mvMoreOptionsBtn', 50);
return;
}
if (isInSearchBar) {
stopEvent();
this.closeSearchBarAndFocus();
return;
}
if (isInSidebar && this.toolbarEvents.sidebarOpen.getValue()) {
const sidebarContainer = document.getElementById('sidebarContainer');
const hasBookmarksContainer = !!sidebarContainer?.querySelector('#bookmarkContainer');
stopEvent();
this.toolbarEvents.toggleSideBar(false);
this.isIndexOpen = false;
this.isBookmarksOpen = false;
this.toolbarFocusService.focusToolbarButton('#mvToolbarMain', hasBookmarksContainer ? 'mvBookmarksBtn' : 'mvIndexBtn', 50);
return;
}
let didClose = false;
switch (targetId) {
case 'mvSearchBtn':
if (!this.toolbarEvents.searchBarHidden.getValue()) {
this.closeSearchBarAndFocus();
didClose = true;
}
break;
case 'mvIndexBtn':
if (this.isIndexOpen) {
this.toolbarEvents.toggleSideBar(false);
this.isIndexOpen = false;
didClose = true;
}
break;
case 'mvBookmarksBtn':
if (this.isBookmarksOpen) {
this.toolbarEvents.toggleSideBar(false);
this.isBookmarksOpen = false;
didClose = true;
}
break;
case 'mvHighlightBtn':
if (this.isHighlightOpen) {
this.toolbarEvents.toggleHighlightToolbar();
didClose = true;
}
break;
case 'mvRedactBtn':
if (this.isRedactOpen) {
this.toggleRedactBar();
didClose = true;
}
break;
case 'mvCommentsBtn':
case 'commentSubPane0':
if (this.isCommentsOpen) {
this.toggleCommentsPanel();
didClose = true;
}
break;
}
if (didClose) {
stopEvent();
}
}
closeSearchBarAndFocus() {
this.toolbarEvents.searchBarHidden
.pipe(filter$1(hidden => hidden), take(1))
.subscribe(() => {
this.toolbarFocusService.focusToolbarButton('#mvToolbarMain', 'mvSearchBtn');
});
this.toolbarEvents.searchBarHidden.next(true);
}
isEventWithin(event, selector) {
const target = event.target ?? document.activeElement;
if (target instanceof Element && target.closest(selector)) {
return true;
}
const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
return path.some(node => node instanceof Element && node.matches(selector));
}
onMoreOptionsKeyDown(event) {
if (event.key === 'ArrowDown' && !this.isDropdownMenuOpen) {
event.preventDefault();
event.stopPropagation();
this.toggleMoreOptions();
}
}
onHighlightKeyDown(event) {
if (event.key === 'ArrowDown') {
event.preventDefault();
event.stopPropagation();
if (!this.isHighlightOpen) {
this.openHighlightToolbarAndFocus();
}
else {
this.focusHighlightButton();
}
}
}
onClickHighlightToggle() {
this.toolbarEvents.toggleHighlightToolbar();
}
openHighlightToolbarAndFocus() {
if (!this.isHighlightOpen) {
this.toolbarEvents.toggleHighlightToolbar();
this.focusHighlightButton();
}
}
focusHighlightButton() {
this.toolbarFocusService.focusToolbarButton('.redaction');
}
onClickDrawToggle() {
this.toolbarEvents.toggleDrawMode();
}
toggleIndexSideBar() {
const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
if (!(sidebarOpen && !sidebarView)) {
this.toolbarEvents.toggleSideBar(!sidebarOpen);
}
this.toolbarEvents.toggleSideBarView(true);
this.isIndexOpen = !this.isIndexOpen;
}
onToolBarOffSetChange(buttonId) {
if (this.mvToolbarMain && this.mvToolbarMain.nativeElement.offsetWidth < this.widthRequiredForBtn[buttonId]) {
return "button-hidden-on-toolbar";
}
return "button-hidden-on-dropdown";
}
toggleBookmarksSideBar() {
const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
if (!(sidebarOpen && sidebarView)) {
this.toolbarEvents.toggleSideBar(!sidebarOpen);
}
this.toolbarEvents.toggleSideBarView(false);
this.isBookmarksOpen = !this.isBookmarksOpen;
}
togglePresentBar() {
this.toolbarEvents.searchBarHidden.next(true);
this.icpEventService.enable();
}
increasePageNumber() {
this.toolbarEvents.incrementPage(1);
}
decreasePageNumber() {
this.toolbarEvents.incrementPage(-1);
}
onPageNumberInputChange(pageNumber) {
if (Number(pageNumber) < 1) {
pageNumber = '1';
}
if (Number(pageNumber) > this.pageCount) {
pageNumber = this.pageCount.toString();
}
this.toolbarEvents.setPage(Number.parseInt(pageNumber, 10));
}
setCurrentPage(pageNumber) {
this.pageNumber = pageNumber;
}
rotate(rotation) {
this.toolbarEvents.rotate(rotation);
}
printFile() {
this.toolbarEvents.print();
}
downloadFile() {
this.toolbarEvents.download();
}
zoom(zoomFactor) {
this.toolbarEvents.zoom(+zoomFactor);
}
stepZoom(zoomFactor) {
this.toolbarEvents.stepZoom(zoomFactor);
this.zoomSelect.nativeElement.selected = 'selected';
}
toggleCommentsPanel() {
this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
this.isCommentsOpen = !this.isCommentsOpen;
}
toggleRedactBar() {
this.toolbarEvents.toggleRedactionMode();
}
onRedactKeyDown(event) {
if (event.key === 'ArrowDown') {
event.preventDefault();
event.stopPropagation();
if (!this.isRedactOpen) {
this.openRedactToolbarAndFocus();
}
else {
this.focusRedactButton();
}
}
}
openRedactToolbarAndFocus() {
if (!this.isRedactOpen) {
this.toolbarEvents.toggleRedactionMode();
this.focusRedactButton();
}
}
focusRedactButton() {
this.toolbarFocusService.focusToolbarButton('mv-redaction-toolbar .redaction');
}
toggleGrabNDrag() {
this.toolbarEvents.toggleGrabNDrag();
}
isPdf() {
return this.contentType === 'pdf';
}
toggleSearchBar() {
this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
}
toggleMoreOptions() {
this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
if (this.isDropdownMenuOpen) {
this.toolbarFocusService.focusToolbarButton('.cdk-overlay-pane .dropdown-menu');
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MainToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i0.ChangeDetectorRef }, { token: NumberHelperService }, { token: IcpEventService }, { token: ToolbarFocusService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: MainToolbarComponent, isStandalone: false, selector: "mv-main-toolbar", inputs: { enableAnnotations: "enableAnnotations", enableRedactions: "enableRedactions", enableICP: "enableICP", contentType: "contentType" }, host: { listeners: { "window:resize": "onResize()", "document:keydown.control.p": "onControlPrint($event)", "document:keydown.meta.p": "onControlPrint($event)", "document:keydown.escape": "onEscapeKey($event)" } }, viewQueries: [{ propertyName: "zoomSelect", first: true, predicate: ["zoomSelect"], descendants: true }, { propertyName: "mvToolbarMain", first: true, predicate: ["mvToolbarMain"], descendants: true }, { propertyName: "mvMenuItems", first: true, predicate: ["dropdownMenu"], descendants: true }], ngImport: i0, template: "<div class=\"toolbar\">\n <div id=\"toolbarContainer\">\n <div class=\"mv-toolbar__container\">\n <div #mvToolbar class=\"mv-toolbar\" [class.notSupported]=\"!contentType\" [mvKeyboardNav]=\"'horizontal'\">\n <!-- The mvToolbarMain div contains all toolbar buttons except the \"More options\" button. This allows for calculation of the available space to display buttons -->\n <div id=\"mvToolbarMain\" class=\"mv-toolbar-main\" #mvToolbarMain>\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n <!-- The mvToolbarMoreOptions div contains the \"More options\" toolbar button (and the overlay template for the dropdown menu).\n The space occupied by the button (if visible) is excluded from the toolbar space available calculation -->\n <div id=\"mvToolbarMoreOptions\" class=\"mv-toolbar-more-options\">\n <button\n id=\"mvMoreOptionsBtn\"\n class=\"mv-button mv-toolbar__menu-button--more-options mv-tooltip\"\n [class.mv-toolbar__menu-button--more-options__hidden]=\"\n mvToolbar.offsetWidth >= allButtonsWidth\n \"\n [attr.aria-expanded]=\"isDropdownMenuOpen\"\n [attr.data-tooltip]=\"'More options' | rpxTranslate\"\n [attr.aria-label]=\"'More options' | rpxTranslate\"\n (click)=\"toggleMoreOptions()\"\n (keydown)=\"onMoreOptionsKeyDown($event)\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span aria-hidden=\"true\">{{ \"More options\" | rpxTranslate }}</span>\n </button>\n <!-- This template displays the overlay content for the dropdown menu and is connected to the \"More options\" button -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isDropdownMenuOpen\"\n [cdkConnectedOverlayPositions]=\"dropdownMenuPositions\"\n >\n <div class=\"dropdown-menu\" #dropdownMenu [mvKeyboardNav]=\"'vertical'\">\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n </ng-template>\n </div>\n </div>\n <div id=\"mvMenuItems\" #mvMenuItems>\n <ng-template #menuItems>\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvIndexBtn\"\n data-l10n-id=\"index\"\n [attr.data-tooltip]=\"'Index' | rpxTranslate\"\n [attr.aria-label]=\"'Index' | rpxTranslate\"\n #mvIndexBtn\n class=\"mv-button mv-toolbar__menu-button--index mv-tooltip\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvIndexBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvIndexBtn']\n \"\n [attr.aria-expanded]=\"isIndexOpen\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Index\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvBookmarksBtn\"\n data-l10n-id=\"bookmarks\"\n [attr.data-tooltip]=\"'Bookmarks' | rpxTranslate\"\n [attr.aria-label]=\"'Bookmarks' | rpxTranslate\"\n #mvBookmarksBtn\n [ngClass]=\"{\n 'mv-button mv-tooltip mv-toolbar__menu-button--bookmarks': true,\n 'button-hidden-on-toolbar':\n mvToolbarMain.offsetWidth <\n widthRequiredForBtn['mvBookmarksBtn'],\n 'button-hidden-on-dropdown':\n mvToolbarMain.offsetWidth >=\n widthRequiredForBtn['mvBookmarksBtn']\n }\"\n [attr.aria-expanded]=\"isBookmarksOpen\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Bookmarks\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showDrawButton\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDrawBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDrawBtn']\n \"\n [class.toggled]=\"toolbarEvents.drawModeSubject | async\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\" aria-hidden=\"true\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showHighlightButton\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvHighlightBtn\"\n #mvHighlightBtn\n class=\"mv-button mv-toolbar__menu-button--highlight mv-tooltip\"\n [attr.data-tooltip]=\"'Highlight' | rpxTranslate\"\n [attr.aria-label]=\"'Highlight' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvHighlightBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvHighlightBtn']\n \"\n [class.toggled]=\"toolbarEvents.highlightToolbarSubject | async\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n (keydown)=\"onHighlightKeyDown($event)\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\" aria-hidden=\"true\">{{\n \"Highlight\" | rpxTranslate\n }}</span>\n </button>\n\n <ng-container *ngIf=\"toolbarButtons.showNavigation\">\n <div\n id=\"mvPageBtn\"\n #mvPageBtn\n class=\"mv-toolbar__menu-button--page\"\n [attr.data-tooltip]=\"'Page' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPageBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPageBtn']\n \"\n >\n <span>{{ \"Page\" | rpxTranslate }}</span>\n\n <button\n id=\"mvUpBtn\"\n [disabled]=\"pageNumber === 1 || redactAllInProgress\"\n [attr.data-tooltip]=\"'Previous page' | rpxTranslate\"\n [attr.aria-label]=\"'Previous page' | rpxTranslate\"\n class=\"mv-toolbar__menu-button--up button-image mv-tooltip\"\n data-l10n-id=\"previous\"\n (click)=\"decreasePageNumber()\"\n >\n <span></span>\n </button>\n <button\n id=\"mvDownBtn\"\n [disabled]=\"pageNumber === pageCount || redactAllInProgress\"\n [attr.data-tooltip]=\"'Next page' | rpxTranslate\"\n [attr.aria-label]=\"'Next page' | rpxTranslate\"\n class=\"mv-toolbar__menu-button--down button-image mv-tooltip\"\n data-l10n-id=\"next\"\n (click)=\"increasePageNumber()\"\n >\n <span></span>\n </button>\n\n <span class=\"mv-tooltip\" [attr.data-tooltip]=\"'Page number' | rpxTranslate\">\n <input\n type=\"number\"\n id=\"pageNumber\"\n class=\"hmcts-toolbar-input govuk-input--width-2\"\n [attr.aria-label]=\"'Page number' | rpxTranslate\"\n value=\"1\"\n size=\"4\"\n min=\"1\"\n [value]=\"pageNumber\"\n tabindex=\"0\"\n data-l10n-id=\"page\"\n (change)=\"onPageNumberInputChange(pageNumberInput.value)\"\n [disabled]=\"redactAllInProgress\"\n #pageNumberInput\n />\n </span>\n <span id=\"numPages\" class=\"toolbarLabel\">/ {{ pageCount }}</span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"toolbarButtons.showZoom\">\n <div\n id=\"mvZoomBtn\"\n #mvZoomBtn\n class=\"mv-toolbar__menu-button--zoom\"\n aria-pressed=\"false\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvZoomBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvZoomBtn']\n \"\n >\n <button\n [disabled]=\"\n toolbarEvents.zoomValueSubject.value === 0.1 ||\n redactAllInProgress\n \"\n id=\"mvMinusBtn\"\n class=\"mv-toolbar__menu-button--zoom-out button-image mv-tooltip\"\n [attr.data-tooltip]=\"'Zoom out' | rpxTranslate\"\n [attr.aria-label]=\"'Zoom out' | rpxTranslate\"\n data-l10n-id=\"zoom_out\"\n (click)=\"stepZoom(-0.1)\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\" aria-hidden=\"true\">{{\n \"Zoom Out\" | rpxTranslate\n }}</span>\n </button>\n <span class=\"mv-tooltip\" [attr.data-tooltip]=\"'Zoom' | rpxTranslate\"> \n <select\n id=\"scaleSelect\"\n class=\"hmcts-toolbar-select\"\n data-l10n-id=\"zoom\"\n (change)=\"zoom($event.target.value)\"\n tabindex=\"0\"\n [disabled]=\"redactAllInProgress\"\n >\n <option\n #zoomSelect\n id=\"customScaleOption\"\n title=\"\"\n [value]=\"toolbarEvents.zoomValueSubject.value\"\n >\n {{\n toolbarEvents.zoomValueSubject.value * 100\n | number : \"1.0-0\"\n }}%\n </option>\n <option\n *ngFor=\"let zoomScale of zoomScales\"\n title=\"\"\n [value]=\"zoomScale\"\n [attr.data-l10n-id]=\"'page_scale_percent_' + zoomScale * 100\"\n >\n {{ zoomScale * 100 }}%\n </option>\n </select>\n </span>\n <button\n [disabled]=\"\n toolbarEvents.zoomValueSubject.value === 5 ||\n redactAllInProgress\n \"\n id=\"mvPlusBtn\"\n class=\"mv-toolbar__menu-button--zoom-in button-image mv-tooltip\"\n (click)=\"stepZoom(0.1)\"\n [attr.data-tooltip]=\"'Zoom in' | rpxTranslate\"\n [attr.aria-label]=\"'Zoom in' | rpxTranslate\"\n data-l10n-id=\"zoom_in\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\" aria-hidden=\"true\">{{\n \"Zoom In\" | rpxTranslate\n }}</span>\n </button>\n </div>\n </ng-container>\n\n <div\n *ngIf=\"toolbarButtons.showRotate\"\n id=\"mvRotateBtn\"\n #mvRotateBtn\n class=\"mv-toolbar__menu-button--rotate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRotateBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRotateBtn']\n \"\n >\n <button\n id=\"mvRotateLeftBtn\"\n class=\"mv-toolbar__menu-button--rotate_left button-image mv-tooltip\"\n [attr.aria-label]=\"'Rotate counterclockwise' | rpxTranslate\"\n [attr.data-tooltip]=\"'Rotate counterclockwise' | rpxTranslate\"\n data-l10n-id=\"page_rotate_ccw\"\n (click)=\"rotate(270)\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <button\n id=\"mvRotateRightBtn\"\n class=\"mv-toolbar__menu-button--rotate_right button-image mv-tooltip\"\n [attr.aria-label]=\"'Rotate clockwise' | rpxTranslate\"\n [attr.data-tooltip]=\"'Rotate clockwise' | rpxTranslate\"\n data-l10n-id=\"page_rotate_cw\"\n (click)=\"rotate(90)\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <span>{{ \"Rotate\" | rpxTranslate }}</span>\n </div>\n\n <button\n *ngIf=\"toolbarButtons.showSearchBar\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvSearchBtn\"\n #mvSearchBtn\n [attr.data-tooltip]=\"'Search' | rpxTranslate\"\n [attr.aria-label]=\"'Search' | rpxTranslate\"\n data-l10n-id=\"searchbar\"\n class=\"mv-button mv-toolbar__menu-button--search mv-tooltip\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvSearchBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvSearchBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleSearchBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Search\" | rpxTranslate }}</span>\n </button>\n <mv-search-bar\n *ngIf=\"!toolbarEvents.searchBarHidden.getValue()\"\n ></mv-search-bar>\n\n <button\n *ngIf=\"enableICP && toolbarButtons.showPresentationMode && isPdf()\"\n [disabled]=\"icpEnabled || !contentType || redactionEnabled\"\n id=\"mvPresentBtn\"\n #mvPresentBtn\n class=\"mv-button mv-toolbar__menu-button--present mv-tooltip\"\n [attr.data-tooltip]=\"'In-Court presentation mode' | rpxTranslate\"\n [attr.aria-label]=\"'Present' | rpxTranslate\"\n data-l10n-id=\"icpMode_label\"\n [ngClass]=\"onToolBarOffSetChange('mvPresentBtn')\"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\" aria-hidden=\"true\">{{\n \"Present\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"enableRedactions && toolbarButtons.showRedact\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvRedactBtn\"\n #mvRedactBtn\n [attr.data-tooltip]=\"'Redact' | rpxTranslate\"\n [attr.aria-label]=\"'Redact' | rpxTranslate\"\n data-l10n-id=\"redact\"\n class=\"mv-button mv-toolbar__menu-button--redact mv-tooltip\"\n [ngClass]=\"onToolBarOffSetChange('mvRedactBtn')\"\n [attr.aria-expanded]=\"isRedactOpen\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n (keydown)=\"onRedactKeyDown($event)\"\n >\n <span aria-hidden=\"true\">{{ \"Redact\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showGrabNDragButton\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvGrabBtn\"\n #mvGrabBtn\n class=\"mv-button mv-toolbar__menu-button--grab mv-tooltip\"\n data-tooltip=\"Grab and drag\"\n [attr.aria-label]=\"'Grab and drag' | rpxTranslate\"\n [ngClass]=\"onToolBarOffSetChange('mvGrabBtn')\"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Grab and drag\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showDownload\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvDownloadBtn\"\n #mvDownloadBtn\n class=\"mv-button mv-toolbar__menu-button--download mv-tooltip\"\n [attr.data-tooltip]=\"'Download' | rpxTranslate\"\n [attr.aria-label]=\"'Download' | rpxTranslate\"\n data-l10n-id=\"download\"\n [ngClass]=\"onToolBarOffSetChange('mvDownloadBtn')\"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Download\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showPrint\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvPrintBtn\"\n #mvPrintBtn\n [attr.data-tooltip]=\"'Print' | rpxTranslate\"\n [attr.aria-label]=\"'Print' | rpxTranslate\"\n data-l10n-id=\"print\"\n class=\"mv-button mv-toolbar__menu-button--print mv-tooltip\"\n [ngClass]=\"onToolBarOffSetChange('mvPrintBtn')\"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Print\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"enableAnnotations && toolbarButtons.showCommentSummary\"\n [disabled]=\"redactionEnabled\"\n id=\"mvCommentsBtn\"\n #mvCommentsBtn\n class=\"mv-button mv-toolbar__menu-button--comments mv-tooltip\"\n [attr.data-tooltip]=\"'Comments' | rpxTranslate\"\n [attr.aria-label]=\"'Comments' | rpxTranslate\"\n data-l10n-id=\"comments\"\n [ngClass]=\"onToolBarOffSetChange('mvCommentsBtn')\"\n [attr.aria-expanded]=\"isCommentsOpen\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Comments\" | rpxTranslate }}</span>\n </button>\n </ng-template>\n </div>\n </div>\n\n <div id=\"loadingBar\">\n <div class=\"progress\">\n <div class=\"glimmer\"></div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: KeyboardNavDirective, selector: "[mvKeyboardNav]", inputs: ["mvKeyboardNav"], outputs: ["itemFocused", "itemActivated"] }, { kind: "component", type: SearchBarComponent, selector: "mv-search-bar" }, { kind: "directive", type: TooltipDismissDirective, selector: ".mv-tooltip, [mvTooltipDismiss]" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.DecimalPipe, name: "number" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MainToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-main-toolbar', standalone: false, template: "<div class=\"toolbar\">\n <div id=\"toolbarContainer\">\n <div class=\"mv-toolbar__container\">\n <div #mvToolbar class=\"mv-toolbar\" [class.notSupported]=\"!contentType\" [mvKeyboardNav]=\"'horizontal'\">\n <!-- The mvToolbarMain div contains all toolbar buttons except the \"More options\" button. This allows for calculation of the available space to display buttons -->\n <div id=\"mvToolbarMain\" class=\"mv-toolbar-main\" #mvToolbarMain>\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n <!-- The mvToolbarMoreOptions div contains the \"More options\" toolbar button (and the overlay template for the dropdown menu).\n The space occupied by the button (if visible) is excluded from the toolbar space available calculation -->\n <div id=\"mvToolbarMoreOptions\" class=\"mv-toolbar-more-options\">\n <button\n id=\"mvMoreOptionsBtn\"\n class=\"mv-button mv-toolbar__menu-button--more-options mv-tooltip\"\n [class.mv-toolbar__menu-button--more-options__hidden]=\"\n mvToolbar.offsetWidth >= allButtonsWidth\n \"\n [attr.aria-expanded]=\"isDropdownMenuOpen\"\n [attr.data-tooltip]=\"'More options' | rpxTranslate\"\n [attr.aria-label]=\"'More options' | rpxTranslate\"\n (click)=\"toggleMoreOptions()\"\n (keydown)=\"onMoreOptionsKeyDown($event)\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span aria-hidden=\"true\">{{ \"More options\" | rpxTranslate }}</span>\n </button>\n <!-- This template displays the overlay content for the dropdown menu and is connected to the \"More options\" button -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isDropdownMenuOpen\"\n [cdkConnectedOverlayPositions]=\"dropdownMenuPositions\"\n >\n <div class=\"dropdown-menu\" #dropdownMenu [mvKeyboardNav]=\"'vertical'\">\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n </ng-template>\n </div>\n </div>\n <div id=\"mvMenuItems\" #mvMenuItems>\n <ng-template #menuItems>\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvIndexBtn\"\n data-l10n-id=\"index\"\n [attr.data-tooltip]=\"'Index' | rpxTranslate\"\n [attr.aria-label]=\"'Index' | rpxTranslate\"\n #mvIndexBtn\n class=\"mv-button mv-toolbar__menu-button--index mv-tooltip\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvIndexBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvIndexBtn']\n \"\n [attr.aria-expanded]=\"isIndexOpen\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Index\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvBookmarksBtn\"\n data-l10n-id=\"bookmarks\"\n [attr.data-tooltip]=\"'Bookmarks' | rpxTranslate\"\n [attr.aria-label]=\"'Bookmarks' | rpxTranslate\"\n #mvBookmarksBtn\n [ngClass]=\"{\n 'mv-button mv-tooltip mv-toolbar__menu-button--bookmarks': true,\n 'button-hidden-on-toolbar':\n mvToolbarMain.offsetWidth <\n widthRequiredForBtn['mvBookmarksBtn'],\n 'button-hidden-on-dropdown':\n mvToolbarMain.offsetWidth >=\n widthRequiredForBtn['mvBookmarksBtn']\n }\"\n [attr.aria-expanded]=\"isBookmarksOpen\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Bookmarks\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showDrawButton\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDrawBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDrawBtn']\n \"\n [class.toggled]=\"toolbarEvents.drawModeSubject | async\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\" aria-hidden=\"true\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showHighlightButton\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvHighlightBtn\"\n #mvHighlightBtn\n class=\"mv-button mv-toolbar__menu-button--highlight mv-tooltip\"\n [attr.data-tooltip]=\"'Highlight' | rpxTranslate\"\n [attr.aria-label]=\"'Highlight' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvHighlightBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvHighlightBtn']\n \"\n [class.toggled]=\"toolbarEvents.highlightToolbarSubject | async\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n (keydown)=\"onHighlightKeyDown($event)\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\" aria-hidden=\"true\">{{\n \"Highlight\" | rpxTranslate\n }}</span>\n </button>\n\n <ng-container *ngIf=\"toolbarButtons.showNavigation\">\n <div\n id=\"mvPageBtn\"\n #mvPageBtn\n class=\"mv-toolbar__menu-button--page\"\n [attr.data-tooltip]=\"'Page' | rpxTranslate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPageBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPageBtn']\n \"\n >\n <span>{{ \"Page\" | rpxTranslate }}</span>\n\n <button\n id=\"mvUpBtn\"\n [disabled]=\"pageNumber === 1 || redactAllInProgress\"\n [attr.data-tooltip]=\"'Previous page' | rpxTranslate\"\n [attr.aria-label]=\"'Previous page' | rpxTranslate\"\n class=\"mv-toolbar__menu-button--up button-image mv-tooltip\"\n data-l10n-id=\"previous\"\n (click)=\"decreasePageNumber()\"\n >\n <span></span>\n </button>\n <button\n id=\"mvDownBtn\"\n [disabled]=\"pageNumber === pageCount || redactAllInProgress\"\n [attr.data-tooltip]=\"'Next page' | rpxTranslate\"\n [attr.aria-label]=\"'Next page' | rpxTranslate\"\n class=\"mv-toolbar__menu-button--down button-image mv-tooltip\"\n data-l10n-id=\"next\"\n (click)=\"increasePageNumber()\"\n >\n <span></span>\n </button>\n\n <span class=\"mv-tooltip\" [attr.data-tooltip]=\"'Page number' | rpxTranslate\">\n <input\n type=\"number\"\n id=\"pageNumber\"\n class=\"hmcts-toolbar-input govuk-input--width-2\"\n [attr.aria-label]=\"'Page number' | rpxTranslate\"\n value=\"1\"\n size=\"4\"\n min=\"1\"\n [value]=\"pageNumber\"\n tabindex=\"0\"\n data-l10n-id=\"page\"\n (change)=\"onPageNumberInputChange(pageNumberInput.value)\"\n [disabled]=\"redactAllInProgress\"\n #pageNumberInput\n />\n </span>\n <span id=\"numPages\" class=\"toolbarLabel\">/ {{ pageCount }}</span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"toolbarButtons.showZoom\">\n <div\n id=\"mvZoomBtn\"\n #mvZoomBtn\n class=\"mv-toolbar__menu-button--zoom\"\n aria-pressed=\"false\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvZoomBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvZoomBtn']\n \"\n >\n <button\n [disabled]=\"\n toolbarEvents.zoomValueSubject.value === 0.1 ||\n redactAllInProgress\n \"\n id=\"mvMinusBtn\"\n class=\"mv-toolbar__menu-button--zoom-out button-image mv-tooltip\"\n [attr.data-tooltip]=\"'Zoom out' | rpxTranslate\"\n [attr.aria-label]=\"'Zoom out' | rpxTranslate\"\n data-l10n-id=\"zoom_out\"\n (click)=\"stepZoom(-0.1)\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\" aria-hidden=\"true\">{{\n \"Zoom Out\" | rpxTranslate\n }}</span>\n </button>\n <span class=\"mv-tooltip\" [attr.data-tooltip]=\"'Zoom' | rpxTranslate\"> \n <select\n id=\"scaleSelect\"\n class=\"hmcts-toolbar-select\"\n data-l10n-id=\"zoom\"\n (change)=\"zoom($event.target.value)\"\n tabindex=\"0\"\n [disabled]=\"redactAllInProgress\"\n >\n <option\n #zoomSelect\n id=\"customScaleOption\"\n title=\"\"\n [value]=\"toolbarEvents.zoomValueSubject.value\"\n >\n {{\n toolbarEvents.zoomValueSubject.value * 100\n | number : \"1.0-0\"\n }}%\n </option>\n <option\n *ngFor=\"let zoomScale of zoomScales\"\n title=\"\"\n [value]=\"zoomScale\"\n [attr.data-l10n-id]=\"'page_scale_percent_' + zoomScale * 100\"\n >\n {{ zoomScale * 100 }}%\n </option>\n </select>\n </span>\n <button\n [disabled]=\"\n toolbarEvents.zoomValueSubject.value === 5 ||\n redactAllInProgress\n \"\n id=\"mvPlusBtn\"\n class=\"mv-toolbar__menu-button--zoom-in button-image mv-tooltip\"\n (click)=\"stepZoom(0.1)\"\n [attr.data-tooltip]=\"'Zoom in' | rpxTranslate\"\n [attr.aria-label]=\"'Zoom in' | rpxTranslate\"\n data-l10n-id=\"zoom_in\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\" aria-hidden=\"true\">{{\n \"Zoom In\" | rpxTranslate\n }}</span>\n </button>\n </div>\n </ng-container>\n\n <div\n *ngIf=\"toolbarButtons.showRotate\"\n id=\"mvRotateBtn\"\n #mvRotateBtn\n class=\"mv-toolbar__menu-button--rotate\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRotateBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRotateBtn']\n \"\n >\n <button\n id=\"mvRotateLeftBtn\"\n class=\"mv-toolbar__menu-button--rotate_left button-image mv-tooltip\"\n [attr.aria-label]=\"'Rotate counterclockwise' | rpxTranslate\"\n [attr.data-tooltip]=\"'Rotate counterclockwise' | rpxTranslate\"\n data-l10n-id=\"page_rotate_ccw\"\n (click)=\"rotate(270)\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <button\n id=\"mvRotateRightBtn\"\n class=\"mv-toolbar__menu-button--rotate_right button-image mv-tooltip\"\n [attr.aria-label]=\"'Rotate clockwise' | rpxTranslate\"\n [attr.data-tooltip]=\"'Rotate clockwise' | rpxTranslate\"\n data-l10n-id=\"page_rotate_cw\"\n (click)=\"rotate(90)\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <span>{{ \"Rotate\" | rpxTranslate }}</span>\n </div>\n\n <button\n *ngIf=\"toolbarButtons.showSearchBar\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvSearchBtn\"\n #mvSearchBtn\n [attr.data-tooltip]=\"'Search' | rpxTranslate\"\n [attr.aria-label]=\"'Search' | rpxTranslate\"\n data-l10n-id=\"searchbar\"\n class=\"mv-button mv-toolbar__menu-button--search mv-tooltip\"\n [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvSearchBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvSearchBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleSearchBar(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Search\" | rpxTranslate }}</span>\n </button>\n <mv-search-bar\n *ngIf=\"!toolbarEvents.searchBarHidden.getValue()\"\n ></mv-search-bar>\n\n <button\n *ngIf=\"enableICP && toolbarButtons.showPresentationMode && isPdf()\"\n [disabled]=\"icpEnabled || !contentType || redactionEnabled\"\n id=\"mvPresentBtn\"\n #mvPresentBtn\n class=\"mv-button mv-toolbar__menu-button--present mv-tooltip\"\n [attr.data-tooltip]=\"'In-Court presentation mode' | rpxTranslate\"\n [attr.aria-label]=\"'Present' | rpxTranslate\"\n data-l10n-id=\"icpMode_label\"\n [ngClass]=\"onToolBarOffSetChange('mvPresentBtn')\"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\" aria-hidden=\"true\">{{\n \"Present\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"enableRedactions && toolbarButtons.showRedact\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvRedactBtn\"\n #mvRedactBtn\n [attr.data-tooltip]=\"'Redact' | rpxTranslate\"\n [attr.aria-label]=\"'Redact' | rpxTranslate\"\n data-l10n-id=\"redact\"\n class=\"mv-button mv-toolbar__menu-button--redact mv-tooltip\"\n [ngClass]=\"onToolBarOffSetChange('mvRedactBtn')\"\n [attr.aria-expanded]=\"isRedactOpen\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n (keydown)=\"onRedactKeyDown($event)\"\n >\n <span aria-hidden=\"true\">{{ \"Redact\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showGrabNDragButton\"\n [disabled]=\"icpEnabled || redactAllInProgress\"\n id=\"mvGrabBtn\"\n #mvGrabBtn\n class=\"mv-button mv-toolbar__menu-button--grab mv-tooltip\"\n data-tooltip=\"Grab and drag\"\n [attr.aria-label]=\"'Grab and drag' | rpxTranslate\"\n [ngClass]=\"onToolBarOffSetChange('mvGrabBtn')\"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Grab and drag\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showDownload\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvDownloadBtn\"\n #mvDownloadBtn\n class=\"mv-button mv-toolbar__menu-button--download mv-tooltip\"\n [attr.data-tooltip]=\"'Download' | rpxTranslate\"\n [attr.aria-label]=\"'Download' | rpxTranslate\"\n data-l10n-id=\"download\"\n [ngClass]=\"onToolBarOffSetChange('mvDownloadBtn')\"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Download\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showPrint\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvPrintBtn\"\n #mvPrintBtn\n [attr.data-tooltip]=\"'Print' | rpxTranslate\"\n [attr.aria-label]=\"'Print' | rpxTranslate\"\n data-l10n-id=\"print\"\n class=\"mv-button mv-toolbar__menu-button--print mv-tooltip\"\n [ngClass]=\"onToolBarOffSetChange('mvPrintBtn')\"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Print\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"enableAnnotations && toolbarButtons.showCommentSummary\"\n [disabled]=\"redactionEnabled\"\n id=\"mvCommentsBtn\"\n #mvCommentsBtn\n class=\"mv-button mv-toolbar__menu-button--comments mv-tooltip\"\n [attr.data-tooltip]=\"'Comments' | rpxTranslate\"\n [attr.aria-label]=\"'Comments' | rpxTranslate\"\n data-l10n-id=\"comments\"\n [ngClass]=\"onToolBarOffSetChange('mvCommentsBtn')\"\n [attr.aria-expanded]=\"isCommentsOpen\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span aria-hidden=\"true\">{{ \"Comments\" | rpxTranslate }}</span>\n </button>\n </ng-template>\n </div>\n </div>\n\n <div id=\"loadingBar\">\n <div class=\"progress\">\n <div class=\"glimmer\"></div>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i0.ChangeDetectorRef }, { type: NumberHelperService }, { type: IcpEventService }, { type: ToolbarFocusService }], propDecorators: { enableAnnotations: [{
type: Input
}], enableRedactions: [{
type: Input
}], enableICP: [{
type: Input
}], contentType: [{
type: Input
}], zoomSelect: [{
type: ViewChild,
args: ['zoomSelect', { static: false }]
}], mvToolbarMain: [{
type: ViewChild,
args: ['mvToolbarMain', { static: false }]
}], mvMenuItems: [{
type: ViewChild,
args: ['dropdownMenu', { static: false }]
}], onResize: [{
type: HostListener,
args: ['window:resize', []]
}], onControlPrint: [{
type: HostListener,
args: ['document:keydown.control.p', ['$event']]
}, {
type: HostListener,
args: ['document:keydown.meta.p', ['$event']]
}], onEscapeKey: [{
type: HostListener,
args: ['document:keydown.escape', ['$event']]
}] } });
class RedactionToolbarComponent {
constructor(toolbarEventService, toolbarButtons, store, toolbarFocusService) {
this.toolbarEventService = toolbarEventService;
this.toolbarButtons = toolbarButtons;
this.store = store;
this.toolbarFocusService = toolbarFocusService;
this.preview = false;
this.hasRedactions = false;
this.subscriptions = [];
}
ngOnInit() {
this.subscriptions.push(this.store.pipe(select(getRedactionArray)).subscribe(redactions => {
this.hasRedactions = !!redactions.redactions.length;
}));
this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
this.redactionAllInProgress = inprogress;
}));
}
onRedactAllSearch() {
this.toolbarEventService.openRedactionSearch.next({ modeType: SearchType.Redact, isOpen: true });
}
toggleTextRedactionMode() {
this.toolbarEventService.highlightModeSubject.next(true);
}
toggleDrawMode() {
this.toolbarEventService.drawModeSubject.next(true);
}
togglePreview() {
this.preview = !this.preview;
this.toolbarEventService.toggleRedactionPreview(this.preview);
}
unmarkAll() {
this.toolbarEventService.unmarkAll();
}
redact() {
this.toolbarEventService.applyRedactionToDocument();
}
toggleRedactBar() {
this.toolbarEventService.toggleRedactionMode();
}
redactPage() {
this.toolbarEventService.drawModeSubject.next(true);
this.toolbarEventService.redactPage();
}
onEscapeKey(event) {
event.preventDefault();
event.stopPropagation();
this.toggleRedactBar();
this.returnFocusToMainToolbar();
}
onArrowUp(event) {
const target = event.target;
const redactionToolbar = target.closest('.redaction');
if (redactionToolbar) {
this.returnFocusToMainToolbar();
}
}
returnFocusToMainToolbar() {
this.toolbarFocusService.focusToolbarButton('#mvRedactBtn');
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i1.Store }, { token: ToolbarFocusService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: RedactionToolbarComponent, isStandalone: false, selector: "mv-redaction-toolbar", inputs: { showRedactSearch: "showRedactSearch" }, host: { listeners: { "keydown.escape": "onEscapeKey($event)", "keydown.arrowup": "onArrowUp($event)" } }, ngImport: i0, template: "<div class=\"redaction\" [mvKeyboardNav]=\"'horizontal'\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\">{{\n \"Redaction options\" | rpxTranslate\n }}</label>\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\" aria-hidden=\"true\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page mv-tooltip\"\n [attr.data-tooltip]=\"'Redact page' | rpxTranslate\"\n [attr.aria-label]=\"'Redact page' | rpxTranslate\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\" aria-hidden=\"true\">{{\n \"Redact page\" | rpxTranslate\n }}</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n class=\"mv-button redaction-button--search mv-tooltip\"\n [attr.data-tooltip]=\"'From search' | rpxTranslate\"\n [attr.aria-label]=\"'From search' | rpxTranslate\"\n data-l10n-id=\"fromSearchButton\"\n (click)=\"onRedactAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\" aria-hidden=\"true\">{{\n \"From search\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showHighlightButton\"\n id=\"toggleHighlightButton\"\n class=\"mv-button redaction-button--redact mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Redact text' | rpxTranslate\"\n [attr.aria-label]=\"'Redact text' | rpxTranslate\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\" aria-hidden=\"true\">{{\n \"Redact text\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvClearBtn\"\n #mvClearBtn\n class=\"mv-button redaction-button--clear mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Clear all' | rpxTranslate\"\n [attr.aria-label]=\"'Clear all' | rpxTranslate\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\" aria-hidden=\"true\">{{ \"Clear all\" | rpxTranslate }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvPreviewBtn\"\n class=\"mv-button mv-tooltip\"\n [class.redaction-button--preview]=\"!preview\"\n [class.redaction-button--hide-preview]=\"preview\"\n redaction-button--preview\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"preview ? ('Hide preview' | rpxTranslate) : ('Preview' | rpxTranslate)\"\n [attr.aria-label]=\"preview ? ('Hide preview' | rpxTranslate) : ('Preview' | rpxTranslate)\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\" aria-hidden=\"true\">{{\n \"Preview\" | rpxTranslate\n }}</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\" aria-hidden=\"true\">{{\n \"Hide preview\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvRedactBtn\"\n class=\"mv-button redaction-button--download mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Save document' | rpxTranslate\"\n [attr.aria-label]=\"'Save document' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\" aria-hidden=\"true\">{{\n \"Save document\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close mv-tooltip\"\n [attr.data-tooltip]=\"'Close Redaction' | rpxTranslate\"\n [attr.aria-label]=\"'Close Redaction' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\" aria-hidden=\"true\">{{\n \"Close Redaction\" | rpxTranslate\n }}</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: KeyboardNavDirective, selector: "[mvKeyboardNav]", inputs: ["mvKeyboardNav"], outputs: ["itemFocused", "itemActivated"] }, { kind: "directive", type: TooltipDismissDirective, selector: ".mv-tooltip, [mvTooltipDismiss]" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redaction-toolbar', standalone: false, template: "<div class=\"redaction\" [mvKeyboardNav]=\"'horizontal'\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\">{{\n \"Redaction options\" | rpxTranslate\n }}</label>\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\" aria-hidden=\"true\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page mv-tooltip\"\n [attr.data-tooltip]=\"'Redact page' | rpxTranslate\"\n [attr.aria-label]=\"'Redact page' | rpxTranslate\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\" aria-hidden=\"true\">{{\n \"Redact page\" | rpxTranslate\n }}</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n class=\"mv-button redaction-button--search mv-tooltip\"\n [attr.data-tooltip]=\"'From search' | rpxTranslate\"\n [attr.aria-label]=\"'From search' | rpxTranslate\"\n data-l10n-id=\"fromSearchButton\"\n (click)=\"onRedactAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\" aria-hidden=\"true\">{{\n \"From search\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showHighlightButton\"\n id=\"toggleHighlightButton\"\n class=\"mv-button redaction-button--redact mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Redact text' | rpxTranslate\"\n [attr.aria-label]=\"'Redact text' | rpxTranslate\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\" aria-hidden=\"true\">{{\n \"Redact text\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvClearBtn\"\n #mvClearBtn\n class=\"mv-button redaction-button--clear mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Clear all' | rpxTranslate\"\n [attr.aria-label]=\"'Clear all' | rpxTranslate\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\" aria-hidden=\"true\">{{ \"Clear all\" | rpxTranslate }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvPreviewBtn\"\n class=\"mv-button mv-tooltip\"\n [class.redaction-button--preview]=\"!preview\"\n [class.redaction-button--hide-preview]=\"preview\"\n redaction-button--preview\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"preview ? ('Hide preview' | rpxTranslate) : ('Preview' | rpxTranslate)\"\n [attr.aria-label]=\"preview ? ('Hide preview' | rpxTranslate) : ('Preview' | rpxTranslate)\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\" aria-hidden=\"true\">{{\n \"Preview\" | rpxTranslate\n }}</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\" aria-hidden=\"true\">{{\n \"Hide preview\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvRedactBtn\"\n class=\"mv-button redaction-button--download mv-tooltip\"\n aria-pressed=\"false\"\n [attr.data-tooltip]=\"'Save document' | rpxTranslate\"\n [attr.aria-label]=\"'Save document' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\" aria-hidden=\"true\">{{\n \"Save document\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close mv-tooltip\"\n [attr.data-tooltip]=\"'Close Redaction' | rpxTranslate\"\n [attr.aria-label]=\"'Close Redaction' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\" aria-hidden=\"true\">{{\n \"Close Redaction\" | rpxTranslate\n }}</span>\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }, { type: ToolbarFocusService }], propDecorators: { showRedactSearch: [{
type: Input
}], onEscapeKey: [{
type: HostListener,
args: ['keydown.escape', ['$event']]
}], onArrowUp: [{
type: HostListener,
args: ['keydown.arrowup', ['$event']]
}] } });
class IcpToolbarComponent {
constructor(toolbarEventService, store, icpEventService) {
this.toolbarEventService = toolbarEventService;
this.store = store;
this.icpEventService = icpEventService;
}
ngOnInit() {
this.$subscription = this.store.pipe(select(isPresenter))
.subscribe(isPresenter => this.isPresenter = isPresenter);
this.$subscription.add(this.store.pipe(select(getPresenterName))
.subscribe(name => this.presenterName = name));
}
ngOnDestroy() {
this.$subscription.unsubscribe();
}
present() {
this.icpEventService.becomePresenter();
}
stopPresenting() {
this.icpEventService.stopPresenting();
}
leaveIcpSession() {
this.icpEventService.leaveSession();
}
showParticipantsList() {
this.toolbarEventService.toggleParticipantsList(!this.icpEventService.participantsListVisible.getValue());
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpToolbarComponent, deps: [{ token: ToolbarEventService }, { token: i1.Store }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: IcpToolbarComponent, isStandalone: false, selector: "mv-icp-toolbar", ngImport: i0, template: "<div id=\"icp-toolbar\" class=\"icpMode\">\n <div id=\"toolbar-title\">\n <label class=\"govuk-label\" data-l10n-id=\"digitalEvidence_label\">Digital evidence presentation</label>\n </div>\n\n <div id=\"presentation-mode\">\n <div class=\"item-label\">\n <label class=\"govuk-label\" data-l10n-id=\"icpInfo_label\">You are in presentation mode</label>\n </div>\n <div class=\"item-button\">\n <button class=\"govuk-button govuk-button--secondary\" data-module=\"govuk-button\" tabindex=\"0\"\n title=\"Leave presentation\" [attr.aria-label]=\"'Leave presentation' | rpxTranslate\" (click)=\"leaveIcpSession()\">\n <span data-l10n-id=\"icp-leave-presentation_label\" aria-hidden=\"true\">Leave presentation</span>\n </button>\n </div>\n </div>\n\n <div id=\"presenter-info\">\n <div class=\"item-label\">\n <label class=\"govuk-label\" *ngIf=\"!presenterName\" data-l10n-id=\"noPresenter_label\">Waiting for presenter</label>\n <label class=\"govuk-label\" *ngIf=\"presenterName && isPresenter\" data-l10n-id=\"isPresenter_label\">You are\n presenting</label>\n <label class=\"govuk-label\" *ngIf=\"presenterName && !isPresenter\"\n data-l10n-id=\"otherPresenter_label\">{{ presenterName | titlecase }}\n is presenting</label>\n </div>\n <div class=\"item-button\">\n <button *ngIf=\"!presenterName\" class=\"govuk-button\" id=\"icp-present\" tabindex=\"0\" [attr.aria-label]=\"'Start presenting' | rpxTranslate\"\n (click)=\"present()\">\n <span data-l10n-id=\"icp-present_label\" aria-hidden=\"true\">Start presenting</span>\n </button>\n <button *ngIf=\"isPresenter\" class=\"govuk-button govuk-button--warning\" id=\"icp-stop-presenting\" tabindex=\"0\"\n [attr.aria-label]=\"'Stop presenting' | rpxTranslate\" (click)=\"stopPresenting()\">\n <span data-l10n-id=\"icp-stop-presenting_label\" aria-hidden=\"true\">Stop Presenting</span>\n </button>\n </div>\n </div>\n\n <div id=\"participant-list\">\n <button class=\"govuk-button govuk-button--secondary\" id=\"icp-participants-list\" tabindex=\"0\"\n [attr.aria-label]=\"'Participants list' | rpxTranslate\" (click)=\"showParticipantsList()\">\n <span\n data-l10n-id=\"icp-stop-presenting_label\" aria-hidden=\"true\">{{(icpEventService.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-icp-toolbar', standalone: false, template: "<div id=\"icp-toolbar\" class=\"icpMode\">\n <div id=\"toolbar-title\">\n <label class=\"govuk-label\" data-l10n-id=\"digitalEvidence_label\">Digital evidence presentation</label>\n </div>\n\n <div id=\"presentation-mode\">\n <div class=\"item-label\">\n <label class=\"govuk-label\" data-l10n-id=\"icpInfo_label\">You are in presentation mode</label>\n </div>\n <div class=\"item-button\">\n <button class=\"govuk-button govuk-button--secondary\" data-module=\"govuk-button\" tabindex=\"0\"\n title=\"Leave presentation\" [attr.aria-label]=\"'Leave presentation' | rpxTranslate\" (click)=\"leaveIcpSession()\">\n <span data-l10n-id=\"icp-leave-presentation_label\" aria-hidden=\"true\">Leave presentation</span>\n </button>\n </div>\n </div>\n\n <div id=\"presenter-info\">\n <div class=\"item-label\">\n <label class=\"govuk-label\" *ngIf=\"!presenterName\" data-l10n-id=\"noPresenter_label\">Waiting for presenter</label>\n <label class=\"govuk-label\" *ngIf=\"presenterName && isPresenter\" data-l10n-id=\"isPresenter_label\">You are\n presenting</label>\n <label class=\"govuk-label\" *ngIf=\"presenterName && !isPresenter\"\n data-l10n-id=\"otherPresenter_label\">{{ presenterName | titlecase }}\n is presenting</label>\n </div>\n <div class=\"item-button\">\n <button *ngIf=\"!presenterName\" class=\"govuk-button\" id=\"icp-present\" tabindex=\"0\" [attr.aria-label]=\"'Start presenting' | rpxTranslate\"\n (click)=\"present()\">\n <span data-l10n-id=\"icp-present_label\" aria-hidden=\"true\">Start presenting</span>\n </button>\n <button *ngIf=\"isPresenter\" class=\"govuk-button govuk-button--warning\" id=\"icp-stop-presenting\" tabindex=\"0\"\n [attr.aria-label]=\"'Stop presenting' | rpxTranslate\" (click)=\"stopPresenting()\">\n <span data-l10n-id=\"icp-stop-presenting_label\" aria-hidden=\"true\">Stop Presenting</span>\n </button>\n </div>\n </div>\n\n <div id=\"participant-list\">\n <button class=\"govuk-button govuk-button--secondary\" id=\"icp-participants-list\" tabindex=\"0\"\n [attr.aria-label]=\"'Participants list' | rpxTranslate\" (click)=\"showParticipantsList()\">\n <span\n data-l10n-id=\"icp-stop-presenting_label\" aria-hidden=\"true\">{{(icpEventService.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: i1.Store }, { type: IcpEventService }] });
class HighlightToolbarComponent {
constructor(toolbarEventService, toolbarButtons, toolbarFocusService) {
this.toolbarEventService = toolbarEventService;
this.toolbarButtons = toolbarButtons;
this.toolbarFocusService = toolbarFocusService;
this.subscriptions = [];
}
ngOnInit() {
this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
this.redactionAllInProgress = inprogress;
}));
}
onAllSearch() {
this.toolbarEventService.openRedactionSearch.next({ modeType: SearchType.Highlight, isOpen: true });
}
onHighlight() {
this.toolbarEventService.toggleHighlightMode();
}
onClickDrawToggle() {
this.toolbarEventService.toggleDrawMode();
}
onClose() {
this.toolbarEventService.highlightToolbarSubject.next(false);
this.toolbarEventService.highlightModeSubject.next(false);
this.toolbarEventService.drawModeSubject.next(false);
this.toolbarEventService.openRedactionSearch.next({ modeType: SearchType.Highlight, isOpen: false });
}
onEscapeKey(event) {
event.preventDefault();
event.stopPropagation();
this.closeAndReturnFocus();
}
onArrowUp(event) {
const target = event.target;
const highlightToolbar = target.closest('.redaction');
if (highlightToolbar) {
this.returnFocusToMainToolbar();
}
}
closeAndReturnFocus() {
this.onClose();
this.returnFocusToMainToolbar();
}
returnFocusToMainToolbar() {
this.toolbarFocusService.focusToolbarButton('#mvHighlightBtn');
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: ToolbarFocusService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: HighlightToolbarComponent, isStandalone: false, selector: "mv-highlight-toolbar", host: { listeners: { "keydown.escape": "onEscapeKey($event)", "keydown.arrowup": "onArrowUp($event)" } }, ngImport: i0, template: "<div class=\"redaction\" [mvKeyboardNav]=\"'horizontal'\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >{{ \"Highlight options\" | rpxTranslate }}</label\n >\n\n <button\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n [class.toggled]=\"toolbarEventService.drawModeSubject | async\"\n aria-pressed=\"false\"\n data-l10n-id=\"mvDrawBtn\"\n (click)=\"onClickDrawToggle()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"draw_label\" aria-hidden=\"true\">{{ \"Draw a box\" | rpxTranslate }}</span>\n </button>\n\n <button\n id=\"highlightTextBtn\"\n class=\"mv-button mv-toolbar__menu-button--highlight mv-tooltip\"\n [attr.data-tooltip]=\"'Highlight text' | rpxTranslate\"\n [attr.aria-label]=\"'Highlight text' | rpxTranslate\"\n data-l10n-id=\"highlightTextBtn\"\n (click)=\"onHighlight()\"\n [class.toggled]=\"toolbarEventService.highlightModeSubject | async\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"highlightTextBtn_label\" aria-hidden=\"true\">{{ \"Highlight text\" | rpxTranslate }}</span>\n </button>\n\n <button\n id=\"mvHighlightFromSearchBtn\"\n [attr.data-tooltip]=\"'From search' | rpxTranslate\"\n [attr.aria-label]=\"'From search' | rpxTranslate\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search mv-tooltip\"\n (click)=\"onAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\" aria-hidden=\"true\"\n >{{ \"From search\" | rpxTranslate }}</span\n >\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close mv-tooltip\"\n [attr.data-tooltip]=\"'Close highlight' | rpxTranslate\"\n [attr.aria-label]=\"'Close highlight' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onClose()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\" aria-hidden=\"true\">{{ \"Close Highlight\" | rpxTranslate }}</span>\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: KeyboardNavDirective, selector: "[mvKeyboardNav]", inputs: ["mvKeyboardNav"], outputs: ["itemFocused", "itemActivated"] }, { kind: "directive", type: TooltipDismissDirective, selector: ".mv-tooltip, [mvTooltipDismiss]" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: HighlightToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-highlight-toolbar', standalone: false, template: "<div class=\"redaction\" [mvKeyboardNav]=\"'horizontal'\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >{{ \"Highlight options\" | rpxTranslate }}</label\n >\n\n <button\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw mv-tooltip\"\n [attr.data-tooltip]=\"'Draw a box' | rpxTranslate\"\n [attr.aria-label]=\"'Draw a box' | rpxTranslate\"\n [class.toggled]=\"toolbarEventService.drawModeSubject | async\"\n aria-pressed=\"false\"\n data-l10n-id=\"mvDrawBtn\"\n (click)=\"onClickDrawToggle()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"draw_label\" aria-hidden=\"true\">{{ \"Draw a box\" | rpxTranslate }}</span>\n </button>\n\n <button\n id=\"highlightTextBtn\"\n class=\"mv-button mv-toolbar__menu-button--highlight mv-tooltip\"\n [attr.data-tooltip]=\"'Highlight text' | rpxTranslate\"\n [attr.aria-label]=\"'Highlight text' | rpxTranslate\"\n data-l10n-id=\"highlightTextBtn\"\n (click)=\"onHighlight()\"\n [class.toggled]=\"toolbarEventService.highlightModeSubject | async\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"highlightTextBtn_label\" aria-hidden=\"true\">{{ \"Highlight text\" | rpxTranslate }}</span>\n </button>\n\n <button\n id=\"mvHighlightFromSearchBtn\"\n [attr.data-tooltip]=\"'From search' | rpxTranslate\"\n [attr.aria-label]=\"'From search' | rpxTranslate\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search mv-tooltip\"\n (click)=\"onAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\" aria-hidden=\"true\"\n >{{ \"From search\" | rpxTranslate }}</span\n >\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close mv-tooltip\"\n [attr.data-tooltip]=\"'Close highlight' | rpxTranslate\"\n [attr.aria-label]=\"'Close highlight' | rpxTranslate\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onClose()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\" aria-hidden=\"true\">{{ \"Close Highlight\" | rpxTranslate }}</span>\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: ToolbarFocusService }], propDecorators: { onEscapeKey: [{
type: HostListener,
args: ['keydown.escape', ['$event']]
}], onArrowUp: [{
type: HostListener,
args: ['keydown.arrowup', ['$event']]
}] } });
/*
* Gov UK Error Message
* Responsible for displaying in-line error messages
* @prop config - obj with properties
* @prop errorMessage - all error bject with messages property that is arry of strings.
* */
class GovUkErrorMessageComponent {
constructor() { }
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkErrorMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: GovUkErrorMessageComponent, isStandalone: false, selector: " mv-gov-uk-error-message", inputs: { config: "config", errorMessage: "errorMessage" }, ngImport: i0, template: `
<span class="govuk-error-message" [id]="config.id + '-error'" *ngFor="let message of errorMessage?.messages">
<span class="govuk-visually-hidden">Error:</span>{{message}}
</span>
`, isInline: true, dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkErrorMessageComponent, decorators: [{
type: Component,
args: [{
selector: ' mv-gov-uk-error-message',
template: `
<span class="govuk-error-message" [id]="config.id + '-error'" *ngFor="let message of errorMessage?.messages">
<span class="govuk-visually-hidden">Error:</span>{{message}}
</span>
`,
standalone: false
}]
}], ctorParameters: () => [], propDecorators: { config: [{
type: Input
}], errorMessage: [{
type: Input
}] } });
/*
* Gov Uk Fieldset Component
* Used to wrap group fieldset elements
* it can conditionally display h1 tag
* @param config
* @param isHeading
* @param errorMessage - used for aria tag
* */
class GovUkFieldsetComponent {
setDescribedBy() {
return HtmlTemplatesHelper.setDescribedBy(this.errorMessage, this.config);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkFieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: GovUkFieldsetComponent, isStandalone: false, selector: "mv-gov-uk-fieldset", inputs: { config: "config", isHeading: "isHeading", errorMessage: "errorMessage" }, ngImport: i0, template: `
<fieldset class="govuk-fieldset" [attr.aria-describedby]="setDescribedBy()">
<legend [class]="config.classes + ' govuk-fieldset__legend'" *ngIf="!isHeading">
{{config.legend}}
</legend>
<legend [class]="config.classes + ' govuk-fieldset__legend'" *ngIf="isHeading">
<h1>{{config.legend}}</h1>
</legend>
<span [id]="config.id+'-hint'" class="govuk-hint" *ngIf="config.hint">
{{config.hint}}
</span>
<ng-content></ng-content>
</fieldset>
`, isInline: true, dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkFieldsetComponent, decorators: [{
type: Component,
args: [{
selector: 'mv-gov-uk-fieldset',
template: `
<fieldset class="govuk-fieldset" [attr.aria-describedby]="setDescribedBy()">
<legend [class]="config.classes + ' govuk-fieldset__legend'" *ngIf="!isHeading">
{{config.legend}}
</legend>
<legend [class]="config.classes + ' govuk-fieldset__legend'" *ngIf="isHeading">
<h1>{{config.legend}}</h1>
</legend>
<span [id]="config.id+'-hint'" class="govuk-hint" *ngIf="config.hint">
{{config.hint}}
</span>
<ng-content></ng-content>
</fieldset>
`,
standalone: false
}]
}], propDecorators: { config: [{
type: Input
}], isHeading: [{
type: Input
}], errorMessage: [{
type: Input
}] } });
/*
* Gov UK Label component
* Responsible for displaying label tag
* @prop isPageHading - boolean to display h1
* @prop config - obj with properties
* */
class GovUkLabelComponent {
constructor() { }
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: GovUkLabelComponent, isStandalone: false, selector: "mv-gov-label", inputs: { config: "config" }, ngImport: i0, template: `<h1 *ngIf="config.isPageHeading else noHeading">
<label *ngIf="config.label" [class]="config.classes + ' govuk-label'"
[for]="config.id" [innerHTML]="config.label">
</label>
</h1>
<ng-template #noHeading>
<label *ngIf="config.label" [class]="config.classes + ' govuk-label'"
[for]="config.id" [innerHTML]="config.label">
</label>
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkLabelComponent, decorators: [{
type: Component,
args: [{
selector: 'mv-gov-label',
template: `<h1 *ngIf="config.isPageHeading else noHeading">
<label *ngIf="config.label" [class]="config.classes + ' govuk-label'"
[for]="config.id" [innerHTML]="config.label">
</label>
</h1>
<ng-template #noHeading>
<label *ngIf="config.label" [class]="config.classes + ' govuk-label'"
[for]="config.id" [innerHTML]="config.label">
</label>
</ng-template>
`,
standalone: false
}]
}], ctorParameters: () => [], propDecorators: { config: [{
type: Input
}] } });
/*
* Gov UK Date Component
* Responsible for displaying 3 input fields:
* day / month / year
* displaying errorMessage messages
* */
class GovUkDateComponent {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: GovUkDateComponent, isStandalone: false, selector: "mv-gov-uk-date", inputs: { config: "config", errorMessage: "errorMessage", formGroup: "formGroup" }, ngImport: i0, template: "<div\n class=\"govuk-form-group\"\n [ngClass]=\"{ 'govuk-form-group--error': errorMessage?.isInvalid }\"\n [formGroup]=\"formGroup\"\n>\n <mv-gov-uk-fieldset\n [config]=\"{\n legend: config.legend,\n classes: 'govuk-label--m',\n id: config.id,\n hint: ' For example, 12 11 2007'\n }\"\n [isHeading]=\"false\"\n [errorMessage]=\"errorMessage\"\n >\n <mv-gov-uk-error-message\n [config]=\"{ id: 'passport' }\"\n *ngIf=\"errorMessage.isInvalid\"\n [errorMessage]=\"errorMessage\"\n >\n </mv-gov-uk-error-message>\n\n <div class=\"govuk-date-input\" [id]=\"config.id + '-date'\">\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Day',\n name: config.id,\n id: config.id + '-day',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-2\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-day'\"\n [name]=\"config.id + '-day'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'day'\"\n />\n </div>\n </div>\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Month',\n name: config.id + '-month',\n id: config.id + '-month',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-2\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-month'\"\n [name]=\"config.id + '-month'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'month'\"\n />\n </div>\n </div>\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Year',\n name: config.id + '-year',\n id: config.id + '-year',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-4\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-year'\"\n [name]=\"config.id + '-year'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'year'\"\n />\n </div>\n </div>\n </div>\n </mv-gov-uk-fieldset>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: GovUkErrorMessageComponent, selector: " mv-gov-uk-error-message", inputs: ["config", "errorMessage"] }, { kind: "component", type: GovUkFieldsetComponent, selector: "mv-gov-uk-fieldset", inputs: ["config", "isHeading", "errorMessage"] }, { kind: "component", type: GovUkLabelComponent, selector: "mv-gov-label", inputs: ["config"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: GovUkDateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-gov-uk-date', standalone: false, template: "<div\n class=\"govuk-form-group\"\n [ngClass]=\"{ 'govuk-form-group--error': errorMessage?.isInvalid }\"\n [formGroup]=\"formGroup\"\n>\n <mv-gov-uk-fieldset\n [config]=\"{\n legend: config.legend,\n classes: 'govuk-label--m',\n id: config.id,\n hint: ' For example, 12 11 2007'\n }\"\n [isHeading]=\"false\"\n [errorMessage]=\"errorMessage\"\n >\n <mv-gov-uk-error-message\n [config]=\"{ id: 'passport' }\"\n *ngIf=\"errorMessage.isInvalid\"\n [errorMessage]=\"errorMessage\"\n >\n </mv-gov-uk-error-message>\n\n <div class=\"govuk-date-input\" [id]=\"config.id + '-date'\">\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Day',\n name: config.id,\n id: config.id + '-day',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-2\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-day'\"\n [name]=\"config.id + '-day'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'day'\"\n />\n </div>\n </div>\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Month',\n name: config.id + '-month',\n id: config.id + '-month',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-2\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-month'\"\n [name]=\"config.id + '-month'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'month'\"\n />\n </div>\n </div>\n <div class=\"govuk-date-input__item\">\n <div class=\"govuk-form-group\">\n <mv-gov-label\n [config]=\"{\n label: 'Year',\n name: config.id + '-year',\n id: config.id + '-year',\n classes: 'govuk-date-input__label'\n }\"\n >\n </mv-gov-label>\n <input\n class=\"govuk-input govuk-date-input__input govuk-input--width-4\"\n [ngClass]=\"{ 'govuk-input--error': errorMessage?.isInvalid }\"\n [id]=\"config.id + '-year'\"\n [name]=\"config.id + '-year'\"\n type=\"number\"\n value=\"\"\n pattern=\"[0-9]*\"\n [formControlName]=\"'year'\"\n />\n </div>\n </div>\n </div>\n </mv-gov-uk-fieldset>\n</div>\n" }]
}], propDecorators: { config: [{
type: Input
}], errorMessage: [{
type: Input
}], formGroup: [{
type: Input
}] } });
class CommentsSummaryComponent {
constructor(store, printService, toolbarEvents, fb) {
this.store = store;
this.printService = printService;
this.toolbarEvents = toolbarEvents;
this.fb = fb;
this.showFilters = false;
this.hasFilter = false;
this.fromFilterValid = true;
this.toFilterValid = true;
}
ngOnInit() {
this.filtersFg = this.fb.group({
dateRangeFrom: new FormGroup({
day: new FormControl('', [Validators.required]),
month: new FormControl('', [Validators.required]),
year: new FormControl('', [Validators.required])
}),
dateRangeTo: new FormGroup({
day: new FormControl('', [Validators.required]),
month: new FormControl('', [Validators.required]),
year: new FormControl('', [Validators.required])
}),
tagFilters: this.fb.group({}),
});
this.comments$ = this.store.pipe(select(getCommentSummary));
this.buildCheckBoxForm();
this.container.nativeElement.focus();
}
buildCheckBoxForm() {
this.filtersFg.reset();
const checkboxes = this.filtersFg.get('tagFilters');
const filters$ = this.store.pipe(select(getCommentSummaryFilters));
this.allTags$ = this.store.pipe(select(getAllTagsArr));
this.$subscriptions = combineLatest([this.allTags$, filters$]).subscribe(([tags, filters]) => {
this.hasFilter = filters.hasFilter;
tags.forEach((val) => {
const checkBoxValue = (filters.filters.tagFilters &&
filters.filters.tagFilters.hasOwnProperty(val.key)) ?
filters.filters.tagFilters[val.key] : false;
checkboxes.addControl(val.key, new FormControl(checkBoxValue));
});
this.filtersFg.updateValueAndValidity();
});
}
onClearFilters() {
this.store.dispatch(new ClearCommentSummaryFilters());
this.buildCheckBoxForm();
}
onFilter() {
const { value } = this.filtersFg;
const hasDateFrom = (value.dateRangeFrom.year && value.dateRangeFrom.month && value.dateRangeFrom.day);
const hasDateTo = (value.dateRangeTo.year && value.dateRangeTo.month && value.dateRangeTo.day);
const dateRangeFrom = hasDateFrom ?
new Date(value.dateRangeFrom.year, value.dateRangeFrom.month - 1, value.dateRangeFrom.day).getTime() : null;
const dateRangeTo = hasDateTo ?
new Date(value.dateRangeTo.year, value.dateRangeTo.month - 1, value.dateRangeTo.day).getTime() : null;
this.fromFilterValid = hasDateFrom ? true : false;
this.toFilterValid = hasDateTo ? true : false;
if (this.filtersFg.valid) {
this.store.dispatch(new ApplyCommentSymmaryFilter({ ...value, dateRangeFrom, dateRangeTo }));
}
}
onFiltersToggle() {
this.showFilters = !this.showFilters;
}
onClose() {
this.toolbarEvents.toggleCommentsSummary(false);
}
onPrint() {
this.printService.printElementNatively(this.commentsTable.nativeElement, 1000, 900);
}
navigateToPage(pageNumber) {
if (this.contentType === 'pdf') {
this.toolbarEvents.setPage(pageNumber);
}
this.toolbarEvents.toggleCommentsSummary(false);
this.toolbarEvents.toggleCommentsPanel(true);
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentsSummaryComponent, deps: [{ token: i1.Store }, { token: PrintService }, { token: ToolbarEventService }, { token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: CommentsSummaryComponent, isStandalone: false, selector: "mv-comments-summary", inputs: { title: "title", contentType: "contentType" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["outerContainer"], descendants: true, static: true }, { propertyName: "commentsTable", first: true, predicate: ["commentContainer"], descendants: true }], ngImport: i0, template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onClose()\">\n <div\n #outerContainer\n id=\"modal\"\n class=\"modal-content govuk-width-container clearfix\"\n (click)=\"$event.stopPropagation()\"\n cdkTrapFocus\n tabindex=\"0\"\n >\n <div id=\"comment-container\" class=\"comment-container\" #commentContainer>\n <h2 class=\"govuk-heading-m\">{{ title }}</h2>\n <ng-container *ngIf=\"comments$ | async as comments\">\n <ng-container *ngIf=\"comments.length\">\n <button\n type=\"submit\"\n class=\"govuk-button govuk-!-margin-right-1\"\n (click)=\"onFiltersToggle()\"\n >\n {{\n !showFilters\n ? (\"Show Filters\" | rpxTranslate)\n : (\"Hide Filters\" | rpxTranslate)\n }}\n </button>\n <button\n type=\"submit\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"onClearFilters()\"\n *ngIf=\"hasFilter\"\n >\n {{ \"Clear Filters\" | rpxTranslate }}\n </button>\n <ng-container *ngIf=\"showFilters\">\n <form [formGroup]=\"filtersFg\" (ngSubmit)=\"onFilter()\">\n <div class=\"dates\">\n <div class=\"dates__from\">\n <mv-gov-uk-date\n [config]=\"{ id: 'dateRangeFrom', legend: 'From' }\"\n [formGroup]=\"filtersFg.controls['dateRangeFrom']\"\n [errorMessage]=\"{\n isInvalid: !fromFilterValid,\n messages: ['From date is required']\n }\"\n ></mv-gov-uk-date>\n </div>\n <div class=\"dates__to\">\n <mv-gov-uk-date\n [config]=\"{ id: 'dateRangeTo', legend: 'To' }\"\n [formGroup]=\"filtersFg.controls['dateRangeTo']\"\n [errorMessage]=\"{\n isInvalid: !toFilterValid,\n messages: ['To date is required']\n }\"\n ></mv-gov-uk-date>\n </div>\n <div class=\"dates__tags\">\n <mv-gov-uk-fieldset\n [config]=\"{\n legend: 'Tags',\n classes: 'govuk-label--m',\n id: 'filters'\n }\"\n [isHeading]=\"false\"\n >\n <div\n class=\"govuk-checkboxes--scroll\"\n [formGroup]=\"filtersFg.controls['tagFilters']\"\n >\n <div class=\"govuk-checkboxes govuk-checkboxes--small\">\n <div\n class=\"govuk-checkboxes__item\"\n *ngFor=\"let item of allTags$ | async\"\n >\n <input\n [formControlName]=\"item.key\"\n class=\"govuk-checkboxes__input\"\n [value]=\"false\"\n type=\"checkbox\"\n />\n <label class=\"govuk-label govuk-checkboxes__label\">\n {{ item.key | unsnake }} ({{ item.length }})\n </label>\n </div>\n </div>\n </div>\n </mv-gov-uk-fieldset>\n </div>\n <div class=\"dates__cta\">\n <button type=\"submit\" class=\"govuk-button\">\n {{ \"Filter comments\" | rpxTranslate }}\n </button>\n </div>\n </div>\n </form>\n </ng-container>\n <ngx-datatable\n [rows]=\"comments\"\n rowHeight=\"auto\"\n [headerHeight]=\"50\"\n [columnMode]=\"'flex'\"\n [sorts]=\"[{ prop: 'date', dir: 'desc' }]\"\n >\n <ngx-datatable-column\n name=\"{{ 'User' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n [sortable]=\"false\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Page' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n >\n <ng-template ngx-datatable-cell-template let-value=\"value\">\n <a\n [routerLink]=\"[]\"\n class=\"comment-summary_pages\"\n (click)=\"navigateToPage(value)\"\n >{{ value }}</a\n >\n </ng-template>\n </ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Date' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Comment' | rpxTranslate }}\"\n [flexGrow]=\"3\"\n [sortable]=\"false\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Tags' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n [sortable]=\"false\"\n >\n <ng-template ngx-datatable-cell-template let-value=\"value\">\n <span class=\"hmcts-badge\" *ngFor=\"let tag of value\">{{\n tag.label\n }}</span>\n </ng-template>\n </ngx-datatable-column>\n </ngx-datatable>\n </ng-container>\n <ng-container *ngIf=\"!comments.length\">\n <h3 class=\"govuk-heading-s\">\n {{\n \"No comments available. Select text to add a highlight or comment.\"\n | rpxTranslate\n }}\n </h3>\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"button-container\">\n <button (click)=\"onPrint()\" class=\"govuk-button govuk-button--secondary\">\n {{ \"Print\" | rpxTranslate }}\n </button>\n <button id=\"modal-close-button\" (click)=\"onClose()\" class=\"govuk-button\">\n {{ \"Close\" | rpxTranslate }}\n </button>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i5$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i8$1.DatatableComponent, selector: "ngx-datatable", inputs: ["targetMarkerTemplate", "rows", "groupRowsBy", "groupedRows", "columns", "selected", "scrollbarV", "scrollbarVDynamic", "scrollbarH", "rowHeight", "columnMode", "headerHeight", "footerHeight", "externalPaging", "externalSorting", "limit", "count", "offset", "loadingIndicator", "ghostLoadingIndicator", "selectionType", "reorderable", "swapColumns", "sortType", "sorts", "cssClasses", "messages", "rowClass", "selectCheck", "displayCheck", "groupExpansionDefault", "trackByProp", "selectAllRowsOnPage", "virtualization", "treeFromRelation", "treeToRelation", "summaryRow", "summaryHeight", "summaryPosition", "disableRowCheck", "rowDraggable", "enableClearingSortState", "rowIdentity"], outputs: ["scroll", "activate", "select", "sort", "page", "reorder", "resize", "tableContextmenu", "treeAction", "rowDragEvents"] }, { kind: "directive", type: i8$1.DataTableColumnDirective, selector: "ngx-datatable-column", inputs: ["name", "prop", "bindAsUnsafeHtml", "frozenLeft", "frozenRight", "flexGrow", "resizeable", "comparator", "pipe", "sortable", "draggable", "canAutoResize", "minWidth", "width", "maxWidth", "checkboxable", "headerCheckboxable", "headerClass", "cellClass", "isTreeColumn", "treeLevelIndent", "summaryFunc", "summaryTemplate", "cellTemplate", "headerTemplate", "treeToggleTemplate", "ghostCellTemplate"] }, { kind: "directive", type: i8$1.DataTableColumnCellDirective, selector: "[ngx-datatable-cell-template]" }, { kind: "component", type: GovUkDateComponent, selector: "mv-gov-uk-date", inputs: ["config", "errorMessage", "formGroup"] }, { kind: "component", type: GovUkFieldsetComponent, selector: "mv-gov-uk-fieldset", inputs: ["config", "isHeading", "errorMessage"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }, { kind: "pipe", type: UnsnakePipe, name: "unsnake" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: CommentsSummaryComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comments-summary', standalone: false, template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onClose()\">\n <div\n #outerContainer\n id=\"modal\"\n class=\"modal-content govuk-width-container clearfix\"\n (click)=\"$event.stopPropagation()\"\n cdkTrapFocus\n tabindex=\"0\"\n >\n <div id=\"comment-container\" class=\"comment-container\" #commentContainer>\n <h2 class=\"govuk-heading-m\">{{ title }}</h2>\n <ng-container *ngIf=\"comments$ | async as comments\">\n <ng-container *ngIf=\"comments.length\">\n <button\n type=\"submit\"\n class=\"govuk-button govuk-!-margin-right-1\"\n (click)=\"onFiltersToggle()\"\n >\n {{\n !showFilters\n ? (\"Show Filters\" | rpxTranslate)\n : (\"Hide Filters\" | rpxTranslate)\n }}\n </button>\n <button\n type=\"submit\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"onClearFilters()\"\n *ngIf=\"hasFilter\"\n >\n {{ \"Clear Filters\" | rpxTranslate }}\n </button>\n <ng-container *ngIf=\"showFilters\">\n <form [formGroup]=\"filtersFg\" (ngSubmit)=\"onFilter()\">\n <div class=\"dates\">\n <div class=\"dates__from\">\n <mv-gov-uk-date\n [config]=\"{ id: 'dateRangeFrom', legend: 'From' }\"\n [formGroup]=\"filtersFg.controls['dateRangeFrom']\"\n [errorMessage]=\"{\n isInvalid: !fromFilterValid,\n messages: ['From date is required']\n }\"\n ></mv-gov-uk-date>\n </div>\n <div class=\"dates__to\">\n <mv-gov-uk-date\n [config]=\"{ id: 'dateRangeTo', legend: 'To' }\"\n [formGroup]=\"filtersFg.controls['dateRangeTo']\"\n [errorMessage]=\"{\n isInvalid: !toFilterValid,\n messages: ['To date is required']\n }\"\n ></mv-gov-uk-date>\n </div>\n <div class=\"dates__tags\">\n <mv-gov-uk-fieldset\n [config]=\"{\n legend: 'Tags',\n classes: 'govuk-label--m',\n id: 'filters'\n }\"\n [isHeading]=\"false\"\n >\n <div\n class=\"govuk-checkboxes--scroll\"\n [formGroup]=\"filtersFg.controls['tagFilters']\"\n >\n <div class=\"govuk-checkboxes govuk-checkboxes--small\">\n <div\n class=\"govuk-checkboxes__item\"\n *ngFor=\"let item of allTags$ | async\"\n >\n <input\n [formControlName]=\"item.key\"\n class=\"govuk-checkboxes__input\"\n [value]=\"false\"\n type=\"checkbox\"\n />\n <label class=\"govuk-label govuk-checkboxes__label\">\n {{ item.key | unsnake }} ({{ item.length }})\n </label>\n </div>\n </div>\n </div>\n </mv-gov-uk-fieldset>\n </div>\n <div class=\"dates__cta\">\n <button type=\"submit\" class=\"govuk-button\">\n {{ \"Filter comments\" | rpxTranslate }}\n </button>\n </div>\n </div>\n </form>\n </ng-container>\n <ngx-datatable\n [rows]=\"comments\"\n rowHeight=\"auto\"\n [headerHeight]=\"50\"\n [columnMode]=\"'flex'\"\n [sorts]=\"[{ prop: 'date', dir: 'desc' }]\"\n >\n <ngx-datatable-column\n name=\"{{ 'User' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n [sortable]=\"false\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Page' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n >\n <ng-template ngx-datatable-cell-template let-value=\"value\">\n <a\n [routerLink]=\"[]\"\n class=\"comment-summary_pages\"\n (click)=\"navigateToPage(value)\"\n >{{ value }}</a\n >\n </ng-template>\n </ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Date' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Comment' | rpxTranslate }}\"\n [flexGrow]=\"3\"\n [sortable]=\"false\"\n ></ngx-datatable-column>\n <ngx-datatable-column\n name=\"{{ 'Tags' | rpxTranslate }}\"\n [flexGrow]=\"1\"\n [sortable]=\"false\"\n >\n <ng-template ngx-datatable-cell-template let-value=\"value\">\n <span class=\"hmcts-badge\" *ngFor=\"let tag of value\">{{\n tag.label\n }}</span>\n </ng-template>\n </ngx-datatable-column>\n </ngx-datatable>\n </ng-container>\n <ng-container *ngIf=\"!comments.length\">\n <h3 class=\"govuk-heading-s\">\n {{\n \"No comments available. Select text to add a highlight or comment.\"\n | rpxTranslate\n }}\n </h3>\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"button-container\">\n <button (click)=\"onPrint()\" class=\"govuk-button govuk-button--secondary\">\n {{ \"Print\" | rpxTranslate }}\n </button>\n <button id=\"modal-close-button\" (click)=\"onClose()\" class=\"govuk-button\">\n {{ \"Close\" | rpxTranslate }}\n </button>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Store }, { type: PrintService }, { type: ToolbarEventService }, { type: i2.FormBuilder }], propDecorators: { title: [{
type: Input
}], contentType: [{
type: Input
}], container: [{
type: ViewChild,
args: ['outerContainer', { static: true }]
}], commentsTable: [{
type: ViewChild,
args: ['commentContainer', { static: false }]
}] } });
class MultimediaPlayerComponent {
constructor(toolbarEvents) {
this.toolbarEvents = toolbarEvents;
this.loadStatus = new EventEmitter();
this.playbackMsg = 'loading';
}
ngOnInit() {
this.subscription = this.toolbarEvents.downloadSubject
.subscribe(() => this.downloadLink.nativeElement.click());
this.loadStatus.emit(ResponseType.SUCCESS);
}
ngOnChanges(changes) {
if (changes.url) {
this.reloadVideo();
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
reloadVideo() {
if (this.videoPlayer) {
this.playbackMsg = 'loading';
this.videoPlayer.nativeElement.load();
}
}
onSuccess() {
this.playbackMsg = 'success';
}
onError() {
this.playbackMsg = 'error';
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MultimediaPlayerComponent, deps: [{ token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: MultimediaPlayerComponent, isStandalone: false, selector: "mv-multimedia-player", inputs: { url: "url", downloadFileName: "downloadFileName", multimediaOn: "multimediaOn" }, outputs: { loadStatus: "loadStatus" }, viewQueries: [{ propertyName: "downloadLink", first: true, predicate: ["downloadLink"], descendants: true }, { propertyName: "videoPlayer", first: true, predicate: ["videoPlayer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"govuk-width-container\">\n <div class=\"govuk-notification-banner__content\">\n <p>\n <span *ngIf=\"multimediaOn && playbackMsg === 'loading'\">{{\n \"Loading multimedia content ...\" | rpxTranslate\n }}</span>\n <span *ngIf=\"!multimediaOn\"\n >{{ \"Multimedia playback is not enabled, \" | rpxTranslate }}\n </span>\n <span *ngIf=\"multimediaOn && playbackMsg === 'success'\"\n >{{ \"Use the player to play to the file or\" | rpxTranslate }}\n </span>\n <span *ngIf=\"multimediaOn && playbackMsg === 'error'\"\n >{{ \"Mime type not supported.\" | rpxTranslate }}\n </span>\n <a\n *ngIf=\"!multimediaOn || playbackMsg !== 'loading'\"\n #downloadLink\n [href]=\"url\"\n [download]=\"downloadFileName\"\n >{{ \"Click here to download\" | rpxTranslate }}</a\n >\n </p>\n <video\n #videoPlayer\n *ngIf=\"multimediaOn\"\n width=\"100%\"\n height=\"100%\"\n preload=\"auto\"\n (canplay)=\"onSuccess()\"\n controls\n >\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/mp4\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/mp4; codecs='avc1, av01, hvc1, hev1, mp4v, mp4a.40.2, opus'\"\n />\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/ogg\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/ogg; codecs='theora, vp8, vp9, flac, opus, vorbis'\"\n />\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/webm\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/webm; codecs='vp8, vp9, vorbis, opus'\"\n />\n </video>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MultimediaPlayerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-multimedia-player', standalone: false, template: "<div class=\"govuk-width-container\">\n <div class=\"govuk-notification-banner__content\">\n <p>\n <span *ngIf=\"multimediaOn && playbackMsg === 'loading'\">{{\n \"Loading multimedia content ...\" | rpxTranslate\n }}</span>\n <span *ngIf=\"!multimediaOn\"\n >{{ \"Multimedia playback is not enabled, \" | rpxTranslate }}\n </span>\n <span *ngIf=\"multimediaOn && playbackMsg === 'success'\"\n >{{ \"Use the player to play to the file or\" | rpxTranslate }}\n </span>\n <span *ngIf=\"multimediaOn && playbackMsg === 'error'\"\n >{{ \"Mime type not supported.\" | rpxTranslate }}\n </span>\n <a\n *ngIf=\"!multimediaOn || playbackMsg !== 'loading'\"\n #downloadLink\n [href]=\"url\"\n [download]=\"downloadFileName\"\n >{{ \"Click here to download\" | rpxTranslate }}</a\n >\n </p>\n <video\n #videoPlayer\n *ngIf=\"multimediaOn\"\n width=\"100%\"\n height=\"100%\"\n preload=\"auto\"\n (canplay)=\"onSuccess()\"\n controls\n >\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/mp4\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/mp4; codecs='avc1, av01, hvc1, hev1, mp4v, mp4a.40.2, opus'\"\n />\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/ogg\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/ogg; codecs='theora, vp8, vp9, flac, opus, vorbis'\"\n />\n <source (error)=\"onError()\" [src]=\"url\" type=\"video/webm\" />\n <source\n (error)=\"onError()\"\n [src]=\"url\"\n type=\"video/webm; codecs='vp8, vp9, vorbis, opus'\"\n />\n </video>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }], propDecorators: { url: [{
type: Input
}], downloadFileName: [{
type: Input
}], multimediaOn: [{
type: Input
}], loadStatus: [{
type: Output
}], downloadLink: [{
type: ViewChild,
args: ['downloadLink', { static: false }]
}], videoPlayer: [{
type: ViewChild,
args: ['videoPlayer', { static: false }]
}] } });
class ConvertibleContentViewerComponent {
constructor(store) {
this.store = store;
this.mediaLoadStatus = new EventEmitter();
this.viewerException = new EventEmitter();
this.documentTitle = new EventEmitter();
}
ngOnInit() {
this.$subscription = this.store.pipe(select(getConvertedDocument), filter$1(value => !!value))
.subscribe((docInfo) => {
if (docInfo.url) {
this.convertedUrl = docInfo.url;
this.store.dispatch(new ClearConvertDocUrl());
}
else {
this.onLoadException(new ViewerException(docInfo.error));
}
});
}
ngOnChanges(changes) {
if (changes.originalUrl?.currentValue && changes.originalUrl.currentValue !== changes.originalUrl.previousValue) {
this.store.dispatch(new Convert(this.extractDMStoreDocId(this.originalUrl)));
}
}
ngOnDestroy() {
this.$subscription.unsubscribe();
}
onMediaLoad(status) {
this.mediaLoadStatus.emit(status);
}
onLoadException(exception) {
this.viewerException.emit(exception);
}
onDocumentTitleChange(title) {
this.documentTitle.emit(title);
}
// todo move this to common place for media viewer and pdf
extractDMStoreDocId(url) {
if (url.includes('/documents/')) {
url = url.split('/documents/')[1];
}
else if (url.includes('/documentsv2/')) {
url = url.split('/documentsv2/')[1];
}
return url.replace('/binary', '');
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ConvertibleContentViewerComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: ConvertibleContentViewerComponent, isStandalone: false, selector: "mv-conversion-viewer", inputs: { originalUrl: "originalUrl", downloadFileName: "downloadFileName", height: "height", enableAnnotations: "enableAnnotations", enableRedactions: "enableRedactions", annotationSet: "annotationSet" }, outputs: { mediaLoadStatus: "mediaLoadStatus", viewerException: "viewerException", documentTitle: "documentTitle" }, usesOnChanges: true, ngImport: i0, template: "<mv-pdf-viewer *ngIf=\"convertedUrl\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (pdfViewerException)=\"onLoadException($event)\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n [url]=\"convertedUrl\"\n [downloadUrl]=\"originalUrl\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [annotationSet]=\"enableAnnotations ? annotationSet : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"height\">\n</mv-pdf-viewer>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdfViewerComponent, selector: "mv-pdf-viewer", inputs: ["downloadUrl", "url", "downloadFileName", "enableAnnotations", "enableRedactions", "enableICP", "annotationSet", "enableRedactSearch", "height", "caseId", "searchBarHidden"], outputs: ["mediaLoadStatus", "pdfViewerException", "documentTitle"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ConvertibleContentViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-conversion-viewer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<mv-pdf-viewer *ngIf=\"convertedUrl\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (pdfViewerException)=\"onLoadException($event)\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n [url]=\"convertedUrl\"\n [downloadUrl]=\"originalUrl\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [annotationSet]=\"enableAnnotations ? annotationSet : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"height\">\n</mv-pdf-viewer>\n" }]
}], ctorParameters: () => [{ type: i1.Store }], propDecorators: { originalUrl: [{
type: Input
}], downloadFileName: [{
type: Input
}], height: [{
type: Input
}], enableAnnotations: [{
type: Input
}], enableRedactions: [{
type: Input
}], annotationSet: [{
type: Input
}], mediaLoadStatus: [{
type: Output
}], viewerException: [{
type: Output
}], documentTitle: [{
type: Output
}] } });
class RotationPersistDirective {
constructor(el, store, toolbarButtons, toolbarEvents) {
this.el = el;
this.store = store;
this.toolbarButtons = toolbarButtons;
this.toolbarEvents = toolbarEvents;
this.rotation = 0;
this.savedRotation = 0;
}
ngOnInit() {
this.$subscriptions = this.toolbarEvents.rotateSubject.pipe(distinctUntilChanged()).subscribe(rotation => this.onRotate(rotation));
this.$subscriptions.add(this.toolbarEvents.saveRotationSubject.subscribe(() => this.saveRotation()));
this.$subscriptions.add(this.store.pipe(select(getRotation)).subscribe(rotation => this.savedRotation = rotation));
this.$subscriptions.add(this.store.pipe(select(getDocumentId)).subscribe(documentId => this.documentId = documentId));
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
onMediaLoad(status) {
this.rotation = 0;
this.store.dispatch(new LoadRotation(this.documentId));
this.store.pipe(select(rotationLoaded), filter$1(value => !!value), take(1))
.subscribe(() => {
if (this.savedRotation) {
this.toolbarEvents.rotateSubject.next(this.savedRotation);
}
});
}
onRotate(rotation) {
this.rotation = (this.rotation + rotation) % 360;
this.toolbarButtons.showSaveRotationButton = this.savedRotation !== this.rotation;
}
saveRotation() {
const payload = {
documentId: this.documentId,
rotationAngle: this.rotation
};
this.store.dispatch(new SaveRotation(payload));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RotationPersistDirective, deps: [{ token: i0.ElementRef }, { token: i1.Store }, { token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.26", type: RotationPersistDirective, isStandalone: false, selector: "[mvRotationPersist]", host: { listeners: { "mediaLoadStatus": "onMediaLoad($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RotationPersistDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvRotationPersist]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }], propDecorators: { onMediaLoad: [{
type: HostListener,
args: ['mediaLoadStatus', ['$event']]
}] } });
class ConfirmActionDialogComponent {
constructor(icpEventService) {
this.icpEventService = icpEventService;
}
ngAfterViewInit() {
if (this.modalContainer) {
this.modalContainer.nativeElement.focus();
}
}
onCancel() {
this.icpEventService.leavingSession.next(false);
}
onConfirm() {
this.icpEventService.confirmExit();
this.icpEventService.leavingSession.next(false);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ConfirmActionDialogComponent, deps: [{ token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: ConfirmActionDialogComponent, isStandalone: false, selector: "mv-confirm-action", viewQueries: [{ propertyName: "modalContainer", first: true, predicate: ["modalContainer"], descendants: true }], ngImport: i0, template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onCancel()\">\n <div id=\"modal\" \n class=\"modal-content govuk-width-container clearfix\"\n (click)=\"$event.stopPropagation()\"\n #modalContainer\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <h2 class=\"govuk-heading-s\">Are you sure you want to leave the presentation?</h2>\n <div class=\"button-container\">\n <button id=\"modal-close-button\" (click)=\"onConfirm()\" class=\"govuk-button\">Confirm</button>\n <button (click)=\"onCancel()\" class=\"govuk-button govuk-button--secondary\">Cancel</button>\n </div>\n </div>\n</div>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ConfirmActionDialogComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-confirm-action', standalone: false, template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onCancel()\">\n <div id=\"modal\" \n class=\"modal-content govuk-width-container clearfix\"\n (click)=\"$event.stopPropagation()\"\n #modalContainer\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <h2 class=\"govuk-heading-s\">Are you sure you want to leave the presentation?</h2>\n <div class=\"button-container\">\n <button id=\"modal-close-button\" (click)=\"onConfirm()\" class=\"govuk-button\">Confirm</button>\n <button (click)=\"onCancel()\" class=\"govuk-button govuk-button--secondary\">Cancel</button>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: IcpEventService }], propDecorators: { modalContainer: [{
type: ViewChild,
args: ['modalContainer']
}] } });
var CoreContentTypes;
(function (CoreContentTypes) {
CoreContentTypes["PDF"] = "pdf";
CoreContentTypes["IMAGE"] = "image";
})(CoreContentTypes || (CoreContentTypes = {}));
var MultimediaContentTypes;
(function (MultimediaContentTypes) {
MultimediaContentTypes["MP4"] = "mp4";
MultimediaContentTypes["MP3"] = "mp3";
})(MultimediaContentTypes || (MultimediaContentTypes = {}));
var ConvertibleContentTypes;
(function (ConvertibleContentTypes) {
ConvertibleContentTypes["EXCEL"] = "excel";
ConvertibleContentTypes["WORD"] = "word";
ConvertibleContentTypes["POWERPOINT"] = "powerpoint";
ConvertibleContentTypes["TXT"] = "txt";
ConvertibleContentTypes["RTF"] = "rtf";
})(ConvertibleContentTypes || (ConvertibleContentTypes = {}));
class MediaViewerComponent {
constructor(store, toolbarButtons, toolbarEvents, api, commentService, elRef, cdr, icpEventService) {
this.store = store;
this.toolbarButtons = toolbarButtons;
this.toolbarEvents = toolbarEvents;
this.api = api;
this.commentService = commentService;
this.elRef = elRef;
this.cdr = cdr;
this.icpEventService = icpEventService;
this.showToolbar = true;
this.toolbarButtonOverrides = {};
this.width = '100%';
this.mediaLoadStatus = new EventEmitter();
this.viewerException = new EventEmitter();
this.toolbarEventsOutput = new EventEmitter();
this.unsavedChanges = new EventEmitter();
this.enableAnnotations = false;
this.enableRedactions = false;
this.enableICP = false;
this.multimediaPlayerEnabled = false;
this.enableRedactSearch = false;
this.multimediaContent = false;
this.convertibleContent = false;
this.unsupportedContent = false;
this.typeException = false;
this.currentRegionIndex = -1;
if (this.annotationApiUrl) {
api.annotationApiUrl = this.annotationApiUrl;
}
}
ngAfterContentInit() {
this.annotationSet$ = this.store.pipe(select(getAnnotationSet));
this.hasDifferentPageSize$ = this.store.pipe(select(getPageDifference));
this.setToolbarButtons();
this.toolbarEventsOutput.emit(this.toolbarEvents);
this.$subscriptions = this.commentService.getUnsavedChanges()
.subscribe(changes => this.onCommentChange(changes));
this.$subscriptions.add(this.toolbarEvents.getShowCommentSummary()
.subscribe(changes => this.showCommentSummary = changes));
}
ngAfterViewChecked() {
if (this.height && this.viewerHeight !== this.height) {
this.viewerHeight = this.height;
this.cdr.detectChanges();
return;
}
if (!this.height) {
const compOffsetTop = HtmlTemplatesHelper.getAdjustedBoundingRect(this.elRef.nativeElement).top;
const viewerOffsetTop = this.viewerRef.nativeElement.offsetTop;
const offset = compOffsetTop + viewerOffsetTop;
if (this.prevOffset !== offset) {
this.viewerHeight = `calc(100vh - ${offset}px)`;
this.prevOffset = offset;
this.cdr.detectChanges();
}
}
}
ngOnChanges(changes) {
if (changes.annotationApiUrl) {
this.api.annotationApiUrl = this.annotationApiUrl;
}
if (changes.contentType) {
this.convertibleContent = this.needsConverting();
this.multimediaContent = this.isMultimedia();
this.unsupportedContent = !this.isSupported();
}
if (changes.url) {
this.toolbarEvents.reset();
this.commentService.resetCommentSet();
this.documentId = this.extractDMStoreDocId(this.url);
this.store.dispatch(new SetDocumentId(this.documentId));
if (this.enableAnnotations && !(this.multimediaContent || this.unsupportedContent)) {
this.store.dispatch(new LoadAnnotationSet(this.documentId));
}
if (this.enableRedactions && !(this.multimediaContent || this.unsupportedContent) && this.documentId) {
this.store.dispatch(new LoadRedactions(this.documentId));
}
if (this.contentType === 'image') {
this.documentTitle = null;
}
}
this.setToolbarButtons();
this.detectOs();
this.typeException = false;
}
ngOnDestroy() {
this.$subscriptions.unsubscribe();
}
needsConverting() {
return this.contentType !== null && Object.keys(ConvertibleContentTypes).includes(this.contentType.toUpperCase());
}
isMultimedia() {
return this.contentType !== null && Object.keys(MultimediaContentTypes).includes(this.contentType.toUpperCase());
}
isSupported() {
const supportedTypes = Object.assign({}, MultimediaContentTypes, ConvertibleContentTypes, CoreContentTypes);
return this.contentType !== null && Object.keys(supportedTypes).includes(this.contentType.toUpperCase());
}
onMediaLoad(status) {
this.mediaLoadStatus.emit(status);
}
setToolbarButtons() {
if (this.contentType === CoreContentTypes.PDF || this.needsConverting()) {
this.toolbarButtons.setup({
...defaultPdfOptions, showHighlightButton: this.enableAnnotations, showDrawButton: false,
...this.toolbarButtonOverrides
});
}
else if (this.contentType === CoreContentTypes.IMAGE) {
this.toolbarButtons.setup({
...defaultImageOptions, showDrawButton: this.enableAnnotations,
...this.toolbarButtonOverrides
});
}
else if (this.isMultimedia()) {
this.toolbarButtons.setup({
...defaultMultimediaOptions,
...this.toolbarButtonOverrides
});
}
else {
this.toolbarButtons.setup({
...defaultUnsupportedOptions,
...this.toolbarButtonOverrides
});
}
}
onLoadException(exception) {
this.viewerException.emit(exception);
if (!this.isSupported()) {
this.typeException = false;
}
else {
this.typeException = true;
this.contentType = null;
this.setToolbarButtons();
}
}
onCommentChange(changes) {
this.unsavedChanges.emit(changes);
}
onDocumentTitleChange(title) {
this.documentTitle = title;
}
// If secure mode is enabled (which adds "documentsv2" to the documentId), get rid of it
extractDMStoreDocId(url) {
url = url.includes('/documents/') ? url.split('/documents/')[1] : url;
url = url.includes('/documentsv2/') ? url.split('/documentsv2/')[1] : url;
return url.replace('/binary', '');
}
detectOs() {
this.hasScrollBar = window.navigator.userAgent.indexOf('Win') !== -1;
}
skipToSidebar(event) {
event.preventDefault();
this.openSidebarAndWait(() => {
const element = document.querySelector('#sidebarContent');
if (element) {
if (!element.hasAttribute('tabindex')) {
element.setAttribute('tabindex', '-1');
}
element.focus();
}
});
}
openSidebarAndWait(callback) {
const isOpen = this.toolbarEvents.sidebarOpen.getValue();
if (!isOpen) {
this.toolbarEvents.toggleSideBar(true);
}
this.toolbarEvents.sidebarOpen.pipe(filter(open => open === true), take$1(1), delay(0)).subscribe(() => {
callback();
});
}
skipToViewer(event) {
event.preventDefault();
const element = document.querySelector('#viewerContainer');
if (element) {
element.focus();
}
}
handleF6Forward(event) {
event.preventDefault();
this.cycleRegion('forward');
}
handleF6Backward(event) {
event.preventDefault();
this.cycleRegion('backward');
}
cycleRegion(direction) {
const regions = [
{ selector: '#toolbarContainer', label: 'Main toolbar', isVisible: () => this.showToolbar },
{ selector: '#sidebarContent', label: 'Index menu', isVisible: () => true },
{ selector: '#viewerContainer', label: 'Document viewer', isVisible: () => true }
];
const visibleRegions = regions.filter(r => r.isVisible());
if (visibleRegions.length === 0)
return;
const previousRegion = visibleRegions[this.currentRegionIndex];
if (direction === 'forward') {
this.currentRegionIndex = (this.currentRegionIndex + 1) % visibleRegions.length;
}
else {
this.currentRegionIndex = this.currentRegionIndex <= 0
? visibleRegions.length - 1
: this.currentRegionIndex - 1;
}
const currentRegion = visibleRegions[this.currentRegionIndex];
if (previousRegion?.selector === '#sidebarContent' && currentRegion.selector !== '#sidebarContent') {
const isOpen = this.toolbarEvents.sidebarOpen.getValue();
if (isOpen) {
this.toolbarEvents.toggleSideBar(false);
}
}
if (currentRegion.selector === '#sidebarContent') {
this.openSidebarAndWait(() => {
this.focusRegion(currentRegion);
});
}
else {
this.focusRegion(currentRegion);
}
}
focusRegion(region) {
const element = document.querySelector(region.selector);
if (!element) {
return;
}
const firstFocusable = element.querySelector('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])');
if (firstFocusable) {
firstFocusable.focus();
}
else {
if (!element.hasAttribute('tabindex')) {
element.setAttribute('tabindex', '-1');
}
element.focus();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerComponent, deps: [{ token: i1.Store }, { token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }, { token: AnnotationApiService }, { token: CommentService }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: MediaViewerComponent, isStandalone: false, selector: "mv-media-viewer", inputs: { url: "url", downloadFileName: "downloadFileName", contentType: "contentType", showToolbar: "showToolbar", toolbarButtonOverrides: "toolbarButtonOverrides", height: "height", width: "width", enableAnnotations: "enableAnnotations", annotationApiUrl: "annotationApiUrl", enableRedactions: "enableRedactions", enableICP: "enableICP", multimediaPlayerEnabled: "multimediaPlayerEnabled", enableRedactSearch: "enableRedactSearch", caseId: "caseId" }, outputs: { mediaLoadStatus: "mediaLoadStatus", viewerException: "viewerException", toolbarEventsOutput: "toolbarEventsOutput", unsavedChanges: "unsavedChanges" }, host: { listeners: { "document:keydown.F6": "handleF6Forward($event)", "document:keydown.shift.F6": "handleF6Backward($event)" } }, viewQueries: [{ propertyName: "viewerRef", first: true, predicate: ["viewerRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n id=\"outerContainer\"\n [ngClass]=\"{\n 'has-redact-bar': toolbarEvents.redactionMode | async,\n 'icp-mode': icpEventService.enabled | async,\n 'is-redaction-preview': toolbarEvents.redactionPreview | async,\n sidebarOpen: toolbarEvents.sidebarOpen | async,\n 'has-scroll-bar': hasScrollBar,\n 'has-different-page-size': hasDifferentPageSize$ | async\n }\"\n [ngStyle]=\"{ width: width }\"\n>\n <div class=\"govuk-skip-link-container\">\n <a href=\"#sidebarContent\" class=\"mv-govuk-skip-link\" (click)=\"skipToSidebar($event)\">Skip to index menu</a>\n <a href=\"#viewerContainer\" class=\"mv-govuk-skip-link\" (click)=\"skipToViewer($event)\">Skip to document</a>\n </div>\n <mv-comments-summary\n *ngIf=\"showCommentSummary\"\n [title]=\"documentTitle || 'Comment Summary'\"\n [contentType]=\"contentType\"\n >\n </mv-comments-summary>\n\n <mv-confirm-action\n *ngIf=\"icpEventService.leavingSession | async\"\n ></mv-confirm-action>\n\n <div id=\"mainContainer\">\n <mv-main-toolbar\n *ngIf=\"showToolbar\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableICP]=\"enableICP\"\n [enableRedactions]=\"enableRedactions\"\n [contentType]=\"contentType\"\n >\n </mv-main-toolbar>\n <mv-redaction-toolbar\n [showRedactSearch]=\"enableRedactSearch\"\n *ngIf=\"toolbarEvents.redactionMode | async\"\n ></mv-redaction-toolbar>\n <mv-highlight-toolbar\n *ngIf=\"toolbarEvents.highlightToolbarSubject | async\"\n ></mv-highlight-toolbar>\n <mv-icp-toolbar *ngIf=\"icpEventService.enabled | async\"></mv-icp-toolbar>\n\n <div #viewerRef>\n <mv-conversion-viewer\n *ngIf=\"convertibleContent\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (viewerException)=\"onLoadException($event)\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [originalUrl]=\"url\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n mvRotationPersist\n >\n </mv-conversion-viewer>\n <mv-pdf-viewer\n *ngIf=\"contentType === 'pdf'\"\n #pdfViewer\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (pdfViewerException)=\"onLoadException($event)\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n [url]=\"url\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [enableICP]=\"enableICP\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n [caseId]=\"caseId\"\n mvRotationPersist\n >\n </mv-pdf-viewer>\n <mv-image-viewer\n *ngIf=\"contentType === 'image'\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (imageViewerException)=\"onLoadException($event)\"\n [url]=\"url\"\n [enableAnnotations]=\"enableAnnotations\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n mvRotationPersist\n >\n </mv-image-viewer>\n <mv-multimedia-player\n *ngIf=\"multimediaContent\"\n [multimediaOn]=\"multimediaPlayerEnabled\"\n [url]=\"url\"\n [downloadFileName]=\"downloadFileName\"\n (loadStatus)=\"onMediaLoad($event)\"\n >\n </mv-multimedia-player>\n <mv-unsupported-viewer\n *ngIf=\"unsupportedContent\"\n [url]=\"url\"\n [typeException]=\"typeException\"\n [downloadFileName]=\"downloadFileName\"\n (loadStatus)=\"onMediaLoad($event)\"\n (unsupportedViewerException)=\"onLoadException($event)\"\n >\n </mv-unsupported-viewer>\n </div>\n </div>\n</div>\n", styles: [".govuk-skip-link-container{position:inline-block;top:0;left:0;width:100%;z-index:101;background:transparent;pointer-events:none;display:flex;flex-direction:row;gap:0}.govuk-skip-link-container .mv-govuk-skip-link{display:inline-block;font-size:0px;height:0px;margin:0;padding:0;width:0;flex-shrink:0;pointer-events:auto}.govuk-skip-link-container .mv-govuk-skip-link:link,.govuk-skip-link-container .mv-govuk-skip-link:visited{color:#0b0c0c}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus),.govuk-skip-link-container:has(.mv-govuk-skip-link:active){background-color:#fd0}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link{font-size:16px;padding:10px 15px;width:auto;min-height:41px;height:auto;display:inline-flex;align-items:center;text-decoration:underline;background-color:transparent;color:#0b0c0c;position:static;clip:auto;clip-path:none;overflow:visible}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link:focus,.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link:active,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link:focus,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link:active{text-decoration:underline;text-decoration-thickness:3px}#outerContainer:has(.mv-govuk-skip-link:focus) mv-side-bar,#outerContainer:has(.mv-govuk-skip-link:active) mv-side-bar{top:101px}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: MainToolbarComponent, selector: "mv-main-toolbar", inputs: ["enableAnnotations", "enableRedactions", "enableICP", "contentType"] }, { kind: "component", type: RedactionToolbarComponent, selector: "mv-redaction-toolbar", inputs: ["showRedactSearch"] }, { kind: "component", type: IcpToolbarComponent, selector: "mv-icp-toolbar" }, { kind: "component", type: HighlightToolbarComponent, selector: "mv-highlight-toolbar" }, { kind: "component", type: CommentsSummaryComponent, selector: "mv-comments-summary", inputs: ["title", "contentType"] }, { kind: "component", type: PdfViewerComponent, selector: "mv-pdf-viewer", inputs: ["downloadUrl", "url", "downloadFileName", "enableAnnotations", "enableRedactions", "enableICP", "annotationSet", "enableRedactSearch", "height", "caseId", "searchBarHidden"], outputs: ["mediaLoadStatus", "pdfViewerException", "documentTitle"] }, { kind: "component", type: ImageViewerComponent, selector: "mv-image-viewer", inputs: ["url", "downloadFileName", "enableAnnotations", "annotationSet", "height"], outputs: ["mediaLoadStatus", "imageViewerException"] }, { kind: "component", type: UnsupportedViewerComponent, selector: "mv-unsupported-viewer", inputs: ["url", "downloadFileName", "typeException"], outputs: ["loadStatus", "unsupportedViewerException"] }, { kind: "component", type: MultimediaPlayerComponent, selector: "mv-multimedia-player", inputs: ["url", "downloadFileName", "multimediaOn"], outputs: ["loadStatus"] }, { kind: "component", type: ConvertibleContentViewerComponent, selector: "mv-conversion-viewer", inputs: ["originalUrl", "downloadFileName", "height", "enableAnnotations", "enableRedactions", "annotationSet"], outputs: ["mediaLoadStatus", "viewerException", "documentTitle"] }, { kind: "directive", type: RotationPersistDirective, selector: "[mvRotationPersist]" }, { kind: "component", type: ConfirmActionDialogComponent, selector: "mv-confirm-action" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-media-viewer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<div\n id=\"outerContainer\"\n [ngClass]=\"{\n 'has-redact-bar': toolbarEvents.redactionMode | async,\n 'icp-mode': icpEventService.enabled | async,\n 'is-redaction-preview': toolbarEvents.redactionPreview | async,\n sidebarOpen: toolbarEvents.sidebarOpen | async,\n 'has-scroll-bar': hasScrollBar,\n 'has-different-page-size': hasDifferentPageSize$ | async\n }\"\n [ngStyle]=\"{ width: width }\"\n>\n <div class=\"govuk-skip-link-container\">\n <a href=\"#sidebarContent\" class=\"mv-govuk-skip-link\" (click)=\"skipToSidebar($event)\">Skip to index menu</a>\n <a href=\"#viewerContainer\" class=\"mv-govuk-skip-link\" (click)=\"skipToViewer($event)\">Skip to document</a>\n </div>\n <mv-comments-summary\n *ngIf=\"showCommentSummary\"\n [title]=\"documentTitle || 'Comment Summary'\"\n [contentType]=\"contentType\"\n >\n </mv-comments-summary>\n\n <mv-confirm-action\n *ngIf=\"icpEventService.leavingSession | async\"\n ></mv-confirm-action>\n\n <div id=\"mainContainer\">\n <mv-main-toolbar\n *ngIf=\"showToolbar\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableICP]=\"enableICP\"\n [enableRedactions]=\"enableRedactions\"\n [contentType]=\"contentType\"\n >\n </mv-main-toolbar>\n <mv-redaction-toolbar\n [showRedactSearch]=\"enableRedactSearch\"\n *ngIf=\"toolbarEvents.redactionMode | async\"\n ></mv-redaction-toolbar>\n <mv-highlight-toolbar\n *ngIf=\"toolbarEvents.highlightToolbarSubject | async\"\n ></mv-highlight-toolbar>\n <mv-icp-toolbar *ngIf=\"icpEventService.enabled | async\"></mv-icp-toolbar>\n\n <div #viewerRef>\n <mv-conversion-viewer\n *ngIf=\"convertibleContent\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (viewerException)=\"onLoadException($event)\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [originalUrl]=\"url\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n mvRotationPersist\n >\n </mv-conversion-viewer>\n <mv-pdf-viewer\n *ngIf=\"contentType === 'pdf'\"\n #pdfViewer\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (pdfViewerException)=\"onLoadException($event)\"\n (documentTitle)=\"onDocumentTitleChange($event)\"\n [url]=\"url\"\n [enableAnnotations]=\"enableAnnotations\"\n [enableRedactions]=\"enableRedactions\"\n [enableICP]=\"enableICP\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n [caseId]=\"caseId\"\n mvRotationPersist\n >\n </mv-pdf-viewer>\n <mv-image-viewer\n *ngIf=\"contentType === 'image'\"\n (mediaLoadStatus)=\"onMediaLoad($event)\"\n (imageViewerException)=\"onLoadException($event)\"\n [url]=\"url\"\n [enableAnnotations]=\"enableAnnotations\"\n [annotationSet]=\"enableAnnotations ? (annotationSet$ | async) : null\"\n [downloadFileName]=\"downloadFileName\"\n [height]=\"viewerHeight\"\n mvRotationPersist\n >\n </mv-image-viewer>\n <mv-multimedia-player\n *ngIf=\"multimediaContent\"\n [multimediaOn]=\"multimediaPlayerEnabled\"\n [url]=\"url\"\n [downloadFileName]=\"downloadFileName\"\n (loadStatus)=\"onMediaLoad($event)\"\n >\n </mv-multimedia-player>\n <mv-unsupported-viewer\n *ngIf=\"unsupportedContent\"\n [url]=\"url\"\n [typeException]=\"typeException\"\n [downloadFileName]=\"downloadFileName\"\n (loadStatus)=\"onMediaLoad($event)\"\n (unsupportedViewerException)=\"onLoadException($event)\"\n >\n </mv-unsupported-viewer>\n </div>\n </div>\n</div>\n", styles: [".govuk-skip-link-container{position:inline-block;top:0;left:0;width:100%;z-index:101;background:transparent;pointer-events:none;display:flex;flex-direction:row;gap:0}.govuk-skip-link-container .mv-govuk-skip-link{display:inline-block;font-size:0px;height:0px;margin:0;padding:0;width:0;flex-shrink:0;pointer-events:auto}.govuk-skip-link-container .mv-govuk-skip-link:link,.govuk-skip-link-container .mv-govuk-skip-link:visited{color:#0b0c0c}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus),.govuk-skip-link-container:has(.mv-govuk-skip-link:active){background-color:#fd0}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link{font-size:16px;padding:10px 15px;width:auto;min-height:41px;height:auto;display:inline-flex;align-items:center;text-decoration:underline;background-color:transparent;color:#0b0c0c;position:static;clip:auto;clip-path:none;overflow:visible}.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link:focus,.govuk-skip-link-container:has(.mv-govuk-skip-link:focus) .mv-govuk-skip-link:active,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link:focus,.govuk-skip-link-container:has(.mv-govuk-skip-link:active) .mv-govuk-skip-link:active{text-decoration:underline;text-decoration-thickness:3px}#outerContainer:has(.mv-govuk-skip-link:focus) mv-side-bar,#outerContainer:has(.mv-govuk-skip-link:active) mv-side-bar{top:101px}\n"] }]
}], ctorParameters: () => [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: AnnotationApiService }, { type: CommentService }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: IcpEventService }], propDecorators: { viewerRef: [{
type: ViewChild,
args: ['viewerRef', { static: false }]
}], url: [{
type: Input
}], downloadFileName: [{
type: Input
}], contentType: [{
type: Input
}], showToolbar: [{
type: Input
}], toolbarButtonOverrides: [{
type: Input
}], height: [{
type: Input
}], width: [{
type: Input
}], mediaLoadStatus: [{
type: Output
}], viewerException: [{
type: Output
}], toolbarEventsOutput: [{
type: Output
}], unsavedChanges: [{
type: Output
}], enableAnnotations: [{
type: Input
}], annotationApiUrl: [{
type: Input
}], enableRedactions: [{
type: Input
}], enableICP: [{
type: Input
}], multimediaPlayerEnabled: [{
type: Input
}], enableRedactSearch: [{
type: Input
}], caseId: [{
type: Input
}], handleF6Forward: [{
type: HostListener,
args: ['document:keydown.F6', ['$event']]
}], handleF6Backward: [{
type: HostListener,
args: ['document:keydown.shift.F6', ['$event']]
}] } });
class SharedModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: SharedModule, declarations: [GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent,
KeyboardNavDirective], imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule], exports: [GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent,
KeyboardNavDirective] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SharedModule, imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: SharedModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule
],
declarations: [
GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent,
KeyboardNavDirective
],
exports: [
GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent,
KeyboardNavDirective
]
}]
}] });
class ToolbarModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
MainToolbarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent,
TooltipDismissDirective], imports: [CommonModule,
FormsModule,
OverlayModule,
RouterModule, i5.RpxTranslationModule, SharedModule], exports: [MainToolbarComponent,
SearchBarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarModule, providers: [
ToolbarButtonVisibilityService,
ToolbarFocusService
], imports: [CommonModule,
FormsModule,
OverlayModule,
RouterModule,
RpxTranslationModule.forChild(),
SharedModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: ToolbarModule, decorators: [{
type: NgModule,
args: [{
declarations: [
SearchBarComponent,
MainToolbarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent,
TooltipDismissDirective
],
providers: [
ToolbarButtonVisibilityService,
ToolbarFocusService
],
exports: [
MainToolbarComponent,
SearchBarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent
],
imports: [
CommonModule,
FormsModule,
OverlayModule,
RouterModule,
RpxTranslationModule.forChild(),
SharedModule
]
}]
}] });
class BookmarksApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.ANNOTATIONS_API = '/em-anno';
}
getBookmarks(documentId) {
return this.httpClient
.get(`${this.ANNOTATIONS_API}/${documentId}/bookmarks`, { observe: 'response', withCredentials: true });
}
createBookmark(bookmark) {
return this.httpClient
.post(`${this.ANNOTATIONS_API}/bookmarks`, bookmark, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
updateBookmark(bookmark) {
return this.httpClient
.put(`${this.ANNOTATIONS_API}/bookmarks`, bookmark, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
updateMultipleBookmarks(bookmarks) {
return this.httpClient
.put(`${this.ANNOTATIONS_API}/bookmarks_multiple`, bookmarks, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
deleteBookmark(bookmarkId) {
return this.httpClient
.delete(`${this.ANNOTATIONS_API}/bookmarks/${bookmarkId}`, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
deleteMultipleBookmarks(payload) {
return this.httpClient
.request('DELETE', `${this.ANNOTATIONS_API}/bookmarks_multiple`, { body: payload, observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class AnnotationsModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: AnnotationsModule, declarations: [MetadataLayerComponent,
AnnotationViewComponent,
BoxHighlightCreateComponent,
AnnotationSetComponent,
RectangleComponent,
CommentComponent,
CommentSetHeaderComponent,
CommentSearchComponent,
TextHighlightDirective,
CtxToolbarComponent,
CommentSetComponent,
CommentsNavigateComponent,
TextareaAutoExpandDirective,
CommentsSummaryComponent,
TagsComponent,
MomentDatePipe,
CommentFilterComponent,
FilterPipe,
UnsnakePipe,
KeyboardBoxDrawDirective,
KeyboardBoxMoveDirective], imports: [A11yModule,
CommonModule,
FormsModule,
HttpClientModule,
MutableDivModule,
TagInputModule,
RouterModule,
ReactiveFormsModule,
NgxDatatableModule,
SharedModule, i5.RpxTranslationModule], exports: [AnnotationViewComponent,
BoxHighlightCreateComponent,
AnnotationSetComponent,
CommentSetComponent,
CommentSetHeaderComponent,
CommentSearchComponent,
CommentsSummaryComponent,
TagsComponent,
CommentFilterComponent,
MomentDatePipe,
FilterPipe,
UnsnakePipe,
MetadataLayerComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationsModule, providers: [
AnnotationApiService,
BookmarksApiService,
CommentSetRenderService,
HighlightCreateService,
CommentService,
TagsServices
], imports: [A11yModule,
CommonModule,
FormsModule,
HttpClientModule,
MutableDivModule,
TagInputModule,
RouterModule,
ReactiveFormsModule,
NgxDatatableModule,
SharedModule,
RpxTranslationModule.forChild()] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationsModule, decorators: [{
type: NgModule,
args: [{
imports: [
A11yModule,
CommonModule,
FormsModule,
HttpClientModule,
MutableDivModule,
TagInputModule,
RouterModule,
ReactiveFormsModule,
NgxDatatableModule,
SharedModule,
RpxTranslationModule.forChild()
],
declarations: [
MetadataLayerComponent,
AnnotationViewComponent,
BoxHighlightCreateComponent,
AnnotationSetComponent,
RectangleComponent,
CommentComponent,
CommentSetHeaderComponent,
CommentSearchComponent,
TextHighlightDirective,
CtxToolbarComponent,
CommentSetComponent,
CommentsNavigateComponent,
TextareaAutoExpandDirective,
CommentsSummaryComponent,
TagsComponent,
MomentDatePipe,
CommentFilterComponent,
FilterPipe,
UnsnakePipe,
KeyboardBoxDrawDirective,
KeyboardBoxMoveDirective
],
providers: [
AnnotationApiService,
BookmarksApiService,
CommentSetRenderService,
HighlightCreateService,
CommentService,
TagsServices
],
exports: [
AnnotationViewComponent,
BoxHighlightCreateComponent,
AnnotationSetComponent,
CommentSetComponent,
CommentSetHeaderComponent,
CommentSearchComponent,
CommentsSummaryComponent,
TagsComponent,
CommentFilterComponent,
MomentDatePipe,
FilterPipe,
UnsnakePipe,
MetadataLayerComponent
]
}]
}] });
class AnnotationEffects {
constructor(actions$, annotationApiService) {
this.actions$ = actions$;
this.annotationApiService = annotationApiService;
this.loadAnnotation$ = createEffect(() => this.actions$.pipe(ofType(LOAD_ANNOTATION_SET), map((action) => action.payload), switchMap((documentId) => {
return this.annotationApiService.getAnnotationSet(documentId).pipe(map(res => {
if (res.status === 204) {
return new LoadAnnotationSetFail(res);
}
return new LoadAnnotationSetSucess(res);
}), catchError(error => {
return of(new LoadAnnotationSetFail(error));
}));
})));
this.postAnnotation$ = createEffect(() => this.actions$.pipe(ofType(SAVE_ANNOTATION), concatMap((action) => {
return this.annotationApiService.postAnnotation(action.payload).pipe(map(annotations => {
const successAction = new SaveAnnotationSuccess(annotations);
successAction.autoSelect = action.autoSelect;
successAction.annotationId = action.payload.id;
return successAction;
}), catchError(error => {
return of(new SaveAnnotationFail(error));
}));
})));
this.deleteAnnotation$ = createEffect(() => this.actions$.pipe(ofType(DELETE_ANNOTATION), map((action) => action.payload), exhaustMap((annotation) => {
return this.annotationApiService.deleteAnnotation(annotation).pipe(map(result => {
return new DeleteAnnotationSuccess(annotation);
}), catchError(error => {
return of(new DeleteAnnotationFail(error));
}));
})));
this.saveAnnotationSet$ = createEffect(() => this.actions$.pipe(ofType(SAVE_ANNOTATION_SET), map((action) => action.payload), switchMap((annotationSet) => {
return this.annotationApiService.postAnnotationSet(annotationSet).pipe(map(res => {
return new SaveAnnotationSetSuccess(res);
}), catchError(error => {
return of(new SaveAnnotationSetFail(error));
}));
})));
this.autoSelectAnnotation$ = createEffect(() => this.actions$.pipe(ofType(SAVE_ANNOTATION_SUCCESS), filter$1((action) => action.autoSelect === true && action.annotationId), map((action) => {
return new SelectedAnnotation({
annotationId: action.annotationId,
editable: false,
selected: true
});
})));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationEffects, deps: [{ token: i1$2.Actions }, { token: AnnotationApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: AnnotationEffects, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$2.Actions }, { type: AnnotationApiService }] });
class BookmarksEffects {
constructor(actions$, store, bookmarksApiService) {
this.actions$ = actions$;
this.store = store;
this.bookmarksApiService = bookmarksApiService;
this.loadBookmarks$ = createEffect(() => this.actions$.pipe(ofType(LOAD_BOOKMARKS), withLatestFrom(this.store.pipe(select(getDocumentId))), map(([, documentId]) => documentId), exhaustMap((documentId) => this.bookmarksApiService.getBookmarks(documentId)
.pipe(map(res => new LoadBookmarksSuccess(res)), catchError(err => of(new LoadBookmarksFailure(err)))))));
this.createBookmark$ = createEffect(() => this.actions$.pipe(ofType(CREATE_BOOKMARK), map((action) => action.payload), exhaustMap((bookmark) => this.bookmarksApiService.createBookmark(bookmark)
.pipe(map(bmrk => new CreateBookmarkSuccess(bmrk)), catchError(error => of(new CreateBookmarkFailure(error)))))));
this.moveBookmark$ = createEffect(() => this.actions$.pipe(ofType(MOVE_BOOKMARK), map((action) => action.payload), exhaustMap((bookmarks) => this.bookmarksApiService.updateMultipleBookmarks(bookmarks)
.pipe(map(bmrks => new MoveBookmarkSuccess(bmrks)), catchError(error => of(new MoveBookmarkFailure(error)))))));
this.deleteBookmark$ = createEffect(() => this.actions$.pipe(ofType(DELETE_BOOKMARK), map((action) => action.payload), exhaustMap(({ deleted, updated }) => this.bookmarksApiService.deleteMultipleBookmarks({ deleted, updated })
.pipe(switchMap(() => {
if (updated) {
return [
new DeleteBookmarkSuccess(deleted),
new UpdateBookmarkSuccess(updated)
];
}
else {
return [new DeleteBookmarkSuccess(deleted)];
}
}), catchError(error => of(new DeleteBookmarkFailure(error)))))));
this.updateBookmark$ = createEffect(() => this.actions$.pipe(ofType(UPDATE_BOOKMARK), map((action) => action.payload), switchMap((bookmark) => this.bookmarksApiService.updateBookmark(bookmark)
.pipe(map(bmrk => new UpdateBookmarkSuccess(bmrk)), catchError(error => of(new UpdateBookmarkFailure(error)))))));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksEffects, deps: [{ token: i1$2.Actions }, { token: i1.Store }, { token: BookmarksApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: BookmarksEffects, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$2.Actions }, { type: i1.Store }, { type: BookmarksApiService }] });
class RedactionApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.markupsApiUrl = '/api/markups';
this.redactApiUrl = '/api/redaction';
this.markupsSearchApiUrl = '/api/markups/search';
}
getRedactions(documentId) {
const fixedUrl = `${this.markupsApiUrl}/${documentId}`;
return this.httpClient
.get(fixedUrl, { observe: 'response', withCredentials: true });
}
saveRedaction(body) {
return this.httpClient
.post(this.markupsApiUrl, body, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body), catchError(() => []));
}
saveBulkRedaction(body) {
return this.httpClient
.post(this.markupsSearchApiUrl, body, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body), catchError(() => []));
}
deleteRedaction(payload) {
const url = `${this.markupsApiUrl}/${payload.documentId}/${payload.redactionId}`;
return this.httpClient
.delete(url, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
deleteAllMarkers(documentId) {
const url = `${this.markupsApiUrl}/${documentId}`;
return this.httpClient
.delete(url, { observe: 'response', withCredentials: true })
.pipe(map(response => response.body));
}
redact(payload) {
return this.httpClient
.post(this.redactApiUrl, payload, { observe: 'response', withCredentials: true, responseType: 'blob' })
.pipe(map(response => response), catchError(() => []));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class RedactionEffects {
constructor(actions$, redactionApiService, toolbarEvents) {
this.actions$ = actions$;
this.redactionApiService = redactionApiService;
this.toolbarEvents = toolbarEvents;
this.loadRedactions$ = createEffect(() => this.actions$.pipe(ofType(LOAD_REDACTIONS), map((action) => action.payload), switchMap((documentId) => {
return this.redactionApiService.getRedactions(documentId).pipe(map(resp => {
return new LoadRedactionSuccess(resp.body);
}), catchError(error => {
return of(new LoadRedactionFailure(error));
}));
})));
this.saveRedaction$ = createEffect(() => this.actions$.pipe(ofType(SAVE_REDACTION), map((action) => action.payload), exhaustMap((redaction) => {
return this.redactionApiService.saveRedaction(redaction).pipe(map(resp => {
return new SaveRedactionSuccess(resp);
}), catchError(error => {
return of(new SaveRedactionFailure(error));
}));
})));
this.saveBulkRedaction$ = createEffect(() => this.actions$.pipe(ofType(SAVE_BULK_REDACTION), map((action) => action.payload), exhaustMap((redaction) => {
return this.redactionApiService.saveBulkRedaction(redaction).pipe(tap(() => this.toolbarEvents.redactAllInProgressSubject.next(false))).pipe(map(resp => {
return new SaveBulkRedactionSuccess(resp);
}), catchError(error => {
return of(new SaveBulkRedactionFailure(error));
}));
})));
this.deleteRedaction$ = createEffect(() => this.actions$.pipe(ofType(DELETE_REDACTION), map((action) => action.payload), exhaustMap((redactionPayload) => {
return this.redactionApiService.deleteRedaction(redactionPayload).pipe(map(() => {
return new DeleteRedactionSuccess(redactionPayload);
}), catchError(error => {
return of(new DeleteRedactionFailure(error));
}));
})));
this.redact$ = createEffect(() => this.actions$.pipe(ofType(REDACT), map((action) => action.payload), exhaustMap((redactionPayload) => {
return this.redactionApiService.redact(redactionPayload).pipe(map((result) => {
const header = result.headers.get('content-disposition').split('filename=');
const filename = header.length > 1 ? header[1].replace(/"/g, '')
: `redacted-document-${redactionPayload.documentId}`;
return new RedactSuccess({ blob: result.body, filename });
}), catchError(error => {
return of(new RedactFailure(error));
}));
})));
this.unmarkAll$ = createEffect(() => this.actions$.pipe(ofType(UNMARK_ALL), map((action) => action.payload), exhaustMap((documentId) => {
return this.redactionApiService.deleteAllMarkers(documentId).pipe(map(result => {
return new UnmarkAllSuccess();
}), catchError(error => {
return of(new DeleteRedactionFailure(error));
}));
})));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionEffects, deps: [{ token: i1$2.Actions }, { token: RedactionApiService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RedactionEffects, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$2.Actions }, { type: RedactionApiService }, { type: ToolbarEventService }] });
class DocumentConversionApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.documentConversionUrl = '/doc-assembly/convert';
}
convert(documentId) {
return this.httpClient
.post(`${this.documentConversionUrl}/${documentId}`, {}, { observe: 'response', withCredentials: true, responseType: 'blob' })
.pipe(map(response => response), catchError(error => of(error)));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentConversionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentConversionApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentConversionApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class RotationApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.rotationApiUrl = '/em-anno/metadata';
}
getRotation(documentId) {
const fixedUrl = `${this.rotationApiUrl}/${documentId}`;
return this.httpClient
.get(fixedUrl, { observe: 'response', withCredentials: true });
}
saveRotation(payload) {
const fixedUrl = `${this.rotationApiUrl}/`;
return this.httpClient
.post(fixedUrl, payload, { observe: 'response', withCredentials: true })
.pipe(map(response => response), catchError(() => []));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RotationApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RotationApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: RotationApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class DocumentEffects {
constructor(actions$, documentConversionService, rotationApiService) {
this.actions$ = actions$;
this.documentConversionService = documentConversionService;
this.rotationApiService = rotationApiService;
this.convert$ = createEffect(() => this.actions$.pipe(ofType(CONVERT), map((action) => action.payload), exhaustMap((documentId) => {
return this.documentConversionService.convert(documentId).pipe(map((result) => {
const url = URL.createObjectURL(result.body);
return new ConvertSuccess(url);
}), catchError(error => {
return of(new ConvertFailure(error));
}));
})));
this.loadRotation$ = createEffect(() => this.actions$.pipe(ofType(LOAD_ROTATION), map((action) => action.payload), switchMap((documentId) => {
return this.rotationApiService.getRotation(documentId).pipe(map(resp => {
return new LoadRotationSuccess(resp.body);
}), catchError(error => {
return of(new LoadRotationFailure(error));
}));
})));
this.saveRotation$ = createEffect(() => this.actions$.pipe(ofType(SAVE_ROTATION), map((action) => action.payload), switchMap((payload) => {
return this.rotationApiService.saveRotation(payload).pipe(map(resp => {
return new SaveRotationSuccess(resp.body);
}), catchError(error => {
return of(new SaveRotationFailure(error));
}));
})));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentEffects, deps: [{ token: i1$2.Actions }, { token: DocumentConversionApiService }, { token: RotationApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: DocumentEffects, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$2.Actions }, { type: DocumentConversionApiService }, { type: RotationApiService }] });
class IcpSessionApiService {
constructor(httpClient) {
this.httpClient = httpClient;
this.ICP_SESSION_API = '/icp/sessions';
}
loadSession(payload) {
return this.httpClient
.get(`${this.ICP_SESSION_API}/${payload.caseId}/${payload.documentId}`, { observe: 'response', withCredentials: true })
.pipe(map(response => {
const token = response.headers.get('X-Access-Token');
return {
...response.body,
token
};
}));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpSessionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpSessionApiService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpSessionApiService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class IcpEffects {
constructor(actions$, icpApiService, icpUpdateService) {
this.actions$ = actions$;
this.icpApiService = icpApiService;
this.icpUpdateService = icpUpdateService;
this.loadIcpSession$ = createEffect(() => this.actions$.pipe(ofType(LOAD_ICP_SESSION), map((action) => action.payload), exhaustMap((payload) => this.icpApiService.loadSession(payload)
.pipe(map(res => new JoinIcpSocketSession(res)), catchError(error => of(new LoadIcpSessionFailure(error)))))));
this.joinIcpSocketSession$ = createEffect(() => this.actions$.pipe(ofType(JOIN_ICP_SOCKET_SESSION), map((action) => action.payload), switchMap((res) => this.icpUpdateService.joinSession(res.username, res.session, res.token)
.pipe(map(participants => new IcpSocketSessionJoined({ session: res.session, participantInfo: participants }))))));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEffects, deps: [{ token: i1$2.Actions }, { token: IcpSessionApiService }, { token: IcpUpdateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: IcpEffects, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$2.Actions }, { type: IcpSessionApiService }, { type: IcpUpdateService }] });
const effects = [AnnotationEffects, BookmarksEffects, RedactionEffects, DocumentEffects, IcpEffects];
class MediaViewerModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerModule, declarations: [PdfViewerComponent,
SideBarComponent,
BookmarksComponent,
OutlineItemComponent,
ImageViewerComponent,
UnsupportedViewerComponent,
MultimediaPlayerComponent,
MediaViewerComponent,
ConvertibleContentViewerComponent,
GrabNDragDirective,
RotationPersistDirective,
HighlightCreateDirective,
KeyboardTextHighlightDirective,
ConfirmActionDialogComponent,
RedactionComponent,
BookmarkIconsComponent,
ParticipantsListComponent], imports: [CommonModule,
HttpClientModule,
ToolbarModule,
AnnotationsModule,
TagInputModule,
FormsModule,
ReactiveFormsModule,
RouterModule, i1.StoreFeatureModule, i1$2.EffectsFeatureModule, MutableDivModule,
CdkTreeModule,
DragDropModule, i5.RpxTranslationModule], exports: [MediaViewerComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerModule, providers: [
PdfJsWrapperFactory,
CommentService,
RedactionApiService,
DocumentConversionApiService,
RotationApiService,
], imports: [CommonModule,
HttpClientModule,
ToolbarModule,
AnnotationsModule,
TagInputModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
StoreModule.forFeature('media-viewer', reducers),
EffectsModule.forFeature(effects),
MutableDivModule,
CdkTreeModule,
DragDropModule,
RpxTranslationModule.forChild()] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: MediaViewerModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
HttpClientModule,
ToolbarModule,
AnnotationsModule,
TagInputModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
StoreModule.forFeature('media-viewer', reducers),
EffectsModule.forFeature(effects),
MutableDivModule,
CdkTreeModule,
DragDropModule,
RpxTranslationModule.forChild()
],
declarations: [
PdfViewerComponent,
SideBarComponent,
BookmarksComponent,
OutlineItemComponent,
ImageViewerComponent,
UnsupportedViewerComponent,
MultimediaPlayerComponent,
MediaViewerComponent,
ConvertibleContentViewerComponent,
GrabNDragDirective,
RotationPersistDirective,
HighlightCreateDirective,
KeyboardTextHighlightDirective,
ConfirmActionDialogComponent,
RedactionComponent,
BookmarkIconsComponent,
ParticipantsListComponent
],
providers: [
PdfJsWrapperFactory,
CommentService,
RedactionApiService,
DocumentConversionApiService,
RotationApiService,
],
exports: [
MediaViewerComponent
]
}]
}] });
/*
* Public API Surface of media-viewer
*/
/**
* Generated bundle index. Do not edit.
*/
export { ADD_OR_EDIT_COMMENT, APPLY_COMMENT_SUMMARY_FILTER, AddOrEditComment, AnnotationApiService, AnnotationEffects, AnnotationSetComponent, AnnotationViewComponent, AnnotationsModule, ApplyCommentSymmaryFilter, BoxHighlightCreateComponent, CLEAR_COMMENT_SUMMARY_FILTER, ClearCommentSummaryFilters, CommentFilterComponent, CommentSearchComponent, CommentSetComponent, CommentSetHeaderComponent, CommentsSummaryComponent, DELETE_ANNOTATION, DELETE_ANNOTATION_FAIL, DELETE_ANNOTATION_SUCCESS, DeleteAnnotation, DeleteAnnotationFail, DeleteAnnotationSuccess, FilterPipe, HighlightToolbarComponent, IcpToolbarComponent, LOAD_ANNOTATION_SET, LOAD_ANNOTATION_SET_FAIL, LOAD_ANNOTATION_SET_SUCCESS, LoadAnnotationSet, LoadAnnotationSetFail, LoadAnnotationSetSucess, MainToolbarComponent, MediaViewerComponent, MediaViewerModule, MetadataLayerComponent, MomentDatePipe, RedactionSearchBarComponent, RedactionToolbarComponent, ResponseType, SAVE_ANNOTATION, SAVE_ANNOTATION_FAIL, SAVE_ANNOTATION_SET, SAVE_ANNOTATION_SET_FAIL, SAVE_ANNOTATION_SET_SUCCESS, SAVE_ANNOTATION_SUCCESS, SEARCH_COMMENT, SELECT_ANNOTATION, SaveAnnotation, SaveAnnotationFail, SaveAnnotationSet, SaveAnnotationSetFail, SaveAnnotationSetSuccess, SaveAnnotationSuccess, SearchBarComponent, SearchComment, SelectedAnnotation, TagsComponent, ToolbarButtonVisibilityService, ToolbarModule, UnsnakePipe, ViewerException, commentSearchQ, getAnnoEnt, getAnnoPageEnt, getAnnoPerPage, getAnnoSet, getAnnotationEntities, getAnnotationSet, getAnnotationsSetState, getCommentEntities, getCommentEnts, getCommentSummary, getCommentSummaryFilters, getCommentsArray, getComponentSearchQueries, getComponentSearchText, getDocumentIdSetId, getFilteredAnnotations, getPageEntities, getSelectedAnno, getSelectedAnnotation, getSet, getSummaryFilters, initialState, reducer };
//# sourceMappingURL=hmcts-media-viewer.mjs.map