@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 { BehaviorSubject, Subject, of, combineLatest, asyncScheduler } from 'rxjs';
@@ -16,12 +16,12 @@ import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';
16
16
  import * as pdfjsLib from 'pdfjs-dist';
17
17
  import 'pdfjs-dist/build/pdf.worker';
18
18
  import uuid$1, { v4 } from 'uuid';
19
- import * as i6 from '@angular/cdk/overlay';
20
- import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
21
- import * as i5 from '@angular/router';
22
- import { RouterModule } from '@angular/router';
23
19
  import * as i4$1 from 'ngx-chips';
24
20
  import { TagInputModule } from 'ngx-chips';
21
+ import * as i5 from '@angular/router';
22
+ import { RouterModule } from '@angular/router';
23
+ import * as i6 from '@angular/cdk/overlay';
24
+ import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
25
25
  import * as i4$2 from 'mutable-div';
26
26
  import { MutableDivModule } from 'mutable-div';
27
27
  import * as i3 from '@circlon/angular-tree-component';
@@ -2473,47 +2473,101 @@ const getFilteredAnnotations = createSelector(getAnnotationEntities, getTagFilte
2473
2473
  .filter(annotation => annotation.comments && annotation.comments.length > 0);
2474
2474
  });
2475
2475
 
2476
- /**
2477
- * Number Helper Service
2478
- * */
2479
- class NumberHelperService {
2480
- constructor() { }
2481
- isNumber(value) {
2482
- return (value !== null
2483
- && value !== undefined
2484
- && value !== ''
2485
- && !isNaN(Number(value.toString())));
2476
+ class HighlightCreateService {
2477
+ constructor(toolBarEvents, store) {
2478
+ this.toolBarEvents = toolBarEvents;
2479
+ this.store = store;
2480
+ }
2481
+ saveAnnotation(rectangles, page) {
2482
+ this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
2483
+ const anno = {
2484
+ id: v4(),
2485
+ color: 'FFFF00',
2486
+ comments: [],
2487
+ page: page,
2488
+ rectangles: rectangles,
2489
+ type: 'highlight',
2490
+ ...anoSetDocId,
2491
+ createdBy: '',
2492
+ createdByDetails: undefined,
2493
+ createdDate: moment.utc().tz('Europe/London').toISOString(),
2494
+ lastModifiedBy: '',
2495
+ lastModifiedByDetails: undefined,
2496
+ lastModifiedDate: '',
2497
+ tags: [],
2498
+ };
2499
+ this.store.dispatch(new SaveAnnotation(anno));
2500
+ });
2501
+ }
2502
+ applyRotation(pageHeight, pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, rotate, zoom) {
2503
+ const { x, y, width, height } = {
2504
+ x: +(offsetLeft / zoom).toFixed(2),
2505
+ y: +(offsetTop / zoom).toFixed(2),
2506
+ width: +(offsetWidth / zoom).toFixed(2),
2507
+ height: +(offsetHeight / zoom).toFixed(2)
2508
+ };
2509
+ const rectangle = { x, y, width, height };
2510
+ switch (rotate) {
2511
+ case 90:
2512
+ rectangle.width = height;
2513
+ rectangle.height = width;
2514
+ rectangle.x = y;
2515
+ rectangle.y = +(pageWidth / zoom - x - width).toFixed(2);
2516
+ break;
2517
+ case 180:
2518
+ rectangle.x = +(pageWidth / zoom - x - width).toFixed(2);
2519
+ rectangle.y = +(pageHeight / zoom - y - height).toFixed(2);
2520
+ break;
2521
+ case 270:
2522
+ rectangle.width = height;
2523
+ rectangle.height = width;
2524
+ rectangle.x = +(pageHeight / zoom - y - height).toFixed(2);
2525
+ rectangle.y = x;
2526
+ break;
2527
+ }
2528
+ return rectangle;
2529
+ }
2530
+ resetHighlight() {
2531
+ window.getSelection().removeAllRanges();
2532
+ this.toolBarEvents.highlightModeSubject.next(false);
2486
2533
  }
2487
2534
  }
