@hmcts/media-viewer
Version:
7,407 lines • 523 kB
JavaScript
import * as i5$1 from 'rpx-xui-translation';
import { RpxTranslationModule } from 'rpx-xui-translation';
import * as i0 from '@angular/core';
import { Injectable, Component, ViewChild, HostListener, Directive, Input, ViewEncapsulation, Pipe, EventEmitter, Output, ViewChildren, NgModule } from '@angular/core';
import * as i5 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, of, combineLatest, asyncScheduler } from 'rxjs';
import { take, distinctUntilChanged, filter, auditTime, tap, 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 from 'ngx-chips';
import { TagInputModule } from 'ngx-chips';
import * as i3 from '@angular/router';
import { RouterModule } from '@angular/router';
import * as i4$1 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$2 from '@angular/cdk/drag-drop';
import { DragDropModule } from '@angular/cdk/drag-drop';
import * as i5$2 from '@angular/cdk/scrolling';
import * as i7 from '@angular/cdk/overlay';
import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
import * as i5$3 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i8 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;
payload.forEach(page => {
if (!hasDifferentPageSize && pageHeight && pageWidth &&
(pageHeight !== page.div['clientHeight'] || pageWidth !== page.div['clientWidth'])) {
hasDifferentPageSize = true;
}
else {
pageHeight = page.div['clientHeight'];
pageWidth = page.div['clientWidth'];
}
const styles = {
left: page.div['offsetLeft'],
height: page.div['clientHeight'],
width: page.div['clientWidth']
};
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) {
this.payload = payload;
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);
pdfjs.GlobalWorkerOptions.workerSrc = '/assets/build/pdf.worker.min.js';
/**
* 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('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;
}
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
});
}
}
}
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 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: "18.2.13", ngImport: i0, type: IcpEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IcpEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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 Subject();
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.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() {
this.highlightToolbarSubject.next(!this.highlightToolbarSubject.getValue());
}
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.drawModeSubject.next(false);
this.grabNDrag.next(false);
this.redactionMode.next(true);
}
else {
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: "18.2.13", ngImport: i0, type: ToolbarEventService, deps: [{ token: IcpEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarEventService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: IcpEventService }] });
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: "18.2.13", ngImport: i0, type: PdfJsWrapperFactory, deps: [{ token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PdfJsWrapperFactory, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: PrintService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PrintService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PrintService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
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: "18.2.13", ngImport: i0, type: ViewerEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ViewerEventService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ViewerEventService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
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}`);
return 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) {
this.socket.send(JSON.stringify({
type: 'event',
event,
data
}));
}
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: "18.2.13", ngImport: i0, type: SocketService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SocketService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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;
console.log('Joining Session');
console.log(this.session);
console.log('Joining session');
this.socket.connect(`${session.connectionUrl}?access_token=${token}`, session);
this.socket.connected().subscribe(isConnected => {
if (isConnected) {
this.socket.join({ ...this.session, username });
}
});
return this.socket.listen(IcpEvents.SESSION_JOINED);
}
leaveSession() {
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: "18.2.13", ngImport: i0, type: IcpUpdateService, deps: [{ token: SocketService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IcpUpdateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: IcpPresenterService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: IcpFollowerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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(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(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: "18.2.13", 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: "18.2.13", ngImport: i0, type: IcpService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: ToolbarButtonVisibilityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarButtonVisibilityService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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
};
/**
* 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();
}
}
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);
if (comments.length) {
const savedComments = comments.filter((comment) => {
return comment.createdByDetails !== undefined;
});
return savedComments.map((comment) => {
return {
page: comment.page,
user: comment.createdByDetails.forename.concat(' ').concat(comment.createdByDetails.surname),
date: moment(comment.lastModifiedDate).format('D MMMM YYYY'),
tags: comment.tags,
comment: comment.content
};
});
}
return [''];
});
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) {
this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
const anno = {
id: 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));
});
}
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: "18.2.13", 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: "18.2.13", ngImport: i0, type: HighlightCreateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HighlightCreateService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: i1.Store }] });
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: "18.2.13", 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: "18.2.13", type: RedactionSearchBarComponent, 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 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: i5.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$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redaction-search-bar', 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 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: "18.2.13", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentSetRenderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: TagsServices, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TagsServices }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: TextHighlightDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TextHighlightDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvTextHighlight]'
}]
}], 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: "18.2.13", ngImport: i0, type: TextareaAutoExpandDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvTextAreaAutoExpand]'
}]
}], 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: "18.2.13", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TagsComponent, 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: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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.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.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: "18.2.13", ngImport: i0, type: TagsComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-tags', encapsulation: ViewEncapsulation.None, 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: "18.2.13", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: MomentDatePipe, name: "momentDate" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MomentDatePipe, decorators: [{
type: Pipe,
args: [{
name: 'momentDate'
}]
}] });
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: "18.2.13", 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: "18.2.13", type: CommentComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }, { kind: "pipe", type: MomentDatePipe, name: "momentDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-anno-comment', 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: "18.2.13", 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: "18.2.13", type: CommentSetComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentSetComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-set', 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: "18.2.13", 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: "18.2.13", type: CommentsNavigateComponent, 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 (click)=\"prevItem()\">Prev</a>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Next comment'\"\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"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comments-navigate', encapsulation: ViewEncapsulation.None, 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 (click)=\"prevItem()\">Prev</a>\n <a [routerLink]=\"[]\"\n class=\"comment-search__item\"\n title=\"Next comment'\"\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: "18.2.13", ngImport: i0, type: CommentSearchComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CommentSearchComponent, 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: i5.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$1.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentSearchComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-search', encapsulation: ViewEncapsulation.None, 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: "18.2.13", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: FilterPipe, name: "filter" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FilterPipe, decorators: [{
type: Pipe,
args: [{
name: 'filter'
}]
}] });
class UnsnakePipe {
transform(items) {
return items.split('_').join(' ');
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: UnsnakePipe, name: "unsnake" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnsnakePipe, decorators: [{
type: Pipe,
args: [{
name: 'unsnake'
}]
}] });
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: "18.2.13", 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: "18.2.13", type: CommentFilterComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.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: "18.2.13", ngImport: i0, type: CommentFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-filter', encapsulation: ViewEncapsulation.None, 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: "18.2.13", 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: "18.2.13", type: CommentSetHeaderComponent, 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\" (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 (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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.TitleCasePipe, name: "titlecase" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comment-set-header', encapsulation: ViewEncapsulation.None, 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\" (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 (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 BoxHighlightCreateComponent {
constructor(toolbarEvents, highlightService) {
this.toolbarEvents = toolbarEvents;
this.highlightService = highlightService;
this.saveSelection = new EventEmitter();
this.drawStartX = -1;
this.drawStartY = -1;
this.backgroundColor = 'none';
this.subscriptions = [];
}
ngOnInit() {
this.subscriptions = [
this.toolbarEvents.drawModeSubject.subscribe(drawMode => {
this.defaultHeight = drawMode ? '100%' : '0px';
this.defaultWidth = drawMode ? '100%' : '0px';
}),
this.toolbarEvents.redactWholePage.subscribe(() => {
this.wholePage = true;
})
];
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
subscription.unsubscribe();
});
}
initHighlight(event) {
if (this.wholePage) {
this.highlightPage();
return;
}
const rect = HtmlTemplatesHelper.getAdjustedBoundingRect(event.target), offsetX = event.clientX - rect.left, offsetY = event.clientY - rect.top;
console.log(`initHighlight: rect=${JSON.stringify(rect)}, clientX=${event.clientX}, clientY=${event.clientY}, offsetX=${offsetX}, offsetY=${offsetY}`);
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;
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;
}
}
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);
rectangle = { id: v4(), ...rectangle };
console.log(`createHighlight: rectangle=${JSON.stringify(rectangle)}`);
this.saveSelection.emit({ rectangles: [rectangle], page: this.page });
this.resetHighlight();
}
}
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: "18.2.13", ngImport: i0, type: BoxHighlightCreateComponent, deps: [{ token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: BoxHighlightCreateComponent, 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 }], ngImport: i0, template: "<div [style.height]=\"defaultHeight\"\n [style.width]=\"defaultWidth\"\n (mousedown)=\"initHighlight($event)\"\n (mousemove)=\"updateHighlight($event)\"\n (mouseup)=\"createHighlight()\">\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" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BoxHighlightCreateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-box-highlight-create', template: "<div [style.height]=\"defaultHeight\"\n [style.width]=\"defaultWidth\"\n (mousedown)=\"initHighlight($event)\"\n (mousemove)=\"updateHighlight($event)\"\n (mouseup)=\"createHighlight()\">\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 }]
}] } });
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.selectEvent = new EventEmitter();
this.updateEvent = new EventEmitter();
this.subscriptions = [];
this.enableGrabNDrag = false;
}
ngOnChanges(changes) {
if (changes.rotate) {
this.adjustForRotation(this.rotate);
}
}
ngAfterViewInit() {
this.subscriptions.push(this.toolbarEvents.grabNDrag.subscribe(grabNDrag => this.enableGrabNDrag = grabNDrag));
}
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);
}
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;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RectangleComponent, deps: [{ token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RectangleComponent, selector: "mv-anno-rectangle", inputs: { color: "color", zoom: "zoom", rotate: "rotate", editable: "editable", pageHeight: "pageHeight", pageWidth: "pageWidth", annoRect: "annoRect", selected: "selected" }, outputs: { selectEvent: "selectEvent", updateEvent: "updateEvent" }, 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 [tabindex]=\"annoRect.id\"\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 (click)=\"onClick()\"\n (stopped)=\"onUpdate(rectElement)\">\n </div>\n</div>\n<ng-template #nonEditable>\n <div #rectElement\n class=\"rectangle\"\n [tabindex]=\"annoRect.id\"\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 (click)=\"onClick()\">\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4$1.DraggableElementDirective, selector: "[draggable]", inputs: ["rotate"], outputs: ["coordinates", "stopped"] }, { kind: "directive", type: i4$1.ResizableElementDirective, selector: "[resizable]", inputs: ["rotate", "selected"], outputs: ["stopped"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RectangleComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-anno-rectangle', template: "<div *ngIf=\"editable; else nonEditable\">\n <div #rectElement\n class=\"rectangle\"\n draggable\n resizable\n [tabindex]=\"annoRect.id\"\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 (click)=\"onClick()\"\n (stopped)=\"onUpdate(rectElement)\">\n </div>\n</div>\n<ng-template #nonEditable>\n <div #rectElement\n class=\"rectangle\"\n [tabindex]=\"annoRect.id\"\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 (click)=\"onClick()\">\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
}], selectEvent: [{
type: Output
}], updateEvent: [{
type: Output
}], viewRect: [{
type: ViewChild,
args: ['rectElement', { static: false }]
}], annoRect: [{
type: Input
}], selected: [{
type: Input
}] } });
class CtxToolbarComponent {
constructor() {
this.createHighlightEvent = new EventEmitter();
this.deleteHighlightEvent = new EventEmitter();
this.addOrEditCommentEvent = new EventEmitter();
this.createBookmarkEvent = new EventEmitter();
this.defaultHeight = 70;
this.defaultWidth = 300;
}
ngOnChanges(changes) {
this.setRectangle();
this.top = this.popupTop();
this.left = this.popupLeft();
}
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;
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CtxToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CtxToolbarComponent, 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" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"toolbar\" *ngIf=\"rectangle\"\n [style.top.px]=\"top\"\n [style.left.px]=\"left\">\n <button *ngIf=\"canHighlight\"\n type=\"button\" title=\"Highlight\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton aui__toolbar-button-highlight\"\n (mousedown)=\"createHighlight()\">\n <span>Highlight</span>\n </button>\n <button *ngIf=\"canDelete\"\n type=\"button\" title=\"Delete\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton\"\n (mousedown)=\"deleteHighlight()\">\n <span>Remove</span>\n </button>\n <button *ngIf=\"canComment\"\n type=\"button\" title=\"Comment\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-comment\"\n (mousedown)=\"addOrEditComment()\">\n <span>Comment</span></button>\n <button *ngIf=\"canBookmark\"\n type=\"button\" title=\"Bookmark\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-bookmark\"\n id=\"bookmarkButton\"\n (mousedown)=\"createBookmark()\">\n <span>Bookmark</span></button>\n <div class=\"arrow-down\">\n <div class=\"inner-triangle\"></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CtxToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-ctx-toolbar', template: "<div class=\"toolbar\" *ngIf=\"rectangle\"\n [style.top.px]=\"top\"\n [style.left.px]=\"left\">\n <button *ngIf=\"canHighlight\"\n type=\"button\" title=\"Highlight\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton aui__toolbar-button-highlight\"\n (mousedown)=\"createHighlight()\">\n <span>Highlight</span>\n </button>\n <button *ngIf=\"canDelete\"\n type=\"button\" title=\"Delete\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn leftButton\"\n (mousedown)=\"deleteHighlight()\">\n <span>Remove</span>\n </button>\n <button *ngIf=\"canComment\"\n type=\"button\" title=\"Comment\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-comment\"\n (mousedown)=\"addOrEditComment()\">\n <span>Comment</span></button>\n <button *ngIf=\"canBookmark\"\n type=\"button\" title=\"Bookmark\"\n class=\"aui__toolbar-button aui__toolbar-button--comments contextual-btn rightButton aui__toolbar-button-bookmark\"\n id=\"bookmarkButton\"\n (mousedown)=\"createBookmark()\">\n <span>Bookmark</span></button>\n <div class=\"arrow-down\">\n <div class=\"inner-triangle\"></div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { 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
}], rectangles: [{
type: Input
}] } });
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;
}
}
constructor(toolbarEvents, store) {
this.toolbarEvents = toolbarEvents;
this.store = store;
this.update = new EventEmitter();
this.delete = new EventEmitter();
this.annotationClick = new EventEmitter();
}
onSelect() {
const annotationId = this.anno.id || this.anno.redactionId;
this.annotationClick.emit({ annotationId, editable: false, selected: 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);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", type: AnnotationViewComponent, 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 }], ngImport: i0, template: "<div #container [tabindex]=\"anno.id\">\n <mv-ctx-toolbar *ngIf=\"selected\"\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 </mv-ctx-toolbar>\n <ng-container *ngFor=\"let rectangle of anno.rectangles\">\n <mv-anno-rectangle\n (selectEvent)=\"onSelect()\"\n (updateEvent)=\"onRectangleUpdate($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 [color]=\"anno.color\">\n </mv-anno-rectangle>\n </ng-container>\n</div>\n", dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: RectangleComponent, selector: "mv-anno-rectangle", inputs: ["color", "zoom", "rotate", "editable", "pageHeight", "pageWidth", "annoRect", "selected"], outputs: ["selectEvent", "updateEvent"] }, { kind: "component", type: CtxToolbarComponent, selector: "mv-ctx-toolbar", inputs: ["zoom", "rotate", "pageHeight", "pageWidth", "canHighlight", "canBookmark", "canComment", "canDelete", "rectangles"], outputs: ["createHighlightEvent", "deleteHighlightEvent", "addOrEditCommentEvent", "createBookmarkEvent"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AnnotationViewComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-annotation', template: "<div #container [tabindex]=\"anno.id\">\n <mv-ctx-toolbar *ngIf=\"selected\"\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 </mv-ctx-toolbar>\n <ng-container *ngFor=\"let rectangle of anno.rectangles\">\n <mv-anno-rectangle\n (selectEvent)=\"onSelect()\"\n (updateEvent)=\"onRectangleUpdate($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 [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 }]
}] } });
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: "18.2.13", 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: "18.2.13", type: AnnotationSetComponent, 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: i5.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: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AnnotationSetComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-annotation-set', 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);
}
}
clearContextToolbar() {
this.rectangles = undefined;
}
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 }) {
this.highlightService.saveAnnotation(rectangles, page);
this.toolbarEvents.drawModeSubject.next(false);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", type: MetadataLayerComponent, selector: "mv-metadata-layer", inputs: { zoom: "zoom", rotate: "rotate" }, 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 </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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.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"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MetadataLayerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-metadata-layer', 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 </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
}] } });
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: "18.2.13", ngImport: i0, type: BookmarksComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: BookmarksComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.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$2.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$2.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: "18.2.13", ngImport: i0, type: BookmarksComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-bookmarks', 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: "18.2.13", ngImport: i0, type: OutlineItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OutlineItemComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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: "18.2.13", ngImport: i0, type: OutlineItemComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-outline-item', 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: "18.2.13", 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: "18.2.13", type: SideBarComponent, 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 title=\"Order by place in document\"\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 title=\"Order manually\"\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 title=\"Add bookmark\"\n tabindex=\"3\"\n data-l10n-id=\"addBookmark\"\n >\n <span data-l10n-id=\"addBookmark_label\">{{\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: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5$2.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SideBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-side-bar', 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 title=\"Order by place in document\"\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 title=\"Order manually\"\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 title=\"Add bookmark\"\n tabindex=\"3\"\n data-l10n-id=\"addBookmark\"\n >\n <span data-l10n-id=\"addBookmark_label\">{{\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: "18.2.13", ngImport: i0, type: GrabNDragDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: GrabNDragDirective, 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: "18.2.13", ngImport: i0, type: GrabNDragDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvGrabNDrag]'
}]
}], 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 HighlightCreateDirective {
constructor(element, toolbarEvents, viewerEvents, highlightService, store) {
this.element = element;
this.toolbarEvents = toolbarEvents;
this.viewerEvents = viewerEvents;
this.highlightService = highlightService;
this.store = store;
}
ngOnInit() {
this.$subscription = this.store.select(getPages).subscribe((pages) => {
if (pages[1]) {
this.allPages = pages;
}
});
}
ngOnDestroy() {
if (this.$subscription) {
this.$subscription.unsubscribe();
}
}
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 });
}
}
onPdfViewerClick(event) {
this.store.dispatch(new SelectedAnnotation({
annotationId: '',
selected: false,
editable: false,
}));
this.viewerEvents.clearCtxToolbar();
}
getRectangles(event, 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 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 parentRect = HtmlTemplatesHelper
.getAdjustedBoundingRect(localElement.closest(".textLayer"));
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;
}
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();
}
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", type: HighlightCreateDirective, selector: "[mvCreateTextHighlight]", host: { listeners: { "mouseup": "onMouseUp($event)", "mousedown": "onPdfViewerClick($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HighlightCreateDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvCreateTextHighlight]'
}]
}], 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']]
}] } });
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(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: "18.2.13", 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: "18.2.13", type: RedactionComponent, 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 [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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedactionComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redactions', 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 [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: "18.2.13", ngImport: i0, type: BookmarkIconsComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: BookmarkIconsComponent, 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: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BookmarkIconsComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-bookmark-icons', 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: "18.2.13", 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: "18.2.13", type: ParticipantsListComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ParticipantsListComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-participants-list', 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.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(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();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", type: PdfViewerComponent, 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 }], 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 >\n <div\n #pdfViewer\n class=\"pdfViewer\"\n mvCreateTextHighlight\n [ngClass]=\"{\n hidden: false,\n highlightMode: highlightMode | async,\n drawMode: drawMode | async\n }\"\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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.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: "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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PdfViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-pdf-viewer', encapsulation: ViewEncapsulation.None, 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 >\n <div\n #pdfViewer\n class=\"pdfViewer\"\n mvCreateTextHighlight\n [ngClass]=\"{\n hidden: false,\n highlightMode: highlightMode | async,\n drawMode: drawMode | async\n }\"\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 }]
}], 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: "18.2.13", ngImport: i0, type: ViewerUtilService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ViewerUtilService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", type: ImageViewerComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ImageViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-image-viewer', 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: "18.2.13", ngImport: i0, type: UnsupportedViewerComponent, deps: [{ token: ToolbarEventService }, { token: ViewerUtilService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UnsupportedViewerComponent, 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnsupportedViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-unsupported-viewer', 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: "18.2.13", ngImport: i0, type: AnnotationApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AnnotationApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NumberHelperService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NumberHelperService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
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: "18.2.13", ngImport: i0, type: SearchBarComponent, deps: [{ token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SearchBarComponent, 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 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 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-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 data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\"\n ></button>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SearchBarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-search-bar', 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 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 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-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 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 MainToolbarComponent {
constructor(toolbarEvents, toolbarButtons, cdr, numberHelper, icpEventService) {
this.toolbarEvents = toolbarEvents;
this.toolbarButtons = toolbarButtons;
this.cdr = cdr;
this.numberHelper = numberHelper;
this.icpEventService = icpEventService;
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.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.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
this.redactAllInProgress = disable;
}));
}
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();
}
onClickHighlightToggle() {
this.toolbarEvents.toggleHighlightToolbar();
}
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();
this.isRedactOpen = !this.isRedactOpen;
}
toggleGrabNDrag() {
this.toolbarEvents.toggleGrabNDrag();
}
isPdf() {
return this.contentType === 'pdf';
}
toggleSearchBar() {
this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
}
toggleMoreOptions() {
this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
setTimeout(() => {
if (this.mvMenuItems) {
this.mvMenuItems.nativeElement.focus();
}
}, 100);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MainToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i0.ChangeDetectorRef }, { token: NumberHelperService }, { token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MainToolbarComponent, 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)" } }, 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\">\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\"\n [class.mv-toolbar__menu-button--more-options__hidden]=\"\n mvToolbar.offsetWidth >= allButtonsWidth\n \"\n [attr.aria-expanded]=\"isDropdownMenuOpen\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>{{ \"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 tabindex=\"0\">\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n </ng-template>\n </div>\n </div>\n\n <div id=\"mvMenuItems\" #mvMenuItems>\n <ng-template #menuItems>\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvIndexBtn\"\n title=\"Index\"\n data-l10n-id=\"index\"\n #mvIndexBtn\n class=\"mv-button mv-toolbar__menu-button--index\"\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>{{ \"Index\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvBookmarksBtn\"\n title=\"Bookmarks\"\n data-l10n-id=\"bookmarks\"\n #mvBookmarksBtn\n [ngClass]=\"{\n 'mv-button 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>{{ \"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\"\n title=\"Draw a box\"\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\">{{\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\"\n title=\"Highlight\"\n aria-label=\"Highlight\"\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 data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">{{\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 [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 title=\"Previous Page\"\n class=\"mv-toolbar__menu-button--up button-image\"\n data-l10n-id=\"previous\"\n (click)=\"decreasePageNumber()\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <button\n id=\"mvDownBtn\"\n [disabled]=\"pageNumber === pageCount || redactAllInProgress\"\n title=\"Next Page\"\n class=\"mv-toolbar__menu-button--down button-image\"\n data-l10n-id=\"next\"\n (click)=\"increasePageNumber()\"\n >\n <span></span>\n </button>\n\n <input\n type=\"number\"\n id=\"pageNumber\"\n class=\"hmcts-toolbar-input govuk-input--width-2\"\n title=\"Page Number\"\n value=\"1\"\n size=\"4\"\n min=\"1\"\n [value]=\"pageNumber\"\n aria-label=\"page number\"\n tabindex=\"0\"\n data-l10n-id=\"page\"\n (change)=\"onPageNumberInputChange(pageNumberInput.value)\"\n [disabled]=\"redactAllInProgress\"\n #pageNumberInput\n />\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\"\n title=\"Zoom Out\"\n data-l10n-id=\"zoom_out\"\n (click)=\"stepZoom(-0.1)\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\">{{\n \"Zoom Out\" | rpxTranslate\n }}</span>\n </button>\n <select\n id=\"scaleSelect\"\n class=\"hmcts-toolbar-select\"\n title=\"Zoom\"\n tabindex=\"0\"\n data-l10n-id=\"zoom\"\n (change)=\"zoom($event.target.value)\"\n aria-label=\"zoom\"\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\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\"\n (click)=\"stepZoom(0.1)\"\n title=\"Zoom In\"\n data-l10n-id=\"zoom_in\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\">{{\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\"\n title=\"Rotate Counterclockwise\"\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\"\n title=\"Rotate Clockwise\"\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 title=\"Search\"\n data-l10n-id=\"searchbar\"\n class=\"mv-button mv-toolbar__menu-button--search\"\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>{{ \"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\"\n title=\"In-Court Presentation Mode\"\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\">{{\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 title=\"Redact\"\n data-l10n-id=\"redact\"\n class=\"mv-button mv-toolbar__menu-button--redact\"\n [ngClass]=\"onToolBarOffSetChange('mvRedactBtn')\"\n [attr.aria-expanded]=\"isRedactOpen\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Grab and drag\"\n [ngClass]=\"onToolBarOffSetChange('mvGrabBtn')\"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Download\"\n data-l10n-id=\"download\"\n [ngClass]=\"onToolBarOffSetChange('mvDownloadBtn')\"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"Download\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showPrint\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvPrintBtn\"\n #mvPrintBtn\n title=\"Print\"\n data-l10n-id=\"print\"\n class=\"mv-button mv-toolbar__menu-button--print\"\n [ngClass]=\"onToolBarOffSetChange('mvPrintBtn')\"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Comments\"\n data-l10n-id=\"comments\"\n [ngClass]=\"onToolBarOffSetChange('mvCommentsBtn')\"\n [attr.aria-expanded]=\"isCommentsOpen\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.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: i7.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: i7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: SearchBarComponent, selector: "mv-search-bar" }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MainToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-main-toolbar', template: "<div class=\"toolbar\">\n <div id=\"toolbarContainer\">\n <div class=\"mv-toolbar__container\">\n <div #mvToolbar class=\"mv-toolbar\" [class.notSupported]=\"!contentType\">\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\"\n [class.mv-toolbar__menu-button--more-options__hidden]=\"\n mvToolbar.offsetWidth >= allButtonsWidth\n \"\n [attr.aria-expanded]=\"isDropdownMenuOpen\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>{{ \"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 tabindex=\"0\">\n <ng-container *ngTemplateOutlet=\"menuItems\"></ng-container>\n </div>\n </ng-template>\n </div>\n </div>\n\n <div id=\"mvMenuItems\" #mvMenuItems>\n <ng-template #menuItems>\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvIndexBtn\"\n title=\"Index\"\n data-l10n-id=\"index\"\n #mvIndexBtn\n class=\"mv-button mv-toolbar__menu-button--index\"\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>{{ \"Index\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showSidebar\"\n id=\"mvBookmarksBtn\"\n title=\"Bookmarks\"\n data-l10n-id=\"bookmarks\"\n #mvBookmarksBtn\n [ngClass]=\"{\n 'mv-button 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>{{ \"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\"\n title=\"Draw a box\"\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\">{{\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\"\n title=\"Highlight\"\n aria-label=\"Highlight\"\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 data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">{{\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 [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 title=\"Previous Page\"\n class=\"mv-toolbar__menu-button--up button-image\"\n data-l10n-id=\"previous\"\n (click)=\"decreasePageNumber()\"\n [disabled]=\"redactAllInProgress\"\n >\n <span></span>\n </button>\n <button\n id=\"mvDownBtn\"\n [disabled]=\"pageNumber === pageCount || redactAllInProgress\"\n title=\"Next Page\"\n class=\"mv-toolbar__menu-button--down button-image\"\n data-l10n-id=\"next\"\n (click)=\"increasePageNumber()\"\n >\n <span></span>\n </button>\n\n <input\n type=\"number\"\n id=\"pageNumber\"\n class=\"hmcts-toolbar-input govuk-input--width-2\"\n title=\"Page Number\"\n value=\"1\"\n size=\"4\"\n min=\"1\"\n [value]=\"pageNumber\"\n aria-label=\"page number\"\n tabindex=\"0\"\n data-l10n-id=\"page\"\n (change)=\"onPageNumberInputChange(pageNumberInput.value)\"\n [disabled]=\"redactAllInProgress\"\n #pageNumberInput\n />\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\"\n title=\"Zoom Out\"\n data-l10n-id=\"zoom_out\"\n (click)=\"stepZoom(-0.1)\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\">{{\n \"Zoom Out\" | rpxTranslate\n }}</span>\n </button>\n <select\n id=\"scaleSelect\"\n class=\"hmcts-toolbar-select\"\n title=\"Zoom\"\n tabindex=\"0\"\n data-l10n-id=\"zoom\"\n (change)=\"zoom($event.target.value)\"\n aria-label=\"zoom\"\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\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\"\n (click)=\"stepZoom(0.1)\"\n title=\"Zoom In\"\n data-l10n-id=\"zoom_in\"\n >\n <span class=\"mv-toolbar__menu-button--zoom-out-text\">{{\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\"\n title=\"Rotate Counterclockwise\"\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\"\n title=\"Rotate Clockwise\"\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 title=\"Search\"\n data-l10n-id=\"searchbar\"\n class=\"mv-button mv-toolbar__menu-button--search\"\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>{{ \"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\"\n title=\"In-Court Presentation Mode\"\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\">{{\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 title=\"Redact\"\n data-l10n-id=\"redact\"\n class=\"mv-button mv-toolbar__menu-button--redact\"\n [ngClass]=\"onToolBarOffSetChange('mvRedactBtn')\"\n [attr.aria-expanded]=\"isRedactOpen\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Grab and drag\"\n [ngClass]=\"onToolBarOffSetChange('mvGrabBtn')\"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Download\"\n data-l10n-id=\"download\"\n [ngClass]=\"onToolBarOffSetChange('mvDownloadBtn')\"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"Download\" | rpxTranslate }}</span>\n </button>\n\n <button\n *ngIf=\"toolbarButtons.showPrint\"\n [disabled]=\"icpEnabled || redactionEnabled\"\n id=\"mvPrintBtn\"\n #mvPrintBtn\n title=\"Print\"\n data-l10n-id=\"print\"\n class=\"mv-button mv-toolbar__menu-button--print\"\n [ngClass]=\"onToolBarOffSetChange('mvPrintBtn')\"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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\"\n title=\"Comments\"\n data-l10n-id=\"comments\"\n [ngClass]=\"onToolBarOffSetChange('mvCommentsBtn')\"\n [attr.aria-expanded]=\"isCommentsOpen\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>{{ \"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 }], 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']]
}] } });
class RedactionToolbarComponent {
constructor(toolbarEventService, toolbarButtons, store) {
this.toolbarEventService = toolbarEventService;
this.toolbarButtons = toolbarButtons;
this.store = store;
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();
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedactionToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RedactionToolbarComponent, selector: "mv-redaction-toolbar", inputs: { showRedactSearch: "showRedactSearch" }, ngImport: i0, template: "<div class=\"redaction\">\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\"\n title=\"{{ 'Draw a box' | rpxTranslate }}}\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"{{ 'Redact page' | rpxTranslate }}\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">{{\n \"Redact page\" | rpxTranslate\n }}</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"{{ 'From search' | rpxTranslate }}\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search\"\n (click)=\"onRedactAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Redact text' | rpxTranslate }}\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Clear all' | rpxTranslate }}\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">{{ \"Clear all\" | rpxTranslate }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvPreviewBtn\"\n class=\"mv-button\"\n [class.redaction-button--preview]=\"!preview\"\n [class.redaction-button--hide-preview]=\"preview\"\n redaction-button--preview\n aria-pressed=\"false\"\n title=\"{{ 'Preview' | rpxTranslate }}\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">{{\n \"Preview\" | rpxTranslate\n }}</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Save document' | rpxTranslate }}\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">{{\n \"Save document\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close\"\n title=\"{{ 'Close Redaction' | rpxTranslate }}\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">{{\n \"Close Redaction\" | rpxTranslate\n }}</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-redaction-toolbar', template: "<div class=\"redaction\">\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\"\n title=\"{{ 'Draw a box' | rpxTranslate }}}\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">{{\n \"Draw a box\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"{{ 'Redact page' | rpxTranslate }}\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">{{\n \"Redact page\" | rpxTranslate\n }}</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"{{ 'From search' | rpxTranslate }}\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search\"\n (click)=\"onRedactAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Redact text' | rpxTranslate }}\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Clear all' | rpxTranslate }}\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">{{ \"Clear all\" | rpxTranslate }}</span>\n </button>\n\n <button\n [disabled]=\"!hasRedactions || redactionAllInProgress\"\n id=\"mvPreviewBtn\"\n class=\"mv-button\"\n [class.redaction-button--preview]=\"!preview\"\n [class.redaction-button--hide-preview]=\"preview\"\n redaction-button--preview\n aria-pressed=\"false\"\n title=\"{{ 'Preview' | rpxTranslate }}\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">{{\n \"Preview\" | rpxTranslate\n }}</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\">{{\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\"\n aria-pressed=\"false\"\n title=\"{{ 'Save document' | rpxTranslate }}\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">{{\n \"Save document\" | rpxTranslate\n }}</span>\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close\"\n title=\"{{ 'Close Redaction' | rpxTranslate }}\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">{{\n \"Close Redaction\" | rpxTranslate\n }}</span>\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }], propDecorators: { showRedactSearch: [{
type: Input
}] } });
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: "18.2.13", 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: "18.2.13", type: IcpToolbarComponent, 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\" (click)=\"leaveIcpSession()\">\n <span data-l10n-id=\"icp-leave-presentation_label\">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\" title=\"Present\"\n (click)=\"present()\">\n <span data-l10n-id=\"icp-present_label\">Start presenting</span>\n </button>\n <button *ngIf=\"isPresenter\" class=\"govuk-button govuk-button--warning\" id=\"icp-stop-presenting\" tabindex=\"0\"\n title=\"Stop presenting\" (click)=\"stopPresenting()\">\n <span data-l10n-id=\"icp-stop-presenting_label\">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 title=\"Participants list\" (click)=\"showParticipantsList()\">\n <span\n data-l10n-id=\"icp-stop-presenting_label\">{{(icpEventService.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.TitleCasePipe, name: "titlecase" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IcpToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-icp-toolbar', 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\" (click)=\"leaveIcpSession()\">\n <span data-l10n-id=\"icp-leave-presentation_label\">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\" title=\"Present\"\n (click)=\"present()\">\n <span data-l10n-id=\"icp-present_label\">Start presenting</span>\n </button>\n <button *ngIf=\"isPresenter\" class=\"govuk-button govuk-button--warning\" id=\"icp-stop-presenting\" tabindex=\"0\"\n title=\"Stop presenting\" (click)=\"stopPresenting()\">\n <span data-l10n-id=\"icp-stop-presenting_label\">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 title=\"Participants list\" (click)=\"showParticipantsList()\">\n <span\n data-l10n-id=\"icp-stop-presenting_label\">{{(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) {
this.toolbarEventService = toolbarEventService;
this.toolbarButtons = toolbarButtons;
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.openRedactionSearch.next({ modeType: SearchType.Highlight, isOpen: false });
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HighlightToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: HighlightToolbarComponent, selector: "mv-highlight-toolbar", ngImport: i0, template: "<div class=\"redaction\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >Highlight options</label\n >\n\n <button\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw\"\n title=\"Draw a box\"\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\">Draw a box</span>\n </button>\n\n <button\n id=\"highlightTextBtn\"\n class=\"mv-button mv-toolbar__menu-button--highlight\"\n title=\"Highlight text\"\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\">Highlight text</span>\n </button>\n\n <button\n id=\"mvHighlightFromSearchBtn\"\n title=\"From search\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search\"\n (click)=\"onAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\"\n >From search</span\n >\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close\"\n title=\"Close Redaction\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onClose()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Highlight</span>\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HighlightToolbarComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-highlight-toolbar', template: "<div class=\"redaction\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >Highlight options</label\n >\n\n <button\n id=\"mvDrawBtn\"\n #mvDrawBtn\n class=\"mv-button mv-toolbar__menu-button--draw\"\n title=\"Draw a box\"\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\">Draw a box</span>\n </button>\n\n <button\n id=\"highlightTextBtn\"\n class=\"mv-button mv-toolbar__menu-button--highlight\"\n title=\"Highlight text\"\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\">Highlight text</span>\n </button>\n\n <button\n id=\"mvHighlightFromSearchBtn\"\n title=\"From search\"\n data-l10n-id=\"fromSearchButton\"\n class=\"mv-button redaction-button--search\"\n (click)=\"onAllSearch()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span style=\"width: 5rem\" data-l10n-id=\"fromSearchButton_label\"\n >From search</span\n >\n </button>\n\n <button\n id=\"mvCloseBtn\"\n #mvCloseBtn\n class=\"mv-button redaction-button--close\"\n title=\"Close Redaction\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"onClose()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Highlight</span>\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }] });
/*
* 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: "18.2.13", ngImport: i0, type: GovUkErrorMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GovUkErrorMessageComponent, 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: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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>
`
}]
}], 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: "18.2.13", ngImport: i0, type: GovUkFieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GovUkFieldsetComponent, 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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>
`
}]
}], 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: "18.2.13", ngImport: i0, type: GovUkLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GovUkLabelComponent, 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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>
`
}]
}], 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: "18.2.13", ngImport: i0, type: GovUkDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GovUkDateComponent, 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: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.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: "18.2.13", ngImport: i0, type: GovUkDateComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-gov-uk-date', 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: "18.2.13", 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: "18.2.13", type: CommentsSummaryComponent, 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 <ng-container *ngIf=\"comments[0]\">\n <ngx-datatable\n [rows]=\"comments\"\n rowHeight=\"auto\"\n headerHeight=\"auto\"\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>\n <ng-container *ngIf=\"!comments[0]\">\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$3.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.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.DatatableComponent, selector: "ngx-datatable", inputs: ["targetMarkerTemplate", "rows", "groupRowsBy", "groupedRows", "columns", "selected", "scrollbarV", "scrollbarH", "rowHeight", "columnMode", "headerHeight", "footerHeight", "externalPaging", "externalSorting", "limit", "count", "offset", "loadingIndicator", "selectionType", "reorderable", "swapColumns", "sortType", "sorts", "cssClasses", "messages", "rowClass", "selectCheck", "displayCheck", "groupExpansionDefault", "trackByProp", "selectAllRowsOnPage", "virtualization", "treeFromRelation", "treeToRelation", "summaryRow", "summaryHeight", "summaryPosition", "rowIdentity"], outputs: ["scroll", "activate", "select", "sort", "page", "reorder", "resize", "tableContextmenu", "treeAction"] }, { kind: "directive", type: i8.DataTableColumnDirective, selector: "ngx-datatable-column", inputs: ["name", "prop", "frozenLeft", "frozenRight", "flexGrow", "resizeable", "comparator", "pipe", "sortable", "draggable", "canAutoResize", "minWidth", "width", "maxWidth", "checkboxable", "headerCheckboxable", "headerClass", "cellClass", "isTreeColumn", "treeLevelIndent", "summaryFunc", "summaryTemplate", "cellTemplate", "headerTemplate", "treeToggleTemplate"] }, { kind: "directive", type: i8.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: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }, { kind: "pipe", type: UnsnakePipe, name: "unsnake" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CommentsSummaryComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-comments-summary', 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 <ng-container *ngIf=\"comments[0]\">\n <ngx-datatable\n [rows]=\"comments\"\n rowHeight=\"auto\"\n headerHeight=\"auto\"\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>\n <ng-container *ngIf=\"!comments[0]\">\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: "18.2.13", ngImport: i0, type: MultimediaPlayerComponent, deps: [{ token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MultimediaPlayerComponent, 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5$1.RpxTranslatePipe, name: "rpxTranslate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MultimediaPlayerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-multimedia-player', 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(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: "18.2.13", ngImport: i0, type: ConvertibleContentViewerComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ConvertibleContentViewerComponent, 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: i5.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: "18.2.13", ngImport: i0, type: ConvertibleContentViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-conversion-viewer', encapsulation: ViewEncapsulation.None, 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(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: "18.2.13", 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: "18.2.13", type: RotationPersistDirective, selector: "[mvRotationPersist]", host: { listeners: { "mediaLoadStatus": "onMediaLoad($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RotationPersistDirective, decorators: [{
type: Directive,
args: [{
selector: '[mvRotationPersist]'
}]
}], 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;
}
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: "18.2.13", ngImport: i0, type: ConfirmActionDialogComponent, deps: [{ token: IcpEventService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ConfirmActionDialogComponent, selector: "mv-confirm-action", ngImport: i0, template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onCancel()\">\n <div id=\"modal\" class=\"modal-content govuk-width-container clearfix\" (click)=\"$event.stopPropagation()\">\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: "18.2.13", ngImport: i0, type: ConfirmActionDialogComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-confirm-action', template: "<div id=\"modal-background\" class=\"modal\" (click)=\"onCancel()\">\n <div id=\"modal\" class=\"modal-content govuk-width-container clearfix\" (click)=\"$event.stopPropagation()\">\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 }] });
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;
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;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", type: MediaViewerComponent, 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" }, 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 <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", dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.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: i5.AsyncPipe, name: "async" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MediaViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'mv-media-viewer', encapsulation: ViewEncapsulation.None, 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 <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" }]
}], 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
}] } });
class ToolbarModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
MainToolbarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent], imports: [CommonModule,
FormsModule,
OverlayModule,
RouterModule, i5$1.RpxTranslationModule], exports: [MainToolbarComponent,
SearchBarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarModule, providers: [
ToolbarButtonVisibilityService
], imports: [CommonModule,
FormsModule,
OverlayModule,
RouterModule,
RpxTranslationModule.forChild()] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToolbarModule, decorators: [{
type: NgModule,
args: [{
declarations: [
SearchBarComponent,
MainToolbarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent
],
providers: [
ToolbarButtonVisibilityService
],
exports: [
MainToolbarComponent,
SearchBarComponent,
RedactionToolbarComponent,
IcpToolbarComponent,
RedactionSearchBarComponent,
HighlightToolbarComponent
],
imports: [
CommonModule,
FormsModule,
OverlayModule,
RouterModule,
RpxTranslationModule.forChild(),
]
}]
}] });
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: "18.2.13", ngImport: i0, type: BookmarksApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BookmarksApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BookmarksApiService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
class SharedModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: SharedModule, declarations: [GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent], imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule], exports: [GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SharedModule, imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SharedModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
RouterModule
],
declarations: [
GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent
],
exports: [
GovUkDateComponent,
GovUkErrorMessageComponent,
GovUkFieldsetComponent,
GovUkLabelComponent
]
}]
}] });
class AnnotationsModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AnnotationsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: AnnotationsModule, declarations: [MetadataLayerComponent,
AnnotationViewComponent,
BoxHighlightCreateComponent,
AnnotationSetComponent,
RectangleComponent,
CommentComponent,
CommentSetHeaderComponent,
CommentSearchComponent,
TextHighlightDirective,
CtxToolbarComponent,
CommentSetComponent,
CommentsNavigateComponent,
TextareaAutoExpandDirective,
CommentsSummaryComponent,
TagsComponent,
MomentDatePipe,
CommentFilterComponent,
FilterPipe,
UnsnakePipe], imports: [A11yModule,
CommonModule,
FormsModule,
HttpClientModule,
MutableDivModule,
TagInputModule,
RouterModule,
ReactiveFormsModule,
NgxDatatableModule,
SharedModule, i5$1.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: "18.2.13", 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: "18.2.13", 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
],
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 => {
return new SaveAnnotationSuccess(annotations);
}), catchError(error => {
return of(new LoadAnnotationSetFail(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));
}));
})));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: AnnotationEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: BookmarksEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: RedactionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedactionApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: RedactionEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: DocumentConversionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DocumentConversionApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: RotationApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RotationApiService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: DocumentEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: IcpSessionApiService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IcpSessionApiService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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: "18.2.13", ngImport: i0, type: IcpEffects }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", ngImport: i0, type: MediaViewerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: MediaViewerModule, declarations: [PdfViewerComponent,
SideBarComponent,
BookmarksComponent,
OutlineItemComponent,
ImageViewerComponent,
UnsupportedViewerComponent,
MultimediaPlayerComponent,
MediaViewerComponent,
ConvertibleContentViewerComponent,
GrabNDragDirective,
RotationPersistDirective,
HighlightCreateDirective,
ConfirmActionDialogComponent,
RedactionComponent,
BookmarkIconsComponent,
ParticipantsListComponent], imports: [CommonModule,
HttpClientModule,
ToolbarModule,
AnnotationsModule,
TagInputModule,
FormsModule,
ReactiveFormsModule,
RouterModule, i1.StoreFeatureModule, i1$2.EffectsFeatureModule, MutableDivModule,
CdkTreeModule,
DragDropModule, i5$1.RpxTranslationModule], exports: [MediaViewerComponent] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", 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: "18.2.13", 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,
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