@hmcts/media-viewer 2.10.0 → 2.10.1-RC.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Component, ViewChild, HostListener, Input, NgModule, Directive, ViewEncapsulation, Pipe, EventEmitter, Output, ViewChildren } from '@angular/core';
2
+ import { Injectable, Component, ViewChild, HostListener, Directive, Input, ViewEncapsulation, Pipe, EventEmitter, Output, ViewChildren, NgModule } from '@angular/core';
3
3
  import * as i4 from '@angular/common';
4
- import { CommonModule, DatePipe } from '@angular/common';
4
+ import { DatePipe, CommonModule } from '@angular/common';
5
5
  import * as i2 from '@angular/forms';
6
- import { FormsModule, UntypedFormControl, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms';
6
+ import { UntypedFormControl, FormsModule, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms';
7
7
  import * as i1$1 from '@angular/common/http';
8
8
  import { HttpClientModule } from '@angular/common/http';
9
9
  import { __awaiter } from 'tslib';
@@ -17,12 +17,12 @@ import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';
17
17
  import * as pdfjsLib from 'pdfjs-dist';
18
18
  import 'pdfjs-dist/build/pdf.worker';
19
19
  import uuid$1, { v4 } from 'uuid';
20
- import * as i6 from '@angular/cdk/overlay';
21
- import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
22
- import * as i5 from '@angular/router';
23
- import { RouterModule } from '@angular/router';
24
20
  import * as i4$1 from 'ngx-chips';
25
21
  import { TagInputModule } from 'ngx-chips';
22
+ import * as i5 from '@angular/router';
23
+ import { RouterModule } from '@angular/router';
24
+ import * as i6 from '@angular/cdk/overlay';
25
+ import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
26
26
  import * as i4$2 from 'mutable-div';
27
27
  import { MutableDivModule } from 'mutable-div';
28
28
  import * as i3 from '@circlon/angular-tree-component';
@@ -2227,47 +2227,86 @@ const getFilteredAnnotations = createSelector(getAnnotationEntities, getTagFilte
2227
2227
  .filter(annotation => annotation.comments && annotation.comments.length > 0);
2228
2228
  });
2229
2229
 
2230
- /**
2231
- * Number Helper Service
2232
- * */
2233
- class NumberHelperService {
2234
- constructor() { }
2235
- isNumber(value) {
2236
- return (value !== null
2237
- && value !== undefined
2238
- && value !== ''
2239
- && !isNaN(Number(value.toString())));
2230
+ class HighlightCreateService {
2231
+ constructor(toolBarEvents, store) {
2232
+ this.toolBarEvents = toolBarEvents;
2233
+ this.store = store;
2234
+ }
2235
+ saveAnnotation(rectangles, page) {
2236
+ this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
2237
+ const anno = Object.assign(Object.assign({ 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: [] });
2238
+ this.store.dispatch(new SaveAnnotation(anno));
2239
+ });
2240
+ }
2241
+ applyRotation(pageHeight, pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, rotate, zoom) {
2242
+ const { x, y, width, height } = {
2243
+ x: +(offsetLeft / zoom).toFixed(2),
2244
+ y: +(offsetTop / zoom).toFixed(2),
2245
+ width: +(offsetWidth / zoom).toFixed(2),
2246
+ height: +(offsetHeight / zoom).toFixed(2)
2247
+ };
2248
+ const rectangle = { x, y, width, height };
2249
+ switch (rotate) {
2250
+ case 90:
2251
+ rectangle.width = height;
2252
+ rectangle.height = width;
2253
+ rectangle.x = y;
2254
+ rectangle.y = +(pageWidth / zoom - x - width).toFixed(2);
2255
+ break;
2256
+ case 180:
2257
+ rectangle.x = +(pageWidth / zoom - x - width).toFixed(2);
2258
+ rectangle.y = +(pageHeight / zoom - y - height).toFixed(2);
2259
+ break;
2260
+ case 270:
2261
+ rectangle.width = height;
2262
+ rectangle.height = width;
2263
+ rectangle.x = +(pageHeight / zoom - y - height).toFixed(2);
2264
+ rectangle.y = x;
2265
+ break;
2266
+ }
2267
+ return rectangle;
2268
+ }
2269
+ resetHighlight() {
2270
+ window.getSelection().removeAllRanges();
2271
+ this.toolBarEvents.highlightModeSubject.next(false);
2240
2272
  }
2241
2273
  }
2242
- /** @nocollapse */ NumberHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2243
- /** @nocollapse */ NumberHelperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, providedIn: 'root' });
2244
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, decorators: [{
2245
- type: Injectable,
2246
- args: [{
2247
- providedIn: 'root'
2248
- }]
2249
- }], ctorParameters: function () { return []; } });
2274
+ /** @nocollapse */ HighlightCreateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable });
2275
+ /** @nocollapse */ HighlightCreateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService });
2276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, decorators: [{
2277
+ type: Injectable
2278
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
2250
2279
 
2251
- class SearchBarComponent {
2252
- constructor(toolbarButtons, toolbarEvents) {
2280
+ class RedactionSearchBarComponent {
2281
+ constructor(store, toolbarButtons, toolbarEvents, highlightService) {
2282
+ this.store = store;
2253
2283
  this.toolbarButtons = toolbarButtons;
2254
2284
  this.toolbarEvents = toolbarEvents;
2285
+ this.highlightService = highlightService;
2255
2286
  this.highlightAll = true;
2256
2287
  this.matchCase = false;
2257
2288
  this.wholeWord = false;
2258
2289
  this.resultsText = '';
2259
2290
  this.searchText = '';
2260
2291
  this.resultCount = 0;
2261
- this.subscriptions = [];
2292
+ this.redactElements = [];
2262
2293
  this.advancedSearchVisible = false;
2263
2294
  }
2264
2295
  ngOnInit() {
2265
- this.subscriptions.push(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
2296
+ this.subscription = this.toolbarEvents.redactionSerachSubject.subscribe((results) => this.redactAllSearched(results));
2297
+ this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
2298
+ this.subscription.add(this.store.pipe(select(getPages)).subscribe((pages) => {
2299
+ if (pages[1]) {
2300
+ this.allPages = pages;
2301
+ }
2302
+ }));
2303
+ this.subscription.add(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
2304
+ this.subscription.add(this.toolbarEvents.openRedactionSearch.subscribe(isOpen => this.openSearchModal = isOpen));
2305
+ this.subscription.add(this.toolbarEvents.redactAllInProgressSubject
2306
+ .subscribe(inProgress => this.redactAllInProgress = inProgress));
2266
2307
  }
2267
2308
  ngOnDestroy() {
2268
- for (const subscription of this.subscriptions) {
2269
- subscription.unsubscribe();
2270
- }
2309
+ this.subscription.unsubscribe();
2271
2310
  }
2272
2311
  onWindowKeyDown(e) {
2273
2312
  if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
@@ -2276,1507 +2315,1468 @@ class SearchBarComponent {
2276
2315
  setTimeout(() => this.findInput.nativeElement.focus(), 200);
2277
2316
  }
2278
2317
  }
2279
- searchNext() {
2318
+ search(reset = true) {
2319
+ this.redactAll = !reset;
2320
+ if (this.redactAll) {
2321
+ this.toolbarEvents.redactAllInProgressSubject.next(true);
2322
+ }
2323
+ if (reset) {
2324
+ this.redactElements = [];
2325
+ }
2280
2326
  this.toolbarEvents.search({
2281
2327
  searchTerm: this.searchText,
2282
2328
  highlightAll: this.highlightAll,
2283
2329
  matchCase: this.matchCase,
2284
2330
  wholeWord: this.wholeWord,
2285
2331
  previous: false,
2286
- reset: false
2332
+ reset
2287
2333
  });
2288
2334
  }
2289
- searchPrev() {
2290
- this.toolbarEvents.search({
2291
- searchTerm: this.searchText,
2292
- highlightAll: this.highlightAll,
2293
- matchCase: this.matchCase,
2294
- wholeWord: this.wholeWord,
2295
- previous: true,
2296
- reset: false
2335
+ saveRedaction(redactRectangle) {
2336
+ const redaction = redactRectangle.map(ele => {
2337
+ return { page: ele.page, rectangles: ele.rectangles, redactionId: uuid$1(), documentId: this.documentId };
2297
2338
  });
2339
+ this.store.dispatch(new SaveBulkRedaction({ searchRedactions: redaction }));
2298
2340
  }
2299
- search() {
2300
- this.toolbarEvents.search({
2301
- searchTerm: this.searchText,
2302
- highlightAll: this.highlightAll,
2303
- matchCase: this.matchCase,
2304
- wholeWord: this.wholeWord,
2305
- previous: false,
2306
- reset: true
2307
- });
2341
+ existInRedactElements(pageNumber, matechedIndex, rectangles) {
2342
+ if (this.redactElements && this.redactElements.length > 0) {
2343
+ const pagesFound = this.redactElements.find(re => re.page === pageNumber && re.matchedIndex === matechedIndex);
2344
+ const pageRectangles = pagesFound === null || pagesFound === void 0 ? void 0 : pagesFound.rectangles;
2345
+ if (!pageRectangles || pageRectangles.length <= 0) {
2346
+ return false;
2347
+ }
2348
+ let matchesRectangles = 0;
2349
+ for (let rectIndx = 0; rectIndx < pageRectangles.length; rectIndx++) {
2350
+ const rectangle = pageRectangles[rectIndx];
2351
+ const foundRectangle = rectangles.find(re => re.width === rectangle.width &&
2352
+ re.height === rectangle.height && re.x === rectangle.x && re.y === rectangle.y);
2353
+ if (foundRectangle) {
2354
+ matchesRectangles++;
2355
+ }
2356
+ }
2357
+ return pageRectangles.length === matchesRectangles;
2358
+ }
2359
+ return false;
2360
+ }
2361
+ onCloseSearchModal() {
2362
+ this.toolbarEvents.openRedactionSearch.next(false);
2308
2363
  }
2309
2364
  setSearchResultsCount(results) {
2310
2365
  this.resultCount = results.total;
2311
2366
  this.resultsText = this.resultCount > 0
2312
- ? `Found ${results.current} of ${results.total}`
2367
+ ? `${results.total} results founds`
2313
2368
  : 'No results found';
2314
- if (this.resultCount && this.resultCount > 0) {
2315
- setTimeout(() => {
2316
- this.findNext.nativeElement.focus();
2317
- }, 1000);
2369
+ }
2370
+ redactAllSearched(results) {
2371
+ const $this = this;
2372
+ const intervalId = setInterval(() => {
2373
+ const highlightElement = document.getElementsByClassName('highlight selected');
2374
+ if (highlightElement && highlightElement.length > 0) {
2375
+ clearInterval(intervalId);
2376
+ $this.redactAllSearchedTick(results);
2377
+ }
2378
+ }, 100);
2379
+ }
2380
+ redactAllSearchedTick(results) {
2381
+ const highlightElement = document.getElementsByClassName('highlight selected');
2382
+ if (highlightElement && highlightElement.length > 0) {
2383
+ this.resultCount = results.matchesCount;
2384
+ const pageNumber = results.page + 1;
2385
+ const rectangles = this.getRectangles(pageNumber);
2386
+ if (rectangles && this.redactElements.length <= this.resultCount
2387
+ && !this.existInRedactElements(pageNumber, results.matchedIndex, rectangles)) {
2388
+ this.redactElements.push({ page: pageNumber, matchedIndex: results === null || results === void 0 ? void 0 : results.matchedIndex, rectangles });
2389
+ this.CreateRedactAllText();
2390
+ }
2391
+ if (this.redactAll && this.resultCount && this.resultCount > 0
2392
+ && rectangles && this.redactElements.length < this.resultCount) {
2393
+ this.search(false);
2394
+ }
2395
+ if (this.redactAll && this.resultCount && this.redactElements.length === this.resultCount) {
2396
+ this.redactAll = false;
2397
+ this.redactAllText = null;
2398
+ this.saveRedaction(this.redactElements);
2399
+ }
2318
2400
  }
2319
2401
  }
2402
+ CreateRedactAllText() {
2403
+ this.redactAllText = `${this.redactElements.length} of ${this.resultCount}`;
2404
+ }
2320
2405
  onEscapeKeyPress(e) {
2321
2406
  this.toolbarEvents.searchBarHidden.next(true);
2322
2407
  }
2323
2408
  onEnterKeyPress(e) {
2324
2409
  this.search();
2325
2410
  }
2326
- toggleAdvancedSearch() {
2327
- this.advancedSearchVisible = !this.advancedSearchVisible;
2328
- }
2329
2411
  toggleSearchBar() {
2330
2412
  this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
2331
2413
  }
2414
+ getRectangles(page) {
2415
+ this.pageHeight = this.allPages[page].styles.height;
2416
+ this.pageWidth = this.allPages[page].styles.width;
2417
+ this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
2418
+ this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
2419
+ const selectedHighLightedElements = document.getElementsByClassName('highlight selected');
2420
+ if (selectedHighLightedElements && selectedHighLightedElements.length > 0) {
2421
+ const docRange = document.createRange();
2422
+ docRange.selectNodeContents(selectedHighLightedElements[0]);
2423
+ const selection = window.getSelection();
2424
+ selection === null || selection === void 0 ? void 0 : selection.removeAllRanges();
2425
+ selection === null || selection === void 0 ? void 0 : selection.addRange(docRange);
2426
+ if (selection.rangeCount && !selection.isCollapsed) {
2427
+ const range = selection.getRangeAt(0).cloneRange();
2428
+ const clientRects = range.getClientRects();
2429
+ if (clientRects) {
2430
+ const parentRect = selectedHighLightedElements[0].parentElement.parentElement.getBoundingClientRect();
2431
+ const selectionRectangles = [];
2432
+ for (let i = 0; i < clientRects.length; i++) {
2433
+ const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
2434
+ const findSelecttionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
2435
+ if (!findSelecttionRectangle) {
2436
+ selectionRectangles.push(selectionRectangle);
2437
+ }
2438
+ }
2439
+ return selectionRectangles;
2440
+ }
2441
+ }
2442
+ }
2443
+ }
2444
+ createTextRectangle(rect, parentRect) {
2445
+ const height = rect.bottom - rect.top;
2446
+ const width = rect.right - rect.left;
2447
+ const top = rect.top - parentRect.top;
2448
+ const left = rect.left - parentRect.left;
2449
+ let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
2450
+ rectangle = Object.assign({ id: uuid$1() }, rectangle);
2451
+ return rectangle;
2452
+ }
2332
2453
  }
2333
- /** @nocollapse */ SearchBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, deps: [{ token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
2334
- /** @nocollapse */ SearchBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 class=\"searchbar govuk-!-padding-3\" [hidden]=\"toolbarEvents.searchBarHidden | async\">\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\" aria-label=\"Find in document\" #findInput [(ngModel)]=\"searchText\" (keydown.escape)=\"onEscapeKeyPress($event)\" (keydown.enter)=\"onEnterKeyPress($event)\" title=\"Find in document\"\n placeholder=\"Find in document\u2026\" tabindex=\"0\" data-l10n-id=\"find_input\" />\n <button class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\" data-module=\"govuk-button\"\n (click)=\"search()\" style=\" position: absolute; top: 40px; right: 10px; \">\n Search\n </button>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button searchbar-button--close\" title=\"Close Search\" data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\">\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span id=\"findResultsCount\" class=\"govuk-!-display-inline-block govuk-!-margin-right-4\">{{resultsText}}</span>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\" title=\"Find the previous occurrence of the phrase\" data-l10n-id=\"find_previous\">Prev</a>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" #findNext class=\"govuk-link govuk-link--no-visited-state\" (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\" data-l10n-id=\"find_next\">Next</a>\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state\" (click)=\"toggleAdvancedSearch()\" title=\"Advanced\"\n data-l10n-id=\"find_advanced\" style=\" position: absolute; top: 95px; right: 15px; \">Advanced</a>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\" *ngIf=\"advancedSearchVisible\">\n <div class=\"govuk-grid-column-full\">\n <div class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\">\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findHighlightAll\" name=\"findHighlightAll\" type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\" [checked]=\"highlightAll\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findHighlightAll\" data-l10n-id=\"find_highlight\">\n Highlight all\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findMatchCase\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\" [checked]=\"matchCase\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\">\n Match text (exact case)\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findEntireWord\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\" [checked]=\"wholeWord\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\">\n Match whole words or sentences\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
2335
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, decorators: [{
2454
+ /** @nocollapse */ RedactionSearchBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, deps: [{ token: i1.Store }, { token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component });
2455
+ /** @nocollapse */ RedactionSearchBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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=\"Find in document\"\n #findInput\n title=\"Find in document\"\n placeholder=\"Redact from search...\"\n tabindex=\"0\"\n data-l10n-id=\"search_input\"\n [(ngModel)]=\"searchText\"\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\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\"\n >\n Redact all\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 Redacting in progress\n </h4>\n {{ redactAllInProgress ? \"\" : resultsText }}</span\n >\n <span>\n <h5\n id=\"redactAllInProgressCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n >\n {{ redactAllInProgress ? redactAllText : \"\" }}\n </h5>\n </span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".redaction-search-buttons-area{display:flex;flex-direction:row;padding-top:.5rem}.redaction-search-bar{width:20rem;left:73%;top:2%}.redaction-status-text{height:2.6rem}\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
2456
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
2336
2457
  type: Component,
2337
- args: [{ selector: 'mv-search-bar', template: "<div class=\"searchbar govuk-!-padding-3\" [hidden]=\"toolbarEvents.searchBarHidden | async\">\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\" aria-label=\"Find in document\" #findInput [(ngModel)]=\"searchText\" (keydown.escape)=\"onEscapeKeyPress($event)\" (keydown.enter)=\"onEnterKeyPress($event)\" title=\"Find in document\"\n placeholder=\"Find in document\u2026\" tabindex=\"0\" data-l10n-id=\"find_input\" />\n <button class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\" data-module=\"govuk-button\"\n (click)=\"search()\" style=\" position: absolute; top: 40px; right: 10px; \">\n Search\n </button>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button searchbar-button--close\" title=\"Close Search\" data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\">\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span id=\"findResultsCount\" class=\"govuk-!-display-inline-block govuk-!-margin-right-4\">{{resultsText}}</span>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\" title=\"Find the previous occurrence of the phrase\" data-l10n-id=\"find_previous\">Prev</a>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" #findNext class=\"govuk-link govuk-link--no-visited-state\" (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\" data-l10n-id=\"find_next\">Next</a>\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state\" (click)=\"toggleAdvancedSearch()\" title=\"Advanced\"\n data-l10n-id=\"find_advanced\" style=\" position: absolute; top: 95px; right: 15px; \">Advanced</a>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\" *ngIf=\"advancedSearchVisible\">\n <div class=\"govuk-grid-column-full\">\n <div class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\">\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findHighlightAll\" name=\"findHighlightAll\" type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\" [checked]=\"highlightAll\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findHighlightAll\" data-l10n-id=\"find_highlight\">\n Highlight all\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findMatchCase\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\" [checked]=\"matchCase\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\">\n Match text (exact case)\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findEntireWord\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\" [checked]=\"wholeWord\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\">\n Match whole words or sentences\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n</div>\n" }]
2338
- }], ctorParameters: function () { return [{ type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }]; }, propDecorators: { findInput: [{
2458
+ 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=\"Find in document\"\n #findInput\n title=\"Find in document\"\n placeholder=\"Redact from search...\"\n tabindex=\"0\"\n data-l10n-id=\"search_input\"\n [(ngModel)]=\"searchText\"\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\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\"\n >\n Redact all\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 Redacting in progress\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"] }]
2459
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: HighlightCreateService }]; }, propDecorators: { findInput: [{
2339
2460
  type: ViewChild,
2340
2461
  args: ['findInput', { static: true }]
2341
- }], findNext: [{
2342
- type: ViewChild,
2343
- args: ['findNext', { static: false }]
2344
2462
  }], onWindowKeyDown: [{
2345
2463
  type: HostListener,
2346
2464
  args: ['window:keydown', ['$event']]
2347
2465
  }] } });
2348
2466
 
2349
- class MainToolbarComponent {
2350
- constructor(toolbarEvents, toolbarButtons, cdr, numberHelper) {
2351
- this.toolbarEvents = toolbarEvents;
2352
- this.toolbarButtons = toolbarButtons;
2353
- this.cdr = cdr;
2354
- this.numberHelper = numberHelper;
2355
- this.enableAnnotations = false;
2356
- this.enableRedactions = false;
2357
- this.enableICP = false;
2358
- this.contentType = null;
2359
- this.subscriptions = [];
2360
- this.icpEnabled = false;
2361
- this.redactionEnabled = false;
2362
- this.pageNumber = 1;
2363
- this.pageCount = 0;
2364
- this.isDropdownMenuOpen = false;
2365
- this.dropdownMenuPositions = [
2366
- new ConnectionPositionPair({
2367
- originX: 'end',
2368
- originY: 'bottom'
2369
- }, {
2370
- overlayX: 'end',
2371
- overlayY: 'top'
2372
- }, 0, 3)
2373
- ];
2374
- this.zoomScales = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2.5, 3, 5];
2375
- this.allButtonsWidth = 0;
2376
- this.widthRequiredForBtn = {};
2377
- }
2378
- ngOnInit() {
2379
- 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.toolbarEvents.icp.enabled.subscribe(enabled => {
2380
- this.icpEnabled = enabled;
2381
- if (this.icpEnabled) {
2382
- this.toolbarEvents.toggleCommentsPanel(!enabled);
2383
- this.toolbarEvents.sidebarOpen.next(false);
2384
- }
2385
- }), this.toolbarEvents.redactionMode.subscribe(enabled => {
2386
- this.redactionEnabled = enabled;
2387
- }), this.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
2388
- this.redactAllInProgress = disable;
2389
- }));
2467
+ // TODO: replace by NgRx
2468
+ class CommentService {
2469
+ constructor() {
2470
+ this.unsavedChanges = new Subject();
2471
+ this.marginToCommentEmitter = new BehaviorSubject(false);
2390
2472
  }
2391
- ngOnDestroy() {
2392
- for (const subscription of this.subscriptions) {
2393
- subscription.unsubscribe();
2394
- }
2473
+ setCommentSet(commentSetComponent) {
2474
+ this.commentSetComponent = commentSetComponent;
2395
2475
  }
2396
- ngAfterViewInit() {
2397
- Array.from(this.mvToolbarMain.nativeElement.children).forEach(button => {
2398
- this.allButtonsWidth += button.getBoundingClientRect().width;
2399
- this.widthRequiredForBtn[button.id] = this.allButtonsWidth;
2400
- });
2401
- this.cdr.detectChanges();
2476
+ onCommentChange(changes) {
2477
+ this.unsavedChanges.next(changes);
2402
2478
  }
2403
- onResize() {
2404
- this.cdr.detectChanges();
2479
+ getUnsavedChanges() {
2480
+ return this.unsavedChanges.asObservable();
2405
2481
  }
2406
- onControlPrint(event) {
2407
- event.preventDefault();
2408
- this.printFile();
2482
+ hasUnsavedComments(annotation) {
2483
+ if (annotation.comments.length > 0) {
2484
+ const comment = this.getComment(annotation);
2485
+ return comment.hasUnsavedChanges;
2486
+ }
2487
+ return false;
2409
2488
  }
2410
- onClickHighlightToggle() {
2411
- this.toolbarEvents.toggleHighlightMode();
2489
+ updateUnsavedCommentsStatus(annotation, hasUnsavedChanges) {
2490
+ const comment = this.getComment(annotation);
2491
+ comment.hasUnsavedChanges = hasUnsavedChanges;
2492
+ this.allCommentsSaved();
2412
2493
  }
2413
- onClickDrawToggle() {
2414
- this.toolbarEvents.toggleDrawMode();
2494
+ getComment(annotation) {
2495
+ return this.commentSetComponent.commentComponents
2496
+ .find(c => c.comment.annotationId === annotation.comments[0].annotationId);
2415
2497
  }
2416
- toggleIndexSideBar() {
2417
- const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
2418
- const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
2419
- if (!(sidebarOpen && !sidebarView)) {
2420
- this.toolbarEvents.toggleSideBar(!sidebarOpen);
2421
- }
2422
- this.toolbarEvents.toggleSideBarView(true);
2498
+ resetCommentSet() {
2499
+ this.commentSetComponent = null;
2423
2500
  }
2424
- toggleBookmarksSideBar() {
2425
- const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
2426
- const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
2427
- if (!(sidebarOpen && sidebarView)) {
2428
- this.toolbarEvents.toggleSideBar(!sidebarOpen);
2429
- }
2430
- this.toolbarEvents.toggleSideBarView(false);
2501
+ allCommentsSaved() {
2502
+ this.onCommentChange(this.commentSetComponent.commentComponents.some(comment => comment.hasUnsavedChanges === true));
2431
2503
  }
2432
- togglePresentBar() {
2433
- this.toolbarEvents.searchBarHidden.next(true);
2434
- this.toolbarEvents.icp.enable();
2504
+ createMarginToCommentEvent(margin) {
2505
+ this.marginToCommentEmitter.next(margin);
2435
2506
  }
2436
- increasePageNumber() {
2437
- this.toolbarEvents.incrementPage(1);
2507
+ }
2508
+ /** @nocollapse */ CommentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2509
+ /** @nocollapse */ CommentService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService });
2510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, decorators: [{
2511
+ type: Injectable
2512
+ }] });
2513
+
2514
+ class CommentSetRenderService {
2515
+ redrawComponents(commentComponents, pageHeights, rotate, zoom) {
2516
+ let prevComment;
2517
+ this.sortComponents(commentComponents, pageHeights, rotate, zoom).forEach((comment) => {
2518
+ this.adjustIfOverlapping(comment, prevComment, zoom);
2519
+ prevComment = comment;
2520
+ });
2438
2521
  }
2439
- decreasePageNumber() {
2440
- this.toolbarEvents.incrementPage(-1);
2522
+ sortComponents(commentComponents, pageHeights, rotate, zoom) {
2523
+ return commentComponents.sort((a, b) => {
2524
+ a.rectTop = this.top(a._rectangle, pageHeights[a.page - 1], rotate, zoom);
2525
+ b.rectTop = this.top(b._rectangle, pageHeights[b.page - 1], rotate, zoom);
2526
+ return this.processSort(a, b);
2527
+ });
2441
2528
  }
2442
- onPageNumberInputChange(pageNumber) {
2443
- if (Number(pageNumber) < 1) {
2444
- pageNumber = '1';
2445
- }
2446
- if (Number(pageNumber) > this.pageCount) {
2447
- pageNumber = this.pageCount.toString();
2529
+ adjustIfOverlapping(comment, prevComment, zoom) {
2530
+ if (prevComment) {
2531
+ const endOfPrevComment = prevComment.commentTop + this.height(prevComment);
2532
+ if (comment.commentTop <= endOfPrevComment) {
2533
+ comment.rectTop = (endOfPrevComment - comment.totalPrevPagesHeight) / zoom;
2534
+ }
2448
2535
  }
2449
- this.toolbarEvents.setPage(Number.parseInt(pageNumber, 10));
2450
2536
  }
2451
- setCurrentPage(pageNumber) {
2452
- this.pageNumber = pageNumber;
2537
+ processSort(a, b) {
2538
+ if (this.onSameLine(a, b)) {
2539
+ return a.rectLeft >= b.rectLeft ? 1 : -1;
2540
+ }
2541
+ return a.commentTop >= b.commentTop ? 1 : -1;
2453
2542
  }
2454
- rotate(rotation) {
2455
- this.toolbarEvents.rotate(rotation);
2543
+ onSameLine(a, b) {
2544
+ return this.difference(a.commentTop, b.commentTop) === 0;
2456
2545
  }
2457
- printFile() {
2458
- this.toolbarEvents.print();
2546
+ top(rectangle, height, rotate, zoom) {
2547
+ const actualHeight = height / zoom;
2548
+ switch (rotate) {
2549
+ case 90: return rectangle.x;
2550
+ case 180: return actualHeight - (rectangle.y + rectangle.height);
2551
+ case 270: return actualHeight - (rectangle.x + rectangle.width);
2552
+ default: return rectangle.y;
2553
+ }
2459
2554
  }
2460
- downloadFile() {
2461
- this.toolbarEvents.download();
2555
+ height(element) {
2556
+ return element.form.nativeElement.getBoundingClientRect().height;
2462
2557
  }
2463
- zoom(zoomFactor) {
2464
- this.toolbarEvents.zoom(+zoomFactor);
2558
+ difference(a, b) { return Math.abs(a - b); }
2559
+ }
2560
+ /** @nocollapse */ CommentSetRenderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2561
+ /** @nocollapse */ CommentSetRenderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService });
2562
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, decorators: [{
2563
+ type: Injectable
2564
+ }] });
2565
+
2566
+ class TagsServices {
2567
+ constructor(http) {
2568
+ this.http = http;
2569
+ this.snakeCase = string => {
2570
+ // transform string_to_snake_case
2571
+ return string.replace(/ +/g, ' ') // find space
2572
+ .split(/ |\B(?=[A-Z])/) // split it into array
2573
+ .map(word => word.toLowerCase()) // transform to lover case
2574
+ .join('_'); // trun array into sting using _
2575
+ };
2465
2576
  }
2466
- stepZoom(zoomFactor) {
2467
- this.toolbarEvents.stepZoom(zoomFactor);
2468
- this.zoomSelect.nativeElement.selected = 'selected';
2577
+ getAllTags(createdBy) {
2578
+ const url = `/em-anno/tags/${createdBy}`;
2579
+ return this.http.get(url);
2469
2580
  }
2470
- toggleCommentsPanel() {
2471
- this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
2581
+ // @TODO: Move everything below this to NgRx store
2582
+ getNewTags(annoid) {
2583
+ return this.tagItems ? this.tagItems[annoid] : [];
2472
2584
  }
2473
- toggleRedactBar() {
2474
- this.toolbarEvents.toggleRedactionMode();
2585
+ updateTagItems(items, annoId) {
2586
+ const snakeCased = items.map(item => {
2587
+ return Object.assign(Object.assign({}, item), { name: this.snakeCase(item.name) });
2588
+ });
2589
+ this.tagItems = Object.assign(Object.assign({}, this.tagItems), { [annoId]: snakeCased });
2475
2590
  }
2476
- toggleGrabNDrag() {
2477
- this.toolbarEvents.toggleGrabNDrag();
2591
+ }
2592
+ /** @nocollapse */ TagsServices.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
2593
+ /** @nocollapse */ TagsServices.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices });
2594
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, decorators: [{
2595
+ type: Injectable
2596
+ }], ctorParameters: function () { return [{ type: i1$1.HttpClient }]; } });
2597
+
2598
+ class TextHighlightDirective {
2599
+ constructor(element) {
2600
+ this.element = element;
2478
2601
  }
2479
- isPdf() {
2480
- return this.contentType === 'pdf';
2602
+ ngAfterViewChecked() {
2603
+ if (this.textToHighlight) {
2604
+ this.highlightInputText(this.textToHighlight);
2605
+ }
2481
2606
  }
2482
- toggleSearchBar() {
2483
- this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
2607
+ highlightInputText(textToHighlight) {
2608
+ this.resetHighlight();
2609
+ this.textToHighlight = textToHighlight;
2610
+ const searchPattern = new RegExp(textToHighlight, 'gi');
2611
+ const hostElement = this.element.nativeElement;
2612
+ if (hostElement.innerHTML.match(searchPattern)) {
2613
+ hostElement.innerHTML = hostElement.innerHTML
2614
+ .replace(searchPattern, this.highlightPattern('$&'));
2615
+ }
2616
+ this.textToHighlight = undefined;
2484
2617
  }
2485
- toggleMoreOptions() {
2486
- this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
2487
- setTimeout(() => {
2488
- if (this.mvMenuItems) {
2489
- this.mvMenuItems.nativeElement.focus();
2618
+ resetHighlight() {
2619
+ const hostElement = this.element.nativeElement;
2620
+ const searchPattern = new RegExp(this.highlightPattern('(.*?)'), 'gi');
2621
+ while (hostElement.innerHTML.match(searchPattern)) {
2622
+ const matchGroups = searchPattern.exec(hostElement.innerHTML);
2623
+ if (matchGroups) {
2624
+ hostElement.innerHTML = hostElement.innerHTML.replace(matchGroups[0], matchGroups[1]);
2490
2625
  }
2491
- }, 100);
2626
+ }
2627
+ }
2628
+ highlightPattern(dynamicText) {
2629
+ return '<span class="mvTextHighlight">' + dynamicText + '</span>';
2492
2630
  }
2493
2631
  }
2494
- /** @nocollapse */ MainToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i0.ChangeDetectorRef }, { token: NumberHelperService }], target: i0.ɵɵFactoryTarget.Component });
2495
- /** @nocollapse */ MainToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 aria-pressed=\"false\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>More options</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Index</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Bookmarks</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 tabindex=\"-1\"\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-hidden=\"true\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\">Draw a box</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 tabindex=\"0\"\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.highlightModeSubject | async\"\n aria-hidden=\"true\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">Highlight</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 aria-pressed=\"false\"\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</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></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></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 aria-pressed=\"false\"\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</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 ></button>\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPresentBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPresentBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\">Present</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRedactBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRedactBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>Redact</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvGrabBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvGrabBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>Grab and drag</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDownloadBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDownloadBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPrintBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPrintBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvCommentsBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvCommentsBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>Comments</span>\n </button>\n </ng-template>\n </div>\n <mv-search-bar></mv-search-bar>\n </div>\n\n <div id=\"loadingBar\">\n <div class=\"progress\">\n <div class=\"glimmer\"></div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i6.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"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i6.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: SearchBarComponent, selector: "mv-search-bar" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.DecimalPipe, name: "number" }] });
2496
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, decorators: [{
2497
- type: Component,
2498
- 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 aria-pressed=\"false\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>More options</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Index</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Bookmarks</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 tabindex=\"-1\"\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-hidden=\"true\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\">Draw a box</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 tabindex=\"0\"\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.highlightModeSubject | async\"\n aria-hidden=\"true\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">Highlight</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 aria-pressed=\"false\"\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</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></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></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 aria-pressed=\"false\"\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</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 ></button>\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPresentBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPresentBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\">Present</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRedactBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRedactBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>Redact</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvGrabBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvGrabBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>Grab and drag</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDownloadBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDownloadBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPrintBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPrintBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvCommentsBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvCommentsBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>Comments</span>\n </button>\n </ng-template>\n </div>\n <mv-search-bar></mv-search-bar>\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" }]
2499
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i0.ChangeDetectorRef }, { type: NumberHelperService }]; }, propDecorators: { enableAnnotations: [{
2500
- type: Input
2501
- }], enableRedactions: [{
2502
- type: Input
2503
- }], enableICP: [{
2504
- type: Input
2505
- }], contentType: [{
2632
+ /** @nocollapse */ TextHighlightDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2633
+ /** @nocollapse */ TextHighlightDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 });
2634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, decorators: [{
2635
+ type: Directive,
2636
+ args: [{
2637
+ selector: '[mvTextHighlight]'
2638
+ }]
2639
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { textToHighlight: [{
2506
2640
  type: Input
2507
- }], zoomSelect: [{
2508
- type: ViewChild,
2509
- args: ['zoomSelect', { static: false }]
2510
- }], mvToolbarMain: [{
2511
- type: ViewChild,
2512
- args: ['mvToolbarMain', { static: false }]
2513
- }], mvMenuItems: [{
2514
- type: ViewChild,
2515
- args: ['dropdownMenu', { static: false }]
2516
- }], onResize: [{
2517
- type: HostListener,
2518
- args: ['window:resize', []]
2519
- }], onControlPrint: [{
2520
- type: HostListener,
2521
- args: ['document:keydown.control.p', ['$event']]
2522
- }, {
2523
- type: HostListener,
2524
- args: ['document:keydown.meta.p', ['$event']]
2525
2641
  }] } });
2526
2642
 
2527
- const getRedactionState = createSelector(getMVState, (state) => state.redactions);
2528
- const getRedactionPages = createSelector(getRedactionState, getPageEnt);
2529
- const getSelected = createSelector(getRedactionState, getSelectedRedaction);
2530
- const getRedactedDocumentInfo = createSelector(getRedactionState, getRedactedDocInfo);
2531
- const getRedactionEnt = createSelector(getRedactionState, getRedactionEnt$1);
2532
- const getRedactionArray = createSelector(getRedactionEnt, getDocumentId, (ent, documentId) => {
2533
- const redactions = Object.keys(ent).map(key => ent[key]);
2534
- return { redactions, documentId };
2535
- });
2536
- const getRedactionsPerPage = createSelector(getPages, getRedactionPages, (pages, pageEnt) => {
2537
- if (pages && pageEnt) {
2538
- const arr = [];
2539
- Object.keys(pages).forEach(key => {
2540
- arr.push({
2541
- anno: pageEnt[key] ? pageEnt[key] : [],
2542
- styles: pages[key].styles
2543
- });
2544
- });
2545
- return arr;
2546
- }
2547
- });
2548
-
2549
- class RedactionToolbarComponent {
2550
- constructor(toolbarEventService, toolbarButtons, store) {
2551
- this.toolbarEventService = toolbarEventService;
2552
- this.toolbarButtons = toolbarButtons;
2553
- this.store = store;
2554
- this.preview = false;
2555
- this.hasRedactions = false;
2556
- this.subscriptions = [];
2557
- }
2558
- ngOnInit() {
2559
- this.subscriptions.push(this.store.pipe(select(getRedactionArray)).subscribe(redactions => {
2560
- this.hasRedactions = !!redactions.redactions.length;
2561
- }));
2562
- this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
2563
- this.redactionAllInProgress = inprogress;
2564
- }));
2565
- }
2566
- onRedactAllSearch() {
2567
- this.toolbarEventService.openRedactionSearch.next(true);
2568
- }
2569
- toggleTextRedactionMode() {
2570
- this.toolbarEventService.highlightModeSubject.next(true);
2643
+ class TextareaAutoExpandDirective {
2644
+ constructor(el) {
2645
+ this.el = el;
2571
2646
  }
2572
- toggleDrawMode() {
2573
- this.toolbarEventService.drawModeSubject.next(true);
2647
+ ngAfterContentChecked() {
2648
+ this.adjustHeight();
2574
2649
  }
2575
- togglePreview() {
2576
- this.preview = !this.preview;
2577
- this.toolbarEventService.toggleRedactionPreview(this.preview);
2650
+ onMouseDown() {
2651
+ this.adjustHeight();
2578
2652
  }
2579
- unmarkAll() {
2580
- this.toolbarEventService.unmarkAll();
2653
+ adjustHeight() {
2654
+ const nativeElement = this.el.nativeElement;
2655
+ nativeElement.style.overflow = 'hidden';
2656
+ nativeElement.style.height = 'auto';
2657
+ nativeElement.style.height = nativeElement.scrollHeight - 5 + 'px';
2581
2658
  }
2582
- redact() {
2583
- this.toolbarEventService.applyRedactionToDocument();
2659
+ }
2660
+ /** @nocollapse */ TextareaAutoExpandDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2661
+ /** @nocollapse */ TextareaAutoExpandDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 });
2662
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
2663
+ type: Directive,
2664
+ args: [{
2665
+ selector: '[mvTextAreaAutoExpand]'
2666
+ }]
2667
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { onMouseDown: [{
2668
+ type: HostListener,
2669
+ args: ['input']
2670
+ }] } });
2671
+
2672
+ class TagsComponent {
2673
+ constructor(tagsServices) {
2674
+ this.tagsServices = tagsServices;
2675
+ this.validators = [this.minLength, this.maxLength20];
2676
+ this.errorMessages = {
2677
+ 'minLength': 'Minimum of 2 characters',
2678
+ 'maxLength20': 'Maximum of 20 characters'
2679
+ };
2680
+ this.requestAutocompleteItems = (text) => {
2681
+ return this.tagsServices.getAllTags(this.userId);
2682
+ };
2584
2683
  }
2585
- toggleRedactBar() {
2586
- this.toolbarEventService.toggleRedactionMode();
2684
+ onUpdateTags(value) {
2685
+ this.tagsServices.updateTagItems(value, this.annoId);
2587
2686
  }
2588
- redactPage() {
2589
- this.toolbarEventService.drawModeSubject.next(true);
2590
- this.toolbarEventService.redactPage();
2687
+ minLength(control) {
2688
+ if (control.value.length < 2) {
2689
+ return {
2690
+ 'minLength': true
2691
+ };
2692
+ }
2693
+ return null;
2591
2694
  }
2592
- ngOnDestroy() {
2593
- for (const subscription of this.subscriptions) {
2594
- subscription.unsubscribe();
2695
+ maxLength20(control) {
2696
+ if (control.value.length >= 20) {
2697
+ return {
2698
+ 'maxLength20': true
2699
+ };
2595
2700
  }
2701
+ return null;
2596
2702
  }
2597
2703
  }
2598
- /** @nocollapse */ RedactionToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
2599
- /** @nocollapse */ RedactionToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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</label\n >\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw\"\n title=\"Draw a box\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">Draw a box</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"Redact Page\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">Redact page</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"From search\"\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</span\n >\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\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">Redact text</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\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">Clear all</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\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">Preview</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\"\n >Hide preview</span\n >\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=\"Redact\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">Save document</span>\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)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Redaction</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2600
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
2704
+ /** @nocollapse */ TagsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
2705
+ /** @nocollapse */ TagsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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)=\"onUpdateTags($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n [placeholder]=\"'Add tag'\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [onTextChangeDebounce]=\"250\"\n [secondaryPlaceholder]=\"'Search or add tags'\"\n [validators]=\"validators\"\n [errorMessages]=\"errorMessages\"\n [onlyFromAutocomplete]=\"false\">\n <tag-input-dropdown\n [autocompleteObservable]=\"requestAutocompleteItems\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [zIndex]=\"10000\"\n [minimumTextLength]=\"2\">\n <ng-template let-item=\"item\" let-index=\"index\">\n {{ item.label }}\n </ng-template>\n </tag-input-dropdown>\n </tag-input>\n\n <div class=\"tags__renderer\" *ngIf=\"!editable && (tagItems && tagItems.length)\">\n <span *ngFor=\"let tag of tagItems\" class=\"tags__item hmcts-badge\">\n {{tag.label}}\n </span>\n </div>\n</div>\n\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$1.TagInputComponent, selector: "tag-input", inputs: ["separatorKeys", "separatorKeyCodes", "placeholder", "secondaryPlaceholder", "maxItems", "validators", "asyncValidators", "onlyFromAutocomplete", "errorMessages", "theme", "onTextChangeDebounce", "inputId", "inputClass", "clearOnBlur", "hideForm", "addOnBlur", "addOnPaste", "pasteSplitPattern", "blinkIfDupe", "removable", "editable", "allowDupes", "modelAsStrings", "trimTags", "ripple", "tabindex", "disable", "dragZone", "onRemoving", "onAdding", "animationDuration", "inputText"], outputs: ["onAdd", "onRemove", "onSelect", "onFocus", "onBlur", "onTextChange", "onPaste", "onValidationError", "onTagEdited", "inputTextChange"] }, { kind: "component", type: i4$1.TagInputDropdown, selector: "tag-input-dropdown", inputs: ["offset", "focusFirstElement", "showDropdownIfEmpty", "minimumTextLength", "limitItemsTo", "displayBy", "identifyBy", "matchingFn", "appendToBody", "keepOpen", "dynamicUpdate", "zIndex", "autocompleteItems", "autocompleteObservable"] }], encapsulation: i0.ViewEncapsulation.None });
2706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, decorators: [{
2601
2707
  type: Component,
2602
- args: [{ selector: 'mv-redaction-toolbar', template: "<div class=\"redaction\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >Redaction options</label\n >\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw\"\n title=\"Draw a box\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">Draw a box</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"Redact Page\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">Redact page</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"From search\"\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</span\n >\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\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">Redact text</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\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">Clear all</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\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">Preview</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\"\n >Hide preview</span\n >\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=\"Redact\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">Save document</span>\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)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Redaction</span>\n </button>\n</div>\n" }]
2603
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }]; }, propDecorators: { showRedactSearch: [{
2708
+ 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)=\"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" }]
2709
+ }], ctorParameters: function () { return [{ type: TagsServices }]; }, propDecorators: { tagItems: [{
2710
+ type: Input
2711
+ }], userId: [{
2712
+ type: Input
2713
+ }], editable: [{
2714
+ type: Input
2715
+ }], annoId: [{
2604
2716
  type: Input
2605
2717
  }] } });
2606
2718
 
2607
- class IcpToolbarComponent {
2608
- constructor(toolbarEventService, store) {
2609
- this.toolbarEventService = toolbarEventService;
2719
+ /**
2720
+ * A moment timezone pipe to support parsing based on time zone abbreviations
2721
+ * covers all cases of offset variation due to daylight saving.
2722
+ *
2723
+ * Same API as DatePipe with additional timezone abbreviation support
2724
+ * Official date pipe dropped support for abbreviations names from Angular V5
2725
+ */
2726
+ class MomentDatePipe extends DatePipe {
2727
+ transform(value, format = 'mediumDate', timezone = 'Europe/London') {
2728
+ const timezoneOffset = moment(value).tz(timezone).format('Z');
2729
+ return super.transform(value, format, timezoneOffset);
2730
+ }
2731
+ }
2732
+ /** @nocollapse */ MomentDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe });
2733
+ /** @nocollapse */ MomentDatePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, name: "momentDate" });
2734
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, decorators: [{
2735
+ type: Pipe,
2736
+ args: [{
2737
+ name: 'momentDate'
2738
+ }]
2739
+ }] });
2740
+
2741
+ class CommentComponent {
2742
+ constructor(store, commentService, tagsServices) {
2610
2743
  this.store = store;
2744
+ this.commentService = commentService;
2745
+ this.tagsServices = tagsServices;
2746
+ this.CHAR_LIMIT = 5000;
2747
+ this.totalPrevPagesHeight = 0;
2748
+ this.hasUnsavedChanges = false;
2749
+ this.commentClick = new EventEmitter();
2750
+ this.renderComments = new EventEmitter();
2751
+ this.delete = new EventEmitter();
2752
+ this.updated = new EventEmitter();
2753
+ this.changes = new EventEmitter();
2754
+ this.rotate = 0;
2755
+ this.zoom = 1;
2611
2756
  }
2612
2757
  ngOnInit() {
2613
- this.$subscription = this.store.pipe(select(isPresenter))
2614
- .subscribe(isPresenter => this.isPresenter = isPresenter);
2615
- this.$subscription.add(this.store.pipe(select(getPresenterName))
2616
- .subscribe(name => this.presenterName = name));
2758
+ this.subscriptions = this.store.select(getComponentSearchText)
2759
+ .pipe(distinctUntilChanged()).subscribe(searchString => this.searchString = searchString);
2760
+ this.reRenderComments();
2761
+ this.marginToComment$ = this.commentService.marginToCommentEmitter.asObservable();
2617
2762
  }
2618
- ngOnDestroy() {
2619
- this.$subscription.unsubscribe();
2763
+ ngAfterContentInit() {
2764
+ if (this.tagItems && this.tagItems.length) {
2765
+ this.tagsServices.updateTagItems(this.tagItems, this._comment.annotationId);
2766
+ }
2620
2767
  }
2621
- present() {
2622
- this.toolbarEventService.icp.becomePresenter();
2768
+ ngOnDestroy() {
2769
+ this.subscriptions.unsubscribe();
2623
2770
  }
2624
- stopPresenting() {
2625
- this.toolbarEventService.icp.stopPresenting();
2771
+ set comment(comment) {
2772
+ this._comment = Object.assign({}, comment);
2773
+ this.page = this._comment.page;
2774
+ this.lastUpdate = comment.lastModifiedDate ? comment.lastModifiedDate : comment.createdDate;
2775
+ this.author = comment.createdByDetails;
2776
+ this.createdBy = comment.createdBy;
2777
+ this.editor = comment.lastModifiedByDetails;
2778
+ this.originalComment = comment.content;
2779
+ this.fullComment = this.originalComment;
2780
+ this.selected = this._comment.selected;
2781
+ this._editable = this._comment.editable;
2782
+ this.tagItems = this._comment.tags;
2783
+ const pageMarginBottom = 10;
2784
+ this.totalPrevPagesHeight = 0;
2785
+ for (let i = 0; i < this.page - 1; i++) {
2786
+ const height = this._comment.pages[i + 1] ? this._comment.pages[i + 1].styles.height : undefined;
2787
+ if (height) {
2788
+ this.totalPrevPagesHeight += height + pageMarginBottom;
2789
+ }
2790
+ }
2626
2791
  }
2627
- leaveIcpSession() {
2628
- this.toolbarEventService.icp.leaveSession();
2792
+ get comment() {
2793
+ return this._comment;
2629
2794
  }
2630
- showParticipantsList() {
2631
- this.toolbarEventService.toggleParticipantsList(!this.toolbarEventService.icp.participantsListVisible.getValue());
2795
+ set annotation(annotation) {
2796
+ this._rectangle = annotation.rectangles
2797
+ .reduce((prev, current) => prev.y < current.y ? prev : current);
2798
+ const actualHeight = this._comment.pages[this.page].styles.height / this.zoom;
2799
+ switch (this.rotate) {
2800
+ case 90:
2801
+ this.rectTop = this._rectangle.x;
2802
+ break;
2803
+ case 180:
2804
+ this.rectTop = actualHeight - (this._rectangle.y + this._rectangle.height);
2805
+ break;
2806
+ case 270:
2807
+ this.rectTop = actualHeight - (this._rectangle.x + this._rectangle.width);
2808
+ break;
2809
+ default: this.rectTop = this._rectangle.y;
2810
+ }
2811
+ this.rectLeft = this._rectangle.x;
2632
2812
  }
2633
- }
2634
- /** @nocollapse */ IcpToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
2635
- /** @nocollapse */ IcpToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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\">{{(toolbarEventService.icp.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }] });
2636
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, decorators: [{
2637
- type: Component,
2638
- 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\">{{(toolbarEventService.icp.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>" }]
2639
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
2640
-
2641
- class ToolbarModule {
2642
- }
2643
- /** @nocollapse */ ToolbarModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2644
- /** @nocollapse */ ToolbarModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
2645
- MainToolbarComponent,
2646
- RedactionToolbarComponent,
2647
- IcpToolbarComponent,
2648
- RedactionSearchBarComponent], imports: [CommonModule,
2649
- FormsModule,
2650
- OverlayModule,
2651
- RouterModule], exports: [MainToolbarComponent,
2652
- SearchBarComponent,
2653
- RedactionToolbarComponent,
2654
- IcpToolbarComponent,
2655
- RedactionSearchBarComponent] });
2656
- /** @nocollapse */ ToolbarModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, providers: [
2657
- ToolbarButtonVisibilityService,
2658
- ToolbarEventService
2659
- ], imports: [CommonModule,
2660
- FormsModule,
2661
- OverlayModule,
2662
- RouterModule] });
2663
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, decorators: [{
2664
- type: NgModule,
2665
- args: [{
2666
- declarations: [
2667
- SearchBarComponent,
2668
- MainToolbarComponent,
2669
- RedactionToolbarComponent,
2670
- IcpToolbarComponent,
2671
- RedactionSearchBarComponent
2672
- ],
2673
- providers: [
2674
- ToolbarButtonVisibilityService,
2675
- ToolbarEventService
2676
- ],
2677
- exports: [
2678
- MainToolbarComponent,
2679
- SearchBarComponent,
2680
- RedactionToolbarComponent,
2681
- IcpToolbarComponent,
2682
- RedactionSearchBarComponent
2683
- ],
2684
- imports: [
2685
- CommonModule,
2686
- FormsModule,
2687
- OverlayModule,
2688
- RouterModule
2689
- ]
2690
- }]
2691
- }] });
2692
-
2693
- class HighlightCreateService {
2694
- constructor(toolBarEvents, store) {
2695
- this.toolBarEvents = toolBarEvents;
2696
- this.store = store;
2813
+ get editable() {
2814
+ return this._editable;
2697
2815
  }
2698
- saveAnnotation(rectangles, page) {
2699
- this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
2700
- const anno = Object.assign(Object.assign({ 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: [] });
2701
- this.store.dispatch(new SaveAnnotation(anno));
2702
- });
2816
+ onCommentChange(updatedComment) {
2817
+ this.hasUnsavedChanges =
2818
+ this.originalComment.substring(0, this.CHAR_LIMIT) !== updatedComment.substring(0, this.CHAR_LIMIT);
2819
+ this.commentService.onCommentChange(this.hasUnsavedChanges);
2703
2820
  }
2704
- applyRotation(pageHeight, pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, rotate, zoom) {
2705
- const { x, y, width, height } = {
2706
- x: +(offsetLeft / zoom).toFixed(2),
2707
- y: +(offsetTop / zoom).toFixed(2),
2708
- width: +(offsetWidth / zoom).toFixed(2),
2709
- height: +(offsetHeight / zoom).toFixed(2)
2710
- };
2711
- const rectangle = { x, y, width, height };
2712
- switch (rotate) {
2713
- case 90:
2714
- rectangle.width = height;
2715
- rectangle.height = width;
2716
- rectangle.x = y;
2717
- rectangle.y = +(pageWidth / zoom - x - width).toFixed(2);
2718
- break;
2719
- case 180:
2720
- rectangle.x = +(pageWidth / zoom - x - width).toFixed(2);
2721
- rectangle.y = +(pageHeight / zoom - y - height).toFixed(2);
2722
- break;
2723
- case 270:
2724
- rectangle.width = height;
2725
- rectangle.height = width;
2726
- rectangle.x = +(pageHeight / zoom - y - height).toFixed(2);
2727
- rectangle.y = x;
2728
- break;
2821
+ deleteOrCancel() {
2822
+ if (!this.editable) {
2823
+ this.delete.emit(this._comment);
2824
+ }
2825
+ else {
2826
+ this.hasUnsavedChanges = false;
2827
+ this._editable = false;
2828
+ this.fullComment = this.originalComment;
2829
+ this.changes.emit(false);
2830
+ if (!this.author && !this.fullComment) {
2831
+ this.delete.emit(this._comment);
2832
+ }
2729
2833
  }
2730
- return rectangle;
2731
2834
  }
2732
- resetHighlight() {
2733
- window.getSelection().removeAllRanges();
2734
- this.toolBarEvents.highlightModeSubject.next(false);
2835
+ editOrSave() {
2836
+ if (!this.editable) {
2837
+ this._editable = true;
2838
+ }
2839
+ else {
2840
+ this._comment.content = this.fullComment.substring(0, this.CHAR_LIMIT);
2841
+ const tags = this.tagsServices.getNewTags(this._comment.annotationId);
2842
+ const payload = {
2843
+ comment: this._comment,
2844
+ tags
2845
+ };
2846
+ this.updated.emit(payload);
2847
+ this.hasUnsavedChanges = false;
2848
+ this._editable = false;
2849
+ this.changes.emit(false);
2850
+ }
2851
+ }
2852
+ onCommentClick() {
2853
+ if (!this.selected) {
2854
+ this.selected = true;
2855
+ this._editable = false;
2856
+ this.commentClick.emit({ annotationId: this._comment.annotationId, editable: this._editable, selected: true });
2857
+ }
2858
+ }
2859
+ reRenderComments() {
2860
+ this.renderComments.emit(this._comment);
2861
+ }
2862
+ get commentTop() {
2863
+ return this.totalPrevPagesHeight + (this.rectTop * this.zoom);
2864
+ }
2865
+ get height() {
2866
+ return this.form.nativeElement.getBoundingClientRect().height / this.zoom;
2735
2867
  }
2736
2868
  }
2737
- /** @nocollapse */ HighlightCreateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable });
2738
- /** @nocollapse */ HighlightCreateServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService });
2739
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, decorators: [{
2740
- type: Injectable
2741
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
2869
+ /** @nocollapse */ CommentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
2870
+ /** @nocollapse */ CommentComponentcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 #form (click)=\"onCommentClick()\" class=\"aui-comment\"\n [ngClass]=\"{'stylestoggle' : (marginToComment$ | async ) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\">\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 </mv-tags>\n <textarea *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)=\"reRenderComments(); onCommentChange($event);\"\n aria-label=\"comment\">\n </textarea>\n <p *ngIf=\"!editable\"\n mvTextHighlight class=\"commentText\" [textToHighlight]=\"searchString\">\n {{ fullComment }}\n </p>\n <div *ngIf=\"selected || this.editable || (!fullComment.length && (tagItems && !tagItems.length))\"\n class=\"aui-comment__footer commentBtns\">\n <button class=\"govuk-button\"\n type=\"button\" role=\"button\"\n (click)=\"editOrSave()\">\n {{ !editable ? 'Edit' : 'Save' }}\n </button>\n <button type=\"button\" role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\">\n {{ !editable ? 'Delete' : 'Cancel' }}\n </button>\n </div>\n <span class=\"aui-comment__private\">private</span>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: ["textToHighlight"] }, { kind: "directive", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]" }, { kind: "component", type: TagsComponent, selector: "mv-tags", inputs: ["tagItems", "userId", "editable", "annoId"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: MomentDatePipe, name: "momentDate" }] });
2871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, decorators: [{
2872
+ type: Component,
2873
+ args: [{ selector: 'mv-anno-comment', template: "<div #form (click)=\"onCommentClick()\" class=\"aui-comment\"\n [ngClass]=\"{'stylestoggle' : (marginToComment$ | async ) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\">\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 </mv-tags>\n <textarea *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)=\"reRenderComments(); onCommentChange($event);\"\n aria-label=\"comment\">\n </textarea>\n <p *ngIf=\"!editable\"\n mvTextHighlight class=\"commentText\" [textToHighlight]=\"searchString\">\n {{ fullComment }}\n </p>\n <div *ngIf=\"selected || this.editable || (!fullComment.length && (tagItems && !tagItems.length))\"\n class=\"aui-comment__footer commentBtns\">\n <button class=\"govuk-button\"\n type=\"button\" role=\"button\"\n (click)=\"editOrSave()\">\n {{ !editable ? 'Edit' : 'Save' }}\n </button>\n <button type=\"button\" role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\">\n {{ !editable ? 'Delete' : 'Cancel' }}\n </button>\n </div>\n <span class=\"aui-comment__private\">private</span>\n</div>\n" }]
2874
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: TagsServices }]; }, propDecorators: { commentClick: [{
2875
+ type: Output
2876
+ }], renderComments: [{
2877
+ type: Output
2878
+ }], delete: [{
2879
+ type: Output
2880
+ }], updated: [{
2881
+ type: Output
2882
+ }], changes: [{
2883
+ type: Output
2884
+ }], rotate: [{
2885
+ type: Input
2886
+ }], zoom: [{
2887
+ type: Input
2888
+ }], index: [{
2889
+ type: Input
2890
+ }], page: [{
2891
+ type: Input
2892
+ }], form: [{
2893
+ type: ViewChild,
2894
+ args: ['form', { static: false }]
2895
+ }], editableComment: [{
2896
+ type: ViewChild,
2897
+ args: ['editableComment', { static: false }]
2898
+ }], comment: [{
2899
+ type: Input
2900
+ }], annotation: [{
2901
+ type: Input
2902
+ }] } });
2742
2903
 
2743
- class RedactionSearchBarComponent {
2744
- constructor(store, toolbarButtons, toolbarEvents, highlightService) {
2904
+ class CommentSetComponent {
2905
+ constructor(store, commentService, renderService, toolbarEvents) {
2745
2906
  this.store = store;
2746
- this.toolbarButtons = toolbarButtons;
2907
+ this.commentService = commentService;
2908
+ this.renderService = renderService;
2747
2909
  this.toolbarEvents = toolbarEvents;
2748
- this.highlightService = highlightService;
2749
- this.highlightAll = true;
2750
- this.matchCase = false;
2751
- this.wholeWord = false;
2752
- this.resultsText = '';
2753
- this.searchText = '';
2754
- this.resultCount = 0;
2755
- this.redactElements = [];
2756
- this.advancedSearchVisible = false;
2910
+ this.pageHeights = [];
2911
+ this.subscriptions = [];
2912
+ this.clearSelection();
2757
2913
  }
2758
2914
  ngOnInit() {
2759
- this.subscription = this.toolbarEvents.redactionSerachSubject.subscribe((results) => this.redactAllSearched(results));
2760
- this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
2761
- this.subscription.add(this.store.pipe(select(getPages)).subscribe((pages) => {
2762
- if (pages[1]) {
2763
- this.allPages = pages;
2764
- }
2915
+ this.comments$ = this.store.pipe(select(getCommentsArray));
2916
+ this.annoEntities$ = this.store.pipe(select(getAnnotationEntities));
2917
+ this.subscriptions.push(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
2918
+ this.redrawComments();
2919
+ this.showCommentsPanel = toggle;
2765
2920
  }));
2766
- this.subscription.add(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
2767
- this.subscription.add(this.toolbarEvents.openRedactionSearch.subscribe(isOpen => this.openSearchModal = isOpen));
2768
- this.subscription.add(this.toolbarEvents.redactAllInProgressSubject
2769
- .subscribe(inProgress => this.redactAllInProgress = inProgress));
2770
- }
2771
- ngOnDestroy() {
2772
- this.subscription.unsubscribe();
2773
- }
2774
- onWindowKeyDown(e) {
2775
- if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
2776
- e.preventDefault();
2777
- this.toolbarEvents.searchBarHidden.next(false);
2778
- setTimeout(() => this.findInput.nativeElement.focus(), 200);
2779
- }
2921
+ this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument()));
2780
2922
  }
2781
- search(reset = true) {
2782
- this.redactAll = !reset;
2783
- if (this.redactAll) {
2784
- this.toolbarEvents.redactAllInProgressSubject.next(true);
2923
+ ngOnChanges(changes) {
2924
+ if (changes.annotationSet) {
2925
+ this.commentService.setCommentSet(this);
2785
2926
  }
2786
- if (reset) {
2787
- this.redactElements = [];
2927
+ if (changes.contentScrollTop) {
2928
+ if (this.container) {
2929
+ this.container.nativeElement.scrollTo(0, this.contentScrollTop);
2930
+ }
2788
2931
  }
2789
- this.toolbarEvents.search({
2790
- searchTerm: this.searchText,
2791
- highlightAll: this.highlightAll,
2792
- matchCase: this.matchCase,
2793
- wholeWord: this.wholeWord,
2794
- previous: false,
2795
- reset
2796
- });
2797
- }
2798
- saveRedaction(redactRectangle) {
2799
- const redaction = redactRectangle.map(ele => {
2800
- return { page: ele.page, rectangles: ele.rectangles, redactionId: uuid$1(), documentId: this.documentId };
2801
- });
2802
- this.store.dispatch(new SaveBulkRedaction({ searchRedactions: redaction }));
2803
2932
  }
2804
- existInRedactElements(pageNumber, matechedIndex, rectangles) {
2805
- if (this.redactElements && this.redactElements.length > 0) {
2806
- const pagesFound = this.redactElements.find(re => re.page === pageNumber && re.matchedIndex === matechedIndex);
2807
- const pageRectangles = pagesFound === null || pagesFound === void 0 ? void 0 : pagesFound.rectangles;
2808
- if (!pageRectangles || pageRectangles.length <= 0) {
2809
- return false;
2810
- }
2811
- let matchesRectangles = 0;
2812
- for (let rectIndx = 0; rectIndx < pageRectangles.length; rectIndx++) {
2813
- const rectangle = pageRectangles[rectIndx];
2814
- const foundRectangle = rectangles.find(re => re.width === rectangle.width &&
2815
- re.height === rectangle.height && re.x === rectangle.x && re.y === rectangle.y);
2816
- if (foundRectangle) {
2817
- matchesRectangles++;
2818
- }
2819
- }
2820
- return pageRectangles.length === matchesRectangles;
2933
+ ngOnDestroy() {
2934
+ if (this.subscriptions.length > 0) {
2935
+ this.subscriptions.forEach(subscription => subscription.unsubscribe());
2821
2936
  }
2822
- return false;
2823
2937
  }
2824
- onCloseSearchModal() {
2825
- this.toolbarEvents.openRedactionSearch.next(false);
2938
+ onSelect(annotationId) {
2939
+ this.store.dispatch(new SelectedAnnotation(annotationId));
2826
2940
  }
2827
- setSearchResultsCount(results) {
2828
- this.resultCount = results.total;
2829
- this.resultsText = this.resultCount > 0
2830
- ? `${results.total} results founds`
2831
- : 'No results found';
2941
+ onCommentDelete(comment) {
2942
+ const annotation = this.annotationSet.annotations.find(anno => anno.id === comment.annotationId);
2943
+ const comments = [];
2944
+ const annot = Object.assign(Object.assign({}, annotation), { comments });
2945
+ this.onAnnotationUpdate(annot);
2946
+ this.redrawComments();
2832
2947
  }
2833
- redactAllSearched(results) {
2834
- const $this = this;
2835
- const intervalId = setInterval(() => {
2836
- const highlightElement = document.getElementsByClassName('highlight selected');
2837
- if (highlightElement && highlightElement.length > 0) {
2838
- clearInterval(intervalId);
2839
- $this.redactAllSearchedTick(results);
2840
- }
2841
- }, 100);
2948
+ redrawComments() {
2949
+ setTimeout(() => {
2950
+ const componentList = this.commentComponents.map(comment => comment);
2951
+ this.renderService.redrawComponents(componentList, this.pageHeights, this.rotate, this.zoom);
2952
+ }, 0);
2842
2953
  }
2843
- redactAllSearchedTick(results) {
2844
- const highlightElement = document.getElementsByClassName('highlight selected');
2845
- if (highlightElement && highlightElement.length > 0) {
2846
- this.resultCount = results.matchesCount;
2847
- const pageNumber = results.page + 1;
2848
- const rectangles = this.getRectangles(pageNumber);
2849
- if (rectangles && this.redactElements.length <= this.resultCount
2850
- && !this.existInRedactElements(pageNumber, results.matchedIndex, rectangles)) {
2851
- this.redactElements.push({ page: pageNumber, matchedIndex: results === null || results === void 0 ? void 0 : results.matchedIndex, rectangles });
2852
- this.CreateRedactAllText();
2853
- }
2854
- if (this.redactAll && this.resultCount && this.resultCount > 0
2855
- && rectangles && this.redactElements.length < this.resultCount) {
2856
- this.search(false);
2857
- }
2858
- if (this.redactAll && this.resultCount && this.redactElements.length === this.resultCount) {
2859
- this.redactAll = false;
2860
- this.redactAllText = null;
2861
- this.saveRedaction(this.redactElements);
2862
- }
2954
+ rotateDocument() {
2955
+ if (this.panel) {
2956
+ this.panel.nativeElement.style.height = '0';
2863
2957
  }
2864
2958
  }
2865
- CreateRedactAllText() {
2866
- this.redactAllText = `${this.redactElements.length} of ${this.resultCount}`;
2867
- }
2868
- onEscapeKeyPress(e) {
2869
- this.toolbarEvents.searchBarHidden.next(true);
2870
- }
2871
- onEnterKeyPress(e) {
2872
- this.search();
2959
+ onCommentUpdate(payload) {
2960
+ const annotation = this.annotationSet.annotations.find(anno => anno.id === payload.comment.annotationId);
2961
+ const comments = [payload.comment];
2962
+ const tags = payload.tags;
2963
+ const annot = Object.assign(Object.assign({}, annotation), { comments,
2964
+ tags });
2965
+ this.onAnnotationUpdate(annot);
2873
2966
  }
2874
- toggleSearchBar() {
2875
- this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
2967
+ onAnnotationUpdate(annotation) {
2968
+ this.store.dispatch(new SaveAnnotation(annotation));
2876
2969
  }
2877
- getRectangles(page) {
2878
- this.pageHeight = this.allPages[page].styles.height;
2879
- this.pageWidth = this.allPages[page].styles.width;
2880
- this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
2881
- this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
2882
- const selectedHighLightedElements = document.getElementsByClassName('highlight selected');
2883
- if (selectedHighLightedElements && selectedHighLightedElements.length > 0) {
2884
- const docRange = document.createRange();
2885
- docRange.selectNodeContents(selectedHighLightedElements[0]);
2886
- const selection = window.getSelection();
2887
- selection === null || selection === void 0 ? void 0 : selection.removeAllRanges();
2888
- selection === null || selection === void 0 ? void 0 : selection.addRange(docRange);
2889
- if (selection.rangeCount && !selection.isCollapsed) {
2890
- const range = selection.getRangeAt(0).cloneRange();
2891
- const clientRects = range.getClientRects();
2892
- if (clientRects) {
2893
- const parentRect = selectedHighLightedElements[0].parentElement.parentElement.getBoundingClientRect();
2894
- const selectionRectangles = [];
2895
- for (let i = 0; i < clientRects.length; i++) {
2896
- const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
2897
- const findSelecttionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
2898
- if (!findSelecttionRectangle) {
2899
- selectionRectangles.push(selectionRectangle);
2900
- }
2901
- }
2902
- return selectionRectangles;
2903
- }
2904
- }
2970
+ onContainerClick(e) {
2971
+ if (e.path && e.path[0] === this.panel.nativeElement) {
2972
+ this.clearSelection();
2905
2973
  }
2906
2974
  }
2907
- createTextRectangle(rect, parentRect) {
2908
- const height = rect.bottom - rect.top;
2909
- const width = rect.right - rect.left;
2910
- const top = rect.top - parentRect.top;
2911
- const left = rect.left - parentRect.left;
2912
- let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
2913
- rectangle = Object.assign({ id: uuid$1() }, rectangle);
2914
- return rectangle;
2975
+ clearSelection() {
2976
+ this.store.dispatch(new SelectedAnnotation({ annotationId: '', editable: false, selected: false }));
2977
+ }
2978
+ allCommentsSaved() {
2979
+ this.commentService.allCommentsSaved();
2915
2980
  }
2916
2981
  }
2917
- /** @nocollapse */ RedactionSearchBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, deps: [{ token: i1.Store }, { token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }, { token: HighlightCreateService }], target: i0.ɵɵFactoryTarget.Component });
2918
- /** @nocollapse */ RedactionSearchBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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=\"Find in document\"\n #findInput\n title=\"Find in document\"\n placeholder=\"Redact from search...\"\n tabindex=\"0\"\n data-l10n-id=\"search_input\"\n [(ngModel)]=\"searchText\"\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\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\"\n >\n Redact all\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 Redacting in progress\n </h4>\n {{ redactAllInProgress ? \"\" : resultsText }}</span\n >\n <span>\n <h5\n id=\"redactAllInProgressCount\"\n class=\"govuk-!-display-inline-block govuk-!-margin-top-0 govuk-!-margin-bottom-1\"\n >\n {{ redactAllInProgress ? redactAllText : \"\" }}\n </h5>\n </span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".redaction-search-buttons-area{display:flex;flex-direction:row;padding-top:.5rem}.redaction-search-bar{width:20rem;left:73%;top:2%}.redaction-status-text{height:2.6rem}\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
2919
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
2982
+ /** @nocollapse */ CommentSetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: CommentSetRenderService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
2983
+ /** @nocollapse */ CommentSetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: CommentComponent, selector: "mv-anno-comment", inputs: ["rotate", "zoom", "index", "page", "comment", "annotation"], outputs: ["commentClick", "renderComments", "delete", "updated", "changes"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
2984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, decorators: [{
2920
2985
  type: Component,
2921
- 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=\"Find in document\"\n #findInput\n title=\"Find in document\"\n placeholder=\"Redact from search...\"\n tabindex=\"0\"\n data-l10n-id=\"search_input\"\n [(ngModel)]=\"searchText\"\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\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\"\n >\n Redact all\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 Redacting in progress\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"] }]
2922
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: HighlightCreateService }]; }, propDecorators: { findInput: [{
2986
+ 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" }]
2987
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: CommentSetRenderService }, { type: ToolbarEventService }]; }, propDecorators: { annotationSet: [{
2988
+ type: Input
2989
+ }], zoom: [{
2990
+ type: Input
2991
+ }], rotate: [{
2992
+ type: Input
2993
+ }], height: [{
2994
+ type: Input
2995
+ }], pageHeights: [{
2996
+ type: Input
2997
+ }], contentScrollTop: [{
2998
+ type: Input
2999
+ }], container: [{
2923
3000
  type: ViewChild,
2924
- args: ['findInput', { static: true }]
2925
- }], onWindowKeyDown: [{
2926
- type: HostListener,
2927
- args: ['window:keydown', ['$event']]
3001
+ args: ['container', { static: false }]
3002
+ }], panel: [{
3003
+ type: ViewChild,
3004
+ args: ['panel', { static: false }]
3005
+ }], commentComponents: [{
3006
+ type: ViewChildren,
3007
+ args: ['commentComponent']
2928
3008
  }] } });
2929
3009
 
2930
- // TODO: replace by NgRx
2931
- class CommentService {
2932
- constructor() {
2933
- this.unsavedChanges = new Subject();
2934
- this.marginToCommentEmitter = new BehaviorSubject(false);
2935
- }
2936
- setCommentSet(commentSetComponent) {
2937
- this.commentSetComponent = commentSetComponent;
2938
- }
2939
- onCommentChange(changes) {
2940
- this.unsavedChanges.next(changes);
2941
- }
2942
- getUnsavedChanges() {
2943
- return this.unsavedChanges.asObservable();
3010
+ class CommentsNavigateComponent {
3011
+ constructor(store, toolbarEvents) {
3012
+ this.store = store;
3013
+ this.toolbarEvents = toolbarEvents;
3014
+ this.autoSelect = false;
3015
+ this.navigationList = [];
3016
+ this.index = 0;
2944
3017
  }
2945
- hasUnsavedComments(annotation) {
2946
- if (annotation.comments.length > 0) {
2947
- const comment = this.getComment(annotation);
2948
- return comment.hasUnsavedChanges;
3018
+ ngOnChanges(changes) {
3019
+ if (changes.annotationList) {
3020
+ this.initNavigationList();
2949
3021
  }
2950
- return false;
2951
- }
2952
- updateUnsavedCommentsStatus(annotation, hasUnsavedChanges) {
2953
- const comment = this.getComment(annotation);
2954
- comment.hasUnsavedChanges = hasUnsavedChanges;
2955
- this.allCommentsSaved();
2956
- }
2957
- getComment(annotation) {
2958
- return this.commentSetComponent.commentComponents
2959
- .find(c => c.comment.annotationId === annotation.comments[0].annotationId);
2960
- }
2961
- resetCommentSet() {
2962
- this.commentSetComponent = null;
2963
- }
2964
- allCommentsSaved() {
2965
- this.onCommentChange(this.commentSetComponent.commentComponents.some(comment => comment.hasUnsavedChanges === true));
2966
- }
2967
- createMarginToCommentEvent(margin) {
2968
- this.marginToCommentEmitter.next(margin);
2969
3022
  }
2970
- }
2971
- /** @nocollapse */ CommentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2972
- /** @nocollapse */ CommentService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService });
2973
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, decorators: [{
2974
- type: Injectable
2975
- }] });
2976
-
2977
- class CommentSetRenderService {
2978
- redrawComponents(commentComponents, pageHeights, rotate, zoom) {
2979
- let prevComment;
2980
- this.sortComponents(commentComponents, pageHeights, rotate, zoom).forEach((comment) => {
2981
- this.adjustIfOverlapping(comment, prevComment, zoom);
2982
- prevComment = comment;
2983
- });
2984
- }
2985
- sortComponents(commentComponents, pageHeights, rotate, zoom) {
2986
- return commentComponents.sort((a, b) => {
2987
- a.rectTop = this.top(a._rectangle, pageHeights[a.page - 1], rotate, zoom);
2988
- b.rectTop = this.top(b._rectangle, pageHeights[b.page - 1], rotate, zoom);
2989
- return this.processSort(a, b);
2990
- });
3023
+ initNavigationList() {
3024
+ this.index = 0;
3025
+ this.navigationList = [...this.annotationList || []]
3026
+ .map(annotation => ({
3027
+ content: annotation.comments[0].content,
3028
+ annotationId: annotation.id,
3029
+ page: annotation.page,
3030
+ rectangle: this.upperRectangle(annotation.rectangles),
3031
+ }))
3032
+ .sort(this.sortComments);
3033
+ if (this.autoSelect) {
3034
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[0].page, 0));
3035
+ this.store.dispatch(new SelectedAnnotation({
3036
+ annotationId: this.navigationList[0].annotationId,
3037
+ editable: false,
3038
+ selected: true
3039
+ }));
3040
+ }
2991
3041
  }
2992
- adjustIfOverlapping(comment, prevComment, zoom) {
2993
- if (prevComment) {
2994
- const endOfPrevComment = prevComment.commentTop + this.height(prevComment);
2995
- if (comment.commentTop <= endOfPrevComment) {
2996
- comment.rectTop = (endOfPrevComment - comment.totalPrevPagesHeight) / zoom;
3042
+ sortComments(mappedCommentA, mappedCommentB) {
3043
+ if (mappedCommentA.page !== mappedCommentB.page) {
3044
+ return mappedCommentA.page - mappedCommentB.page;
3045
+ }
3046
+ else {
3047
+ const rectA = mappedCommentA.rectangle;
3048
+ const rectB = mappedCommentB.rectangle;
3049
+ if (rectA.y !== rectB.y) {
3050
+ return rectA.y - rectB.y;
3051
+ }
3052
+ else {
3053
+ return rectA.x - rectB.x;
2997
3054
  }
2998
3055
  }
2999
3056
  }
3000
- processSort(a, b) {
3001
- if (this.onSameLine(a, b)) {
3002
- return a.rectLeft >= b.rectLeft ? 1 : -1;
3057
+ nextItem() {
3058
+ this.index += 1;
3059
+ if (this.index === this.annotationList.length) {
3060
+ this.index = 0;
3003
3061
  }
3004
- return a.commentTop >= b.commentTop ? 1 : -1;
3005
- }
3006
- onSameLine(a, b) {
3007
- return this.difference(a.commentTop, b.commentTop) === 0;
3062
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3063
+ this.store.dispatch(new SelectedAnnotation({
3064
+ annotationId: this.navigationList[this.index].annotationId, editable: false, selected: true
3065
+ }));
3008
3066
  }
3009
- top(rectangle, height, rotate, zoom) {
3010
- const actualHeight = height / zoom;
3011
- switch (rotate) {
3012
- case 90: return rectangle.x;
3013
- case 180: return actualHeight - (rectangle.y + rectangle.height);
3014
- case 270: return actualHeight - (rectangle.x + rectangle.width);
3015
- default: return rectangle.y;
3067
+ prevItem() {
3068
+ this.index -= 1;
3069
+ if (this.index < 0) {
3070
+ this.index = this.navigationList.length - 1;
3016
3071
  }
3072
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3073
+ this.store.dispatch(new SelectedAnnotation({
3074
+ annotationId: this.navigationList[this.index].annotationId,
3075
+ editable: false,
3076
+ selected: true
3077
+ }));
3017
3078
  }
3018
- height(element) {
3019
- return element.form.nativeElement.getBoundingClientRect().height;
3079
+ upperRectangle(rectangles) {
3080
+ [...rectangles].sort((rect1, rect2) => rect1.y - rect2.y);
3081
+ return { x: rectangles[0].x, y: rectangles[0].y };
3020
3082
  }
3021
- difference(a, b) { return Math.abs(a - b); }
3022
3083
  }
3023
- /** @nocollapse */ CommentSetRenderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3024
- /** @nocollapse */ CommentSetRenderServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService });
3025
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, decorators: [{
3026
- type: Injectable
3027
- }] });
3084
+ /** @nocollapse */ CommentsNavigateComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, deps: [{ token: i1.Store }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3085
+ /** @nocollapse */ CommentsNavigateComponentcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], encapsulation: i0.ViewEncapsulation.None });
3086
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
3087
+ type: Component,
3088
+ 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" }]
3089
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarEventService }]; }, propDecorators: { annotationList: [{
3090
+ type: Input
3091
+ }], autoSelect: [{
3092
+ type: Input
3093
+ }] } });
3028
3094
 
3029
- class TagsServices {
3030
- constructor(http) {
3031
- this.http = http;
3032
- this.snakeCase = string => {
3033
- // transform string_to_snake_case
3034
- return string.replace(/ +/g, ' ') // find space
3035
- .split(/ |\B(?=[A-Z])/) // split it into array
3036
- .map(word => word.toLowerCase()) // transform to lover case
3037
- .join('_'); // trun array into sting using _
3038
- };
3095
+ class CommentSearchComponent {
3096
+ constructor(store) {
3097
+ this.store = store;
3098
+ this.searchResults = [];
3099
+ this.searchIndex = 0;
3039
3100
  }
3040
- getAllTags(createdBy) {
3041
- const url = `/em-anno/tags/${createdBy}`;
3042
- return this.http.get(url);
3101
+ ngAfterViewInit() {
3102
+ if (this.searchInput) {
3103
+ this.searchInput.nativeElement.focus();
3104
+ }
3043
3105
  }
3044
- // @TODO: Move everything below this to NgRx store
3045
- getNewTags(annoid) {
3046
- return this.tagItems ? this.tagItems[annoid] : [];
3106
+ ngOnDestroy() {
3107
+ // TODO workaround for tab error
3108
+ setTimeout(() => { this.store.dispatch(new SearchComment('')); }, 250);
3047
3109
  }
3048
- updateTagItems(items, annoId) {
3049
- const snakeCased = items.map(item => {
3050
- return Object.assign(Object.assign({}, item), { name: this.snakeCase(item.name) });
3051
- });
3052
- this.tagItems = Object.assign(Object.assign({}, this.tagItems), { [annoId]: snakeCased });
3110
+ searchComments(searchText) {
3111
+ this.clearSearch();
3112
+ if (searchText.length > 2) {
3113
+ this.searchString = searchText;
3114
+ this.searchResults = this.annotations
3115
+ .filter(annotation => annotation.comments.length > 0)
3116
+ .filter(annotation => annotation.comments[0].content.toLowerCase().includes(this.searchString.toLowerCase()));
3117
+ if (this.searchResults.length > 0) {
3118
+ this.store.dispatch(new SearchComment(searchText));
3119
+ }
3120
+ }
3121
+ }
3122
+ clearSearch() {
3123
+ this.searchString = undefined;
3124
+ this.searchResults = [];
3125
+ this.searchIndex = 0;
3126
+ this.store.dispatch(new SearchComment(''));
3053
3127
  }
3054
3128
  }
3055
- /** @nocollapse */ TagsServices.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
3056
- /** @nocollapse */ TagsServicesprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices });
3057
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, decorators: [{
3058
- type: Injectable
3059
- }], ctorParameters: function () { return [{ type: i1$1.HttpClient }]; } });
3129
+ /** @nocollapse */ CommentSearchComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
3130
+ /** @nocollapse */ CommentSearchComponentcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: CommentSearchComponent, selector: "mv-comment-search", inputs: { annotations: "annotations" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], ngImport: i0, template: "<input #searchInput type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\" name=\"searchString\"\n aria-label=\"search comments input\" [ngModel]=\"searchString\">\n<button type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\">Search</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\n </p>\n</ng-container>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }], encapsulation: i0.ViewEncapsulation.None });
3131
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, decorators: [{
3132
+ type: Component,
3133
+ args: [{ selector: 'mv-comment-search', encapsulation: ViewEncapsulation.None, template: "<input #searchInput type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\" name=\"searchString\"\n aria-label=\"search comments input\" [ngModel]=\"searchString\">\n<button type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\">Search</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\n </p>\n</ng-container>\n" }]
3134
+ }], ctorParameters: function () { return [{ type: i1.Store }]; }, propDecorators: { annotations: [{
3135
+ type: Input
3136
+ }], searchInput: [{
3137
+ type: ViewChild,
3138
+ args: ['searchInput', { static: false }]
3139
+ }] } });
3060
3140
 
3061
- class TextHighlightDirective {
3062
- constructor(element) {
3063
- this.element = element;
3064
- }
3065
- ngAfterViewChecked() {
3066
- if (this.textToHighlight) {
3067
- this.highlightInputText(this.textToHighlight);
3141
+ class FilterPipe {
3142
+ transform(items, searchText, fieldName) {
3143
+ if (!items) {
3144
+ return [];
3068
3145
  }
3069
- }
3070
- highlightInputText(textToHighlight) {
3071
- this.resetHighlight();
3072
- this.textToHighlight = textToHighlight;
3073
- const searchPattern = new RegExp(textToHighlight, 'gi');
3074
- const hostElement = this.element.nativeElement;
3075
- if (hostElement.innerHTML.match(searchPattern)) {
3076
- hostElement.innerHTML = hostElement.innerHTML
3077
- .replace(searchPattern, this.highlightPattern('$&'));
3146
+ if (!searchText) {
3147
+ return items;
3078
3148
  }
3079
- this.textToHighlight = undefined;
3080
- }
3081
- resetHighlight() {
3082
- const hostElement = this.element.nativeElement;
3083
- const searchPattern = new RegExp(this.highlightPattern('(.*?)'), 'gi');
3084
- while (hostElement.innerHTML.match(searchPattern)) {
3085
- const matchGroups = searchPattern.exec(hostElement.innerHTML);
3086
- if (matchGroups) {
3087
- hostElement.innerHTML = hostElement.innerHTML.replace(matchGroups[0], matchGroups[1]);
3149
+ return items.filter(item => {
3150
+ if (item) {
3151
+ if (item[fieldName]) {
3152
+ return item[fieldName].toLowerCase().includes(searchText.toLowerCase());
3153
+ }
3154
+ else {
3155
+ return item.toLowerCase().includes(searchText.toLowerCase());
3156
+ }
3088
3157
  }
3089
- }
3158
+ return false;
3159
+ });
3090
3160
  }
3091
- highlightPattern(dynamicText) {
3092
- return '<span class="mvTextHighlight">' + dynamicText + '</span>';
3161
+ }
3162
+ /** @nocollapse */ FilterPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3163
+ /** @nocollapse */ FilterPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, name: "filter" });
3164
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, decorators: [{
3165
+ type: Pipe,
3166
+ args: [{
3167
+ name: 'filter'
3168
+ }]
3169
+ }] });
3170
+
3171
+ class UnsnakePipe {
3172
+ transform(items) {
3173
+ return items.split('_').join(' ');
3093
3174
  }
3094
3175
  }
3095
- /** @nocollapse */ TextHighlightDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
3096
- /** @nocollapse */ TextHighlightDirectivedir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 });
3097
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, decorators: [{
3098
- type: Directive,
3176
+ /** @nocollapse */ UnsnakePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3177
+ /** @nocollapse */ UnsnakePipepipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, name: "unsnake" });
3178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, decorators: [{
3179
+ type: Pipe,
3099
3180
  args: [{
3100
- selector: '[mvTextHighlight]'
3181
+ name: 'unsnake'
3101
3182
  }]
3102
- }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { textToHighlight: [{
3103
- type: Input
3104
- }] } });
3183
+ }] });
3105
3184
 
3106
- class TextareaAutoExpandDirective {
3107
- constructor(el) {
3108
- this.el = el;
3185
+ class CommentFilterComponent {
3186
+ constructor(store, fb) {
3187
+ this.store = store;
3188
+ this.fb = fb;
3189
+ this.isPreview = false;
3190
+ }
3191
+ ngOnInit() {
3192
+ this.tagGroup = this.fb.group({
3193
+ 'tagFilters': this.fb.group({}),
3194
+ });
3195
+ this.filter$ = this.store.pipe(select(getTagFilters));
3196
+ this.$subscriptions = this.tagGroup.valueChanges.pipe(auditTime(5)).subscribe(value => {
3197
+ const tagFilters = value['tagFilters'];
3198
+ this.store.dispatch(new AddFilterTags(tagFilters));
3199
+ });
3200
+ this.buildFrom();
3201
+ }
3202
+ buildFrom() {
3203
+ const checkboxes = this.tagGroup.get('tagFilters');
3204
+ this.allTags$ = this.store.pipe(select(getAllTagsArr)).pipe(tap(tags => {
3205
+ this.tagGroup.reset();
3206
+ tags.forEach((value) => {
3207
+ checkboxes.addControl(value.key, new UntypedFormControl(false));
3208
+ });
3209
+ }));
3210
+ }
3211
+ onClearFilters() {
3212
+ this.tagGroup.reset();
3213
+ this.store.dispatch(new ClearFilterTags());
3109
3214
  }
3110
- ngAfterContentChecked() {
3111
- this.adjustHeight();
3215
+ ngOnDestroy() {
3216
+ this.$subscriptions.unsubscribe();
3112
3217
  }
3113
- onMouseDown() {
3114
- this.adjustHeight();
3218
+ onRemoveFilter(tagName) {
3219
+ const checkboxes = this.tagGroup.get('tagFilters');
3220
+ checkboxes.controls[tagName].setValue(false);
3115
3221
  }
3116
- adjustHeight() {
3117
- const nativeElement = this.el.nativeElement;
3118
- nativeElement.style.overflow = 'hidden';
3119
- nativeElement.style.height = 'auto';
3120
- nativeElement.style.height = nativeElement.scrollHeight - 5 + 'px';
3222
+ onToggleFilterView() {
3223
+ this.isPreview = !this.isPreview;
3121
3224
  }
3122
3225
  }
3123
- /** @nocollapse */ TextareaAutoExpandDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
3124
- /** @nocollapse */ TextareaAutoExpandDirectivedir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 });
3125
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
3126
- type: Directive,
3127
- args: [{
3128
- selector: '[mvTextAreaAutoExpand]'
3129
- }]
3130
- }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { onMouseDown: [{
3131
- type: HostListener,
3132
- args: ['input']
3133
- }] } });
3226
+ /** @nocollapse */ CommentFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, deps: [{ token: i1.Store }, { token: i2.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component });
3227
+ /** @nocollapse */ CommentFilterComponentcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{'govuk-accordion__section--expanded': isPreview}\"\n (click)=\"onToggleFilterView()\"><strong >Filter options</strong><span class=\"govuk-accordion__icon\"></span></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><a (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\">Clear filters</a></p>\n </div>\n </div>\n\n <h4 class=\"govuk-heading-s govuk-!-margin-bottom-0\" *ngIf=\"filters.length\">Tag filters</h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a class=\"hmcts-filter__tag\" [routerLink]=\"[]\" (click)=\"onRemoveFilter(tagName)\">\n <span class=\"govuk-visually-hidden\">Remove this filter</span>{{tagName | unsnake}}</a>\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\n </label>\n <input class=\"govuk-input\" id=\"keywords\" name=\"keywords\" type=\"text\" [(ngModel)]=\"searchValue\">\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 class=\"govuk-checkboxes__item\" *ngFor=\"let item of (allTags$ | async) | filter: searchValue: 'key'\">\n <input [formControlName]=\"item.key\" [id]=\"item.key\" class=\"govuk-checkboxes__input\" [value]=\"false\"\n [attr.aria-describedby]=\"item.key\" type=\"checkbox\">\n <label class=\"govuk-label govuk-checkboxes__label\" [for]=\"item.key\">\n {{ item.key | unsnake}} ({{item.length}})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: FilterPipe, name: "filter" }, { kind: "pipe", type: UnsnakePipe, name: "unsnake" }], encapsulation: i0.ViewEncapsulation.None });
3228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, decorators: [{
3229
+ type: Component,
3230
+ 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 class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{'govuk-accordion__section--expanded': isPreview}\"\n (click)=\"onToggleFilterView()\"><strong >Filter options</strong><span class=\"govuk-accordion__icon\"></span></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><a (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\">Clear filters</a></p>\n </div>\n </div>\n\n <h4 class=\"govuk-heading-s govuk-!-margin-bottom-0\" *ngIf=\"filters.length\">Tag filters</h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a class=\"hmcts-filter__tag\" [routerLink]=\"[]\" (click)=\"onRemoveFilter(tagName)\">\n <span class=\"govuk-visually-hidden\">Remove this filter</span>{{tagName | unsnake}}</a>\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\n </label>\n <input class=\"govuk-input\" id=\"keywords\" name=\"keywords\" type=\"text\" [(ngModel)]=\"searchValue\">\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 class=\"govuk-checkboxes__item\" *ngFor=\"let item of (allTags$ | async) | filter: searchValue: 'key'\">\n <input [formControlName]=\"item.key\" [id]=\"item.key\" class=\"govuk-checkboxes__input\" [value]=\"false\"\n [attr.aria-describedby]=\"item.key\" type=\"checkbox\">\n <label class=\"govuk-label govuk-checkboxes__label\" [for]=\"item.key\">\n {{ item.key | unsnake}} ({{item.length}})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n\n </div>\n</div>\n" }]
3231
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: i2.UntypedFormBuilder }]; } });
3134
3232
 