2488
- /** @nocollapse */ NumberHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2489
- /** @nocollapse */ NumberHelperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, providedIn: 'root' });
2490
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, decorators: [{
2491
- type: Injectable,
2492
- args: [{
2493
- providedIn: 'root'
2494
- }]
2495
- }], ctorParameters: function () { return []; } });
2535
+ /** @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 });
2536
+ /** @nocollapse */ HighlightCreateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService });
2537
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, decorators: [{
2538
+ type: Injectable
2539
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
2496
2540
 
2497
- class SearchBarComponent {
2498
- constructor(toolbarButtons, toolbarEvents) {
2541
+ class RedactionSearchBarComponent {
2542
+ constructor(store, toolbarButtons, toolbarEvents, highlightService) {
2543
+ this.store = store;
2499
2544
  this.toolbarButtons = toolbarButtons;
2500
2545
  this.toolbarEvents = toolbarEvents;
2546
+ this.highlightService = highlightService;
2501
2547
  this.highlightAll = true;
2502
2548
  this.matchCase = false;
2503
2549
  this.wholeWord = false;
2504
2550
  this.resultsText = '';
2505
2551
  this.searchText = '';
2506
2552
  this.resultCount = 0;
2507
- this.subscriptions = [];
2553
+ this.redactElements = [];
2508
2554
  this.advancedSearchVisible = false;
2509
2555
  }
2510
2556
  ngOnInit() {
2511
- this.subscriptions.push(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
2557
+ this.subscription = this.toolbarEvents.redactionSerachSubject.subscribe((results) => this.redactAllSearched(results));
2558
+ this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
2559
+ this.subscription.add(this.store.pipe(select(getPages)).subscribe((pages) => {
2560
+ if (pages[1]) {
2561
+ this.allPages = pages;
2562
+ }
2563
+ }));
2564
+ this.subscription.add(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
2565
+ this.subscription.add(this.toolbarEvents.openRedactionSearch.subscribe(isOpen => this.openSearchModal = isOpen));
2566
+ this.subscription.add(this.toolbarEvents.redactAllInProgressSubject
2567
+ .subscribe(inProgress => this.redactAllInProgress = inProgress));
2512
2568
  }
2513
2569
  ngOnDestroy() {
2514
- for (const subscription of this.subscriptions) {
2515
- subscription.unsubscribe();
2516
- }
2570
+ this.subscription.unsubscribe();
2517
2571
  }
2518
2572
  onWindowKeyDown(e) {
2519
2573
  if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
@@ -2522,1534 +2576,1480 @@ class SearchBarComponent {
2522
2576
  setTimeout(() => this.findInput.nativeElement.focus(), 200);
2523
2577
  }
2524
2578
  }
2525
- searchNext() {
2579
+ search(reset = true) {
2580
+ this.redactAll = !reset;
2581
+ if (this.redactAll) {
2582
+ this.toolbarEvents.redactAllInProgressSubject.next(true);
2583
+ }
2584
+ if (reset) {
2585
+ this.redactElements = [];
2586
+ }
2526
2587
  this.toolbarEvents.search({
2527
2588
  searchTerm: this.searchText,
2528
2589
  highlightAll: this.highlightAll,
2529
2590
  matchCase: this.matchCase,
2530
2591
  wholeWord: this.wholeWord,
2531
2592
  previous: false,
2532
- reset: false
2593
+ reset
2533
2594
  });
2534
2595
  }
2535
- searchPrev() {
2536
- this.toolbarEvents.search({
2537
- searchTerm: this.searchText,
2538
- highlightAll: this.highlightAll,
2539
- matchCase: this.matchCase,
2540
- wholeWord: this.wholeWord,
2541
- previous: true,
2542
- reset: false
2596
+ saveRedaction(redactRectangle) {
2597
+ const redaction = redactRectangle.map(ele => {
2598
+ return { page: ele.page, rectangles: ele.rectangles, redactionId: uuid$1(), documentId: this.documentId };
2543
2599
  });
2600
+ this.store.dispatch(new SaveBulkRedaction({ searchRedactions: redaction }));
2544
2601
  }
2545
- search() {
2546
- this.toolbarEvents.search({
2547
- searchTerm: this.searchText,
2548
- highlightAll: this.highlightAll,
2549
- matchCase: this.matchCase,
2550
- wholeWord: this.wholeWord,
2551
- previous: false,
2552
- reset: true
2553
- });
2602
+ existInRedactElements(pageNumber, matechedIndex, rectangles) {
2603
+ if (this.redactElements && this.redactElements.length > 0) {
2604
+ const pagesFound = this.redactElements.find(re => re.page === pageNumber && re.matchedIndex === matechedIndex);
2605
+ const pageRectangles = pagesFound?.rectangles;
2606
+ if (!pageRectangles || pageRectangles.length <= 0) {
2607
+ return false;
2608
+ }
2609
+ let matchesRectangles = 0;
2610
+ for (let rectIndx = 0; rectIndx < pageRectangles.length; rectIndx++) {
2611
+ const rectangle = pageRectangles[rectIndx];
2612
+ const foundRectangle = rectangles.find(re => re.width === rectangle.width &&
2613
+ re.height === rectangle.height && re.x === rectangle.x && re.y === rectangle.y);
2614
+ if (foundRectangle) {
2615
+ matchesRectangles++;
2616
+ }
2617
+ }
2618
+ return pageRectangles.length === matchesRectangles;
2619
+ }
2620
+ return false;
2621
+ }
2622
+ onCloseSearchModal() {
2623
+ this.toolbarEvents.openRedactionSearch.next(false);
2554
2624
  }
2555
2625
  setSearchResultsCount(results) {
2556
2626
  this.resultCount = results.total;
2557
2627
  this.resultsText = this.resultCount > 0
2558
- ? `Found ${results.current} of ${results.total}`
2628
+ ? `${results.total} results founds`
2559
2629
  : 'No results found';
2560
- if (this.resultCount && this.resultCount > 0) {
2561
- setTimeout(() => {
2562
- this.findNext.nativeElement.focus();
2563
- }, 1000);
2630
+ }
2631
+ redactAllSearched(results) {
2632
+ const $this = this;
2633
+ const intervalId = setInterval(() => {
2634
+ const highlightElement = document.getElementsByClassName('highlight selected');
2635
+ if (highlightElement && highlightElement.length > 0) {
2636
+ clearInterval(intervalId);
2637
+ $this.redactAllSearchedTick(results);
2638
+ }
2639
+ }, 100);
2640
+ }
2641
+ redactAllSearchedTick(results) {
2642
+ const highlightElement = document.getElementsByClassName('highlight selected');
2643
+ if (highlightElement && highlightElement.length > 0) {
2644
+ this.resultCount = results.matchesCount;
2645
+ const pageNumber = results.page + 1;
2646
+ const rectangles = this.getRectangles(pageNumber);
2647
+ if (rectangles && this.redactElements.length <= this.resultCount
2648
+ && !this.existInRedactElements(pageNumber, results.matchedIndex, rectangles)) {
2649
+ this.redactElements.push({ page: pageNumber, matchedIndex: results?.matchedIndex, rectangles });
2650
+ this.CreateRedactAllText();
2651
+ }
2652
+ if (this.redactAll && this.resultCount && this.resultCount > 0
2653
+ && rectangles && this.redactElements.length < this.resultCount) {
2654
+ this.search(false);
2655
+ }
2656
+ if (this.redactAll && this.resultCount && this.redactElements.length === this.resultCount) {
2657
+ this.redactAll = false;
2658
+ this.redactAllText = null;
2659
+ this.saveRedaction(this.redactElements);
2660
+ }
2564
2661
  }
2565
2662
  }
2663
+ CreateRedactAllText() {
2664
+ this.redactAllText = `${this.redactElements.length} of ${this.resultCount}`;
2665
+ }
2566
2666
  onEscapeKeyPress(e) {
2567
2667
  this.toolbarEvents.searchBarHidden.next(true);
2568
2668
  }
2569
2669
  onEnterKeyPress(e) {
2570
2670
  this.search();
2571
2671
  }
2572
- toggleAdvancedSearch() {
2573
- this.advancedSearchVisible = !this.advancedSearchVisible;
2574
- }
2575
2672
  toggleSearchBar() {
2576
2673
  this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
2577
2674
  }
2675
+ getRectangles(page) {
2676
+ this.pageHeight = this.allPages[page].styles.height;
2677
+ this.pageWidth = this.allPages[page].styles.width;
2678
+ this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
2679
+ this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
2680
+ const selectedHighLightedElements = document.getElementsByClassName('highlight selected');
2681
+ if (selectedHighLightedElements && selectedHighLightedElements.length > 0) {
2682
+ const docRange = document.createRange();
2683
+ docRange.selectNodeContents(selectedHighLightedElements[0]);
2684
+ const selection = window.getSelection();
2685
+ selection?.removeAllRanges();
2686
+ selection?.addRange(docRange);
2687
+ if (selection.rangeCount && !selection.isCollapsed) {
2688
+ const range = selection.getRangeAt(0).cloneRange();
2689
+ const clientRects = range.getClientRects();
2690
+ if (clientRects) {
2691
+ const parentRect = selectedHighLightedElements[0].parentElement.parentElement.getBoundingClientRect();
2692
+ const selectionRectangles = [];
2693
+ for (let i = 0; i < clientRects.length; i++) {
2694
+ const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
2695
+ const findSelecttionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
2696
+ if (!findSelecttionRectangle) {
2697
+ selectionRectangles.push(selectionRectangle);
2698
+ }
2699
+ }
2700
+ return selectionRectangles;
2701
+ }
2702
+ }
2703
+ }
2704
+ }
2705
+ createTextRectangle(rect, parentRect) {
2706
+ const height = rect.bottom - rect.top;
2707
+ const width = rect.right - rect.left;
2708
+ const top = rect.top - parentRect.top;
2709
+ const left = rect.left - parentRect.left;
2710
+ let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
2711
+ rectangle = { id: uuid$1(), ...rectangle };
2712
+ return rectangle;
2713
+ }
2578
2714
  }
2579
- /** @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 });
2580
- /** @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" }] });
2581
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, decorators: [{
2715
+ /** @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 });
2716
+ /** @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"] }] });
2717
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
2582
2718
  type: Component,
2583
- 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" }]
2584
- }], ctorParameters: function () { return [{ type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }]; }, propDecorators: { findInput: [{
2719
+ 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"] }]
2720
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: HighlightCreateService }]; }, propDecorators: { findInput: [{
2585
2721
  type: ViewChild,
2586
2722
  args: ['findInput', { static: true }]
2587
- }], findNext: [{
2588
- type: ViewChild,
2589
- args: ['findNext', { static: false }]
2590
2723
  }], onWindowKeyDown: [{
2591
2724
  type: HostListener,
2592
2725
  args: ['window:keydown', ['$event']]
2593
2726
  }] } });
2594
2727
 
2595
- class MainToolbarComponent {
2596
- constructor(toolbarEvents, toolbarButtons, cdr, numberHelper) {
2597
- this.toolbarEvents = toolbarEvents;
2598
- this.toolbarButtons = toolbarButtons;
2599
- this.cdr = cdr;
2600
- this.numberHelper = numberHelper;
2601
- this.enableAnnotations = false;
2602
- this.enableRedactions = false;
2603
- this.enableICP = false;
2604
- this.contentType = null;
2605
- this.subscriptions = [];
2606
- this.icpEnabled = false;
2607
- this.redactionEnabled = false;
2608
- this.pageNumber = 1;
2609
- this.pageCount = 0;
2610
- this.isDropdownMenuOpen = false;
2611
- this.dropdownMenuPositions = [
2612
- new ConnectionPositionPair({
2613
- originX: 'end',
2614
- originY: 'bottom'
2615
- }, {
2616
- overlayX: 'end',
2617
- overlayY: 'top'
2618
- }, 0, 3)
2619
- ];
2620
- this.zoomScales = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2.5, 3, 5];
2621
- this.allButtonsWidth = 0;
2622
- this.widthRequiredForBtn = {};
2623
- }
2624
- ngOnInit() {
2625
- 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 => {
2626
- this.icpEnabled = enabled;
2627
- if (this.icpEnabled) {
2628
- this.toolbarEvents.toggleCommentsPanel(!enabled);
2629
- this.toolbarEvents.sidebarOpen.next(false);
2630
- }
2631
- }), this.toolbarEvents.redactionMode.subscribe(enabled => {
2632
- this.redactionEnabled = enabled;
2633
- }), this.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
2634
- this.redactAllInProgress = disable;
2635
- }));
2728
+ // TODO: replace by NgRx
2729
+ class CommentService {
2730
+ constructor() {
2731
+ this.unsavedChanges = new Subject();
2732
+ this.marginToCommentEmitter = new BehaviorSubject(false);
2636
2733
  }
2637
- ngOnDestroy() {
2638
- for (const subscription of this.subscriptions) {
2639
- subscription.unsubscribe();
2640
- }
2734
+ setCommentSet(commentSetComponent) {
2735
+ this.commentSetComponent = commentSetComponent;
2641
2736
  }
2642
- ngAfterViewInit() {
2643
- Array.from(this.mvToolbarMain.nativeElement.children).forEach(button => {
2644
- this.allButtonsWidth += button.getBoundingClientRect().width;
2645
- this.widthRequiredForBtn[button.id] = this.allButtonsWidth;
2646
- });
2647
- this.cdr.detectChanges();
2737
+ onCommentChange(changes) {
2738
+ this.unsavedChanges.next(changes);
2648
2739
  }
2649
- onResize() {
2650
- this.cdr.detectChanges();
2740
+ getUnsavedChanges() {
2741
+ return this.unsavedChanges.asObservable();
2651
2742
  }
2652
- onControlPrint(event) {
2653
- event.preventDefault();
2654
- this.printFile();
2743
+ hasUnsavedComments(annotation) {
2744
+ if (annotation.comments.length > 0) {
2745
+ const comment = this.getComment(annotation);
2746
+ return comment.hasUnsavedChanges;
2747
+ }
2748
+ return false;
2655
2749
  }
2656
- onClickHighlightToggle() {
2657
- this.toolbarEvents.toggleHighlightMode();
2750
+ updateUnsavedCommentsStatus(annotation, hasUnsavedChanges) {
2751
+ const comment = this.getComment(annotation);
2752
+ comment.hasUnsavedChanges = hasUnsavedChanges;
2753
+ this.allCommentsSaved();
2658
2754
  }
2659
- onClickDrawToggle() {
2660
- this.toolbarEvents.toggleDrawMode();
2755
+ getComment(annotation) {
2756
+ return this.commentSetComponent.commentComponents
2757
+ .find(c => c.comment.annotationId === annotation.comments[0].annotationId);
2661
2758
  }
2662
- toggleIndexSideBar() {
2663
- const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
2664
- const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
2665
- if (!(sidebarOpen && !sidebarView)) {
2666
- this.toolbarEvents.toggleSideBar(!sidebarOpen);
2667
- }
2668
- this.toolbarEvents.toggleSideBarView(true);
2759
+ resetCommentSet() {
2760
+ this.commentSetComponent = null;
2669
2761
  }
2670
- toggleBookmarksSideBar() {
2671
- const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
2672
- const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
2673
- if (!(sidebarOpen && sidebarView)) {
2674
- this.toolbarEvents.toggleSideBar(!sidebarOpen);
2675
- }
2676
- this.toolbarEvents.toggleSideBarView(false);
2762
+ allCommentsSaved() {
2763
+ this.onCommentChange(this.commentSetComponent.commentComponents.some(comment => comment.hasUnsavedChanges === true));
2677
2764
  }
2678
- togglePresentBar() {
2679
- this.toolbarEvents.searchBarHidden.next(true);
2680
- this.toolbarEvents.icp.enable();
2765
+ createMarginToCommentEvent(margin) {
2766
+ this.marginToCommentEmitter.next(margin);
2681
2767
  }
2682
- increasePageNumber() {
2683
- this.toolbarEvents.incrementPage(1);
2768
+ }
2769
+ /** @nocollapse */ CommentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2770
+ /** @nocollapse */ CommentService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService });
2771
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, decorators: [{
2772
+ type: Injectable
2773
+ }] });
2774
+
2775
+ class CommentSetRenderService {
2776
+ redrawComponents(commentComponents, pageHeights, rotate, zoom) {
2777
+ let prevComment;
2778
+ this.sortComponents(commentComponents, pageHeights, rotate, zoom).forEach((comment) => {
2779
+ this.adjustIfOverlapping(comment, prevComment, zoom);
2780
+ prevComment = comment;
2781
+ });
2684
2782
  }
2685
- decreasePageNumber() {
2686
- this.toolbarEvents.incrementPage(-1);
2783
+ sortComponents(commentComponents, pageHeights, rotate, zoom) {
2784
+ return commentComponents.sort((a, b) => {
2785
+ a.rectTop = this.top(a._rectangle, pageHeights[a.page - 1], rotate, zoom);
2786
+ b.rectTop = this.top(b._rectangle, pageHeights[b.page - 1], rotate, zoom);
2787
+ return this.processSort(a, b);
2788
+ });
2687
2789
  }
2688
- onPageNumberInputChange(pageNumber) {
2689
- if (Number(pageNumber) < 1) {
2690
- pageNumber = '1';
2790
+ adjustIfOverlapping(comment, prevComment, zoom) {
2791
+ if (prevComment) {
2792
+ const endOfPrevComment = prevComment.commentTop + this.height(prevComment);
2793
+ if (comment.commentTop <= endOfPrevComment) {
2794
+ comment.rectTop = (endOfPrevComment - comment.totalPrevPagesHeight) / zoom;
2795
+ }
2691
2796
  }
2692
- if (Number(pageNumber) > this.pageCount) {
2693
- pageNumber = this.pageCount.toString();
2797
+ }
2798
+ processSort(a, b) {
2799
+ if (this.onSameLine(a, b)) {
2800
+ return a.rectLeft >= b.rectLeft ? 1 : -1;
2694
2801
  }
2695
- this.toolbarEvents.setPage(Number.parseInt(pageNumber, 10));
2802
+ return a.commentTop >= b.commentTop ? 1 : -1;
2696
2803
  }
2697
- setCurrentPage(pageNumber) {
2698
- this.pageNumber = pageNumber;
2804
+ onSameLine(a, b) {
2805
+ return this.difference(a.commentTop, b.commentTop) === 0;
2699
2806
  }
2700
- rotate(rotation) {
2701
- this.toolbarEvents.rotate(rotation);
2807
+ top(rectangle, height, rotate, zoom) {
2808
+ const actualHeight = height / zoom;
2809
+ switch (rotate) {
2810
+ case 90: return rectangle.x;
2811
+ case 180: return actualHeight - (rectangle.y + rectangle.height);
2812
+ case 270: return actualHeight - (rectangle.x + rectangle.width);
2813
+ default: return rectangle.y;
2814
+ }
2702
2815
  }
2703
- printFile() {
2704
- this.toolbarEvents.print();
2816
+ height(element) {
2817
+ return element.form.nativeElement.getBoundingClientRect().height;
2705
2818
  }
2706
- downloadFile() {
2707
- this.toolbarEvents.download();
2819
+ difference(a, b) { return Math.abs(a - b); }
2820
+ }
2821
+ /** @nocollapse */ CommentSetRenderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2822
+ /** @nocollapse */ CommentSetRenderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService });
2823
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, decorators: [{
2824
+ type: Injectable
2825
+ }] });
2826
+
2827
+ class TagsServices {
2828
+ constructor(http) {
2829
+ this.http = http;
2830
+ this.snakeCase = string => {
2831
+ // transform string_to_snake_case
2832
+ return string.replace(/ +/g, ' ') // find space
2833
+ .split(/ |\B(?=[A-Z])/) // split it into array
2834
+ .map(word => word.toLowerCase()) // transform to lover case
2835
+ .join('_'); // trun array into sting using _
2836
+ };
2708
2837
  }
2709
- zoom(zoomFactor) {
2710
- this.toolbarEvents.zoom(+zoomFactor);
2838
+ getAllTags(createdBy) {
2839
+ const url = `/em-anno/tags/${createdBy}`;
2840
+ return this.http.get(url);
2711
2841
  }
2712
- stepZoom(zoomFactor) {
2713
- this.toolbarEvents.stepZoom(zoomFactor);
2714
- this.zoomSelect.nativeElement.selected = 'selected';
2842
+ // @TODO: Move everything below this to NgRx store
2843
+ getNewTags(annoid) {
2844
+ return this.tagItems ? this.tagItems[annoid] : [];
2715
2845
  }
2716
- toggleCommentsPanel() {
2717
- this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
2846
+ updateTagItems(items, annoId) {
2847
+ const snakeCased = items.map(item => {
2848
+ return {
2849
+ ...item,
2850
+ name: this.snakeCase(item.name)
2851
+ };
2852
+ });
2853
+ this.tagItems = {
2854
+ ...this.tagItems,
2855
+ [annoId]: snakeCased
2856
+ };
2718
2857
  }
2719
- toggleRedactBar() {
2720
- this.toolbarEvents.toggleRedactionMode();
2858
+ }
2859
+ /** @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 });
2860
+ /** @nocollapse */ TagsServices.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices });
2861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, decorators: [{
2862
+ type: Injectable
2863
+ }], ctorParameters: function () { return [{ type: i1$1.HttpClient }]; } });
2864
+
2865
+ class TextHighlightDirective {
2866
+ constructor(element) {
2867
+ this.element = element;
2721
2868
  }
2722
- toggleGrabNDrag() {
2723
- this.toolbarEvents.toggleGrabNDrag();
2869
+ ngAfterViewChecked() {
2870
+ if (this.textToHighlight) {
2871
+ this.highlightInputText(this.textToHighlight);
2872
+ }
2724
2873
  }
2725
- isPdf() {
2726
- return this.contentType === 'pdf';
2874
+ highlightInputText(textToHighlight) {
2875
+ this.resetHighlight();
2876
+ this.textToHighlight = textToHighlight;
2877
+ const searchPattern = new RegExp(textToHighlight, 'gi');
2878
+ const hostElement = this.element.nativeElement;
2879
+ if (hostElement.innerHTML.match(searchPattern)) {
2880
+ hostElement.innerHTML = hostElement.innerHTML
2881
+ .replace(searchPattern, this.highlightPattern('$&'));
2882
+ }
2883
+ this.textToHighlight = undefined;
2727
2884
  }
2728
- toggleSearchBar() {
2729
- this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
2885
+ resetHighlight() {
2886
+ const hostElement = this.element.nativeElement;
2887
+ const searchPattern = new RegExp(this.highlightPattern('(.*?)'), 'gi');
2888
+ while (hostElement.innerHTML.match(searchPattern)) {
2889
+ const matchGroups = searchPattern.exec(hostElement.innerHTML);
2890
+ if (matchGroups) {
2891
+ hostElement.innerHTML = hostElement.innerHTML.replace(matchGroups[0], matchGroups[1]);
2892
+ }
2893
+ }
2730
2894
  }
2731
- toggleMoreOptions() {
2732
- this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
2733
- setTimeout(() => {
2734
- if (this.mvMenuItems) {
2735
- this.mvMenuItems.nativeElement.focus();
2736
- }
2737
- }, 100);
2895
+ highlightPattern(dynamicText) {
2896
+ return '<span class="mvTextHighlight">' + dynamicText + '</span>';
2738
2897
  }
2739
2898
  }
2740
- /** @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 });
2741
- /** @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" }] });
2742
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, decorators: [{
2743
- type: Component,
2744
- 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" }]
2745
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i0.ChangeDetectorRef }, { type: NumberHelperService }]; }, propDecorators: { enableAnnotations: [{
2746
- type: Input
2747
- }], enableRedactions: [{
2748
- type: Input
2749
- }], enableICP: [{
2750
- type: Input
2751
- }], contentType: [{
2899
+ /** @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 });
2900
+ /** @nocollapse */ TextHighlightDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 });
2901
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, decorators: [{
2902
+ type: Directive,
2903
+ args: [{
2904
+ selector: '[mvTextHighlight]'
2905
+ }]
2906
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { textToHighlight: [{
2752
2907
  type: Input
2753
- }], zoomSelect: [{
2754
- type: ViewChild,
2755
- args: ['zoomSelect', { static: false }]
2756
- }], mvToolbarMain: [{
2757
- type: ViewChild,
2758
- args: ['mvToolbarMain', { static: false }]
2759
- }], mvMenuItems: [{
2760
- type: ViewChild,
2761
- args: ['dropdownMenu', { static: false }]
2762
- }], onResize: [{
2763
- type: HostListener,
2764
- args: ['window:resize', []]
2765
- }], onControlPrint: [{
2766
- type: HostListener,
2767
- args: ['document:keydown.control.p', ['$event']]
2768
- }, {
2769
- type: HostListener,
2770
- args: ['document:keydown.meta.p', ['$event']]
2771
2908
  }] } });
2772
2909
 
2773
- const getRedactionState = createSelector(getMVState, (state) => state.redactions);
2774
- const getRedactionPages = createSelector(getRedactionState, getPageEnt);
2775
- const getSelected = createSelector(getRedactionState, getSelectedRedaction);
2776
- const getRedactedDocumentInfo = createSelector(getRedactionState, getRedactedDocInfo);
2777
- const getRedactionEnt = createSelector(getRedactionState, getRedactionEnt$1);
2778
- const getRedactionArray = createSelector(getRedactionEnt, getDocumentId, (ent, documentId) => {
2779
- const redactions = Object.keys(ent).map(key => ent[key]);
2780
- return { redactions, documentId };
2781
- });
2782
- const getRedactionsPerPage = createSelector(getPages, getRedactionPages, (pages, pageEnt) => {
2783
- if (pages && pageEnt) {
2784
- const arr = [];
2785
- Object.keys(pages).forEach(key => {
2786
- arr.push({
2787
- anno: pageEnt[key] ? pageEnt[key] : [],
2788
- styles: pages[key].styles
2789
- });
2790
- });
2791
- return arr;
2792
- }
2793
- });
2794
-
2795
- class RedactionToolbarComponent {
2796
- constructor(toolbarEventService, toolbarButtons, store) {
2797
- this.toolbarEventService = toolbarEventService;
2798
- this.toolbarButtons = toolbarButtons;
2799
- this.store = store;
2800
- this.preview = false;
2801
- this.hasRedactions = false;
2802
- this.subscriptions = [];
2803
- }
2804
- ngOnInit() {
2805
- this.subscriptions.push(this.store.pipe(select(getRedactionArray)).subscribe(redactions => {
2806
- this.hasRedactions = !!redactions.redactions.length;
2807
- }));
2808
- this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
2809
- this.redactionAllInProgress = inprogress;
2810
- }));
2811
- }
2812
- onRedactAllSearch() {
2813
- this.toolbarEventService.openRedactionSearch.next(true);
2814
- }
2815
- toggleTextRedactionMode() {
2816
- this.toolbarEventService.highlightModeSubject.next(true);
2910
+ class TextareaAutoExpandDirective {
2911
+ constructor(el) {
2912
+ this.el = el;
2817
2913
  }
2818
- toggleDrawMode() {
2819
- this.toolbarEventService.drawModeSubject.next(true);
2914
+ ngAfterContentChecked() {
2915
+ this.adjustHeight();
2820
2916
  }
2821
- togglePreview() {
2822
- this.preview = !this.preview;
2823
- this.toolbarEventService.toggleRedactionPreview(this.preview);
2917
+ onMouseDown() {
2918
+ this.adjustHeight();
2824
2919
  }
2825
- unmarkAll() {
2826
- this.toolbarEventService.unmarkAll();
2920
+ adjustHeight() {
2921
+ const nativeElement = this.el.nativeElement;
2922
+ nativeElement.style.overflow = 'hidden';
2923
+ nativeElement.style.height = 'auto';
2924
+ nativeElement.style.height = nativeElement.scrollHeight - 5 + 'px';
2827
2925
  }
2828
- redact() {
2829
- this.toolbarEventService.applyRedactionToDocument();
2926
+ }
2927
+ /** @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 });
2928
+ /** @nocollapse */ TextareaAutoExpandDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 });
2929
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
2930
+ type: Directive,
2931
+ args: [{
2932
+ selector: '[mvTextAreaAutoExpand]'
2933
+ }]
2934
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { onMouseDown: [{
2935
+ type: HostListener,
2936
+ args: ['input']
2937
+ }] } });
2938
+
2939
+ class TagsComponent {
2940
+ constructor(tagsServices) {
2941
+ this.tagsServices = tagsServices;
2942
+ this.validators = [this.minLength, this.maxLength20];
2943
+ this.errorMessages = {
2944
+ 'minLength': 'Minimum of 2 characters',
2945
+ 'maxLength20': 'Maximum of 20 characters'
2946
+ };
2947
+ this.requestAutocompleteItems = (text) => {
2948
+ return this.tagsServices.getAllTags(this.userId);
2949
+ };
2830
2950
  }
2831
- toggleRedactBar() {
2832
- this.toolbarEventService.toggleRedactionMode();
2951
+ onUpdateTags(value) {
2952
+ this.tagsServices.updateTagItems(value, this.annoId);
2833
2953
  }
2834
- redactPage() {
2835
- this.toolbarEventService.drawModeSubject.next(true);
2836
- this.toolbarEventService.redactPage();
2954
+ minLength(control) {
2955
+ if (control.value.length < 2) {
2956
+ return {
2957
+ 'minLength': true
2958
+ };
2959
+ }
2960
+ return null;
2837
2961
  }
2838
- ngOnDestroy() {
2839
- for (const subscription of this.subscriptions) {
2840
- subscription.unsubscribe();
2962
+ maxLength20(control) {
2963
+ if (control.value.length >= 20) {
2964
+ return {
2965
+ 'maxLength20': true
2966
+ };
2841
2967
  }
2968
+ return null;
2842
2969
  }
2843
2970
  }
2844
- /** @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 });
2845
- /** @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"] }] });
2846
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
2971
+ /** @nocollapse */ TagsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
2972
+ /** @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 });
2973
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, decorators: [{
2847
2974
  type: Component,
2848
- 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" }]
2849
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }]; }, propDecorators: { showRedactSearch: [{
2975
+ 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" }]
2976
+ }], ctorParameters: function () { return [{ type: TagsServices }]; }, propDecorators: { tagItems: [{
2977
+ type: Input
2978
+ }], userId: [{
2979
+ type: Input
2980
+ }], editable: [{
2981
+ type: Input
2982
+ }], annoId: [{
2850
2983
  type: Input
2851
2984
  }] } });
2852
2985
 
2853
- class IcpToolbarComponent {
2854
- constructor(toolbarEventService, store) {
2855
- this.toolbarEventService = toolbarEventService;
2986
+ /**
2987
+ * A moment timezone pipe to support parsing based on time zone abbreviations
2988
+ * covers all cases of offset variation due to daylight saving.
2989
+ *
2990
+ * Same API as DatePipe with additional timezone abbreviation support
2991
+ * Official date pipe dropped support for abbreviations names from Angular V5
2992
+ */
2993
+ class MomentDatePipe extends DatePipe {
2994
+ transform(value, format = 'mediumDate', timezone = 'Europe/London') {
2995
+ const timezoneOffset = moment(value).tz(timezone).format('Z');
2996
+ return super.transform(value, format, timezoneOffset);
2997
+ }
2998
+ }
2999
+ /** @nocollapse */ MomentDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe });
3000
+ /** @nocollapse */ MomentDatePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, name: "momentDate" });
3001
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, decorators: [{
3002
+ type: Pipe,
3003
+ args: [{
3004
+ name: 'momentDate'
3005
+ }]
3006
+ }] });
3007
+
3008
+ class CommentComponent {
3009
+ constructor(store, commentService, tagsServices) {
2856
3010
  this.store = store;
3011
+ this.commentService = commentService;
3012
+ this.tagsServices = tagsServices;
3013
+ this.CHAR_LIMIT = 5000;
3014
+ this.totalPrevPagesHeight = 0;
3015
+ this.hasUnsavedChanges = false;
3016
+ this.commentClick = new EventEmitter();
3017
+ this.renderComments = new EventEmitter();
3018
+ this.delete = new EventEmitter();
3019
+ this.updated = new EventEmitter();
3020
+ this.changes = new EventEmitter();
3021
+ this.rotate = 0;
3022
+ this.zoom = 1;
2857
3023
  }
2858
3024
  ngOnInit() {
2859
- this.$subscription = this.store.pipe(select(isPresenter))
2860
- .subscribe(isPresenter => this.isPresenter = isPresenter);
2861
- this.$subscription.add(this.store.pipe(select(getPresenterName))
2862
- .subscribe(name => this.presenterName = name));
3025
+ this.subscriptions = this.store.select(getComponentSearchText)
3026
+ .pipe(distinctUntilChanged()).subscribe(searchString => this.searchString = searchString);
3027
+ this.reRenderComments();
3028
+ this.marginToComment$ = this.commentService.marginToCommentEmitter.asObservable();
3029
+ }
3030
+ ngAfterContentInit() {
3031
+ if (this.tagItems && this.tagItems.length) {
3032
+ this.tagsServices.updateTagItems(this.tagItems, this._comment.annotationId);
3033
+ }
2863
3034
  }
2864
3035
  ngOnDestroy() {
2865
- this.$subscription.unsubscribe();
3036
+ this.subscriptions.unsubscribe();
2866
3037
  }
2867
- present() {
2868
- this.toolbarEventService.icp.becomePresenter();
3038
+ set comment(comment) {
3039
+ this._comment = { ...comment };
3040
+ this.page = this._comment.page;
3041
+ this.lastUpdate = comment.lastModifiedDate ? comment.lastModifiedDate : comment.createdDate;
3042
+ this.author = comment.createdByDetails;
3043
+ this.createdBy = comment.createdBy;
3044
+ this.editor = comment.lastModifiedByDetails;
3045
+ this.originalComment = comment.content;
3046
+ this.fullComment = this.originalComment;
3047
+ this.selected = this._comment.selected;
3048
+ this._editable = this._comment.editable;
3049
+ this.tagItems = this._comment.tags;
3050
+ const pageMarginBottom = 10;
3051
+ this.totalPrevPagesHeight = 0;
3052
+ for (let i = 0; i < this.page - 1; i++) {
3053
+ const height = this._comment.pages[i + 1] ? this._comment.pages[i + 1].styles.height : undefined;
3054
+ if (height) {
3055
+ this.totalPrevPagesHeight += height + pageMarginBottom;
3056
+ }
3057
+ }
2869
3058
  }
2870
- stopPresenting() {
2871
- this.toolbarEventService.icp.stopPresenting();
3059
+ get comment() {
3060
+ return this._comment;
2872
3061
  }
2873
- leaveIcpSession() {
2874
- this.toolbarEventService.icp.leaveSession();
2875
- }
2876
- showParticipantsList() {
2877
- this.toolbarEventService.toggleParticipantsList(!this.toolbarEventService.icp.participantsListVisible.getValue());
2878
- }
2879
- }
2880
- /** @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 });
2881
- /** @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" }] });
2882
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, decorators: [{
2883
- type: Component,
2884
- 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>" }]
2885
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
2886
-
2887
- class ToolbarModule {
2888
- }
2889
- /** @nocollapse */ ToolbarModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2890
- /** @nocollapse */ ToolbarModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
2891
- MainToolbarComponent,
2892
- RedactionToolbarComponent,
2893
- IcpToolbarComponent,
2894
- RedactionSearchBarComponent], imports: [CommonModule,
2895
- FormsModule,
2896
- OverlayModule,
2897
- RouterModule], exports: [MainToolbarComponent,
2898
- SearchBarComponent,
2899
- RedactionToolbarComponent,
2900
- IcpToolbarComponent,
2901
- RedactionSearchBarComponent] });
2902
- /** @nocollapse */ ToolbarModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, providers: [
2903
- ToolbarButtonVisibilityService,
2904
- ToolbarEventService
2905
- ], imports: [CommonModule,
2906
- FormsModule,
2907
- OverlayModule,
2908
- RouterModule] });
2909
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, decorators: [{
2910
- type: NgModule,
2911
- args: [{
2912
- declarations: [
2913
- SearchBarComponent,
2914
- MainToolbarComponent,
2915
- RedactionToolbarComponent,
2916
- IcpToolbarComponent,
2917
- RedactionSearchBarComponent
2918
- ],
2919
- providers: [
2920
- ToolbarButtonVisibilityService,
2921
- ToolbarEventService
2922
- ],
2923
- exports: [
2924
- MainToolbarComponent,
2925
- SearchBarComponent,
2926
- RedactionToolbarComponent,
2927
- IcpToolbarComponent,
2928
- RedactionSearchBarComponent
2929
- ],
2930
- imports: [
2931
- CommonModule,
2932
- FormsModule,
2933
- OverlayModule,
2934
- RouterModule
2935
- ]
2936
- }]
2937
- }] });
2938
-
2939
- class HighlightCreateService {
2940
- constructor(toolBarEvents, store) {
2941
- this.toolBarEvents = toolBarEvents;
2942
- this.store = store;
2943
- }
2944
- saveAnnotation(rectangles, page) {
2945
- this.store.pipe(select(getDocumentIdSetId), take(1)).subscribe(anoSetDocId => {
2946
- const anno = {
2947
- id: v4(),
2948
- color: 'FFFF00',
2949
- comments: [],
2950
- page: page,
2951
- rectangles: rectangles,
2952
- type: 'highlight',
2953
- ...anoSetDocId,
2954
- createdBy: '',
2955
- createdByDetails: undefined,
2956
- createdDate: moment.utc().tz('Europe/London').toISOString(),
2957
- lastModifiedBy: '',
2958
- lastModifiedByDetails: undefined,
2959
- lastModifiedDate: '',
2960
- tags: [],
2961
- };
2962
- this.store.dispatch(new SaveAnnotation(anno));
2963
- });
2964
- }
2965
- applyRotation(pageHeight, pageWidth, offsetHeight, offsetWidth, offsetTop, offsetLeft, rotate, zoom) {
2966
- const { x, y, width, height } = {
2967
- x: +(offsetLeft / zoom).toFixed(2),
2968
- y: +(offsetTop / zoom).toFixed(2),
2969
- width: +(offsetWidth / zoom).toFixed(2),
2970
- height: +(offsetHeight / zoom).toFixed(2)
2971
- };
2972
- const rectangle = { x, y, width, height };
2973
- switch (rotate) {
3062
+ set annotation(annotation) {
3063
+ this._rectangle = annotation.rectangles
3064
+ .reduce((prev, current) => prev.y < current.y ? prev : current);
3065
+ const actualHeight = this._comment.pages[this.page].styles.height / this.zoom;
3066
+ switch (this.rotate) {
2974
3067
  case 90:
2975
- rectangle.width = height;
2976
- rectangle.height = width;
2977
- rectangle.x = y;
2978
- rectangle.y = +(pageWidth / zoom - x - width).toFixed(2);
3068
+ this.rectTop = this._rectangle.x;
2979
3069
  break;
2980
3070
  case 180:
2981
- rectangle.x = +(pageWidth / zoom - x - width).toFixed(2);
2982
- rectangle.y = +(pageHeight / zoom - y - height).toFixed(2);
3071
+ this.rectTop = actualHeight - (this._rectangle.y + this._rectangle.height);
2983
3072
  break;
2984
3073
  case 270:
2985
- rectangle.width = height;
2986
- rectangle.height = width;
2987
- rectangle.x = +(pageHeight / zoom - y - height).toFixed(2);
2988
- rectangle.y = x;
3074
+ this.rectTop = actualHeight - (this._rectangle.x + this._rectangle.width);
2989
3075
  break;
3076
+ default: this.rectTop = this._rectangle.y;
2990
3077
  }
2991
- return rectangle;
2992
- }
2993
- resetHighlight() {
2994
- window.getSelection().removeAllRanges();
2995
- this.toolBarEvents.highlightModeSubject.next(false);
2996
- }
2997
- }
2998
- /** @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 });
2999
- /** @nocollapse */ HighlightCreateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService });
3000
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HighlightCreateService, decorators: [{
3001
- type: Injectable
3002
- }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
3003
-
3004
- class RedactionSearchBarComponent {
3005
- constructor(store, toolbarButtons, toolbarEvents, highlightService) {
3006
- this.store = store;
3007
- this.toolbarButtons = toolbarButtons;
3008
- this.toolbarEvents = toolbarEvents;
3009
- this.highlightService = highlightService;
3010
- this.highlightAll = true;
3011
- this.matchCase = false;
3012
- this.wholeWord = false;
3013
- this.resultsText = '';
3014
- this.searchText = '';
3015
- this.resultCount = 0;
3016
- this.redactElements = [];
3017
- this.advancedSearchVisible = false;
3018
- }
3019
- ngOnInit() {
3020
- this.subscription = this.toolbarEvents.redactionSerachSubject.subscribe((results) => this.redactAllSearched(results));
3021
- this.subscription.add(this.store.pipe(select(getDocumentId)).subscribe(docId => this.documentId = docId));
3022
- this.subscription.add(this.store.pipe(select(getPages)).subscribe((pages) => {
3023
- if (pages[1]) {
3024
- this.allPages = pages;
3025
- }
3026
- }));
3027
- this.subscription.add(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
3028
- this.subscription.add(this.toolbarEvents.openRedactionSearch.subscribe(isOpen => this.openSearchModal = isOpen));
3029
- this.subscription.add(this.toolbarEvents.redactAllInProgressSubject
3030
- .subscribe(inProgress => this.redactAllInProgress = inProgress));
3031
- }
3032
- ngOnDestroy() {
3033
- this.subscription.unsubscribe();
3034
- }
3035
- onWindowKeyDown(e) {
3036
- if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
3037
- e.preventDefault();
3038
- this.toolbarEvents.searchBarHidden.next(false);
3039
- setTimeout(() => this.findInput.nativeElement.focus(), 200);
3040
- }
3041
- }
3042
- search(reset = true) {
3043
- this.redactAll = !reset;
3044
- if (this.redactAll) {
3045
- this.toolbarEvents.redactAllInProgressSubject.next(true);
3046
- }
3047
- if (reset) {
3048
- this.redactElements = [];
3049
- }
3050
- this.toolbarEvents.search({
3051
- searchTerm: this.searchText,
3052
- highlightAll: this.highlightAll,
3053
- matchCase: this.matchCase,
3054
- wholeWord: this.wholeWord,
3055
- previous: false,
3056
- reset
3057
- });
3058
- }
3059
- saveRedaction(redactRectangle) {
3060
- const redaction = redactRectangle.map(ele => {
3061
- return { page: ele.page, rectangles: ele.rectangles, redactionId: uuid$1(), documentId: this.documentId };
3062
- });
3063
- this.store.dispatch(new SaveBulkRedaction({ searchRedactions: redaction }));
3064
- }
3065
- existInRedactElements(pageNumber, matechedIndex, rectangles) {
3066
- if (this.redactElements && this.redactElements.length > 0) {
3067
- const pagesFound = this.redactElements.find(re => re.page === pageNumber && re.matchedIndex === matechedIndex);
3068
- const pageRectangles = pagesFound?.rectangles;
3069
- if (!pageRectangles || pageRectangles.length <= 0) {
3070
- return false;
3071
- }
3072
- let matchesRectangles = 0;
3073
- for (let rectIndx = 0; rectIndx < pageRectangles.length; rectIndx++) {
3074
- const rectangle = pageRectangles[rectIndx];
3075
- const foundRectangle = rectangles.find(re => re.width === rectangle.width &&
3076
- re.height === rectangle.height && re.x === rectangle.x && re.y === rectangle.y);
3077
- if (foundRectangle) {
3078
- matchesRectangles++;
3079
- }
3080
- }
3081
- return pageRectangles.length === matchesRectangles;
3082
- }
3083
- return false;
3084
- }
3085
- onCloseSearchModal() {
3086
- this.toolbarEvents.openRedactionSearch.next(false);
3087
- }
3088
- setSearchResultsCount(results) {
3089
- this.resultCount = results.total;
3090
- this.resultsText = this.resultCount > 0
3091
- ? `${results.total} results founds`
3092
- : 'No results found';
3093
- }
3094
- redactAllSearched(results) {
3095
- const $this = this;
3096
- const intervalId = setInterval(() => {
3097
- const highlightElement = document.getElementsByClassName('highlight selected');
3098
- if (highlightElement && highlightElement.length > 0) {
3099
- clearInterval(intervalId);
3100
- $this.redactAllSearchedTick(results);
3101
- }
3102
- }, 100);
3103
- }
3104
- redactAllSearchedTick(results) {
3105
- const highlightElement = document.getElementsByClassName('highlight selected');
3106
- if (highlightElement && highlightElement.length > 0) {
3107
- this.resultCount = results.matchesCount;
3108
- const pageNumber = results.page + 1;
3109
- const rectangles = this.getRectangles(pageNumber);
3110
- if (rectangles && this.redactElements.length <= this.resultCount
3111
- && !this.existInRedactElements(pageNumber, results.matchedIndex, rectangles)) {
3112
- this.redactElements.push({ page: pageNumber, matchedIndex: results?.matchedIndex, rectangles });
3113
- this.CreateRedactAllText();
3114
- }
3115
- if (this.redactAll && this.resultCount && this.resultCount > 0
3116
- && rectangles && this.redactElements.length < this.resultCount) {
3117
- this.search(false);
3118
- }
3119
- if (this.redactAll && this.resultCount && this.redactElements.length === this.resultCount) {
3120
- this.redactAll = false;
3121
- this.redactAllText = null;
3122
- this.saveRedaction(this.redactElements);
3123
- }
3124
- }
3125
- }
3126
- CreateRedactAllText() {
3127
- this.redactAllText = `${this.redactElements.length} of ${this.resultCount}`;
3128
- }
3129
- onEscapeKeyPress(e) {
3130
- this.toolbarEvents.searchBarHidden.next(true);
3078
+ this.rectLeft = this._rectangle.x;
3131
3079
  }
3132
- onEnterKeyPress(e) {
3133
- this.search();
3080
+ get editable() {
3081
+ return this._editable;
3134
3082
  }
3135
- toggleSearchBar() {
3136
- this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
3083
+ onCommentChange(updatedComment) {
3084
+ this.hasUnsavedChanges =
3085
+ this.originalComment.substring(0, this.CHAR_LIMIT) !== updatedComment.substring(0, this.CHAR_LIMIT);
3086
+ this.commentService.onCommentChange(this.hasUnsavedChanges);
3137
3087
  }
3138
- getRectangles(page) {
3139
- this.pageHeight = this.allPages[page].styles.height;
3140
- this.pageWidth = this.allPages[page].styles.width;
3141
- this.zoom = parseFloat(this.allPages[page].scaleRotation.scale);
3142
- this.rotate = parseInt(this.allPages[page].scaleRotation.rotation, 10);
3143
- const selectedHighLightedElements = document.getElementsByClassName('highlight selected');
3144
- if (selectedHighLightedElements && selectedHighLightedElements.length > 0) {
3145
- const docRange = document.createRange();
3146
- docRange.selectNodeContents(selectedHighLightedElements[0]);
3147
- const selection = window.getSelection();
3148
- selection?.removeAllRanges();
3149
- selection?.addRange(docRange);
3150
- if (selection.rangeCount && !selection.isCollapsed) {
3151
- const range = selection.getRangeAt(0).cloneRange();
3152
- const clientRects = range.getClientRects();
3153
- if (clientRects) {
3154
- const parentRect = selectedHighLightedElements[0].parentElement.parentElement.getBoundingClientRect();
3155
- const selectionRectangles = [];
3156
- for (let i = 0; i < clientRects.length; i++) {
3157
- const selectionRectangle = this.createTextRectangle(clientRects[i], parentRect);
3158
- const findSelecttionRectangle = selectionRectangles.find((rect) => rect.width === selectionRectangle.width && rect.x === selectionRectangle.x);
3159
- if (!findSelecttionRectangle) {
3160
- selectionRectangles.push(selectionRectangle);
3161
- }
3162
- }
3163
- return selectionRectangles;
3164
- }
3088
+ deleteOrCancel() {
3089
+ if (!this.editable) {
3090
+ this.delete.emit(this._comment);
3091
+ }
3092
+ else {
3093
+ this.hasUnsavedChanges = false;
3094
+ this._editable = false;
3095
+ this.fullComment = this.originalComment;
3096
+ this.changes.emit(false);
3097
+ if (!this.author && !this.fullComment) {
3098
+ this.delete.emit(this._comment);
3165
3099
  }
3166
3100
  }
3167
3101
  }
3168
- createTextRectangle(rect, parentRect) {
3169
- const height = rect.bottom - rect.top;
3170
- const width = rect.right - rect.left;
3171
- const top = rect.top - parentRect.top;
3172
- const left = rect.left - parentRect.left;
3173
- let rectangle = this.highlightService.applyRotation(this.pageHeight, this.pageWidth, height, width, top, left, this.rotate, this.zoom);
3174
- rectangle = { id: uuid$1(), ...rectangle };
3175
- return rectangle;
3102
+ editOrSave() {
3103
+ if (!this.editable) {
3104
+ this._editable = true;
3105
+ }
3106
+ else {
3107
+ this._comment.content = this.fullComment.substring(0, this.CHAR_LIMIT);
3108
+ const tags = this.tagsServices.getNewTags(this._comment.annotationId);
3109
+ const payload = {
3110
+ comment: this._comment,
3111
+ tags
3112
+ };
3113
+ this.updated.emit(payload);
3114
+ this.hasUnsavedChanges = false;
3115
+ this._editable = false;
3116
+ this.changes.emit(false);
3117
+ }
3118
+ }
3119
+ onCommentClick() {
3120
+ if (!this.selected) {
3121
+ this.selected = true;
3122
+ this._editable = false;
3123
+ this.commentClick.emit({ annotationId: this._comment.annotationId, editable: this._editable, selected: true });
3124
+ }
3125
+ }
3126
+ reRenderComments() {
3127
+ this.renderComments.emit(this._comment);
3128
+ }
3129
+ get commentTop() {
3130
+ return this.totalPrevPagesHeight + (this.rectTop * this.zoom);
3131
+ }
3132
+ get height() {
3133
+ return this.form.nativeElement.getBoundingClientRect().height / this.zoom;
3176
3134
  }
3177
3135
  }
3178
- /** @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 });
3179
- /** @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"] }] });
3180
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionSearchBarComponent, decorators: [{
3136
+ /** @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 });
3137
+ /** @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" }] });
3138
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, decorators: [{
3181
3139
  type: Component,
3182
- 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"] }]
3183
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }, { type: HighlightCreateService }]; }, propDecorators: { findInput: [{
3140
+ 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" }]
3141
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: TagsServices }]; }, propDecorators: { commentClick: [{
3142
+ type: Output
3143
+ }], renderComments: [{
3144
+ type: Output
3145
+ }], delete: [{
3146
+ type: Output
3147
+ }], updated: [{
3148
+ type: Output
3149
+ }], changes: [{
3150
+ type: Output
3151
+ }], rotate: [{
3152
+ type: Input
3153
+ }], zoom: [{
3154
+ type: Input
3155
+ }], index: [{
3156
+ type: Input
3157
+ }], page: [{
3158
+ type: Input
3159
+ }], form: [{
3184
3160
  type: ViewChild,
3185
- args: ['findInput', { static: true }]
3186
- }], onWindowKeyDown: [{
3187
- type: HostListener,
3188
- args: ['window:keydown', ['$event']]
3161
+ args: ['form', { static: false }]
3162
+ }], editableComment: [{
3163
+ type: ViewChild,
3164
+ args: ['editableComment', { static: false }]
3165
+ }], comment: [{
3166
+ type: Input
3167
+ }], annotation: [{
3168
+ type: Input
3189
3169
  }] } });
3190
3170
 
3191
- // TODO: replace by NgRx
3192
- class CommentService {
3193
- constructor() {
3194
- this.unsavedChanges = new Subject();
3195
- this.marginToCommentEmitter = new BehaviorSubject(false);
3171
+ class CommentSetComponent {
3172
+ constructor(store, commentService, renderService, toolbarEvents) {
3173
+ this.store = store;
3174
+ this.commentService = commentService;
3175
+ this.renderService = renderService;
3176
+ this.toolbarEvents = toolbarEvents;
3177
+ this.pageHeights = [];
3178
+ this.subscriptions = [];
3179
+ this.clearSelection();
3196
3180
  }
3197
- setCommentSet(commentSetComponent) {
3198
- this.commentSetComponent = commentSetComponent;
3181
+ ngOnInit() {
3182
+ this.comments$ = this.store.pipe(select(getCommentsArray));
3183
+ this.annoEntities$ = this.store.pipe(select(getAnnotationEntities));
3184
+ this.subscriptions.push(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
3185
+ this.redrawComments();
3186
+ this.showCommentsPanel = toggle;
3187
+ }));
3188
+ this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument()));
3199
3189
  }
3200
- onCommentChange(changes) {
3201
- this.unsavedChanges.next(changes);
3190
+ ngOnChanges(changes) {
3191
+ if (changes.annotationSet) {
3192
+ this.commentService.setCommentSet(this);
3193
+ }
3194
+ if (changes.contentScrollTop) {
3195
+ if (this.container) {
3196
+ this.container.nativeElement.scrollTo(0, this.contentScrollTop);
3197
+ }
3198
+ }
3202
3199
  }
3203
- getUnsavedChanges() {
3204
- return this.unsavedChanges.asObservable();
3200
+ ngOnDestroy() {
3201
+ if (this.subscriptions.length > 0) {
3202
+ this.subscriptions.forEach(subscription => subscription.unsubscribe());
3203
+ }
3205
3204
  }
3206
- hasUnsavedComments(annotation) {
3207
- if (annotation.comments.length > 0) {
3208
- const comment = this.getComment(annotation);
3209
- return comment.hasUnsavedChanges;
3205
+ onSelect(annotationId) {
3206
+ this.store.dispatch(new SelectedAnnotation(annotationId));
3207
+ }
3208
+ onCommentDelete(comment) {
3209
+ const annotation = this.annotationSet.annotations.find(anno => anno.id === comment.annotationId);
3210
+ const comments = [];
3211
+ const annot = {
3212
+ ...annotation,
3213
+ comments
3214
+ };
3215
+ this.onAnnotationUpdate(annot);
3216
+ this.redrawComments();
3217
+ }
3218
+ redrawComments() {
3219
+ setTimeout(() => {
3220
+ const componentList = this.commentComponents.map(comment => comment);
3221
+ this.renderService.redrawComponents(componentList, this.pageHeights, this.rotate, this.zoom);
3222
+ }, 0);
3223
+ }
3224
+ rotateDocument() {
3225
+ if (this.panel) {
3226
+ this.panel.nativeElement.style.height = '0';
3210
3227
  }
3211
- return false;
3212
3228
  }
3213
- updateUnsavedCommentsStatus(annotation, hasUnsavedChanges) {
3214
- const comment = this.getComment(annotation);
3215
- comment.hasUnsavedChanges = hasUnsavedChanges;
3216
- this.allCommentsSaved();
3229
+ onCommentUpdate(payload) {
3230
+ const annotation = this.annotationSet.annotations.find(anno => anno.id === payload.comment.annotationId);
3231
+ const comments = [payload.comment];
3232
+ const tags = payload.tags;
3233
+ const annot = {
3234
+ ...annotation,
3235
+ comments,
3236
+ tags
3237
+ };
3238
+ this.onAnnotationUpdate(annot);
3217
3239
  }
3218
- getComment(annotation) {
3219
- return this.commentSetComponent.commentComponents
3220
- .find(c => c.comment.annotationId === annotation.comments[0].annotationId);
3240
+ onAnnotationUpdate(annotation) {
3241
+ this.store.dispatch(new SaveAnnotation(annotation));
3221
3242
  }
3222
- resetCommentSet() {
3223
- this.commentSetComponent = null;
3243
+ onContainerClick(e) {
3244
+ if (e.path && e.path[0] === this.panel.nativeElement) {
3245
+ this.clearSelection();
3246
+ }
3224
3247
  }
3225
- allCommentsSaved() {
3226
- this.onCommentChange(this.commentSetComponent.commentComponents.some(comment => comment.hasUnsavedChanges === true));
3248
+ clearSelection() {
3249
+ this.store.dispatch(new SelectedAnnotation({ annotationId: '', editable: false, selected: false }));
3227
3250
  }
3228
- createMarginToCommentEvent(margin) {
3229
- this.marginToCommentEmitter.next(margin);
3251
+ allCommentsSaved() {
3252
+ this.commentService.allCommentsSaved();
3230
3253
  }
3231
3254
  }
3232
- /** @nocollapse */ CommentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3233
- /** @nocollapse */ CommentServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService });
3234
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentService, decorators: [{
3235
- type: Injectable
3236
- }] });
3255
+ /** @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 });
3256
+ /** @nocollapse */ CommentSetComponentcmp = 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" }] });
3257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, decorators: [{
3258
+ type: Component,
3259
+ 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" }]
3260
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: CommentSetRenderService }, { type: ToolbarEventService }]; }, propDecorators: { annotationSet: [{
3261
+ type: Input
3262
+ }], zoom: [{
3263
+ type: Input
3264
+ }], rotate: [{
3265
+ type: Input
3266
+ }], height: [{
3267
+ type: Input
3268
+ }], pageHeights: [{
3269
+ type: Input
3270
+ }], contentScrollTop: [{
3271
+ type: Input
3272
+ }], container: [{
3273
+ type: ViewChild,
3274
+ args: ['container', { static: false }]
3275
+ }], panel: [{
3276
+ type: ViewChild,
3277
+ args: ['panel', { static: false }]
3278
+ }], commentComponents: [{
3279
+ type: ViewChildren,
3280
+ args: ['commentComponent']
3281
+ }] } });
3237
3282
 
3238
- class CommentSetRenderService {
3239
- redrawComponents(commentComponents, pageHeights, rotate, zoom) {
3240
- let prevComment;
3241
- this.sortComponents(commentComponents, pageHeights, rotate, zoom).forEach((comment) => {
3242
- this.adjustIfOverlapping(comment, prevComment, zoom);
3243
- prevComment = comment;
3244
- });
3283
+ class CommentsNavigateComponent {
3284
+ constructor(store, toolbarEvents) {
3285
+ this.store = store;
3286
+ this.toolbarEvents = toolbarEvents;
3287
+ this.autoSelect = false;
3288
+ this.navigationList = [];
3289
+ this.index = 0;
3245
3290
  }
3246
- sortComponents(commentComponents, pageHeights, rotate, zoom) {
3247
- return commentComponents.sort((a, b) => {
3248
- a.rectTop = this.top(a._rectangle, pageHeights[a.page - 1], rotate, zoom);
3249
- b.rectTop = this.top(b._rectangle, pageHeights[b.page - 1], rotate, zoom);
3250
- return this.processSort(a, b);
3251
- });
3291
+ ngOnChanges(changes) {
3292
+ if (changes.annotationList) {
3293
+ this.initNavigationList();
3294
+ }
3295
+ }
3296
+ initNavigationList() {
3297
+ this.index = 0;
3298
+ this.navigationList = [...this.annotationList || []]
3299
+ .map(annotation => ({
3300
+ content: annotation.comments[0].content,
3301
+ annotationId: annotation.id,
3302
+ page: annotation.page,
3303
+ rectangle: this.upperRectangle(annotation.rectangles),
3304
+ }))
3305
+ .sort(this.sortComments);
3306
+ if (this.autoSelect) {
3307
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[0].page, 0));
3308
+ this.store.dispatch(new SelectedAnnotation({
3309
+ annotationId: this.navigationList[0].annotationId,
3310
+ editable: false,
3311
+ selected: true
3312
+ }));
3313
+ }
3252
3314
  }
3253
- adjustIfOverlapping(comment, prevComment, zoom) {
3254
- if (prevComment) {
3255
- const endOfPrevComment = prevComment.commentTop + this.height(prevComment);
3256
- if (comment.commentTop <= endOfPrevComment) {
3257
- comment.rectTop = (endOfPrevComment - comment.totalPrevPagesHeight) / zoom;
3315
+ sortComments(mappedCommentA, mappedCommentB) {
3316
+ if (mappedCommentA.page !== mappedCommentB.page) {
3317
+ return mappedCommentA.page - mappedCommentB.page;
3318
+ }
3319
+ else {
3320
+ const rectA = mappedCommentA.rectangle;
3321
+ const rectB = mappedCommentB.rectangle;
3322
+ if (rectA.y !== rectB.y) {
3323
+ return rectA.y - rectB.y;
3324
+ }
3325
+ else {
3326
+ return rectA.x - rectB.x;
3258
3327
  }
3259
3328
  }
3260
3329
  }
3261
- processSort(a, b) {
3262
- if (this.onSameLine(a, b)) {
3263
- return a.rectLeft >= b.rectLeft ? 1 : -1;
3330
+ nextItem() {
3331
+ this.index += 1;
3332
+ if (this.index === this.annotationList.length) {
3333
+ this.index = 0;
3264
3334
  }
3265
- return a.commentTop >= b.commentTop ? 1 : -1;
3266
- }
3267
- onSameLine(a, b) {
3268
- return this.difference(a.commentTop, b.commentTop) === 0;
3335
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3336
+ this.store.dispatch(new SelectedAnnotation({
3337
+ annotationId: this.navigationList[this.index].annotationId, editable: false, selected: true
3338
+ }));
3269
3339
  }
3270
- top(rectangle, height, rotate, zoom) {
3271
- const actualHeight = height / zoom;
3272
- switch (rotate) {
3273
- case 90: return rectangle.x;
3274
- case 180: return actualHeight - (rectangle.y + rectangle.height);
3275
- case 270: return actualHeight - (rectangle.x + rectangle.width);
3276
- default: return rectangle.y;
3340
+ prevItem() {
3341
+ this.index -= 1;
3342
+ if (this.index < 0) {
3343
+ this.index = this.navigationList.length - 1;
3277
3344
  }
3345
+ this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3346
+ this.store.dispatch(new SelectedAnnotation({
3347
+ annotationId: this.navigationList[this.index].annotationId,
3348
+ editable: false,
3349
+ selected: true
3350
+ }));
3278
3351
  }
3279
- height(element) {
3280
- return element.form.nativeElement.getBoundingClientRect().height;
3352
+ upperRectangle(rectangles) {
3353
+ [...rectangles].sort((rect1, rect2) => rect1.y - rect2.y);
3354
+ return { x: rectangles[0].x, y: rectangles[0].y };
3281
3355
  }
3282
- difference(a, b) { return Math.abs(a - b); }
3283
3356
  }
3284
- /** @nocollapse */ CommentSetRenderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3285
- /** @nocollapse */ CommentSetRenderServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService });
3286
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetRenderService, decorators: [{
3287
- type: Injectable
3288
- }] });
3357
+ /** @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 });
3358
+ /** @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 });
3359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
3360
+ type: Component,
3361
+ 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" }]
3362
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarEventService }]; }, propDecorators: { annotationList: [{
3363
+ type: Input
3364
+ }], autoSelect: [{
3365
+ type: Input
3366
+ }] } });
3289
3367
 
3290
- class TagsServices {
3291
- constructor(http) {
3292
- this.http = http;
3293
- this.snakeCase = string => {
3294
- // transform string_to_snake_case
3295
- return string.replace(/ +/g, ' ') // find space
3296
- .split(/ |\B(?=[A-Z])/) // split it into array
3297
- .map(word => word.toLowerCase()) // transform to lover case
3298
- .join('_'); // trun array into sting using _
3299
- };
3368
+ class CommentSearchComponent {
3369
+ constructor(store) {
3370
+ this.store = store;
3371
+ this.searchResults = [];
3372
+ this.searchIndex = 0;
3300
3373
  }
3301
- getAllTags(createdBy) {
3302
- const url = `/em-anno/tags/${createdBy}`;
3303
- return this.http.get(url);
3374
+ ngAfterViewInit() {
3375
+ if (this.searchInput) {
3376
+ this.searchInput.nativeElement.focus();
3377
+ }
3304
3378
  }
3305
- // @TODO: Move everything below this to NgRx store
3306
- getNewTags(annoid) {
3307
- return this.tagItems ? this.tagItems[annoid] : [];
3379
+ ngOnDestroy() {
3380
+ // TODO workaround for tab error
3381
+ setTimeout(() => { this.store.dispatch(new SearchComment('')); }, 250);
3308
3382
  }
3309
- updateTagItems(items, annoId) {
3310
- const snakeCased = items.map(item => {
3311
- return {
3312
- ...item,
3313
- name: this.snakeCase(item.name)
3314
- };
3315
- });
3316
- this.tagItems = {
3317
- ...this.tagItems,
3318
- [annoId]: snakeCased
3319
- };
3383
+ searchComments(searchText) {
3384
+ this.clearSearch();
3385
+ if (searchText.length > 2) {
3386
+ this.searchString = searchText;
3387
+ this.searchResults = this.annotations
3388
+ .filter(annotation => annotation.comments.length > 0)
3389
+ .filter(annotation => annotation.comments[0].content.toLowerCase().includes(this.searchString.toLowerCase()));
3390
+ if (this.searchResults.length > 0) {
3391
+ this.store.dispatch(new SearchComment(searchText));
3392
+ }
3393
+ }
3394
+ }
3395
+ clearSearch() {
3396
+ this.searchString = undefined;
3397
+ this.searchResults = [];
3398
+ this.searchIndex = 0;
3399
+ this.store.dispatch(new SearchComment(''));
3320
3400
  }
3321
3401
  }
3322
- /** @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 });
3323
- /** @nocollapse */ TagsServicesprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices });
3324
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsServices, decorators: [{
3325
- type: Injectable
3326
- }], ctorParameters: function () { return [{ type: i1$1.HttpClient }]; } });
3402
+ /** @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 });
3403
+ /** @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 });
3404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, decorators: [{
3405
+ type: Component,
3406
+ 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" }]
3407
+ }], ctorParameters: function () { return [{ type: i1.Store }]; }, propDecorators: { annotations: [{
3408
+ type: Input
3409
+ }], searchInput: [{
3410
+ type: ViewChild,
3411
+ args: ['searchInput', { static: false }]
3412
+ }] } });
3327
3413
 
3328
- class TextHighlightDirective {
3329
- constructor(element) {
3330
- this.element = element;
3331
- }
3332
- ngAfterViewChecked() {
3333
- if (this.textToHighlight) {
3334
- this.highlightInputText(this.textToHighlight);
3414
+ class FilterPipe {
3415
+ transform(items, searchText, fieldName) {
3416
+ if (!items) {
3417
+ return [];
3335
3418
  }
3336
- }
3337
- highlightInputText(textToHighlight) {
3338
- this.resetHighlight();
3339
- this.textToHighlight = textToHighlight;
3340
- const searchPattern = new RegExp(textToHighlight, 'gi');
3341
- const hostElement = this.element.nativeElement;
3342
- if (hostElement.innerHTML.match(searchPattern)) {
3343
- hostElement.innerHTML = hostElement.innerHTML
3344
- .replace(searchPattern, this.highlightPattern('$&'));
3419
+ if (!searchText) {
3420
+ return items;
3345
3421
  }
3346
- this.textToHighlight = undefined;
3347
- }
3348
- resetHighlight() {
3349
- const hostElement = this.element.nativeElement;
3350
- const searchPattern = new RegExp(this.highlightPattern('(.*?)'), 'gi');
3351
- while (hostElement.innerHTML.match(searchPattern)) {
3352
- const matchGroups = searchPattern.exec(hostElement.innerHTML);
3353
- if (matchGroups) {
3354
- hostElement.innerHTML = hostElement.innerHTML.replace(matchGroups[0], matchGroups[1]);
3422
+ return items.filter(item => {
3423
+ if (item) {
3424
+ if (item[fieldName]) {
3425
+ return item[fieldName].toLowerCase().includes(searchText.toLowerCase());
3426
+ }
3427
+ else {
3428
+ return item.toLowerCase().includes(searchText.toLowerCase());
3429
+ }
3355
3430
  }
3356
- }
3431
+ return false;
3432
+ });
3357
3433
  }
3358
- highlightPattern(dynamicText) {
3359
- return '<span class="mvTextHighlight">' + dynamicText + '</span>';
3434
+ }
3435
+ /** @nocollapse */ FilterPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3436
+ /** @nocollapse */ FilterPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, name: "filter" });
3437
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, decorators: [{
3438
+ type: Pipe,
3439
+ args: [{
3440
+ name: 'filter'
3441
+ }]
3442
+ }] });
3443
+
3444
+ class UnsnakePipe {
3445
+ transform(items) {
3446
+ return items.split('_').join(' ');
3360
3447
  }
3361
3448
  }
3362
- /** @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 });
3363
- /** @nocollapse */ TextHighlightDirectivedir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextHighlightDirective, selector: "[mvTextHighlight]", inputs: { textToHighlight: "textToHighlight" }, ngImport: i0 });
3364
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextHighlightDirective, decorators: [{
3365
- type: Directive,
3449
+ /** @nocollapse */ UnsnakePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3450
+ /** @nocollapse */ UnsnakePipepipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, name: "unsnake" });
3451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, decorators: [{
3452
+ type: Pipe,
3366
3453
  args: [{
3367
- selector: '[mvTextHighlight]'
3454
+ name: 'unsnake'
3368
3455
  }]
3369
- }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { textToHighlight: [{
3370
- type: Input
3371
- }] } });
3456
+ }] });
3372
3457
 
3373
- class TextareaAutoExpandDirective {
3374
- constructor(el) {
3375
- this.el = el;
3458
+ class CommentFilterComponent {
3459
+ constructor(store, fb) {
3460
+ this.store = store;
3461
+ this.fb = fb;
3462
+ this.isPreview = false;
3376
3463
  }
3377
- ngAfterContentChecked() {
3378
- this.adjustHeight();
3464
+ ngOnInit() {
3465
+ this.tagGroup = this.fb.group({
3466
+ 'tagFilters': this.fb.group({}),
3467
+ });
3468
+ this.filter$ = this.store.pipe(select(getTagFilters));
3469
+ this.$subscriptions = this.tagGroup.valueChanges.pipe(auditTime(5)).subscribe(value => {
3470
+ const tagFilters = value['tagFilters'];
3471
+ this.store.dispatch(new AddFilterTags(tagFilters));
3472
+ });
3473
+ this.buildFrom();
3474
+ }
3475
+ buildFrom() {
3476
+ const checkboxes = this.tagGroup.get('tagFilters');
3477
+ this.allTags$ = this.store.pipe(select(getAllTagsArr)).pipe(tap(tags => {
3478
+ this.tagGroup.reset();
3479
+ tags.forEach((value) => {
3480
+ checkboxes.addControl(value.key, new UntypedFormControl(false));
3481
+ });
3482
+ }));
3483
+ }
3484
+ onClearFilters() {
3485
+ this.tagGroup.reset();
3486
+ this.store.dispatch(new ClearFilterTags());
3379
3487
  }
3380
- onMouseDown() {
3381
- this.adjustHeight();
3488
+ ngOnDestroy() {
3489
+ this.$subscriptions.unsubscribe();
3382
3490
  }
3383
- adjustHeight() {
3384
- const nativeElement = this.el.nativeElement;
3385
- nativeElement.style.overflow = 'hidden';
3386
- nativeElement.style.height = 'auto';
3387
- nativeElement.style.height = nativeElement.scrollHeight - 5 + 'px';
3491
+ onRemoveFilter(tagName) {
3492
+ const checkboxes = this.tagGroup.get('tagFilters');
3493
+ checkboxes.controls[tagName].setValue(false);
3494
+ }
3495
+ onToggleFilterView() {
3496
+ this.isPreview = !this.isPreview;
3388
3497
  }
3389
3498
  }
3390
- /** @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 });
3391
- /** @nocollapse */ TextareaAutoExpandDirectivedir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TextareaAutoExpandDirective, selector: "[mvTextAreaAutoExpand]", host: { listeners: { "input": "onMouseDown()" } }, ngImport: i0 });
3392
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextareaAutoExpandDirective, decorators: [{
3393
- type: Directive,
3394
- args: [{
3395
- selector: '[mvTextAreaAutoExpand]'
3396
- }]
3397
- }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { onMouseDown: [{
3398
- type: HostListener,
3399
- args: ['input']
3400
- }] } });
3499
+ /** @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 });
3500
+ /** @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 });
3501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, decorators: [{
3502
+ type: Component,
3503
+ 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" }]
3504
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: i2.UntypedFormBuilder }]; } });
3401
3505
 
3402
- class TagsComponent {
3403
- constructor(tagsServices) {
3404
- this.tagsServices = tagsServices;
3405
- this.validators = [this.minLength, this.maxLength20];
3406
- this.errorMessages = {
3407
- 'minLength': 'Minimum of 2 characters',
3408
- 'maxLength20': 'Maximum of 20 characters'
3409
- };
3410
- this.requestAutocompleteItems = (text) => {
3411
- return this.tagsServices.getAllTags(this.userId);
3412
- };
3506
+ class CommentSetHeaderComponent {
3507
+ constructor(store, commentService, toolbarEvents) {
3508
+ this.store = store;
3509
+ this.commentService = commentService;
3510
+ this.toolbarEvents = toolbarEvents;
3511
+ this.showCommentSummaryDialog = new EventEmitter();
3512
+ this.tabs = [];
3513
+ this.tabSelected = '';
3413
3514
  }
3414
- onUpdateTags(value) {
3415
- this.tagsServices.updateTagItems(value, this.annoId);
3515
+ ngOnInit() {
3516
+ const tagFilter$ = this.store.pipe(select(getTagFilters));
3517
+ const filteredAnnotation$ = this.store.pipe(select(getFilteredAnnotations));
3518
+ this.$subscriptions = combineLatest([tagFilter$, filteredAnnotation$]).subscribe(([formData, filteredAnno]) => {
3519
+ this.navigationList = filteredAnno;
3520
+ this.tabs = this.navigationList.length > 0 ?
3521
+ [{ label: 'comments' }, { label: 'filter' }, { label: 'search' }] : [{ label: 'comments' }];
3522
+ this.isFiltered = !formData.length;
3523
+ this.tabs = [...this.tabs].map((tab) => {
3524
+ return !this.isFiltered && tab.label === 'filter' ? { ...tab, isFiltered: true } : { ...tab, isFiltered: false };
3525
+ });
3526
+ });
3416
3527
  }
3417
- minLength(control) {
3418
- if (control.value.length < 2) {
3419
- return {
3420
- 'minLength': true
3421
- };
3422
- }
3423
- return null;
3528
+ toggleCommentsSummary() {
3529
+ this.showCommentSummaryDialog.emit();
3424
3530
  }
3425
- maxLength20(control) {
3426
- if (control.value.length >= 20) {
3427
- return {
3428
- 'maxLength20': true
3429
- };
3531
+ selectTab(tab) {
3532
+ this.tabSelected = tab !== this.tabSelected ? tab : undefined;
3533
+ if (this.tabSelected) {
3534
+ this.marginToComment = true;
3535
+ this.commentService.createMarginToCommentEvent(this.marginToComment);
3430
3536
  }
3431
- return null;
3537
+ else {
3538
+ this.marginToComment = false;
3539
+ this.commentService.createMarginToCommentEvent(this.marginToComment);
3540
+ }
3541
+ }
3542
+ toggleCommentsPanel() {
3543
+ this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3544
+ }
3545
+ ngOnDestroy() {
3546
+ this.$subscriptions.unsubscribe();
3432
3547
  }
3433
3548
  }
3434
- /** @nocollapse */ TagsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, deps: [{ token: TagsServices }], target: i0.ɵɵFactoryTarget.Component });
3435
- /** @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 });
3436
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TagsComponent, decorators: [{
3549
+ /** @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 });
3550
+ /** @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 });
3551
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
3437
3552
  type: Component,
3438
- 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" }]
3439
- }], ctorParameters: function () { return [{ type: TagsServices }]; }, propDecorators: { tagItems: [{
3440
- type: Input
3441
- }], userId: [{
3442
- type: Input
3443
- }], editable: [{
3444
- type: Input
3445
- }], annoId: [{
3553
+ 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" }]
3554
+ }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: ToolbarEventService }]; }, propDecorators: { showCommentSummary: [{
3446
3555
  type: Input
3556
+ }], showCommentSummaryDialog: [{
3557
+ type: Output
3447
3558
  }] } });
3448
3559
 
3560
+ const getBookmarkState = createSelector(getMVState, (state) => state.bookmarks);
3561
+ const getBookmarkPages = createSelector(getBookmarkState, getBookmarkPageEnt);
3562
+ const getBookmarkEntities = createSelector(getBookmarkState, getBookmarkEnts);
3563
+ const getBookmarkNodes = createSelector(getBookmarkEntities, (entities) => generateBookmarkNodes(entities));
3564
+ const getEditableBookmark = createSelector(getBookmarkState, getEditBookmark);
3565
+ const getBookmarkInfo = createSelector(getBookmarkNodes, getDocumentId, getPdfPosition, getPages, (bookmarkNodes, docId, pdfPosition, pages) => {
3566
+ return {
3567
+ pageNumber: pdfPosition.pageNumber - 1,
3568
+ xCoordinate: pdfPosition.left,
3569
+ yCoordinate: ((pages[pdfPosition.pageNumber].styles.height)
3570
+ - (pdfPosition.top * (pages[pdfPosition.pageNumber].viewportScale)))
3571
+ / parseFloat(pages[pdfPosition.pageNumber].scaleRotation.scale),
3572
+ previous: bookmarkNodes.length > 0 ? bookmarkNodes.sort((a, b) => a.index - b.index)[bookmarkNodes.length - 1].id : undefined,
3573
+ documentId: docId
3574
+ };
3575
+ });
3576
+ const getBookmarksPerPage = createSelector(getPages, getBookmarkPages, (pages, pageEnt) => {
3577
+ if (pages && pageEnt) {
3578
+ const arr = [];
3579
+ Object.keys(pages).forEach(key => {
3580
+ const pageIdx = Number(key) - 1; // -1 as the thisPages array is 0 indexed
3581
+ const thisPage = pageEnt[pageIdx];
3582
+ arr.push({
3583
+ bookmark: thisPage ? thisPage : [],
3584
+ styles: pages[key].styles
3585
+ });
3586
+ });
3587
+ return arr;
3588
+ }
3589
+ });
3590
+
3449
3591
  /**
3450
- * A moment timezone pipe to support parsing based on time zone abbreviations
3451
- * covers all cases of offset variation due to daylight saving.
3452
- *
3453
- * Same API as DatePipe with additional timezone abbreviation support
3454
- * Official date pipe dropped support for abbreviations names from Angular V5
3455
- */
3456
- class MomentDatePipe extends DatePipe {
3457
- transform(value, format = 'mediumDate', timezone = 'Europe/London') {
3458
- const timezoneOffset = moment(value).tz(timezone).format('Z');
3459
- return super.transform(value, format, timezoneOffset);
3592
+ * Number Helper Service
3593
+ * */
3594
+ class NumberHelperService {
3595
+ constructor() { }
3596
+ isNumber(value) {
3597
+ return (value !== null
3598
+ && value !== undefined
3599
+ && value !== ''
3600
+ && !isNaN(Number(value.toString())));
3460
3601
  }
3461
3602
  }
3462
- /** @nocollapse */ MomentDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, deps: null, target: i0.ɵɵFactoryTarget.Pipe });
3463
- /** @nocollapse */ MomentDatePipepipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, name: "momentDate" });
3464
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MomentDatePipe, decorators: [{
3465
- type: Pipe,
3603
+ /** @nocollapse */ NumberHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3604
+ /** @nocollapse */ NumberHelperServiceprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, providedIn: 'root' });
3605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NumberHelperService, decorators: [{
3606
+ type: Injectable,
3466
3607
  args: [{
3467
- name: 'momentDate'
3608
+ providedIn: 'root'
3468
3609
  }]
3469
- }] });
3610
+ }], ctorParameters: function () { return []; } });
3470
3611
 