3135
- class TagsComponent {
3136
- constructor(tagsServices) {
3137
- this.tagsServices = tagsServices;
3138
- this.validators = [this.minLength, this.maxLength20];
3139
- this.errorMessages = {
3140
- 'minLength': 'Minimum of 2 characters',
3141
- 'maxLength20': 'Maximum of 20 characters'
3142
- };
3143
- this.requestAutocompleteItems = (text) => {
3144
- return this.tagsServices.getAllTags(this.userId);
3145
- };
3233
+ class CommentSetHeaderComponent {
3234
+ constructor(store, commentService, toolbarEvents) {
3235
+ this.store = store;
3236
+ this.commentService = commentService;
3237
+ this.toolbarEvents = toolbarEvents;
3238
+ this.showCommentSummaryDialog = new EventEmitter();
3239
+ this.tabs = [];
3240
+ this.tabSelected = '';
3146
3241
  }
3147
- onUpdateTags(value) {
3148
- this.tagsServices.updateTagItems(value, this.annoId);
3242
+ ngOnInit() {
3243
+ const tagFilter$ = this.store.pipe(select(getTagFilters));
3244
+ const filteredAnnotation$ = this.store.pipe(select(getFilteredAnnotations));
3245
+ this.$subscriptions = combineLatest([tagFilter$, filteredAnnotation$]).subscribe(([formData, filteredAnno]) => {
3246
+ this.navigationList = filteredAnno;
3247
+ this.tabs = this.navigationList.length > 0 ?
3248
+ [{ label: 'comments' }, { label: 'filter' }, { label: 'search' }] : [{ label: 'comments' }];
3249
+ this.isFiltered = !formData.length;
3250
+ this.tabs = [...this.tabs].map((tab) => {
3251
+ return !this.isFiltered && tab.label === 'filter' ? Object.assign(Object.assign({}, tab), { isFiltered: true }) : Object.assign(Object.assign({}, tab), { isFiltered: false });
3252
+ });
3253
+ });
3149
3254
  }
3150
- minLength(control) {
3151
- if (control.value.length < 2) {
3152
- return {
3153
- 'minLength': true
3154
- };
3155
- }
3156
- return null;
3255
+ toggleCommentsSummary() {
3256
+ this.showCommentSummaryDialog.emit();
3157
3257
  }
3158
- maxLength20(control) {
3159
- if (control.value.length >= 20) {
3160
- return {
3161
- 'maxLength20': true
3162
- };
3258
+ selectTab(tab) {
3259
+ this.tabSelected = tab !== this.tabSelected ? tab : undefined;
3260
+ if (this.tabSelected) {
3261
+ this.marginToComment = true;
3262
+ this.commentService.createMarginToCommentEvent(this.marginToComment);
3163
3263
  }
3164
- return null;
3264
+ else {
3265
+ this.marginToComment = false;
3266
+ this.commentService.createMarginToCommentEvent(this.marginToComment);
3267
+ }
3268
+ }
3269
+ toggleCommentsPanel() {
3270
+ this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3271
+ }
3272
+ ngOnDestroy() {
3273
+ this.$subscriptions.unsubscribe();
3165
3274
  }
3166
3275
  }
3167
- /** @nocollapse */ TagsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
3168
- /** @nocollapse */ TagsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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)=\"onUpdateTags($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n [placeholder]=\"'Add tag'\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [onTextChangeDebounce]=\"250\"\n [secondaryPlaceholder]=\"'Search or add tags'\"\n [validators]=\"validators\"\n [errorMessages]=\"errorMessages\"\n [onlyFromAutocomplete]=\"false\">\n <tag-input-dropdown\n [autocompleteObservable]=\"requestAutocompleteItems\"\n [displayBy]=\"'label'\"\n [identifyBy]=\"'name'\"\n [zIndex]=\"10000\"\n [minimumTextLength]=\"2\">\n <ng-template let-item=\"item\" let-index=\"index\">\n {{ item.label }}\n </ng-template>\n </tag-input-dropdown>\n </tag-input>\n\n <div class=\"tags__renderer\" *ngIf=\"!editable && (tagItems && tagItems.length)\">\n <span *ngFor=\"let tag of tagItems\" class=\"tags__item hmcts-badge\">\n {{tag.label}}\n </span>\n </div>\n</div>\n\n", dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$1.TagInputComponent, selector: "tag-input", inputs: ["separatorKeys", "separatorKeyCodes", "placeholder", "secondaryPlaceholder", "maxItems", "validators", "asyncValidators", "onlyFromAutocomplete", "errorMessages", "theme", "onTextChangeDebounce", "inputId", "inputClass", "clearOnBlur", "hideForm", "addOnBlur", "addOnPaste", "pasteSplitPattern", "blinkIfDupe", "removable", "editable", "allowDupes", "modelAsStrings", "trimTags", "ripple", "tabindex", "disable", "dragZone", "onRemoving", "onAdding", "animationDuration", "inputText"], outputs: ["onAdd", "onRemove", "onSelect", "onFocus", "onBlur", "onTextChange", "onPaste", "onValidationError", "onTagEdited", "inputTextChange"] }, { kind: "component", type: i4$1.TagInputDropdown, selector: "tag-input-dropdown", inputs: ["offset", "focusFirstElement", "showDropdownIfEmpty", "minimumTextLength", "limitItemsTo", "displayBy", "identifyBy", "matchingFn", "appendToBody", "keepOpen", "dynamicUpdate", "zIndex", "autocompleteItems", "autocompleteObservable"] }], encapsulation: i0.ViewEncapsulation.None });
3169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, decorators: [{
3276
+ /** @nocollapse */ CommentSetHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3277
+ /** @nocollapse */ CommentSetHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: CommentSetHeaderComponent, selector: "mv-comment-set-header", inputs: { showCommentSummary: "showCommentSummary" }, outputs: { showCommentSummaryDialog: "showCommentSummaryDialog" }, ngImport: i0, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': toolbarEvents.icp.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\">\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()\">\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 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: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: CommentSearchComponent, selector: "mv-comment-search", inputs: ["annotations"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }, { kind: "component", type: CommentFilterComponent, selector: "mv-comment-filter" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }], encapsulation: i0.ViewEncapsulation.None });
3278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
3170
3279
  type: Component,
3171
- 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)=\"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" }]
3172
- }], ctorParameters: function () { return [{ type: TagsServices }]; }, propDecorators: { tagItems: [{
3173
- type: Input
3174
- }], userId: [{
3175
- type: Input
3176
- }], editable: [{
3177
- type: Input
3178
- }], annoId: [{
3280
+ args: [{ selector: 'mv-comment-set-header', encapsulation: ViewEncapsulation.None, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': toolbarEvents.icp.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\">\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()\">\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 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" }]
3281
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: ToolbarEventService }]; }, propDecorators: { showCommentSummary: [{
3179
3282
  type: Input
3283
+ }], showCommentSummaryDialog: [{
3284
+ type: Output
3180
3285
  }] } });