3471
- class CommentComponent {
3472
- constructor(store, commentService, tagsServices) {
3473
- this.store = store;
3474
- this.commentService = commentService;
3475
- this.tagsServices = tagsServices;
3476
- this.CHAR_LIMIT = 5000;
3477
- this.totalPrevPagesHeight = 0;
3478
- this.hasUnsavedChanges = false;
3479
- this.commentClick = new EventEmitter();
3480
- this.renderComments = new EventEmitter();
3481
- this.delete = new EventEmitter();
3482
- this.updated = new EventEmitter();
3483
- this.changes = new EventEmitter();
3484
- this.rotate = 0;
3485
- this.zoom = 1;
3612
+ class SearchBarComponent {
3613
+ constructor(toolbarButtons, toolbarEvents) {
3614
+ this.toolbarButtons = toolbarButtons;
3615
+ this.toolbarEvents = toolbarEvents;
3616
+ this.highlightAll = true;
3617
+ this.matchCase = false;
3618
+ this.wholeWord = false;
3619
+ this.resultsText = '';
3620
+ this.searchText = '';
3621
+ this.resultCount = 0;
3622
+ this.subscriptions = [];
3623
+ this.advancedSearchVisible = false;
3486
3624
  }
3487
3625
  ngOnInit() {
3488
- this.subscriptions = this.store.select(getComponentSearchText)
3489
- .pipe(distinctUntilChanged()).subscribe(searchString => this.searchString = searchString);
3490
- this.reRenderComments();
3491
- this.marginToComment$ = this.commentService.marginToCommentEmitter.asObservable();
3492
- }
3493
- ngAfterContentInit() {
3494
- if (this.tagItems && this.tagItems.length) {
3495
- this.tagsServices.updateTagItems(this.tagItems, this._comment.annotationId);
3496
- }
3626
+ this.subscriptions.push(this.toolbarEvents.searchResultsCountSubject.subscribe(results => this.setSearchResultsCount(results)));
3497
3627
  }
3498
3628
  ngOnDestroy() {
3499
- this.subscriptions.unsubscribe();
3500
- }
3501
- set comment(comment) {
3502
- this._comment = { ...comment };
3503
- this.page = this._comment.page;
3504
- this.lastUpdate = comment.lastModifiedDate ? comment.lastModifiedDate : comment.createdDate;
3505
- this.author = comment.createdByDetails;
3506
- this.createdBy = comment.createdBy;
3507
- this.editor = comment.lastModifiedByDetails;
3508
- this.originalComment = comment.content;
3509
- this.fullComment = this.originalComment;
3510
- this.selected = this._comment.selected;
3511
- this._editable = this._comment.editable;
3512
- this.tagItems = this._comment.tags;
3513
- const pageMarginBottom = 10;
3514
- this.totalPrevPagesHeight = 0;
3515
- for (let i = 0; i < this.page - 1; i++) {
3516
- const height = this._comment.pages[i + 1] ? this._comment.pages[i + 1].styles.height : undefined;
3517
- if (height) {
3518
- this.totalPrevPagesHeight += height + pageMarginBottom;
3519
- }
3629
+ for (const subscription of this.subscriptions) {
3630
+ subscription.unsubscribe();
3520
3631
  }
3521
3632
  }
3522
- get comment() {
3523
- return this._comment;
3524
- }
3525
- set annotation(annotation) {
3526
- this._rectangle = annotation.rectangles
3527
- .reduce((prev, current) => prev.y < current.y ? prev : current);
3528
- const actualHeight = this._comment.pages[this.page].styles.height / this.zoom;
3529
- switch (this.rotate) {
3530
- case 90:
3531
- this.rectTop = this._rectangle.x;
3532
- break;
3533
- case 180:
3534
- this.rectTop = actualHeight - (this._rectangle.y + this._rectangle.height);
3535
- break;
3536
- case 270:
3537
- this.rectTop = actualHeight - (this._rectangle.x + this._rectangle.width);
3538
- break;
3539
- default: this.rectTop = this._rectangle.y;
3633
+ onWindowKeyDown(e) {
3634
+ if (e.code === 'F3' || (e.ctrlKey && e.code === 'KeyF')) {
3635
+ e.preventDefault();
3636
+ this.toolbarEvents.searchBarHidden.next(false);
3637
+ setTimeout(() => this.findInput.nativeElement.focus(), 200);
3540
3638
  }
3541
- this.rectLeft = this._rectangle.x;
3542
3639
  }
3543
- get editable() {
3544
- return this._editable;
3640
+ searchNext() {
3641
+ this.toolbarEvents.search({
3642
+ searchTerm: this.searchText,
3643
+ highlightAll: this.highlightAll,
3644
+ matchCase: this.matchCase,
3645
+ wholeWord: this.wholeWord,
3646
+ previous: false,
3647
+ reset: false
3648
+ });
3545
3649
  }
3546
- onCommentChange(updatedComment) {
3547
- this.hasUnsavedChanges =
3548
- this.originalComment.substring(0, this.CHAR_LIMIT) !== updatedComment.substring(0, this.CHAR_LIMIT);
3549
- this.commentService.onCommentChange(this.hasUnsavedChanges);
3650
+ searchPrev() {
3651
+ this.toolbarEvents.search({
3652
+ searchTerm: this.searchText,
3653
+ highlightAll: this.highlightAll,
3654
+ matchCase: this.matchCase,
3655
+ wholeWord: this.wholeWord,
3656
+ previous: true,
3657
+ reset: false
3658
+ });
3550
3659
  }
3551
- deleteOrCancel() {
3552
- if (!this.editable) {
3553
- this.delete.emit(this._comment);
3554
- }
3555
- else {
3556
- this.hasUnsavedChanges = false;
3557
- this._editable = false;
3558
- this.fullComment = this.originalComment;
3559
- this.changes.emit(false);
3560
- if (!this.author && !this.fullComment) {
3561
- this.delete.emit(this._comment);
3562
- }
3563
- }
3660
+ search() {
3661
+ this.toolbarEvents.search({
3662
+ searchTerm: this.searchText,
3663
+ highlightAll: this.highlightAll,
3664
+ matchCase: this.matchCase,
3665
+ wholeWord: this.wholeWord,
3666
+ previous: false,
3667
+ reset: true
3668
+ });
3564
3669
  }
3565
- editOrSave() {
3566
- if (!this.editable) {
3567
- this._editable = true;
3568
- }
3569
- else {
3570
- this._comment.content = this.fullComment.substring(0, this.CHAR_LIMIT);
3571
- const tags = this.tagsServices.getNewTags(this._comment.annotationId);
3572
- const payload = {
3573
- comment: this._comment,
3574
- tags
3575
- };
3576
- this.updated.emit(payload);
3577
- this.hasUnsavedChanges = false;
3578
- this._editable = false;
3579
- this.changes.emit(false);
3670
+ setSearchResultsCount(results) {
3671
+ this.resultCount = results.total;
3672
+ this.resultsText = this.resultCount > 0
3673
+ ? `Found ${results.current} of ${results.total}`
3674
+ : 'No results found';
3675
+ if (this.resultCount && this.resultCount > 0) {
3676
+ setTimeout(() => {
3677
+ this.findNext.nativeElement.focus();
3678
+ }, 1000);
3580
3679
  }
3581
3680
  }
3582
- onCommentClick() {
3583
- if (!this.selected) {
3584
- this.selected = true;
3585
- this._editable = false;
3586
- this.commentClick.emit({ annotationId: this._comment.annotationId, editable: this._editable, selected: true });
3587
- }
3681
+ onEscapeKeyPress(e) {
3682
+ this.toolbarEvents.searchBarHidden.next(true);
3588
3683
  }
3589
- reRenderComments() {
3590
- this.renderComments.emit(this._comment);
3684
+ onEnterKeyPress(e) {
3685
+ this.search();
3591
3686
  }
3592
- get commentTop() {
3593
- return this.totalPrevPagesHeight + (this.rectTop * this.zoom);
3687
+ toggleAdvancedSearch() {
3688
+ this.advancedSearchVisible = !this.advancedSearchVisible;
3594
3689
  }
3595
- get height() {
3596
- return this.form.nativeElement.getBoundingClientRect().height / this.zoom;
3690
+ toggleSearchBar() {
3691
+ this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
3597
3692
  }
3598
3693
  }
3599
- /** @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 });
3600
- /** @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" }] });
3601
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentComponent, decorators: [{
3694
+ /** @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 });
3695
+ /** @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" }] });
3696
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SearchBarComponent, decorators: [{
3602
3697
  type: Component,
3603
- 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" }]
3604
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: TagsServices }]; }, propDecorators: { commentClick: [{
3605
- type: Output
3606
- }], renderComments: [{
3607
- type: Output
3608
- }], delete: [{
3609
- type: Output
3610
- }], updated: [{
3611
- type: Output
3612
- }], changes: [{
3613
- type: Output
3614
- }], rotate: [{
3615
- type: Input
3616
- }], zoom: [{
3617
- type: Input
3618
- }], index: [{
3619
- type: Input
3620
- }], page: [{
3621
- type: Input
3622
- }], form: [{
3698
+ 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" }]
3699
+ }], ctorParameters: function () { return [{ type: ToolbarButtonVisibilityService }, { type: ToolbarEventService }]; }, propDecorators: { findInput: [{
3623
3700
  type: ViewChild,
3624
- args: ['form', { static: false }]
3625
- }], editableComment: [{
3701
+ args: ['findInput', { static: true }]
3702
+ }], findNext: [{
3626
3703
  type: ViewChild,
3627
- args: ['editableComment', { static: false }]
3628
- }], comment: [{
3629
- type: Input
3630
- }], annotation: [{
3631
- type: Input
3704
+ args: ['findNext', { static: false }]
3705
+ }], onWindowKeyDown: [{
3706
+ type: HostListener,
3707
+ args: ['window:keydown', ['$event']]
3632
3708
  }] } });
3633
3709
 
3634
- class CommentSetComponent {
3635
- constructor(store, commentService, renderService, toolbarEvents) {
3636
- this.store = store;
3637
- this.commentService = commentService;
3638
- this.renderService = renderService;
3710
+ class MainToolbarComponent {
3711
+ constructor(toolbarEvents, toolbarButtons, cdr, numberHelper) {
3639
3712
  this.toolbarEvents = toolbarEvents;
3640
- this.pageHeights = [];
3713
+ this.toolbarButtons = toolbarButtons;
3714
+ this.cdr = cdr;
3715
+ this.numberHelper = numberHelper;
3716
+ this.enableAnnotations = false;
3717
+ this.enableRedactions = false;
3718
+ this.enableICP = false;
3719
+ this.contentType = null;
3641
3720
  this.subscriptions = [];
3642
- this.clearSelection();
3721
+ this.icpEnabled = false;
3722
+ this.redactionEnabled = false;
3723
+ this.pageNumber = 1;
3724
+ this.pageCount = 0;
3725
+ this.isDropdownMenuOpen = false;
3726
+ this.dropdownMenuPositions = [
3727
+ new ConnectionPositionPair({
3728
+ originX: 'end',
3729
+ originY: 'bottom'
3730
+ }, {
3731
+ overlayX: 'end',
3732
+ overlayY: 'top'
3733
+ }, 0, 3)
3734
+ ];
3735
+ this.zoomScales = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2.5, 3, 5];
3736
+ this.allButtonsWidth = 0;
3737
+ this.widthRequiredForBtn = {};
3643
3738
  }
3644
3739
  ngOnInit() {
3645
- this.comments$ = this.store.pipe(select(getCommentsArray));
3646
- this.annoEntities$ = this.store.pipe(select(getAnnotationEntities));
3647
- this.subscriptions.push(this.toolbarEvents.commentsPanelVisible.subscribe(toggle => {
3648
- this.redrawComments();
3649
- this.showCommentsPanel = toggle;
3650
- }));
3651
- this.subscriptions.push(this.toolbarEvents.rotateSubject.subscribe(rotate => this.rotateDocument()));
3652
- }
3653
- ngOnChanges(changes) {
3654
- if (changes.annotationSet) {
3655
- this.commentService.setCommentSet(this);
3656
- }
3657
- if (changes.contentScrollTop) {
3658
- if (this.container) {
3659
- this.container.nativeElement.scrollTo(0, this.contentScrollTop);
3740
+ 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 => {
3741
+ this.icpEnabled = enabled;
3742
+ if (this.icpEnabled) {
3743
+ this.toolbarEvents.toggleCommentsPanel(!enabled);
3744
+ this.toolbarEvents.sidebarOpen.next(false);
3660
3745
  }
3661
- }
3746
+ }), this.toolbarEvents.redactionMode.subscribe(enabled => {
3747
+ this.redactionEnabled = enabled;
3748
+ }), this.toolbarEvents.redactAllInProgressSubject.subscribe(disable => {
3749
+ this.redactAllInProgress = disable;
3750
+ }));
3662
3751
  }
3663
3752
  ngOnDestroy() {
3664
- if (this.subscriptions.length > 0) {
3665
- this.subscriptions.forEach(subscription => subscription.unsubscribe());
3753
+ for (const subscription of this.subscriptions) {
3754
+ subscription.unsubscribe();
3666
3755
  }
3667
3756
  }
3668
- onSelect(annotationId) {
3669
- this.store.dispatch(new SelectedAnnotation(annotationId));
3757
+ ngAfterViewInit() {
3758
+ Array.from(this.mvToolbarMain.nativeElement.children).forEach(button => {
3759
+ this.allButtonsWidth += button.getBoundingClientRect().width;
3760
+ this.widthRequiredForBtn[button.id] = this.allButtonsWidth;
3761
+ });
3762
+ this.cdr.detectChanges();
3670
3763
  }
3671
- onCommentDelete(comment) {
3672
- const annotation = this.annotationSet.annotations.find(anno => anno.id === comment.annotationId);
3673
- const comments = [];
3674
- const annot = {
3675
- ...annotation,
3676
- comments
3677
- };
3678
- this.onAnnotationUpdate(annot);
3679
- this.redrawComments();
3764
+ onResize() {
3765
+ this.cdr.detectChanges();
3680
3766
  }
3681
- redrawComments() {
3682
- setTimeout(() => {
3683
- const componentList = this.commentComponents.map(comment => comment);
3684
- this.renderService.redrawComponents(componentList, this.pageHeights, this.rotate, this.zoom);
3685
- }, 0);
3767
+ onControlPrint(event) {
3768
+ event.preventDefault();
3769
+ this.printFile();
3686
3770
  }
3687
- rotateDocument() {
3688
- if (this.panel) {
3689
- this.panel.nativeElement.style.height = '0';
3690
- }
3771
+ onClickHighlightToggle() {
3772
+ this.toolbarEvents.toggleHighlightMode();
3691
3773
  }
3692
- onCommentUpdate(payload) {
3693
- const annotation = this.annotationSet.annotations.find(anno => anno.id === payload.comment.annotationId);
3694
- const comments = [payload.comment];
3695
- const tags = payload.tags;
3696
- const annot = {
3697
- ...annotation,
3698
- comments,
3699
- tags
3700
- };
3701
- this.onAnnotationUpdate(annot);
3774
+ onClickDrawToggle() {
3775
+ this.toolbarEvents.toggleDrawMode();
3702
3776
  }
3703
- onAnnotationUpdate(annotation) {
3704
- this.store.dispatch(new SaveAnnotation(annotation));
3777
+ toggleIndexSideBar() {
3778
+ const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
3779
+ const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
3780
+ if (!(sidebarOpen && !sidebarView)) {
3781
+ this.toolbarEvents.toggleSideBar(!sidebarOpen);
3782
+ }
3783
+ this.toolbarEvents.toggleSideBarView(true);
3705
3784
  }
3706
- onContainerClick(e) {
3707
- if (e.path && e.path[0] === this.panel.nativeElement) {
3708
- this.clearSelection();
3785
+ toggleBookmarksSideBar() {
3786
+ const sidebarOpen = this.toolbarEvents.sidebarOpen.getValue();
3787
+ const sidebarView = this.toolbarEvents.sidebarOutlineView.getValue();
3788
+ if (!(sidebarOpen && sidebarView)) {
3789
+ this.toolbarEvents.toggleSideBar(!sidebarOpen);
3709
3790
  }
3791
+ this.toolbarEvents.toggleSideBarView(false);
3710
3792
  }
3711
- clearSelection() {
3712
- this.store.dispatch(new SelectedAnnotation({ annotationId: '', editable: false, selected: false }));
3793
+ togglePresentBar() {
3794
+ this.toolbarEvents.searchBarHidden.next(true);
3795
+ this.toolbarEvents.icp.enable();
3713
3796
  }
3714
- allCommentsSaved() {
3715
- this.commentService.allCommentsSaved();
3797
+ increasePageNumber() {
3798
+ this.toolbarEvents.incrementPage(1);
3716
3799
  }
3717
- }
3718
- /** @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 });
3719
- /** @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" }] });
3720
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetComponent, decorators: [{
3721
- type: Component,
3722
- 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" }]
3723
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: CommentSetRenderService }, { type: ToolbarEventService }]; }, propDecorators: { annotationSet: [{
3724
- type: Input
3725
- }], zoom: [{
3726
- type: Input
3727
- }], rotate: [{
3728
- type: Input
3729
- }], height: [{
3730
- type: Input
3731
- }], pageHeights: [{
3732
- type: Input
3733
- }], contentScrollTop: [{
3734
- type: Input
3735
- }], container: [{
3736
- type: ViewChild,
3737
- args: ['container', { static: false }]
3738
- }], panel: [{
3739
- type: ViewChild,
3740
- args: ['panel', { static: false }]
3741
- }], commentComponents: [{
3742
- type: ViewChildren,
3743
- args: ['commentComponent']
3744
- }] } });
3745
-
3746
- class CommentsNavigateComponent {
3747
- constructor(store, toolbarEvents) {
3748
- this.store = store;
3749
- this.toolbarEvents = toolbarEvents;
3750
- this.autoSelect = false;
3751
- this.navigationList = [];
3752
- this.index = 0;
3800
+ decreasePageNumber() {
3801
+ this.toolbarEvents.incrementPage(-1);
3753
3802
  }
3754
- ngOnChanges(changes) {
3755
- if (changes.annotationList) {
3756
- this.initNavigationList();
3803
+ onPageNumberInputChange(pageNumber) {
3804
+ if (Number(pageNumber) < 1) {
3805
+ pageNumber = '1';
3757
3806
  }
3758
- }
3759
- initNavigationList() {
3760
- this.index = 0;
3761
- this.navigationList = [...this.annotationList || []]
3762
- .map(annotation => ({
3763
- content: annotation.comments[0].content,
3764
- annotationId: annotation.id,
3765
- page: annotation.page,
3766
- rectangle: this.upperRectangle(annotation.rectangles),
3767
- }))
3768
- .sort(this.sortComments);
3769
- if (this.autoSelect) {
3770
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[0].page, 0));
3771
- this.store.dispatch(new SelectedAnnotation({
3772
- annotationId: this.navigationList[0].annotationId,
3773
- editable: false,
3774
- selected: true
3775
- }));
3807
+ if (Number(pageNumber) > this.pageCount) {
3808
+ pageNumber = this.pageCount.toString();
3776
3809
  }
3810
+ this.toolbarEvents.setPage(Number.parseInt(pageNumber, 10));
3777
3811
  }
3778
- sortComments(mappedCommentA, mappedCommentB) {
3779
- if (mappedCommentA.page !== mappedCommentB.page) {
3780
- return mappedCommentA.page - mappedCommentB.page;
3781
- }
3782
- else {
3783
- const rectA = mappedCommentA.rectangle;
3784
- const rectB = mappedCommentB.rectangle;
3785
- if (rectA.y !== rectB.y) {
3786
- return rectA.y - rectB.y;
3787
- }
3788
- else {
3789
- return rectA.x - rectB.x;
3790
- }
3791
- }
3812
+ setCurrentPage(pageNumber) {
3813
+ this.pageNumber = pageNumber;
3792
3814
  }
3793
- nextItem() {
3794
- this.index += 1;
3795
- if (this.index === this.annotationList.length) {
3796
- this.index = 0;
3797
- }
3798
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3799
- this.store.dispatch(new SelectedAnnotation({
3800
- annotationId: this.navigationList[this.index].annotationId, editable: false, selected: true
3801
- }));
3815
+ rotate(rotation) {
3816
+ this.toolbarEvents.rotate(rotation);
3802
3817
  }
3803
- prevItem() {
3804
- this.index -= 1;
3805
- if (this.index < 0) {
3806
- this.index = this.navigationList.length - 1;
3807
- }
3808
- this.toolbarEvents.setPage(Number.parseInt(this.navigationList[this.index].page, 0));
3809
- this.store.dispatch(new SelectedAnnotation({
3810
- annotationId: this.navigationList[this.index].annotationId,
3811
- editable: false,
3812
- selected: true
3813
- }));
3818
+ printFile() {
3819
+ this.toolbarEvents.print();
3814
3820
  }
3815
- upperRectangle(rectangles) {
3816
- [...rectangles].sort((rect1, rect2) => rect1.y - rect2.y);
3817
- return { x: rectangles[0].x, y: rectangles[0].y };
3821
+ downloadFile() {
3822
+ this.toolbarEvents.download();
3818
3823
  }
3819
- }
3820
- /** @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 });
3821
- /** @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 });
3822
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentsNavigateComponent, decorators: [{
3823
- type: Component,
3824
- 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" }]
3825
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: ToolbarEventService }]; }, propDecorators: { annotationList: [{
3826
- type: Input
3827
- }], autoSelect: [{
3828
- type: Input
3829
- }] } });
3830
-
3831
- class CommentSearchComponent {
3832
- constructor(store) {
3833
- this.store = store;
3834
- this.searchResults = [];
3835
- this.searchIndex = 0;
3824
+ zoom(zoomFactor) {
3825
+ this.toolbarEvents.zoom(+zoomFactor);
3836
3826
  }
3837
- ngAfterViewInit() {
3838
- if (this.searchInput) {
3839
- this.searchInput.nativeElement.focus();
3840
- }
3827
+ stepZoom(zoomFactor) {
3828
+ this.toolbarEvents.stepZoom(zoomFactor);
3829
+ this.zoomSelect.nativeElement.selected = 'selected';
3841
3830
  }
3842
- ngOnDestroy() {
3843
- // TODO workaround for tab error
3844
- setTimeout(() => { this.store.dispatch(new SearchComment('')); }, 250);
3831
+ toggleCommentsPanel() {
3832
+ this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3845
3833
  }
3846
- searchComments(searchText) {
3847
- this.clearSearch();
3848
- if (searchText.length > 2) {
3849
- this.searchString = searchText;
3850
- this.searchResults = this.annotations
3851
- .filter(annotation => annotation.comments.length > 0)
3852
- .filter(annotation => annotation.comments[0].content.toLowerCase().includes(this.searchString.toLowerCase()));
3853
- if (this.searchResults.length > 0) {
3854
- this.store.dispatch(new SearchComment(searchText));
3855
- }
3856
- }
3834
+ toggleRedactBar() {
3835
+ this.toolbarEvents.toggleRedactionMode();
3857
3836
  }
3858
- clearSearch() {
3859
- this.searchString = undefined;
3860
- this.searchResults = [];
3861
- this.searchIndex = 0;
3862
- this.store.dispatch(new SearchComment(''));
3837
+ toggleGrabNDrag() {
3838
+ this.toolbarEvents.toggleGrabNDrag();
3839
+ }
3840
+ isPdf() {
3841
+ return this.contentType === 'pdf';
3842
+ }
3843
+ toggleSearchBar() {
3844
+ this.toolbarEvents.searchBarHidden.next(!this.toolbarEvents.searchBarHidden.getValue());
3845
+ }
3846
+ toggleMoreOptions() {
3847
+ this.isDropdownMenuOpen = !this.isDropdownMenuOpen;
3848
+ setTimeout(() => {
3849
+ if (this.mvMenuItems) {
3850
+ this.mvMenuItems.nativeElement.focus();
3851
+ }
3852
+ }, 100);
3863
3853
  }
3864
3854
  }
3865
- /** @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 });
3866
- /** @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 });
3867
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSearchComponent, decorators: [{
3855
+ /** @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 });
3856
+ /** @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" }] });
3857
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MainToolbarComponent, decorators: [{
3868
3858
  type: Component,
3869
- 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" }]
3870
- }], ctorParameters: function () { return [{ type: i1.Store }]; }, propDecorators: { annotations: [{
3859
+ 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" }]
3860
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i0.ChangeDetectorRef }, { type: NumberHelperService }]; }, propDecorators: { enableAnnotations: [{
3871
3861
  type: Input
3872
- }], searchInput: [{
3862
+ }], enableRedactions: [{
3863
+ type: Input
3864
+ }], enableICP: [{
3865
+ type: Input
3866
+ }], contentType: [{
3867
+ type: Input
3868
+ }], zoomSelect: [{
3873
3869
  type: ViewChild,
3874
- args: ['searchInput', { static: false }]
3870
+ args: ['zoomSelect', { static: false }]
3871
+ }], mvToolbarMain: [{
3872
+ type: ViewChild,
3873
+ args: ['mvToolbarMain', { static: false }]
3874
+ }], mvMenuItems: [{
3875
+ type: ViewChild,
3876
+ args: ['dropdownMenu', { static: false }]
3877
+ }], onResize: [{
3878
+ type: HostListener,
3879
+ args: ['window:resize', []]
3880
+ }], onControlPrint: [{
3881
+ type: HostListener,
3882
+ args: ['document:keydown.control.p', ['$event']]
3883
+ }, {
3884
+ type: HostListener,
3885
+ args: ['document:keydown.meta.p', ['$event']]
3875
3886
  }] } });
3876
3887
 
3877
- class FilterPipe {
3878
- transform(items, searchText, fieldName) {
3879
- if (!items) {
3880
- return [];
3881
- }
3882
- if (!searchText) {
3883
- return items;
3884
- }
3885
- return items.filter(item => {
3886
- if (item) {
3887
- if (item[fieldName]) {
3888
- return item[fieldName].toLowerCase().includes(searchText.toLowerCase());
3889
- }
3890
- else {
3891
- return item.toLowerCase().includes(searchText.toLowerCase());
3892
- }
3893
- }
3894
- return false;
3888
+ const getRedactionState = createSelector(getMVState, (state) => state.redactions);
3889
+ const getRedactionPages = createSelector(getRedactionState, getPageEnt);
3890
+ const getSelected = createSelector(getRedactionState, getSelectedRedaction);
3891
+ const getRedactedDocumentInfo = createSelector(getRedactionState, getRedactedDocInfo);
3892
+ const getRedactionEnt = createSelector(getRedactionState, getRedactionEnt$1);
3893
+ const getRedactionArray = createSelector(getRedactionEnt, getDocumentId, (ent, documentId) => {
3894
+ const redactions = Object.keys(ent).map(key => ent[key]);
3895
+ return { redactions, documentId };
3896
+ });
3897
+ const getRedactionsPerPage = createSelector(getPages, getRedactionPages, (pages, pageEnt) => {
3898
+ if (pages && pageEnt) {
3899
+ const arr = [];
3900
+ Object.keys(pages).forEach(key => {
3901
+ arr.push({
3902
+ anno: pageEnt[key] ? pageEnt[key] : [],
3903
+ styles: pages[key].styles
3904
+ });
3895
3905
  });
3906
+ return arr;
3896
3907
  }
3897
- }
3898
- /** @nocollapse */ FilterPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3899
- /** @nocollapse */ FilterPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, name: "filter" });
3900
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FilterPipe, decorators: [{
3901
- type: Pipe,
3902
- args: [{
3903
- name: 'filter'
3904
- }]
3905
- }] });
3906
-
3907
- class UnsnakePipe {
3908
- transform(items) {
3909
- return items.split('_').join(' ');
3910
- }
3911
- }
3912
- /** @nocollapse */ UnsnakePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3913
- /** @nocollapse */ UnsnakePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, name: "unsnake" });
3914
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UnsnakePipe, decorators: [{
3915
- type: Pipe,
3916
- args: [{
3917
- name: 'unsnake'
3918
- }]
3919
- }] });
3908
+ });
3920
3909
 
3921
- class CommentFilterComponent {
3922
- constructor(store, fb) {
3910
+ class RedactionToolbarComponent {
3911
+ constructor(toolbarEventService, toolbarButtons, store) {
3912
+ this.toolbarEventService = toolbarEventService;
3913
+ this.toolbarButtons = toolbarButtons;
3923
3914
  this.store = store;
3924
- this.fb = fb;
3925
- this.isPreview = false;
3915
+ this.preview = false;
3916
+ this.hasRedactions = false;
3917
+ this.subscriptions = [];
3926
3918
  }
3927
3919
  ngOnInit() {
3928
- this.tagGroup = this.fb.group({
3929
- 'tagFilters': this.fb.group({}),
3930
- });
3931
- this.filter$ = this.store.pipe(select(getTagFilters));
3932
- this.$subscriptions = this.tagGroup.valueChanges.pipe(auditTime(5)).subscribe(value => {
3933
- const tagFilters = value['tagFilters'];
3934
- this.store.dispatch(new AddFilterTags(tagFilters));
3935
- });
3936
- this.buildFrom();
3937
- }
3938
- buildFrom() {
3939
- const checkboxes = this.tagGroup.get('tagFilters');
3940
- this.allTags$ = this.store.pipe(select(getAllTagsArr)).pipe(tap(tags => {
3941
- this.tagGroup.reset();
3942
- tags.forEach((value) => {
3943
- checkboxes.addControl(value.key, new UntypedFormControl(false));
3944
- });
3920
+ this.subscriptions.push(this.store.pipe(select(getRedactionArray)).subscribe(redactions => {
3921
+ this.hasRedactions = !!redactions.redactions.length;
3922
+ }));
3923
+ this.subscriptions.push(this.toolbarEventService.redactAllInProgressSubject.subscribe(inprogress => {
3924
+ this.redactionAllInProgress = inprogress;
3945
3925
  }));
3946
3926
  }
3947
- onClearFilters() {
3948
- this.tagGroup.reset();
3949
- this.store.dispatch(new ClearFilterTags());
3927
+ onRedactAllSearch() {
3928
+ this.toolbarEventService.openRedactionSearch.next(true);
3950
3929
  }
3951
- ngOnDestroy() {
3952
- this.$subscriptions.unsubscribe();
3930
+ toggleTextRedactionMode() {
3931
+ this.toolbarEventService.highlightModeSubject.next(true);
3953
3932
  }
3954
- onRemoveFilter(tagName) {
3955
- const checkboxes = this.tagGroup.get('tagFilters');
3956
- checkboxes.controls[tagName].setValue(false);
3933
+ toggleDrawMode() {
3934
+ this.toolbarEventService.drawModeSubject.next(true);
3957
3935
  }
3958
- onToggleFilterView() {
3959
- this.isPreview = !this.isPreview;
3936
+ togglePreview() {
3937
+ this.preview = !this.preview;
3938
+ this.toolbarEventService.toggleRedactionPreview(this.preview);
3939
+ }
3940
+ unmarkAll() {
3941
+ this.toolbarEventService.unmarkAll();
3942
+ }
3943
+ redact() {
3944
+ this.toolbarEventService.applyRedactionToDocument();
3945
+ }
3946
+ toggleRedactBar() {
3947
+ this.toolbarEventService.toggleRedactionMode();
3948
+ }
3949
+ redactPage() {
3950
+ this.toolbarEventService.drawModeSubject.next(true);
3951
+ this.toolbarEventService.redactPage();
3952
+ }
3953
+ ngOnDestroy() {
3954
+ for (const subscription of this.subscriptions) {
3955
+ subscription.unsubscribe();
3956
+ }
3960
3957
  }
3961
3958
  }
3962
- /** @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 });
3963
- /** @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 });
3964
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentFilterComponent, decorators: [{
3959
+ /** @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 });
3960
+ /** @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"] }] });
3961
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RedactionToolbarComponent, decorators: [{
3965
3962
  type: Component,
3966
- 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" }]
3967
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: i2.UntypedFormBuilder }]; } });
3963
+ 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" }]
3964
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: ToolbarButtonVisibilityService }, { type: i1.Store }]; }, propDecorators: { showRedactSearch: [{
3965
+ type: Input
3966
+ }] } });
3968
3967
 
3969
- class CommentSetHeaderComponent {
3970
- constructor(store, commentService, toolbarEvents) {
3968
+ class IcpToolbarComponent {
3969
+ constructor(toolbarEventService, store) {
3970
+ this.toolbarEventService = toolbarEventService;
3971
3971
  this.store = store;
3972
- this.commentService = commentService;
3973
- this.toolbarEvents = toolbarEvents;
3974
- this.showCommentSummaryDialog = new EventEmitter();
3975
- this.tabs = [];
3976
- this.tabSelected = '';
3977
3972
  }
3978
3973
  ngOnInit() {
3979
- const tagFilter$ = this.store.pipe(select(getTagFilters));
3980
- const filteredAnnotation$ = this.store.pipe(select(getFilteredAnnotations));
3981
- this.$subscriptions = combineLatest([tagFilter$, filteredAnnotation$]).subscribe(([formData, filteredAnno]) => {
3982
- this.navigationList = filteredAnno;
3983
- this.tabs = this.navigationList.length > 0 ?
3984
- [{ label: 'comments' }, { label: 'filter' }, { label: 'search' }] : [{ label: 'comments' }];
3985
- this.isFiltered = !formData.length;
3986
- this.tabs = [...this.tabs].map((tab) => {
3987
- return !this.isFiltered && tab.label === 'filter' ? { ...tab, isFiltered: true } : { ...tab, isFiltered: false };
3988
- });
3989
- });
3974
+ this.$subscription = this.store.pipe(select(isPresenter))
3975
+ .subscribe(isPresenter => this.isPresenter = isPresenter);
3976
+ this.$subscription.add(this.store.pipe(select(getPresenterName))
3977
+ .subscribe(name => this.presenterName = name));
3990
3978
  }
3991
- toggleCommentsSummary() {
3992
- this.showCommentSummaryDialog.emit();
3979
+ ngOnDestroy() {
3980
+ this.$subscription.unsubscribe();
3993
3981
  }
3994
- selectTab(tab) {
3995
- this.tabSelected = tab !== this.tabSelected ? tab : undefined;
3996
- if (this.tabSelected) {
3997
- this.marginToComment = true;
3998
- this.commentService.createMarginToCommentEvent(this.marginToComment);
3999
- }
4000
- else {
4001
- this.marginToComment = false;
4002
- this.commentService.createMarginToCommentEvent(this.marginToComment);
4003
- }
3982
+ present() {
3983
+ this.toolbarEventService.icp.becomePresenter();
4004
3984
  }
4005
- toggleCommentsPanel() {
4006
- this.toolbarEvents.toggleCommentsPanel(!this.toolbarEvents.commentsPanelVisible.getValue());
3985
+ stopPresenting() {
3986
+ this.toolbarEventService.icp.stopPresenting();
4007
3987
  }
4008
- ngOnDestroy() {
4009
- this.$subscriptions.unsubscribe();
3988
+ leaveIcpSession() {
3989
+ this.toolbarEventService.icp.leaveSession();
3990
+ }
3991
+ showParticipantsList() {
3992
+ this.toolbarEventService.toggleParticipantsList(!this.toolbarEventService.icp.participantsListVisible.getValue());
4010
3993
  }
4011
3994
  }
4012
- /** @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 });
4013
- /** @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 });
4014
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: CommentSetHeaderComponent, decorators: [{
3995
+ /** @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 });
3996
+ /** @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" }] });
3997
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IcpToolbarComponent, decorators: [{
4015
3998
  type: Component,
4016
- 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" }]
4017
- }], ctorParameters: function () { return [{ type: i1.Store }, { type: CommentService }, { type: ToolbarEventService }]; }, propDecorators: { showCommentSummary: [{
4018
- type: Input
4019
- }], showCommentSummaryDialog: [{
4020
- type: Output
4021
- }] } });
3999
+ 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>" }]
4000
+ }], ctorParameters: function () { return [{ type: ToolbarEventService }, { type: i1.Store }]; } });
4022
4001
 
4023
- const getBookmarkState = createSelector(getMVState, (state) => state.bookmarks);
4024
- const getBookmarkPages = createSelector(getBookmarkState, getBookmarkPageEnt);
4025
- const getBookmarkEntities = createSelector(getBookmarkState, getBookmarkEnts);
4026
- const getBookmarkNodes = createSelector(getBookmarkEntities, (entities) => generateBookmarkNodes(entities));
4027
- const getEditableBookmark = createSelector(getBookmarkState, getEditBookmark);
4028
- const getBookmarkInfo = createSelector(getBookmarkNodes, getDocumentId, getPdfPosition, getPages, (bookmarkNodes, docId, pdfPosition, pages) => {
4029
- return {
4030
- pageNumber: pdfPosition.pageNumber - 1,
4031
- xCoordinate: pdfPosition.left,
4032
- yCoordinate: ((pages[pdfPosition.pageNumber].styles.height)
4033
- - (pdfPosition.top * (pages[pdfPosition.pageNumber].viewportScale)))
4034
- / parseFloat(pages[pdfPosition.pageNumber].scaleRotation.scale),
4035
- previous: bookmarkNodes.length > 0 ? bookmarkNodes.sort((a, b) => a.index - b.index)[bookmarkNodes.length - 1].id : undefined,
4036
- documentId: docId
4037
- };
4038
- });
4039
- const getBookmarksPerPage = createSelector(getPages, getBookmarkPages, (pages, pageEnt) => {
4040
- if (pages && pageEnt) {
4041
- const arr = [];
4042
- Object.keys(pages).forEach(key => {
4043
- const pageIdx = Number(key) - 1; // -1 as the thisPages array is 0 indexed
4044
- const thisPage = pageEnt[pageIdx];
4045
- arr.push({
4046
- bookmark: thisPage ? thisPage : [],
4047
- styles: pages[key].styles
4048
- });
4049
- });
4050
- return arr;
4051
- }
4052
- });
4002
+ class ToolbarModule {
4003
+ }
4004
+ /** @nocollapse */ ToolbarModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4005
+ /** @nocollapse */ ToolbarModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, declarations: [SearchBarComponent,
4006
+ MainToolbarComponent,
4007
+ RedactionToolbarComponent,
4008
+ IcpToolbarComponent,
4009
+ RedactionSearchBarComponent], imports: [CommonModule,
4010
+ FormsModule,
4011
+ OverlayModule,
4012
+ RouterModule], exports: [MainToolbarComponent,
4013
+ SearchBarComponent,
4014
+ RedactionToolbarComponent,
4015
+ IcpToolbarComponent,
4016
+ RedactionSearchBarComponent] });
4017
+ /** @nocollapse */ ToolbarModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, providers: [
4018
+ ToolbarButtonVisibilityService,
4019
+ ToolbarEventService
4020
+ ], imports: [CommonModule,
4021
+ FormsModule,
4022
+ OverlayModule,
4023
+ RouterModule] });
4024
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ToolbarModule, decorators: [{
4025
+ type: NgModule,
4026
+ args: [{
4027
+ declarations: [
4028
+ SearchBarComponent,
4029
+ MainToolbarComponent,
4030
+ RedactionToolbarComponent,
4031
+ IcpToolbarComponent,
4032
+ RedactionSearchBarComponent
4033
+ ],
4034
+ providers: [
4035
+ ToolbarButtonVisibilityService,
4036
+ ToolbarEventService
4037
+ ],
4038
+ exports: [
4039
+ MainToolbarComponent,
4040
+ SearchBarComponent,
4041
+ RedactionToolbarComponent,
4042
+ IcpToolbarComponent,
4043
+ RedactionSearchBarComponent
4044
+ ],
4045
+ imports: [
4046
+ CommonModule,
4047
+ FormsModule,
4048
+ OverlayModule,
4049
+ RouterModule
4050
+ ]
4051
+ }]
4052
+ }] });
4053
4053
 
4054
4054
  class BoxHighlightCreateComponent {
4055
4055
  constructor(toolbarEvents, highlightService) {