3181
3286
 
3287
+ const getBookmarkState = createSelector(getMVState, (state) => state.bookmarks);
3288
+ const getBookmarkPages = createSelector(getBookmarkState, getBookmarkPageEnt);
3289
+ const getBookmarkEntities = createSelector(getBookmarkState, getBookmarkEnts);
3290
+ const getBookmarkNodes = createSelector(getBookmarkEntities, (entities) => generateBookmarkNodes(entities));
3291
+ const getEditableBookmark = createSelector(getBookmarkState, getEditBookmark);
3292
+ const getBookmarkInfo = createSelector(getBookmarkNodes, getDocumentId, getPdfPosition, getPages, (bookmarkNodes, docId, pdfPosition, pages) => {
3293
+ return {
3294
+ pageNumber: pdfPosition.pageNumber - 1,
3295
+ xCoordinate: pdfPosition.left,
3296
+ yCoordinate: ((pages[pdfPosition.pageNumber].styles.height)
3297
+ - (pdfPosition.top * (pages[pdfPosition.pageNumber].viewportScale)))
3298
+ / parseFloat(pages[pdfPosition.pageNumber].scaleRotation.scale),
3299
+ previous: bookmarkNodes.length > 0 ? bookmarkNodes.sort((a, b) => a.index - b.index)[bookmarkNodes.length - 1].id : undefined,
3300
+ documentId: docId
3301
+ };
3302
+ });
3303
+ const getBookmarksPerPage = createSelector(getPages, getBookmarkPages, (pages, pageEnt) => {
3304
+ if (pages && pageEnt) {
3305
+ const arr = [];
3306
+ Object.keys(pages).forEach(key => {
3307
+ const pageIdx = Number(key) - 1; // -1 as the thisPages array is 0 indexed
3308
+ const thisPage = pageEnt[pageIdx];
3309
+ arr.push({
3310
+ bookmark: thisPage ? thisPage : [],
3311
+ styles: pages[key].styles
3312
+ });
3313
+ });
3314
+ return arr;
3315
+ }
3316
+ });
3317
+
3182
3318
  /**
3183
- * A moment timezone pipe to support parsing based on time zone abbreviations
3184
- * covers all cases of offset variation due to daylight saving.
3185
- *
3186
- * Same API as DatePipe with additional timezone abbreviation support
3187
- * Official date pipe dropped support for abbreviations names from Angular V5
3188
- */
3189
- class MomentDatePipe extends DatePipe {
3190
- transform(value, format = 'mediumDate', timezone = 'Europe/London') {
3191
- const timezoneOffset = moment(value).tz(timezone).format('Z');
3192
- return super.transform(value, format, timezoneOffset);
3319
+ * Number Helper Service
3320
+ * */
3321
+ class NumberHelperService {
3322
+ constructor() { }
3323
+ isNumber(value) {
3324
+ return (value !== null
3325
+ && value !== undefined
3326
+ && value !== ''
3327
+ && !isNaN(Number(value.toString())));
3193
3328
  }
3194
3329
  }
3195
- /** @nocollapse */ MomentDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe });
3196
- /** @nocollapse */ MomentDatePipepipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, name: "momentDate" });
3197
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, decorators: [{
3198
- type: Pipe,
3330
+ /** @nocollapse */ NumberHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3331
+ /** @nocollapse */ NumberHelperServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, providedIn: 'root' });
3332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, decorators: [{
3333
+ type: Injectable,
3199
3334
  args: [{
3200
- name: 'momentDate'
3335
+ providedIn: 'root'
3201
3336
  }]
3202
- }] });
3337
+ }], ctorParameters: function () { return []; } });
3203
3338
 
3204
- class CommentComponent {
3205
- constructor(store, commentService, tagsServices) {
3206
- this.store = store;
3207
- this.commentService = commentService;
3208
- this.tagsServices = tagsServices;
3209
- this.CHAR_LIMIT = 5000;
3210
- this.totalPrevPagesHeight = 0;
3211
- this.hasUnsavedChanges = false;
3212
- this.commentClick = new EventEmitter();
3213
- this.renderComments = new EventEmitter();
3214
- this.delete = new EventEmitter();
3215
- this.updated = new EventEmitter();
3216
- this.changes = new EventEmitter();
3217
- this.rotate = 0;
3218
- this.zoom = 1;
3339
+ class SearchBarComponent {
3340
+ constructor(toolbarButtons, toolbarEvents) {
3341
+ this.toolbarButtons = toolbarButtons;
3342
+ this.toolbarEvents = toolbarEvents;
3343
+ this.highlightAll = true;
3344
+ this.matchCase = false;
3345
+ this.wholeWord = false;
3346
+ this.resultsText = '';
3347
+ this.searchText = '';
3348
+ this.resultCount = 0;
3349
+ this.subscriptions = [];
3350
+ this.advancedSearchVisible = false;
3219
3351
  }
3220
3352
  ngOnInit() {
3221
- this.subscriptions = this.store.select(getComponentSearchText)
3222
- .pipe(distinctUntilChanged()).subscribe(searchString => this.searchString = searchString);
3223
- this.reRenderComments();
3224
- this.marginToComment$ = this.commentService.marginToCommentEmitter.asObservable();
3225
- }
3226
- ngAfterContentInit() {
3227
- if (this.tagItems && this.tagItems.length) {
3228
- this.tagsServices.updateTagItems(this.tagItems, this._comment.annotationId);
3229
- }
3353
+ this.subscriptions.push(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
3230
3354
  }
3231
3355
  ngOnDestroy() {
3232
- this.subscriptions.unsubscribe();
3233
- }
3234
- set comment(comment) {
3235
- this._comment = Object.assign({}, comment);
3236
- this.page = this._comment.page;
3237
- this.lastUpdate = comment.lastModifiedDate ? comment.lastModifiedDate : comment.createdDate;
3238
- this.author = comment.createdByDetails;
3239
- this.createdBy = comment.createdBy;
3240
- this.editor = comment.lastModifiedByDetails;
3241
- this.originalComment = comment.content;
3242
- this.fullComment = this.originalComment;
3243
- this.selected = this._comment.selected;
3244
- this._editable = this._comment.editable;
3245
- this.tagItems = this._comment.tags;
3246
- const pageMarginBottom = 10;
3247
- this.totalPrevPagesHeight = 0;
3248
- for (let i = 0; i < this.page - 1; i++) {
3249
- const height = this._comment.pages[i + 1] ? this._comment.pages[i + 1].styles.height : undefined;
3250
- if (height) {
3251
- this.totalPrevPagesHeight += height + pageMarginBottom;
3252
- }
3253
- }
3254
- }
3255
- get comment() {
3256
- return this._comment;
3257
- }
3258
- set annotation(annotation) {
3259
- this._rectangle = annotation.rectangles
3260
- .reduce((prev, current) => prev.y < current.y ? prev : current);
3261
- const actualHeight = this._comment.pages[this.page].styles.height / this.zoom;
3262
- switch (this.rotate) {
3263
- case 90:
3264
- this.rectTop = this._rectangle.x;
3265
- break;
3266
- case 180:
3267
- this.rectTop = actualHeight - (this._rectangle.y + this._rectangle.height);
3268
- break;
3269
- case 270:
3270
- this.rectTop = actualHeight - (this._rectangle.x + this._rectangle.width);
3271
- break;
3272
- default: this.rectTop = this._rectangle.y;
3356
+ for (const subscription of this.subscriptions) {
3357
+ subscription.unsubscribe();
3273
3358
  }
3274
- this.rectLeft = this._rectangle.x;
3275
3359
  }
3276
- get editable() {
3277
- return this._editable;
3360
+ onWindowKeyDown(e) {
3361
+ if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
3362
+ e.preventDefault();
3363
+ this.toolbarEvents.searchBarHidden.next(false);
3364
+ setTimeout(() => this.findInput.nativeElement.focus(), 200);
3365
+ }
3278
3366
  }
3279
- onCommentChange(updatedComment) {
3280
- this.hasUnsavedChanges =
3281
- this.originalComment.substring(0, this.CHAR_LIMIT) !== updatedComment.substring(0, this.CHAR_LIMIT);
3282
- this.commentService.onCommentChange(this.hasUnsavedChanges);
3367
+ searchNext() {
3368
+ this.toolbarEvents.search({
3369
+ searchTerm: this.searchText,
3370
+ highlightAll: this.highlightAll,
3371
+ matchCase: this.matchCase,
3372
+ wholeWord: this.wholeWord,
3373
+ previous: false,
3374
+ reset: false
3375
+ });
3283
3376
  }
3284
- deleteOrCancel() {
3285
- if (!this.editable) {
3286
- this.delete.emit(this._comment);
3287
- }
3288
- else {
3289
- this.hasUnsavedChanges = false;
3290
- this._editable = false;
3291
- this.fullComment = this.originalComment;
3292
- this.changes.emit(false);
3293
- if (!this.author && !this.fullComment) {
3294
- this.delete.emit(this._comment);
3295
- }
3296
- }
3377
+ searchPrev() {
3378
+ this.toolbarEvents.search({
3379
+ searchTerm: this.searchText,
3380
+ highlightAll: this.highlightAll,
3381
+ matchCase: this.matchCase,
3382
+ wholeWord: this.wholeWord,
3383
+ previous: true,
3384
+ reset: false
3385
+ });
3297
3386
  }
3298
- editOrSave() {
3299
- if (!this.editable) {
3300
- this._editable = true;
3301
- }
3302
- else {
3303
- this._comment.content = this.fullComment.substring(0, this.CHAR_LIMIT);
3304
- const tags = this.tagsServices.getNewTags(this._comment.annotationId);
3305
- const payload = {
3306
- comment: this._comment,
3307
- tags
3308
- };
3309
- this.updated.emit(payload);
3310
- this.hasUnsavedChanges = false;
3311
- this._editable = false;
3312
- this.changes.emit(false);
3313
- }
3387
+ search() {
3388
+ this.toolbarEvents.search({
3389
+ searchTerm: this.searchText,
3390
+ highlightAll: this.highlightAll,
3391
+ matchCase: this.matchCase,
3392
+ wholeWord: this.wholeWord,
3393
+ previous: false,
3394
+ reset: true
3395
+ });
3314
3396
  }
3315
- onCommentClick() {
3316
- if (!this.selected) {
3317
- this.selected = true;
3318
- this._editable = false;
3319
- this.commentClick.emit({ annotationId: this._comment.annotationId, editable: this._editable, selected: true });
3397
+ setSearchResultsCount(results) {
3398
+ this.resultCount = results.total;
3399
+ this.resultsText = this.resultCount > 0
3400
+ ? `Found ${results.current} of ${results.total}`
3401
+ : 'No results found';
3402
+ if (this.resultCount && this.resultCount > 0) {
3403
+ setTimeout(() => {
3404
+ this.findNext.nativeElement.focus();
3405
+ }, 1000);
3320
3406
  }
3321
3407
  }
3322
- reRenderComments() {
3323
- this.renderComments.emit(this._comment);
3408
+ onEscapeKeyPress(e) {
3409
+ this.toolbarEvents.searchBarHidden.next(true);
3324
3410
  }
3325
- get commentTop() {
3326
- return this.totalPrevPagesHeight + (this.rectTop * this.zoom);
3411
+ onEnterKeyPress(e) {
3412
+ this.search();
3327
3413
  }
3328
- get height() {
3329
- return this.form.nativeElement.getBoundingClientRect().height / this.zoom;
3414
+ toggleAdvancedSearch() {
3415
+ this.advancedSearchVisible = !this.advancedSearchVisible;
3416
+ }
3417
+ toggleSearchBar() {
3418
+ this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
3330
3419
  }
3331
3420
  }
3332
- /** @nocollapse */ CommentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
3333
- /** @nocollapse */ CommentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 #form (click)=\"onCommentClick()\" class=\"aui-comment\"\n [ngClass]=\"{'stylestoggle' : (marginToComment$ | async ) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\">\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 </mv-tags>\n <textarea *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)=\"reRenderComments(); onCommentChange($event);\"\n aria-label=\"comment\">\n </textarea>\n <p *ngIf=\"!editable\"\n mvTextHighlight class=\"commentText\" [textToHighlight]=\"searchString\">\n {{ fullComment }}\n </p>\n <div *ngIf=\"selected || this.editable || (!fullComment.length && (tagItems && !tagItems.length))\"\n class=\"aui-comment__footer commentBtns\">\n <button class=\"govuk-button\"\n type=\"button\" role=\"button\"\n (click)=\"editOrSave()\">\n {{ !editable ? 'Edit' : 'Save' }}\n </button>\n <button type=\"button\" role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\">\n {{ !editable ? 'Delete' : 'Cancel' }}\n </button>\n </div>\n <span class=\"aui-comment__private\">private</span>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: ["textToHighlight"] }, { kind: "directive", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]" }, { kind: "component", type: TagsComponent, selector: "mv-tags", inputs: ["tagItems", "userId", "editable", "annoId"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: MomentDatePipe, name: "momentDate" }] });
3334
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, decorators: [{
3421
+ /** @nocollapse */ SearchBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, deps: [{ token: ToolbarButtonVisibilityService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3422
+ /** @nocollapse */ SearchBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 class=\"searchbar govuk-!-padding-3\" [hidden]=\"toolbarEvents.searchBarHidden | async\">\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\" aria-label=\"Find in document\" #findInput [(ngModel)]=\"searchText\" (keydown.escape)=\"onEscapeKeyPress($event)\" (keydown.enter)=\"onEnterKeyPress($event)\" title=\"Find in document\"\n placeholder=\"Find in document\u2026\" tabindex=\"0\" data-l10n-id=\"find_input\" />\n <button class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\" data-module=\"govuk-button\"\n (click)=\"search()\" style=\" position: absolute; top: 40px; right: 10px; \">\n Search\n </button>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button searchbar-button--close\" title=\"Close Search\" data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\">\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span id=\"findResultsCount\" class=\"govuk-!-display-inline-block govuk-!-margin-right-4\">{{resultsText}}</span>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\" title=\"Find the previous occurrence of the phrase\" data-l10n-id=\"find_previous\">Prev</a>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" #findNext class=\"govuk-link govuk-link--no-visited-state\" (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\" data-l10n-id=\"find_next\">Next</a>\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state\" (click)=\"toggleAdvancedSearch()\" title=\"Advanced\"\n data-l10n-id=\"find_advanced\" style=\" position: absolute; top: 95px; right: 15px; \">Advanced</a>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\" *ngIf=\"advancedSearchVisible\">\n <div class=\"govuk-grid-column-full\">\n <div class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\">\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findHighlightAll\" name=\"findHighlightAll\" type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\" [checked]=\"highlightAll\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findHighlightAll\" data-l10n-id=\"find_highlight\">\n Highlight all\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findMatchCase\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\" [checked]=\"matchCase\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\">\n Match text (exact case)\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findEntireWord\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\" [checked]=\"wholeWord\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\">\n Match whole words or sentences\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
3423
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, decorators: [{
3335
3424
  type: Component,
3336
- args: [{ selector: 'mv-anno-comment', template: "<div #form (click)=\"onCommentClick()\" class=\"aui-comment\"\n [ngClass]=\"{'stylestoggle' : (marginToComment$ | async ) }\"\n [style.top.px]=\"commentTop\"\n [style.zIndex]=\"selected ? 100 : 0\">\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 </mv-tags>\n <textarea *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)=\"reRenderComments(); onCommentChange($event);\"\n aria-label=\"comment\">\n </textarea>\n <p *ngIf=\"!editable\"\n mvTextHighlight class=\"commentText\" [textToHighlight]=\"searchString\">\n {{ fullComment }}\n </p>\n <div *ngIf=\"selected || this.editable || (!fullComment.length && (tagItems && !tagItems.length))\"\n class=\"aui-comment__footer commentBtns\">\n <button class=\"govuk-button\"\n type=\"button\" role=\"button\"\n (click)=\"editOrSave()\">\n {{ !editable ? 'Edit' : 'Save' }}\n </button>\n <button type=\"button\" role=\"button\"\n class=\"govuk-button govuk-button--secondary\"\n (click)=\"deleteOrCancel()\">\n {{ !editable ? 'Delete' : 'Cancel' }}\n </button>\n </div>\n <span class=\"aui-comment__private\">private</span>\n</div>\n" }]
3337
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: TagsServices }]; }, propDecorators: { commentClick: [{
3338
- type: Output
3339
- }], renderComments: [{
3340
- type: Output
3341
- }], delete: [{
3342
- type: Output
3343
- }], updated: [{
3344
- type: Output
3345
- }], changes: [{
3346
- type: Output
3347
- }], rotate: [{
3348
- type: Input
3349
- }], zoom: [{
3350
- type: Input
3351
- }], index: [{
3352
- type: Input
3353
- }], page: [{
3354
- type: Input
3355
- }], form: [{
3425
+ args: [{ selector: 'mv-search-bar', template: "<div class=\"searchbar govuk-!-padding-3\" [hidden]=\"toolbarEvents.searchBarHidden | async\">\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-full\">\n <input class=\"govuk-input govuk-!-display-inline-block govuk-!-width-three-quarters govuk-!-margin-bottom-5 govuk-!-margin-top-5\"\n type=\"text\" aria-label=\"Find in document\" #findInput [(ngModel)]=\"searchText\" (keydown.escape)=\"onEscapeKeyPress($event)\" (keydown.enter)=\"onEnterKeyPress($event)\" title=\"Find in document\"\n placeholder=\"Find in document\u2026\" tabindex=\"0\" data-l10n-id=\"find_input\" />\n <button class=\"govuk-button govuk-!-display-inline-block govuk-!-margin-bottom-4\" data-module=\"govuk-button\"\n (click)=\"search()\" style=\" position: absolute; top: 40px; right: 10px; \">\n Search\n </button>\n <button id=\"mvCloseBtn\" #mvCloseBtn class=\"mv-button searchbar-button--close\" title=\"Close Search\" data-l10n-id=\"mvRedactBtn\"\n (click)=\"toggleSearchBar()\">\n </button>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\">\n <div class=\"govuk-grid-column-three-quarters\">\n <span id=\"findResultsCount\" class=\"govuk-!-display-inline-block govuk-!-margin-right-4\">{{resultsText}}</span>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state govuk-!-margin-right-2\"\n (click)=\"searchPrev()\" title=\"Find the previous occurrence of the phrase\" data-l10n-id=\"find_previous\">Prev</a>\n <a *ngIf=\"resultCount > 0\" [routerLink]=\"[]\" #findNext class=\"govuk-link govuk-link--no-visited-state\" (click)=\"searchNext()\"\n title=\"Find the next occurrence of the phrase\" data-l10n-id=\"find_next\">Next</a>\n </div>\n <div class=\"govuk-grid-column-one-quarter\">\n <a [routerLink]=\"[]\" class=\"govuk-link govuk-link--no-visited-state\" (click)=\"toggleAdvancedSearch()\" title=\"Advanced\"\n data-l10n-id=\"find_advanced\" style=\" position: absolute; top: 95px; right: 15px; \">Advanced</a>\n </div>\n </div>\n\n <div class=\"govuk-grid-row\" *ngIf=\"advancedSearchVisible\">\n <div class=\"govuk-grid-column-full\">\n <div class=\"govuk-form-group govuk-!-margin-top-3 govuk-!-margin-bottom-1\">\n <fieldset class=\"govuk-fieldset\" aria-describedby=\"advanced\">\n <div class=\"govuk-checkboxes\">\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findHighlightAll\" name=\"findHighlightAll\" type=\"checkbox\"\n (change)=\"highlightAll = !highlightAll; search()\" [checked]=\"highlightAll\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findHighlightAll\" data-l10n-id=\"find_highlight\">\n Highlight all\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findMatchCase\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"matchCase = !matchCase; search()\" [checked]=\"matchCase\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findMatchCase\"\n data-l10n-id=\"find_match_case_label\">\n Match text (exact case)\n </label>\n </div>\n <div class=\"govuk-checkboxes__item\">\n <input class=\"govuk-checkboxes__input\" id=\"findEntireWord\" name=\"findMatchCase\" type=\"checkbox\"\n (change)=\"wholeWord = !wholeWord; search()\" [checked]=\"wholeWord\">\n <label class=\"govuk-label govuk-checkboxes__label\" for=\"findEntireWord\"\n data-l10n-id=\"find_entire_word_label\">\n Match whole words or sentences\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n</div>\n" }]
3426
+ }], ctorParameters: function () { return [{ type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }]; }, propDecorators: { findInput: [{
3356
3427
  type: ViewChild,
3357
- args: ['form', { static: false }]
3358
- }], editableComment: [{
3428
+ args: ['findInput', { static: true }]
3429
+ }], findNext: [{
3359
3430
  type: ViewChild,
3360
- args: ['editableComment', { static: false }]
3361
- }], comment: [{
3362
- type: Input
3363
- }], annotation: [{
3364
- type: Input
3431
+ args: ['findNext', { static: false }]
3432
+ }], onWindowKeyDown: [{
3433
+ type: HostListener,
3434
+ args: ['window:keydown', ['$event']]
3365
3435
  }] } });
3366
3436
 
3367
- class CommentSetComponent {
3368
- constructor(store, commentService, renderService, toolbarEvents) {
3369
- this.store = store;
3370
- this.commentService = commentService;
3371
- this.renderService = renderService;
3437
+ class MainToolbarComponent {
3438
+ constructor(toolbarEvents, toolbarButtons, cdr, numberHelper) {
3372
3439
  this.toolbarEvents = toolbarEvents;
3373
- this.pageHeights = [];
3440
+ this.toolbarButtons = toolbarButtons;
3441
+ this.cdr = cdr;
3442
+ this.numberHelper = numberHelper;
3443
+ this.enableAnnotations = false;
3444
+ this.enableRedactions = false;
3445
+ this.enableICP = false;
3446
+ this.contentType = null;
3374
3447
  this.subscriptions = [];
3375
- this.clearSelection();
3448
+ this.icpEnabled = false;
3449
+ this.redactionEnabled = false;
3450
+ this.pageNumber = 1;
3451
+ this.pageCount = 0;
3452
+ this.isDropdownMenuOpen = false;
3453
+ this.dropdownMenuPositions = [
3454
+ new ConnectionPositionPair({
3455
+ originX: 'end',
3456
+ originY: 'bottom'
3457
+ }, {
3458
+ overlayX: 'end',
3459
+ overlayY: 'top'
3460
+ }, 0, 3)
3461
+ ];
3462
+ this.zoomScales = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2.5, 3, 5];
3463
+ this.allButtonsWidth = 0;
3464
+ this.widthRequiredForBtn = {};
3376
3465
  }
3377
3466
  ngOnInit() {
3378
- this.comments$ = this.store.pipe(select(getCommentsArray));
3379
- this.annoEntities$ = this.store.pipe(select(getAnnotationEntities));
3380
- this.subscriptions.push(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
3381
- this.redrawComments();
3382
- this.showCommentsPanel = toggle;
3383
- }));
3384
- this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument()));
3385
- }
3386
- ngOnChanges(changes) {
3387
- if (changes.annotationSet) {
3388
- this.commentService.setCommentSet(this);
3389
- }
3390
- if (changes.contentScrollTop) {
3391
- if (this.container) {
3392
- this.container.nativeElement.scrollTo(0, this.contentScrollTop);
3467
+ 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.toolbarEvents.icp.enabled.subscribe(enabled => {
3468
+ this.icpEnabled = enabled;
3469
+ if (this.icpEnabled) {
3470
+ this.toolbarEvents.toggleCommentsPanel(!enabled);
3471
+ this.toolbarEvents.sidebarOpen.next(false);
3393
3472
  }
3394
- }
3473
+ }), this.toolbarEvents.redactionMode.subscribe(enabled => {
3474
+ this.redactionEnabled = enabled;
3475
+ }), this.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
3476
+ this.redactAllInProgress = disable;
3477
+ }));
3395
3478
  }
3396
3479
  ngOnDestroy() {
3397
- if (this.subscriptions.length > 0) {
3398
- this.subscriptions.forEach(subscription => subscription.unsubscribe());
3480
+ for (const subscription of this.subscriptions) {
3481
+ subscription.unsubscribe();
3399
3482
  }
3400
3483
  }
3401
- onSelect(annotationId) {
3402
- this.store.dispatch(new SelectedAnnotation(annotationId));
3484
+ ngAfterViewInit() {
3485
+ Array.from(this.mvToolbarMain.nativeElement.children).forEach(button => {
3486
+ this.allButtonsWidth += button.getBoundingClientRect().width;
3487
+ this.widthRequiredForBtn[button.id] = this.allButtonsWidth;
3488
+ });
3489
+ this.cdr.detectChanges();
3403
3490
  }
3404
- onCommentDelete(comment) {
3405
- const annotation = this.annotationSet.annotations.find(anno => anno.id === comment.annotationId);
3406
- const comments = [];
3407
- const annot = Object.assign(Object.assign({}, annotation), { comments });
3408
- this.onAnnotationUpdate(annot);
3409
- this.redrawComments();
3491
+ onResize() {
3492
+ this.cdr.detectChanges();
3410
3493
  }
3411
- redrawComments() {
3412
- setTimeout(() => {
3413
- const componentList = this.commentComponents.map(comment => comment);
3414
- this.renderService.redrawComponents(componentList, this.pageHeights, this.rotate, this.zoom);
3415
- }, 0);
3494
+ onControlPrint(event) {
3495
+ event.preventDefault();
3496
+ this.printFile();
3416
3497
  }
3417
- rotateDocument() {
3418
- if (this.panel) {
3419
- this.panel.nativeElement.style.height = '0';
3420
- }
3498
+ onClickHighlightToggle() {
3499
+ this.toolbarEvents.toggleHighlightMode();
3421
3500
  }
3422
- onCommentUpdate(payload) {
3423
- const annotation = this.annotationSet.annotations.find(anno => anno.id === payload.comment.annotationId);
3424
- const comments = [payload.comment];
3425
- const tags = payload.tags;
3426
- const annot = Object.assign(Object.assign({}, annotation), { comments,
3427
- tags });
3428
- this.onAnnotationUpdate(annot);
3501
+ onClickDrawToggle() {
3502
+ this.toolbarEvents.toggleDrawMode();
3429
3503
  }
3430
- onAnnotationUpdate(annotation) {
3431
- this.store.dispatch(new SaveAnnotation(annotation));
3504
+ toggleIndexSideBar() {
3505
+ const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
3506
+ const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
3507
+ if (!(sidebarOpen && !sidebarView)) {
3508
+ this.toolbarEvents.toggleSideBar(!sidebarOpen);
3509
+ }
3510
+ this.toolbarEvents.toggleSideBarView(true);
3432
3511
  }
3433
- onContainerClick(e) {
3434
- if (e.path && e.path[0] === this.panel.nativeElement) {
3435
- this.clearSelection();
3512
+ toggleBookmarksSideBar() {
3513
+ const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
3514
+ const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
3515
+ if (!(sidebarOpen && sidebarView)) {
3516
+ this.toolbarEvents.toggleSideBar(!sidebarOpen);
3436
3517
  }
3518
+ this.toolbarEvents.toggleSideBarView(false);
3437
3519
  }
3438
- clearSelection() {
3439
- this.store.dispatch(new SelectedAnnotation({ annotationId: '', editable: false, selected: false }));
3520
+ togglePresentBar() {
3521
+ this.toolbarEvents.searchBarHidden.next(true);
3522
+ this.toolbarEvents.icp.enable();
3440
3523
  }
3441
- allCommentsSaved() {
3442
- this.commentService.allCommentsSaved();
3524
+ increasePageNumber() {
3525
+ this.toolbarEvents.incrementPage(1);
3443
3526
  }
3444
- }
3445
- /** @nocollapse */ CommentSetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: CommentSetRenderService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3446
- /** @nocollapse */ CommentSetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: CommentComponent, selector: "mv-anno-comment", inputs: ["rotate", "zoom", "index", "page", "comment", "annotation"], outputs: ["commentClick", "renderComments", "delete", "updated", "changes"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
3447
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, decorators: [{
3448
- type: Component,
3449
- 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" }]
3450
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: CommentSetRenderService }, { type: ToolbarEventService }]; }, propDecorators: { annotationSet: [{
3451
- type: Input
3452
- }], zoom: [{
3453
- type: Input
3454
- }], rotate: [{
3455
- type: Input
3456
- }], height: [{
3457
- type: Input
3458
- }], pageHeights: [{
3459
- type: Input
3460
- }], contentScrollTop: [{
3461
- type: Input
3462
- }], container: [{
3463
- type: ViewChild,
3464
- args: ['container', { static: false }]
3465
- }], panel: [{
3466
- type: ViewChild,
3467
- args: ['panel', { static: false }]
3468
- }], commentComponents: [{
3469
- type: ViewChildren,
3470
- args: ['commentComponent']
3471
- }] } });
3472
-
3473
- class CommentsNavigateComponent {
3474
- constructor(store, toolbarEvents) {
3475
- this.store = store;
3476
- this.toolbarEvents = toolbarEvents;
3477
- this.autoSelect = false;
3478
- this.navigationList = [];
3479
- this.index = 0;
3527
+ decreasePageNumber() {
3528
+ this.toolbarEvents.incrementPage(-1);
3480
3529
  }
3481
- ngOnChanges(changes) {
3482
- if (changes.annotationList) {
3483
- this.initNavigationList();
3530
+ onPageNumberInputChange(pageNumber) {
3531
+ if (Number(pageNumber) < 1) {
3532
+ pageNumber = '1';
3484
3533
  }
3485
- }
3486
- initNavigationList() {
3487
- this.index = 0;
3488
- this.navigationList = [...this.annotationList || []]
3489
- .map(annotation => ({
3490
- content: annotation.comments[0].content,
3491
- annotationId: annotation.id,
3492
- page: annotation.page,
3493
- rectangle: this.upperRectangle(annotation.rectangles),
3494
- }))
3495
- .sort(this.sortComments);
3496
- if (this.autoSelect) {
3497
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[0].page, 0));
3498
- this.store.dispatch(new SelectedAnnotation({
3499
- annotationId: this.navigationList[0].annotationId,
3500
- editable: false,
3501
- selected: true
3502
- }));
3534
+ if (Number(pageNumber) > this.pageCount) {
3535
+ pageNumber = this.pageCount.toString();
3503
3536
  }
3537
+ this.toolbarEvents.setPage(Number.parseInt(pageNumber, 10));
3504
3538
  }
3505
- sortComments(mappedCommentA, mappedCommentB) {
3506
- if (mappedCommentA.page !== mappedCommentB.page) {
3507
- return mappedCommentA.page - mappedCommentB.page;
3508
- }
3509
- else {
3510
- const rectA = mappedCommentA.rectangle;
3511
- const rectB = mappedCommentB.rectangle;
3512
- if (rectA.y !== rectB.y) {
3513
- return rectA.y - rectB.y;
3514
- }
3515
- else {
3516
- return rectA.x - rectB.x;
3517
- }
3518
- }
3539
+ setCurrentPage(pageNumber) {
3540
+ this.pageNumber = pageNumber;
3519
3541
  }
3520
- nextItem() {
3521
- this.index += 1;
3522
- if (this.index === this.annotationList.length) {
3523
- this.index = 0;
3524
- }
3525
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3526
- this.store.dispatch(new SelectedAnnotation({
3527
- annotationId: this.navigationList[this.index].annotationId, editable: false, selected: true
3528
- }));
3542
+ rotate(rotation) {
3543
+ this.toolbarEvents.rotate(rotation);
3529
3544
  }
3530
- prevItem() {
3531
- this.index -= 1;
3532
- if (this.index < 0) {
3533
- this.index = this.navigationList.length - 1;
3534
- }
3535
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3536
- this.store.dispatch(new SelectedAnnotation({
3537
- annotationId: this.navigationList[this.index].annotationId,
3538
- editable: false,
3539
- selected: true
3540
- }));
3545
+ printFile() {
3546
+ this.toolbarEvents.print();
3541
3547
  }
3542
- upperRectangle(rectangles) {
3543
- [...rectangles].sort((rect1, rect2) => rect1.y - rect2.y);
3544
- return { x: rectangles[0].x, y: rectangles[0].y };
3548
+ downloadFile() {
3549
+ this.toolbarEvents.download();
3545
3550
  }
3546
- }
3547
- /** @nocollapse */ CommentsNavigateComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, deps: [{ token: i1.Store }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3548
- /** @nocollapse */ CommentsNavigateComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], encapsulation: i0.ViewEncapsulation.None });
3549
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
3550
- type: Component,
3551
- 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" }]
3552
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarEventService }]; }, propDecorators: { annotationList: [{
3553
- type: Input
3554
- }], autoSelect: [{
3555
- type: Input
3556
- }] } });
3557
-
3558
- class CommentSearchComponent {
3559
- constructor(store) {
3560
- this.store = store;
3561
- this.searchResults = [];
3562
- this.searchIndex = 0;
3551
+ zoom(zoomFactor) {
3552
+ this.toolbarEvents.zoom(+zoomFactor);
3563
3553
  }
3564
- ngAfterViewInit() {
3565
- if (this.searchInput) {
3566
- this.searchInput.nativeElement.focus();
3567
- }
3554
+ stepZoom(zoomFactor) {
3555
+ this.toolbarEvents.stepZoom(zoomFactor);
3556
+ this.zoomSelect.nativeElement.selected = 'selected';
3568
3557
  }
3569
- ngOnDestroy() {
3570
- // TODO workaround for tab error
3571
- setTimeout(() => { this.store.dispatch(new SearchComment('')); }, 250);
3558
+ toggleCommentsPanel() {
3559
+ this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3572
3560
  }
3573
- searchComments(searchText) {
3574
- this.clearSearch();
3575
- if (searchText.length > 2) {
3576
- this.searchString = searchText;
3577
- this.searchResults = this.annotations
3578
- .filter(annotation => annotation.comments.length > 0)
3579
- .filter(annotation => annotation.comments[0].content.toLowerCase().includes(this.searchString.toLowerCase()));
3580
- if (this.searchResults.length > 0) {
3581
- this.store.dispatch(new SearchComment(searchText));
3582
- }
3583
- }
3561
+ toggleRedactBar() {
3562
+ this.toolbarEvents.toggleRedactionMode();
3584
3563
  }
3585
- clearSearch() {
3586
- this.searchString = undefined;
3587
- this.searchResults = [];
3588
- this.searchIndex = 0;
3589
- this.store.dispatch(new SearchComment(''));
3564
+ toggleGrabNDrag() {
3565
+ this.toolbarEvents.toggleGrabNDrag();
3566
+ }
3567
+ isPdf() {
3568
+ return this.contentType === 'pdf';
3569
+ }
3570
+ toggleSearchBar() {
3571
+ this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
3572
+ }
3573
+ toggleMoreOptions() {
3574
+ this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
3575
+ setTimeout(() => {
3576
+ if (this.mvMenuItems) {
3577
+ this.mvMenuItems.nativeElement.focus();
3578
+ }
3579
+ }, 100);
3590
3580
  }
3591
3581
  }
3592
- /** @nocollapse */ CommentSearchComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
3593
- /** @nocollapse */ CommentSearchComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: CommentSearchComponent, selector: "mv-comment-search", inputs: { annotations: "annotations" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], ngImport: i0, template: "<input #searchInput type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\" name=\"searchString\"\n aria-label=\"search comments input\" [ngModel]=\"searchString\">\n<button type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\">Search</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\n </p>\n</ng-container>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }], encapsulation: i0.ViewEncapsulation.None });
3594
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, decorators: [{
3582
+ /** @nocollapse */ MainToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i0.ChangeDetectorRef }, { token: NumberHelperService }], target: i0.ɵɵFactoryTarget.Component });
3583
+ /** @nocollapse */ MainToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 aria-pressed=\"false\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>More options</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Index</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Bookmarks</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 tabindex=\"-1\"\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-hidden=\"true\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\">Draw a box</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 tabindex=\"0\"\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.highlightModeSubject | async\"\n aria-hidden=\"true\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">Highlight</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 aria-pressed=\"false\"\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</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></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></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 aria-pressed=\"false\"\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</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 ></button>\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPresentBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPresentBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\">Present</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRedactBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRedactBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>Redact</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvGrabBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvGrabBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>Grab and drag</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDownloadBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDownloadBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPrintBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPrintBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvCommentsBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvCommentsBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>Comments</span>\n </button>\n </ng-template>\n </div>\n <mv-search-bar></mv-search-bar>\n </div>\n\n <div id=\"loadingBar\">\n <div class=\"progress\">\n <div class=\"glimmer\"></div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i6.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"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i6.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: SearchBarComponent, selector: "mv-search-bar" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.DecimalPipe, name: "number" }] });
3584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, decorators: [{
3595
3585
  type: Component,
3596
- args: [{ selector: 'mv-comment-search', encapsulation: ViewEncapsulation.None, template: "<input #searchInput type=\"text\"\n class=\"govuk-input comment-search__item\"\n id=\"search-comments-input\" name=\"searchString\"\n aria-label=\"search comments input\" [ngModel]=\"searchString\">\n<button type=\"button\"\n [class.govuk-button--disabled]=\"searchInput?.value.length <= 2\"\n class=\"govuk-button comment-search__item\"\n (click)=\"searchComments(searchInput.value.trim())\">Search</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\n </p>\n</ng-container>\n" }]
3597
- }], ctorParameters: function () { return [{ type: i1.Store }]; }, propDecorators: { annotations: [{
3586
+ 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 aria-pressed=\"false\"\n (click)=\"toggleMoreOptions()\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n [disabled]=\"redactAllInProgress\"\n >\n <span>More options</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleIndexSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Index</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 aria-pressed=\"false\"\n [disabled]=\"redactAllInProgress\"\n (click)=\"toggleBookmarksSideBar(); isDropdownMenuOpen = false\"\n >\n <span>Bookmarks</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 tabindex=\"-1\"\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-hidden=\"true\"\n aria-pressed=\"false\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"onClickDrawToggle(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"draw_label\">Draw a box</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 tabindex=\"0\"\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.highlightModeSubject | async\"\n aria-hidden=\"true\"\n aria-pressed=\"false\"\n (click)=\"onClickHighlightToggle(); isDropdownMenuOpen = false\"\n data-l10n-id=\"toggleHighlightButton\"\n >\n <span data-l10n-id=\"highlight_label\">Highlight</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 aria-pressed=\"false\"\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</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></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></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 aria-pressed=\"false\"\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</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 ></button>\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPresentBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPresentBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"togglePresentBar(); isDropdownMenuOpen = false\"\n >\n <span data-l10n-id=\"icpMode_label\">Present</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvRedactBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvRedactBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleRedactBar(); isDropdownMenuOpen = false\"\n >\n <span>Redact</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvGrabBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvGrabBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleGrabNDrag(); isDropdownMenuOpen = false\"\n >\n <span>Grab and drag</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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvDownloadBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvDownloadBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"downloadFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvPrintBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvPrintBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"printFile(); isDropdownMenuOpen = false\"\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 [class.button-hidden-on-toolbar]=\"\n mvToolbarMain.offsetWidth < widthRequiredForBtn['mvCommentsBtn']\n \"\n [class.button-hidden-on-dropdown]=\"\n mvToolbarMain.offsetWidth >= widthRequiredForBtn['mvCommentsBtn']\n \"\n aria-pressed=\"false\"\n (click)=\"toggleCommentsPanel(); isDropdownMenuOpen = false\"\n >\n <span>Comments</span>\n </button>\n </ng-template>\n </div>\n <mv-search-bar></mv-search-bar>\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" }]
3587
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i0.ChangeDetectorRef }, { type: NumberHelperService }]; }, propDecorators: { enableAnnotations: [{
3598
3588
  type: Input
3599
- }], searchInput: [{
3589
+ }], enableRedactions: [{
3590
+ type: Input
3591
+ }], enableICP: [{
3592
+ type: Input
3593
+ }], contentType: [{
3594
+ type: Input
3595
+ }], zoomSelect: [{
3600
3596
  type: ViewChild,
3601
- args: ['searchInput', { static: false }]
3597
+ args: ['zoomSelect', { static: false }]
3598
+ }], mvToolbarMain: [{
3599
+ type: ViewChild,
3600
+ args: ['mvToolbarMain', { static: false }]
3601
+ }], mvMenuItems: [{
3602
+ type: ViewChild,
3603
+ args: ['dropdownMenu', { static: false }]
3604
+ }], onResize: [{
3605
+ type: HostListener,
3606
+ args: ['window:resize', []]
3607
+ }], onControlPrint: [{
3608
+ type: HostListener,
3609
+ args: ['document:keydown.control.p', ['$event']]
3610
+ }, {
3611
+ type: HostListener,
3612
+ args: ['document:keydown.meta.p', ['$event']]
3602
3613
  }] } });
3603
3614
 
3604
- class FilterPipe {
3605
- transform(items, searchText, fieldName) {
3606
- if (!items) {
3607
- return [];
3608
- }
3609
- if (!searchText) {
3610
- return items;
3611
- }
3612
- return items.filter(item => {
3613
- if (item) {
3614
- if (item[fieldName]) {
3615
- return item[fieldName].toLowerCase().includes(searchText.toLowerCase());
3616
- }
3617
- else {
3618
- return item.toLowerCase().includes(searchText.toLowerCase());
3619
- }
3620
- }
3621
- return false;
3615
+ const getRedactionState = createSelector(getMVState, (state) => state.redactions);
3616
+ const getRedactionPages = createSelector(getRedactionState, getPageEnt);
3617
+ const getSelected = createSelector(getRedactionState, getSelectedRedaction);
3618
+ const getRedactedDocumentInfo = createSelector(getRedactionState, getRedactedDocInfo);
3619
+ const getRedactionEnt = createSelector(getRedactionState, getRedactionEnt$1);
3620
+ const getRedactionArray = createSelector(getRedactionEnt, getDocumentId, (ent, documentId) => {
3621
+ const redactions = Object.keys(ent).map(key => ent[key]);
3622
+ return { redactions, documentId };
3623
+ });
3624
+ const getRedactionsPerPage = createSelector(getPages, getRedactionPages, (pages, pageEnt) => {
3625
+ if (pages && pageEnt) {
3626
+ const arr = [];
3627
+ Object.keys(pages).forEach(key => {
3628
+ arr.push({
3629
+ anno: pageEnt[key] ? pageEnt[key] : [],
3630
+ styles: pages[key].styles
3631
+ });
3622
3632
  });
3633
+ return arr;
3623
3634
  }
3624
- }
3625
- /** @nocollapse */ FilterPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3626
- /** @nocollapse */ FilterPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, name: "filter" });
3627
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, decorators: [{
3628
- type: Pipe,
3629
- args: [{
3630
- name: 'filter'
3631
- }]
3632
- }] });
3633
-
3634
- class UnsnakePipe {
3635
- transform(items) {
3636
- return items.split('_').join(' ');
3637
- }
3638
- }
3639
- /** @nocollapse */ UnsnakePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3640
- /** @nocollapse */ UnsnakePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, name: "unsnake" });
3641
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, decorators: [{
3642
- type: Pipe,
3643
- args: [{
3644
- name: 'unsnake'
3645
- }]
3646
- }] });
3635
+ });
3647
3636
 
3648
- class CommentFilterComponent {
3649
- constructor(store, fb) {
3637
+ class RedactionToolbarComponent {
3638
+ constructor(toolbarEventService, toolbarButtons, store) {
3639
+ this.toolbarEventService = toolbarEventService;
3640
+ this.toolbarButtons = toolbarButtons;
3650
3641
  this.store = store;
3651
- this.fb = fb;
3652
- this.isPreview = false;
3642
+ this.preview = false;
3643
+ this.hasRedactions = false;
3644
+ this.subscriptions = [];
3653
3645
  }
3654
3646
  ngOnInit() {
3655
- this.tagGroup = this.fb.group({
3656
- 'tagFilters': this.fb.group({}),
3657
- });
3658
- this.filter$ = this.store.pipe(select(getTagFilters));
3659
- this.$subscriptions = this.tagGroup.valueChanges.pipe(auditTime(5)).subscribe(value => {
3660
- const tagFilters = value['tagFilters'];
3661
- this.store.dispatch(new AddFilterTags(tagFilters));
3662
- });
3663
- this.buildFrom();
3664
- }
3665
- buildFrom() {
3666
- const checkboxes = this.tagGroup.get('tagFilters');
3667
- this.allTags$ = this.store.pipe(select(getAllTagsArr)).pipe(tap(tags => {
3668
- this.tagGroup.reset();
3669
- tags.forEach((value) => {
3670
- checkboxes.addControl(value.key, new UntypedFormControl(false));
3671
- });
3647
+ this.subscriptions.push(this.store.pipe(select(getRedactionArray)).subscribe(redactions => {
3648
+ this.hasRedactions = !!redactions.redactions.length;
3649
+ }));
3650
+ this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
3651
+ this.redactionAllInProgress = inprogress;
3672
3652
  }));
3673
3653
  }
3674
- onClearFilters() {
3675
- this.tagGroup.reset();
3676
- this.store.dispatch(new ClearFilterTags());
3654
+ onRedactAllSearch() {
3655
+ this.toolbarEventService.openRedactionSearch.next(true);
3677
3656
  }
3678
- ngOnDestroy() {
3679
- this.$subscriptions.unsubscribe();
3657
+ toggleTextRedactionMode() {
3658
+ this.toolbarEventService.highlightModeSubject.next(true);
3680
3659
  }
3681
- onRemoveFilter(tagName) {
3682
- const checkboxes = this.tagGroup.get('tagFilters');
3683
- checkboxes.controls[tagName].setValue(false);
3660
+ toggleDrawMode() {
3661
+ this.toolbarEventService.drawModeSubject.next(true);
3684
3662
  }
3685
- onToggleFilterView() {
3686
- this.isPreview = !this.isPreview;
3663
+ togglePreview() {
3664
+ this.preview = !this.preview;
3665
+ this.toolbarEventService.toggleRedactionPreview(this.preview);
3666
+ }
3667
+ unmarkAll() {
3668
+ this.toolbarEventService.unmarkAll();
3669
+ }
3670
+ redact() {
3671
+ this.toolbarEventService.applyRedactionToDocument();
3672
+ }
3673
+ toggleRedactBar() {
3674
+ this.toolbarEventService.toggleRedactionMode();
3675
+ }
3676
+ redactPage() {
3677
+ this.toolbarEventService.drawModeSubject.next(true);
3678
+ this.toolbarEventService.redactPage();
3679
+ }
3680
+ ngOnDestroy() {
3681
+ for (const subscription of this.subscriptions) {
3682
+ subscription.unsubscribe();
3683
+ }
3687
3684
  }
3688
3685
  }
3689
- /** @nocollapse */ CommentFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, deps: [{ token: i1.Store }, { token: i2.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component });
3690
- /** @nocollapse */ CommentFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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 class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{'govuk-accordion__section--expanded': isPreview}\"\n (click)=\"onToggleFilterView()\"><strong >Filter options</strong><span class=\"govuk-accordion__icon\"></span></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><a (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\">Clear filters</a></p>\n </div>\n </div>\n\n <h4 class=\"govuk-heading-s govuk-!-margin-bottom-0\" *ngIf=\"filters.length\">Tag filters</h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a class=\"hmcts-filter__tag\" [routerLink]=\"[]\" (click)=\"onRemoveFilter(tagName)\">\n <span class=\"govuk-visually-hidden\">Remove this filter</span>{{tagName | unsnake}}</a>\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\n </label>\n <input class=\"govuk-input\" id=\"keywords\" name=\"keywords\" type=\"text\" [(ngModel)]=\"searchValue\">\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 class=\"govuk-checkboxes__item\" *ngFor=\"let item of (allTags$ | async) | filter: searchValue: 'key'\">\n <input [formControlName]=\"item.key\" [id]=\"item.key\" class=\"govuk-checkboxes__input\" [value]=\"false\"\n [attr.aria-describedby]=\"item.key\" type=\"checkbox\">\n <label class=\"govuk-label govuk-checkboxes__label\" [for]=\"item.key\">\n {{ item.key | unsnake}} ({{item.length}})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: FilterPipe, name: "filter" }, { kind: "pipe", type: UnsnakePipe, name: "unsnake" }], encapsulation: i0.ViewEncapsulation.None });
3691
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, decorators: [{
3686
+ /** @nocollapse */ RedactionToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, deps: [{ token: ToolbarEventService }, { token: ToolbarButtonVisibilityService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
3687
+ /** @nocollapse */ RedactionToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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</label\n >\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw\"\n title=\"Draw a box\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">Draw a box</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"Redact Page\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">Redact page</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"From search\"\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</span\n >\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\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">Redact text</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\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">Clear all</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\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">Preview</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\"\n >Hide preview</span\n >\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=\"Redact\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">Save document</span>\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)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Redaction</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
3688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
3692
3689
  type: Component,
3693
- 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 class=\"hmcts-filter__toggle-filters-link\"\n [ngClass]=\"{'govuk-accordion__section--expanded': isPreview}\"\n (click)=\"onToggleFilterView()\"><strong >Filter options</strong><span class=\"govuk-accordion__icon\"></span></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><a (click)=\"onClearFilters()\"\n class=\"govuk-link govuk-link--no-visited-state\"\n [routerLink]=\"[]\">Clear filters</a></p>\n </div>\n </div>\n\n <h4 class=\"govuk-heading-s govuk-!-margin-bottom-0\" *ngIf=\"filters.length\">Tag filters</h4>\n\n <ul class=\"hmcts-filter-tags\" *ngIf=\"filters.length\">\n <li *ngFor=\"let tagName of filters\">\n <a class=\"hmcts-filter__tag\" [routerLink]=\"[]\" (click)=\"onRemoveFilter(tagName)\">\n <span class=\"govuk-visually-hidden\">Remove this filter</span>{{tagName | unsnake}}</a>\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\n </label>\n <input class=\"govuk-input\" id=\"keywords\" name=\"keywords\" type=\"text\" [(ngModel)]=\"searchValue\">\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 class=\"govuk-checkboxes__item\" *ngFor=\"let item of (allTags$ | async) | filter: searchValue: 'key'\">\n <input [formControlName]=\"item.key\" [id]=\"item.key\" class=\"govuk-checkboxes__input\" [value]=\"false\"\n [attr.aria-describedby]=\"item.key\" type=\"checkbox\">\n <label class=\"govuk-label govuk-checkboxes__label\" [for]=\"item.key\">\n {{ item.key | unsnake}} ({{item.length}})\n </label>\n </div>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n\n </div>\n</div>\n" }]
3694
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: i2.UntypedFormBuilder }]; } });
3690
+ args: [{ selector: 'mv-redaction-toolbar', template: "<div class=\"redaction\">\n <label class=\"govuk-label redaction-title\" data-l10n-id=\"redaction_options\"\n >Redaction options</label\n >\n <button\n id=\"toggleDrawButton\"\n class=\"mv-button redaction-button--draw\"\n title=\"Draw a box\"\n data-l10n-id=\"toggleDrawButton\"\n (click)=\"toggleDrawMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleDrawButton_label\">Draw a box</span>\n </button>\n\n <button\n id=\"redactPageButton\"\n class=\"mv-button redaction-button--redact-page\"\n title=\"Redact Page\"\n data-l10n-id=\"redactPageButton\"\n (click)=\"redactPage()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"redactPageButton_label\">Redact page</span>\n </button>\n <button\n *ngIf=\"showRedactSearch\"\n id=\"mvRedactFromSearchBtn\"\n title=\"From search\"\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</span\n >\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\"\n data-l10n-id=\"toggleTextRedactionButton\"\n (click)=\"toggleTextRedactionMode()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"toggleTextRedactionButton_label\">Redact text</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\"\n data-l10n-id=\"toggleClearAllButton\"\n (click)=\"unmarkAll()\"\n >\n <span data-l10n-id=\"Clear all\">Clear all</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\"\n data-l10n-id=\"togglePreviewButton\"\n (click)=\"togglePreview()\"\n >\n <span *ngIf=\"!preview\" data-l10n-id=\"redaction-preview_label\">Preview</span>\n <span *ngIf=\"preview\" data-l10n-id=\"redaction-hide-preview_label\"\n >Hide preview</span\n >\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=\"Redact\"\n data-l10n-id=\"mvRedactBtn\"\n (click)=\"redact()\"\n >\n <span data-l10n-id=\"Save Document\">Save document</span>\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)=\"toggleRedactBar()\"\n [disabled]=\"redactionAllInProgress\"\n >\n <span data-l10n-id=\"Close Redaction\">Close Redaction</span>\n </button>\n</div>\n" }]
3691
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }]; }, propDecorators: { showRedactSearch: [{
3692
+ type: Input
3693
+ }] } });
3695
3694
 
3696
- class CommentSetHeaderComponent {
3697
- constructor(store, commentService, toolbarEvents) {
3695
+ class IcpToolbarComponent {
3696
+ constructor(toolbarEventService, store) {
3697
+ this.toolbarEventService = toolbarEventService;
3698
3698
  this.store = store;
3699
- this.commentService = commentService;
3700
- this.toolbarEvents = toolbarEvents;
3701
- this.showCommentSummaryDialog = new EventEmitter();
3702
- this.tabs = [];
3703
- this.tabSelected = '';
3704
3699
  }
3705
3700
  ngOnInit() {
3706
- const tagFilter$ = this.store.pipe(select(getTagFilters));
3707
- const filteredAnnotation$ = this.store.pipe(select(getFilteredAnnotations));
3708
- this.$subscriptions = combineLatest([tagFilter$, filteredAnnotation$]).subscribe(([formData, filteredAnno]) => {
3709
- this.navigationList = filteredAnno;
3710
- this.tabs = this.navigationList.length > 0 ?
3711
- [{ label: 'comments' }, { label: 'filter' }, { label: 'search' }] : [{ label: 'comments' }];
3712
- this.isFiltered = !formData.length;
3713
- this.tabs = [...this.tabs].map((tab) => {
3714
- return !this.isFiltered && tab.label === 'filter' ? Object.assign(Object.assign({}, tab), { isFiltered: true }) : Object.assign(Object.assign({}, tab), { isFiltered: false });
3715
- });
3716
- });
3701
+ this.$subscription = this.store.pipe(select(isPresenter))
3702
+ .subscribe(isPresenter => this.isPresenter = isPresenter);
3703
+ this.$subscription.add(this.store.pipe(select(getPresenterName))
3704
+ .subscribe(name => this.presenterName = name));
3717
3705
  }
3718
- toggleCommentsSummary() {
3719
- this.showCommentSummaryDialog.emit();
3706
+ ngOnDestroy() {
3707
+ this.$subscription.unsubscribe();
3720
3708
  }
3721
- selectTab(tab) {
3722
- this.tabSelected = tab !== this.tabSelected ? tab : undefined;
3723
- if (this.tabSelected) {
3724
- this.marginToComment = true;
3725
- this.commentService.createMarginToCommentEvent(this.marginToComment);
3726
- }
3727
- else {
3728
- this.marginToComment = false;
3729
- this.commentService.createMarginToCommentEvent(this.marginToComment);
3730
- }
3709
+ present() {
3710
+ this.toolbarEventService.icp.becomePresenter();
3731
3711
  }
3732
- toggleCommentsPanel() {
3733
- this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3712
+ stopPresenting() {
3713
+ this.toolbarEventService.icp.stopPresenting();
3734
3714
  }
3735
- ngOnDestroy() {
3736
- this.$subscriptions.unsubscribe();
3715
+ leaveIcpSession() {
3716
+ this.toolbarEventService.icp.leaveSession();
3717
+ }
3718
+ showParticipantsList() {
3719
+ this.toolbarEventService.toggleParticipantsList(!this.toolbarEventService.icp.participantsListVisible.getValue());
3737
3720
  }
3738
3721
  }
3739
- /** @nocollapse */ CommentSetHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, deps: [{ token: i1.Store }, { token: CommentService }, { token: ToolbarEventService }], target: i0.ɵɵFactoryTarget.Component });
3740
- /** @nocollapse */ CommentSetHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: CommentSetHeaderComponent, selector: "mv-comment-set-header", inputs: { showCommentSummary: "showCommentSummary" }, outputs: { showCommentSummaryDialog: "showCommentSummaryDialog" }, ngImport: i0, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': toolbarEvents.icp.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\">\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()\">\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 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: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: CommentSearchComponent, selector: "mv-comment-search", inputs: ["annotations"] }, { kind: "component", type: CommentsNavigateComponent, selector: "mv-comments-navigate", inputs: ["annotationList", "autoSelect"] }, { kind: "component", type: CommentFilterComponent, selector: "mv-comment-filter" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }], encapsulation: i0.ViewEncapsulation.None });
3741
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
3722
+ /** @nocollapse */ IcpToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, deps: [{ token: ToolbarEventService }, { token: i1.Store }], target: i0.ɵɵFactoryTarget.Component });
3723
+ /** @nocollapse */ IcpToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", 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\">{{(toolbarEventService.icp.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TitleCasePipe, name: "titlecase" }] });
3724
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, decorators: [{
3742
3725
  type: Component,
3743
- args: [{ selector: 'mv-comment-set-header', encapsulation: ViewEncapsulation.None, template: "<div class=\"govuk-tabs commentSummaryHeader\" [ngClass]=\"{'icp-mode': toolbarEvents.icp.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\">\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()\">\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 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" }]
3744
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: ToolbarEventService }]; }, propDecorators: { showCommentSummary: [{
3745
- type: Input
3746
- }], showCommentSummaryDialog: [{
3747
- type: Output
3748
- }] } });
3726
+ 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\">{{(toolbarEventService.icp.participantsListVisible | async) ? 'Hide': 'Show'}}\n participants</span>\n </button>\n </div>\n</div>" }]
3727
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
3749
3728
 
3750
- const getBookmarkState = createSelector(getMVState, (state) => state.bookmarks);
3751
- const getBookmarkPages = createSelector(getBookmarkState, getBookmarkPageEnt);
3752
- const getBookmarkEntities = createSelector(getBookmarkState, getBookmarkEnts);
3753
- const getBookmarkNodes = createSelector(getBookmarkEntities, (entities) => generateBookmarkNodes(entities));
3754
- const getEditableBookmark = createSelector(getBookmarkState, getEditBookmark);
3755
- const getBookmarkInfo = createSelector(getBookmarkNodes, getDocumentId, getPdfPosition, getPages, (bookmarkNodes, docId, pdfPosition, pages) => {
3756
- return {
3757
- pageNumber: pdfPosition.pageNumber - 1,
3758
- xCoordinate: pdfPosition.left,
3759
- yCoordinate: ((pages[pdfPosition.pageNumber].styles.height)
3760
- - (pdfPosition.top * (pages[pdfPosition.pageNumber].viewportScale)))
3761
- / parseFloat(pages[pdfPosition.pageNumber].scaleRotation.scale),
3762
- previous: bookmarkNodes.length > 0 ? bookmarkNodes.sort((a, b) => a.index - b.index)[bookmarkNodes.length - 1].id : undefined,
3763
- documentId: docId
3764
- };
3765
- });
3766
- const getBookmarksPerPage = createSelector(getPages, getBookmarkPages, (pages, pageEnt) => {
3767
- if (pages && pageEnt) {
3768
- const arr = [];
3769
- Object.keys(pages).forEach(key => {
3770
- const pageIdx = Number(key) - 1; // -1 as the thisPages array is 0 indexed
3771
- const thisPage = pageEnt[pageIdx];
3772
- arr.push({
3773
- bookmark: thisPage ? thisPage : [],
3774
- styles: pages[key].styles
3775
- });
3776
- });
3777
- return arr;
3778
- }
3779
- });
3729
+ class ToolbarModule {
3730
+ }
3731
+ /** @nocollapse */ ToolbarModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3732
+ /** @nocollapse */ ToolbarModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
3733
+ MainToolbarComponent,
3734
+ RedactionToolbarComponent,
3735
+ IcpToolbarComponent,
3736
+ RedactionSearchBarComponent], imports: [CommonModule,
3737
+ FormsModule,
3738
+ OverlayModule,
3739
+ RouterModule], exports: [MainToolbarComponent,
3740
+ SearchBarComponent,
3741
+ RedactionToolbarComponent,
3742
+ IcpToolbarComponent,
3743
+ RedactionSearchBarComponent] });
3744
+ /** @nocollapse */ ToolbarModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, providers: [
3745
+ ToolbarButtonVisibilityService,
3746
+ ToolbarEventService
3747
+ ], imports: [CommonModule,
3748
+ FormsModule,
3749
+ OverlayModule,
3750
+ RouterModule] });
3751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, decorators: [{
3752
+ type: NgModule,
3753
+ args: [{
3754
+ declarations: [
3755
+ SearchBarComponent,
3756
+ MainToolbarComponent,
3757
+ RedactionToolbarComponent,
3758
+ IcpToolbarComponent,
3759
+ RedactionSearchBarComponent
3760
+ ],
3761
+ providers: [
3762
+ ToolbarButtonVisibilityService,
3763
+ ToolbarEventService
3764
+ ],
3765
+ exports: [
3766
+ MainToolbarComponent,
3767
+ SearchBarComponent,
3768
+ RedactionToolbarComponent,
3769
+ IcpToolbarComponent,
3770
+ RedactionSearchBarComponent
3771
+ ],
3772
+ imports: [
3773
+ CommonModule,
3774
+ FormsModule,
3775
+ OverlayModule,
3776
+ RouterModule
3777
+ ]
3778
+ }]
3779
+ }] });
3780
3780
 
3781
3781
  class BoxHighlightCreateComponent {
3782
3782
  constructor(toolbarEvents, highlightService) {