UNPKG

ng-feedback

Version:

An angular directive for sending feedback featuring Angular 6, Html2canvas, Angular Material, Rxjs, inspired by Google send feedback, based on angular-cli.

817 lines (805 loc) 90.7 kB
import { Injectable, Component, ViewChild, ElementRef, ChangeDetectorRef, HostListener, Input, Output, EventEmitter, Directive, NgModule } from '@angular/core'; import html2canvas from 'html2canvas'; import { Subject, fromEvent } from 'rxjs'; import { takeUntil, finalize, map, mergeMap } from 'rxjs/operators'; import { MatDialogRef, MatDialog, MatDialogModule, MatButtonModule, MatIconModule, MatInputModule, MatTooltipModule, MatCheckboxModule, MatProgressSpinnerModule } from '@angular/material'; import { Overlay } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class Feedback { } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackService { constructor() { this.initialVariables = {}; this.highlightedColor = 'yellow'; this.hiddenColor = 'black'; this.screenshotCanvasSource = new Subject(); this.screenshotCanvas$ = this.screenshotCanvasSource.asObservable(); this.feedbackSource = new Subject(); this.feedback$ = this.feedbackSource.asObservable(); this.isDraggingToolbarSource = new Subject(); this.isDraggingToolbar$ = this.isDraggingToolbarSource.asObservable(); } /** * @return {?} */ initScreenshotCanvas() { /** @type {?} */ const body = document.body; html2canvas(body, { logging: false, width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop, allowTaint: true }).then(bodyCanvas => { this.screenshotCanvasSource.next(bodyCanvas); }); } /** * @param {?} canvas * @return {?} */ setCanvas(canvas) { this.screenshotCanvasSource.next(canvas); } /** * @param {?} feedback * @return {?} */ setFeedback(feedback) { this.feedbackSource.next(feedback); } /** * @param {?} isDragging * @return {?} */ setIsDraggingToolbar(isDragging) { this.isDraggingToolbarSource.next(isDragging); } /** * @param {?} canvas * @return {?} */ getImgEle(canvas) { /** @type {?} */ const img = canvas.toDataURL('image/png'); /** @type {?} */ const imageEle = document.createElement('img'); imageEle.setAttribute('src', img); Object.assign(imageEle.style, { position: 'absolute', top: '50%', right: '0', left: '0', margin: '0 auto', maxHeight: '100%', maxWidth: '100%', transform: 'translateY(-50%)' }); return imageEle; } /** * @return {?} */ hideBackDrop() { /** @type {?} */ const dialogBackDrop = /** @type {?} */ (document.getElementsByClassName('dialogBackDrop')[0]); dialogBackDrop.style.backgroundColor = 'initial'; } /** * @return {?} */ showBackDrop() { /** @type {?} */ const dialogBackDrop = /** @type {?} */ (document.getElementsByClassName('dialogBackDrop')[0]); if (!dialogBackDrop.getAttribute('data-html2canvas-ignore')) { dialogBackDrop.setAttribute('data-html2canvas-ignore', 'true'); } dialogBackDrop.style.backgroundColor = 'rgba(0, 0, 0, .288)'; } } FeedbackService.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class Rectangle { constructor() { this.windowScrollY = window.scrollY; this.windowScrollX = window.scrollX; } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackDialogComponent { /** * @param {?} dialogRef * @param {?} feedbackService * @param {?} detector * @param {?} el */ constructor(dialogRef, feedbackService, detector, el) { this.dialogRef = dialogRef; this.feedbackService = feedbackService; this.detector = detector; this.el = el; this.showToolbar = false; this.vars = {}; this.feedback = new Feedback(); this.includeScreenshot = true; this.showSpinner = true; this.showToolbarTips = true; this.drawColor = this.feedbackService.highlightedColor; this.rectangles = []; this.scrollWidth = document.documentElement.scrollWidth; this.scrollHeight = document.documentElement.scrollHeight; this.elCouldBeHighlighted = ['button', 'a', 'span', 'em', 'i', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'strong', 'small', 'sub', 'sup', 'b', 'time', 'img', 'video', 'input', 'label', 'select', 'textarea', 'article', 'summary', 'section']; this.isDrawingRect = false; this.feedback = new Feedback(); this.feedback.description = ''; this.vars = this.feedbackService.initialVariables; } /** * @return {?} */ ngAfterViewInit() { this.feedbackService.screenshotCanvas$.subscribe((canvas) => { this.showSpinner = false; this.feedback.screenshot = canvas.toDataURL('image/png'); this.screenshotEle = this.feedbackService.getImgEle(canvas); this.appendScreenshot(); }); this.feedbackService.isDraggingToolbar$.subscribe((isDragging) => { if (isDragging) { this.destroyCanvasListeners(); } else { this.addCanvasListeners(); } }); this.dialogRef.afterClosed().subscribe((sendNow) => { if (sendNow === true) { this.feedbackService.setFeedback(this.feedback); } }); this.feedbackService.showBackDrop(); } /** * @return {?} */ expandDrawingBoard() { this.showToolbar = true; if (!this.drawCanvas) { this.detector.detectChanges(); this.initBackgroundCanvas(); this.feedbackService.hideBackDrop(); } this.addCanvasListeners(); this.el.nativeElement.appendChild(this.drawCanvas); this.feedbackService.hideBackDrop(); console.log('expand the board'); } /** * @param {?} evt * @return {?} */ onEscapeKeyDownHandler(evt) { this.showToolbar = false; this.includeScreenshot = true; this.detector.detectChanges(); this.dialogRef.close('key down esc to close'); } /** * @param {?} manipulation * @return {?} */ manipulate(manipulation) { if (manipulation === 'done') { this.showToolbarTips = false; this.showSpinner = true; this.destroyCanvasListeners(); this.showToolbar = false; this.detector.detectChanges(); this.feedbackService.initScreenshotCanvas(); } else { this.startDraw(manipulation); } } /** * @param {?} color * @return {?} */ startDraw(color) { this.drawColor = color; } /** * @return {?} */ isIncludeScreenshot() { if (this.includeScreenshot) { this.detector.detectChanges(); this.showSpinner = false; this.appendScreenshot(); this.feedback.screenshot = this.screenshotEle.getAttribute('src'); } else { delete this.feedback['screenshot']; this.showSpinner = true; } } /** * @return {?} */ appendScreenshot() { if (this.screenshotParent) { this.screenshotParent.nativeElement.appendChild(this.screenshotEle); } } /** * @return {?} */ initBackgroundCanvas() { this.drawCanvas = /** @type {?} */ (document.getElementById('draw-canvas')); // The canvas to draw, must use this way to initial the height and width this.drawCanvas.style.height = this.scrollHeight + ''; this.drawCanvas.style.width = this.scrollWidth + ''; this.drawCanvas.height = this.scrollHeight; this.drawCanvas.width = this.scrollWidth; this.drawContainerRect(); } /** * @return {?} */ drawContainerRect() { /** @type {?} */ const drawContext = this.drawCanvas.getContext('2d'); /** @type {?} */ const width = this.scrollWidth; /** @type {?} */ const height = this.scrollHeight; drawContext.beginPath(); drawContext.fillStyle = 'rgba(0,0,0,0.3)'; drawContext.clearRect(0, 0, width, height); drawContext.fillRect(0, 0, width, height); // draw the rectangle } /** * @param {?} rect * @return {?} */ drawRectangle(rect) { /** @type {?} */ const context = this.drawCanvas.getContext('2d'); context.lineJoin = 'round'; context.beginPath(); if (rect.color === this.feedbackService.hiddenColor) { context.fillStyle = 'rgba(31, 31, 31, 0.75)'; context.fillRect(rect.startX, rect.startY, rect.width, rect.height); context.rect(rect.startX, rect.startY, rect.width, rect.height); } else { context.clearRect(rect.startX, rect.startY, rect.width, rect.height); context.lineWidth = 5; context.strokeStyle = rect.color; context.rect(rect.startX, rect.startY, rect.width, rect.height); context.stroke(); context.clearRect(rect.startX, rect.startY, rect.width, rect.height); this.rectangles.forEach(tmpRect => { if (tmpRect.color === this.feedbackService.highlightedColor) { context.clearRect(tmpRect.startX, tmpRect.startY, tmpRect.width, tmpRect.height); } }); } } /** * @return {?} */ addCanvasListeners() { /** @type {?} */ const mouseUp = fromEvent(document.documentElement, 'mouseup'); /** @type {?} */ const mouseMove = fromEvent(document.documentElement, 'mousemove'); /** @type {?} */ const mouseDown = fromEvent(document.documentElement, 'mousedown'); /** @type {?} */ const scroll = fromEvent(window, 'scroll'); this.manuallyDrawRect(mouseDown, mouseMove, mouseUp); this.autoDrawRect(mouseMove); this.changeRectPosition(scroll); } /** * @param {?} scroll * @return {?} */ changeRectPosition(scroll) { scroll.subscribe(event => { /** @type {?} */ const currentWindowScrollX = window.scrollX; /** @type {?} */ const currentWindowScrollY = window.scrollY; this.rectangles.forEach(rect => { rect.startY = rect.startY - (currentWindowScrollY - rect.windowScrollY); rect.startX = rect.startX - (currentWindowScrollX - rect.windowScrollX); rect.windowScrollY = currentWindowScrollY; rect.windowScrollX = currentWindowScrollX; }); this.drawPersistCanvasRectangles(); }, error => console.error(error)); } /** * @return {?} */ destroyCanvasListeners() { if (this.manuallyDrawRect$) { this.manuallyDrawRect$.unsubscribe(); } if (this.autoDrawRect$) { this.autoDrawRect$.unsubscribe(); } } /** * @param {?} mouseDown * @param {?} mouseMove * @param {?} mouseUp * @return {?} */ manuallyDrawRect(mouseDown, mouseMove, mouseUp) { /** @type {?} */ const mouseDrag = mouseDown.pipe(mergeMap((mouseDownEvent) => { if (this.showToolbarTips) { this.showToolbarTips = false; } this.autoDrawRect$.unsubscribe(); this.isDrawingRect = true; /** @type {?} */ const newRectangle = new Rectangle(); newRectangle.startX = mouseDownEvent.clientX; newRectangle.startY = mouseDownEvent.clientY; newRectangle.color = this.drawColor; return mouseMove.pipe(map((mouseMoveEvent) => { newRectangle.width = mouseMoveEvent.clientX - mouseDownEvent.clientX; newRectangle.height = mouseMoveEvent.clientY - mouseDownEvent.clientY; return newRectangle; }), finalize(() => { // click to draw rectangle if (newRectangle.width === undefined || newRectangle.height === undefined || newRectangle.width === 0 || newRectangle.height === 0) { /** @type {?} */ const rect = this.drawTempCanvasRectangle(mouseDownEvent); if (rect) { this.rectangles.push(rect); } } else { // drag to draw rectangle if (newRectangle.height < 0) { newRectangle.startY = newRectangle.startY + newRectangle.height; newRectangle.height = Math.abs(newRectangle.height); } if (newRectangle.width < 0) { newRectangle.startX = newRectangle.startX + newRectangle.width; newRectangle.width = Math.abs(newRectangle.width); } this.rectangles.push(newRectangle); } this.drawPersistCanvasRectangles(); this.autoDrawRect(mouseMove); this.isDrawingRect = false; }), takeUntil(mouseUp)); })); this.manuallyDrawRect$ = mouseDrag.subscribe((rec) => { this.drawPersistCanvasRectangles(); this.drawRectangle(rec); }); } /** * @param {?} mouseMove * @return {?} */ autoDrawRect(mouseMove) { this.autoDrawRect$ = mouseMove.subscribe({ next: (mouseMoveEvent) => { this.drawPersistCanvasRectangles(); this.drawTempCanvasRectangle(mouseMoveEvent); }, error: err => console.error('something wrong occurred: ' + err), }); } /** * @return {?} */ drawPersistCanvasRectangles() { this.drawContainerRect(); this.rectangles.forEach(tmpRect => { this.drawRectangle(tmpRect); }); } /** * @param {?} event * @return {?} */ drawTempCanvasRectangle(event) { /** @type {?} */ let rectangle = null; /** @type {?} */ const clientX = event.clientX; /** @type {?} */ const clientY = event.clientY; /** @type {?} */ const els = document.elementsFromPoint(clientX, clientY); /** @type {?} */ const el = els[2]; if ((!this.isExcludeRect(els)) && el && this.elCouldBeHighlighted.indexOf(el.nodeName.toLowerCase()) > -1) { rectangle = new Rectangle(); /** @type {?} */ const rect = el.getBoundingClientRect(); this.drawCanvas.style.cursor = 'pointer'; Object.assign(rectangle, { startX: rect.left, startY: rect.top, width: rect.width, height: rect.height, color: this.drawColor }); this.drawRectangle(rectangle); } else { this.drawCanvas.style.cursor = 'crosshair'; } return rectangle; } /** * @param {?} index * @return {?} */ closeRect(index) { this.rectangles.splice(index, 1); this.drawPersistCanvasRectangles(); } /** * @param {?} elements * @return {?} */ isExcludeRect(elements) { /** @type {?} */ const result = elements.some(el => { return el.getAttribute('exclude-rect') === 'true'; }); return result; } } FeedbackDialogComponent.decorators = [ { type: Component, args: [{ selector: 'feedback-dialog', template: "<div class=\"dialog\" *ngIf=\"!showToolbar\" data-html2canvas-ignore=\"true\">\r\n <div class=\"dialog-title\">\r\n <div class=\"title-font\">\r\n {{vars['title']}}\r\n </div>\r\n </div>\r\n <div class=\"dialog-content\">\r\n <div class=\"description-tips\">\r\n <div *ngIf=\"feedback.description==''\">{{vars['placeholder']}}</div>\r\n </div>\r\n <textarea\r\n autofocus\r\n class=\"description\"\r\n [(ngModel)]=\"feedback.description\"\r\n ></textarea>\r\n </div>\r\n <div class=\"screenshot-checkbox\">\r\n <mat-checkbox [(ngModel)]=\"includeScreenshot\" (change)=\"isIncludeScreenshot()\">{{vars['checkboxLabel']}}</mat-checkbox>\r\n </div>\r\n\r\n <div #screenshotParent class=\"screenshot-content\" (click)=\"expandDrawingBoard()\" *ngIf=\"includeScreenshot\">\r\n <mat-spinner class=\"loading\" [diameter]=\"30\" ></mat-spinner>\r\n <div></div>\r\n <div class=\"screenshot-tips\" *ngIf=\"!showSpinner\">\r\n <svg focusable=\"false\" aria-label=\"\" viewBox=\"0 0 24 24\">\r\n <path\r\n d=\"M21 17h-2.58l2.51 2.56c-.18.69-.73 1.26-1.41 1.44L17 18.5V21h-2v-6h6v2zM19 7h2v2h-2V7zm2-2h-2V3.08c1.1 0 2 .92 2 1.92zm-6-2h2v2h-2V3zm4 8h2v2h-2v-2zM9 21H7v-2h2v2zM5 9H3V7h2v2zm0-5.92V5H3c0-1 1-1.92 2-1.92zM5 17H3v-2h2v2zM9 5H7V3h2v2zm4 0h-2V3h2v2zm0 16h-2v-2h2v2zm-8-8H3v-2h2v2zm0 8.08C3.9 21.08 3 20 3 19h2v2.08z\"></path>\r\n </svg>\r\n <span class=\"screenshot-tips-content\">{{vars['editTip']}}</span>\r\n </div>\r\n </div>\r\n\r\n <mat-dialog-actions class=\"dialog-actions\" align=\"end\">\r\n <button mat-button (click)=\"dialogRef.close()\" class=\"action-button\">{{vars['cancelLabel']}}</button>\r\n <button mat-button (click)=\"dialogRef.close(true)\" class=\"submit-button action-button\">{{vars['sendLabel']}}</button>\r\n </mat-dialog-actions>\r\n</div>\r\n<div *ngIf=\"showToolbar\" data-html2canvas-ignore=\"true\">\r\n <div class=\"toolbar-tips\" *ngIf=\"showToolbarTips\">\r\n {{vars['drawRectTip']}}\r\n </div>\r\n\r\n <feedback-toolbar [drawColor]=\"drawColor\" (manipulate)=\"manipulate($event)\"></feedback-toolbar>\r\n</div>\r\n<div *ngFor=\"let rectangle of rectangles; index as index\" >\r\n <feedback-rectangle [rectangle]=\"rectangle\" [noHover]=\"!showToolbar || isDrawingRect\" (close)=\"closeRect(index)\">\r\n </feedback-rectangle>\r\n</div>\r\n<canvas id=\"draw-canvas\" class=\"drawCanvas\" [ngClass]=\"{'pointerCursor': !showToolbar}\"></canvas>\r\n", styles: [".dialog{z-index:1000;position:relative;width:360px;background-color:#fff}.dialog-title{background-color:#607d8b;color:#fff;height:56px}.title-font{color:#fff;float:left;font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:20px;line-height:56px;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;padding:0 16px;margin:0}.dialog-content{display:-webkit-flex;flex-grow:1;height:200px;position:relative}.description{border:none;box-sizing:border-box;box-shadow:none;color:#212121;flex-grow:1;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:16px;line-height:normal;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;max-width:100%;outline:0;padding:18px 16px 0;resize:none;width:100%;height:inherit}.description-tips{color:#bdbdbd;display:block;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:16px;line-height:normal;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;left:0;position:absolute;padding:18px 16px 0;right:0}.screenshot-checkbox{padding:0 16px;background-color:#f8f8f8;height:40px;display:flex;flex-direction:column;justify-content:center}.screenshot-content{border:none;cursor:pointer;text-align:center;display:block;position:relative;padding:0;overflow:hidden;height:192px;width:100%;background:#ededed}.screenshot-tips{align-items:center;background-color:rgba(248,248,248,.6);border-radius:4px;box-sizing:border-box;display:-webkit-flex;flex-direction:column;justify-content:center;min-height:112px;width:224px;z-index:5;position:absolute;top:50%;right:0;bottom:0;left:0;-webkit-transform:translateY(-50%);transform:translateY(-50%);margin:0 auto}.screenshot-content:hover .screenshot-tips{background-color:rgba(248,248,248,.8)}.screenshot-content:hover .screenshot-tips-content{color:#4285f4}.screenshot-content:hover svg{color:#4285f4;fill:currentColor}.screenshot-tips svg{color:#757575;fill:currentColor;height:48px;width:48px}.screenshot-tips-content{color:#757575;font-weight:400;font-size:14px;line-height:20px;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;margin-top:12px}.dialog-actions{border-top:1px solid #e0e0e0}.submit-button{margin-right:8px!important;margin-left:5px!important;color:#4285f4}.action-button{font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:14px;height:35px;line-height:normal;margin:10px 0;padding:0 8px;position:relative;min-width:75px}.loading{margin:0 auto;position:absolute;top:45%;bottom:0;left:0;right:0}.mat-dialog-actions{padding:0!important}::ng-deep .feedbackDialog .mat-dialog-container{padding:0;overflow:visible;background-color:rgba(255,255,255,0);box-shadow:initial}.toolbar-tips{background-color:rgba(255,255,255,.6);border-radius:12px;color:#757575;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:34px;line-height:40px;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;margin-bottom:72px;padding:22px 0;text-align:center;visibility:visible;width:656px;height:auto;-webkit-animation:0s ease-in 5s forwards cssAnimation;animation:0s ease-in 5s forwards cssAnimation}@keyframes cssAnimation{to{width:0;height:0;overflow:hidden}}@-webkit-keyframes cssAnimation{to{width:0;height:0;visibility:hidden}}.drawCanvas{position:absolute;top:0;left:0;z-index:-1;margin:0 auto;cursor:crosshair}.pointerCursor{cursor:default!important}"] }] } ]; /** @nocollapse */ FeedbackDialogComponent.ctorParameters = () => [ { type: MatDialogRef }, { type: FeedbackService }, { type: ChangeDetectorRef }, { type: ElementRef } ]; FeedbackDialogComponent.propDecorators = { screenshotParent: [{ type: ViewChild, args: ['screenshotParent',] }], onEscapeKeyDownHandler: [{ type: HostListener, args: ['document:keydown.escape', ['$event'],] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackToolbarComponent { /** * @param {?} el * @param {?} feedbackService */ constructor(el, feedbackService) { this.el = el; this.feedbackService = feedbackService; this.manipulate = new EventEmitter(); this.disableToolbarTips = false; this.isSwitch = false; this.isDragging = false; this.vars = {}; this.vars = feedbackService.initialVariables; } /** * @return {?} */ ngAfterViewInit() { /** @type {?} */ const elStyle = this.el.nativeElement.style; elStyle.position = 'absolute'; elStyle.left = '43%'; elStyle.top = '60%'; this.addDragListenerOnMoveBtn(); } /** * @return {?} */ ngOnChanges() { this.isSwitch = this.drawColor !== this.feedbackService.highlightedColor; } /** * @return {?} */ done() { this.manipulate.emit('done'); } /** * @return {?} */ toggleHighlight() { this.isSwitch = false; this.manipulate.emit(this.feedbackService.highlightedColor); } /** * @return {?} */ toggleHide() { this.isSwitch = true; this.manipulate.emit(this.feedbackService.hiddenColor); } /** * @return {?} */ addDragListenerOnMoveBtn() { /** @type {?} */ const mouseUp = fromEvent(this.toggleMoveBtn.nativeElement, 'mouseup'); /** @type {?} */ const mouseMove = fromEvent(document.documentElement, 'mousemove'); /** @type {?} */ const mouseDown = fromEvent(this.toggleMoveBtn.nativeElement, 'mousedown'); /** @type {?} */ const mouseDrag = mouseDown.pipe(mergeMap((md) => { this.feedbackService.setIsDraggingToolbar(true); /** @type {?} */ const startX = md.offsetX; /** @type {?} */ const startY = md.offsetY; this.disableToolbarTips = true; this.isDragging = true; // Calculate dif with mousemove until mouseup return mouseMove.pipe(map((mm) => { mm.preventDefault(); return { left: mm.clientX - startX, top: mm.clientY - startY }; }), finalize(() => { this.isDragging = false; this.disableToolbarTips = false; this.feedbackService.setIsDraggingToolbar(false); }), takeUntil(mouseUp)); })); mouseDrag.subscribe((pos) => { this.el.nativeElement.style.left = pos.left + 'px'; this.el.nativeElement.style.top = pos.top + 'px'; }); } } FeedbackToolbarComponent.decorators = [ { type: Component, args: [{ selector: 'feedback-toolbar', template: "<div exclude-rect=\"true\" class=\"toolbar\">\r\n <div class=\"move-toolbar\" #toggleMove [matTooltip]=\"vars['moveToolbarTip']\" [matTooltipDisabled]=\"disableToolbarTips\">\r\n <svg focusable=\"false\" aria-label=\"Drag\" fill=\"#BDBDBD\" height=\"56\" width=\"16\"\r\n viewBox=\"-2 2 12 12\">\r\n <circle cx=\"1.5\" cy=\"1.5\" r=\"1.5\"></circle>\r\n <circle cx=\"1.5\" cy=\"7.5\" r=\"1.5\"></circle>\r\n <circle cx=\"1.5\" cy=\"13.5\" r=\"1.5\"></circle>\r\n <circle cx=\"6.5\" cy=\"1.5\" r=\"1.5\"></circle>\r\n <circle cx=\"6.5\" cy=\"7.5\" r=\"1.5\"></circle>\r\n <circle cx=\"6.5\" cy=\"13.5\" r=\"1.5\"></circle>\r\n </svg>\r\n </div>\r\n <button #highlightBtn (click)=\"toggleHighlight()\" [matTooltip]=\"vars['highlightTip']\"\r\n [matTooltipDisabled]=\"disableToolbarTips\" class=\"highlight-toggle\" [ngClass]=\"{'deepen-color': !isSwitch}\">\r\n <span class=\"toggle\">\r\n <svg focusable=\"false\" aria-label=\"\" viewBox=\"0 0 24 24\" height=\"36\" width=\"36\"\r\n fill=\"#ffd740\">\r\n <path d=\"M3 3h18v18H3z\"></path>\r\n </svg>\r\n\r\n <svg focusable=\"false\" aria-label=\"\" fill=\"#757575\" viewBox=\"0 0 24 24\"\r\n height=\"36\" width=\"36\" [ngClass]=\"{'toggle-decorator': !isSwitch}\" *ngIf=\"!isSwitch\">\r\n <path\r\n d=\"M21 17h-2.58l2.51 2.56c-.18.69-.73 1.26-1.41 1.44L17 18.5V21h-2v-6h6v2zM19 7h2v2h-2V7zm2-2h-2V3.08c1.1 0 2 .92 2 1.92zm-6-2h2v2h-2V3zm4 8h2v2h-2v-2zM9 21H7v-2h2v2zM5 9H3V7h2v2zm0-5.92V5H3c0-1 1-1.92 2-1.92zM5 17H3v-2h2v2zM9 5H7V3h2v2zm4 0h-2V3h2v2zm0 16h-2v-2h2v2zm-8-8H3v-2h2v2zm0 8.08C3.9 21.08 3 20 3 19h2v2.08z\"></path>\r\n </svg>\r\n </span>\r\n </button>\r\n <button (click)=\"toggleHide()\" [matTooltip]=\"vars['hideTip']\" [matTooltipDisabled]=\"disableToolbarTips\"\r\n class=\"hide-toggle\" [ngClass]=\"{'deepen-color': isSwitch}\">\r\n <span class=\"toggle\">\r\n <svg focusable=\"false\" aria-label=\"\" viewBox=\"0 0 24 24\" height=\"36\" width=\"36\"\r\n fill=\"#000\">\r\n <path d=\"M3 3h18v18H3z\"></path>\r\n </svg>\r\n <svg focusable=\"false\" aria-label=\"\" fill=\"#757575\" viewBox=\"0 0 24 24\"\r\n height=\"36\" width=\"36\" [ngClass]=\"{'toggle-decorator': isSwitch}\" *ngIf=\"isSwitch\">\r\n <path\r\n d=\"M21 17h-2.58l2.51 2.56c-.18.69-.73 1.26-1.41 1.44L17 18.5V21h-2v-6h6v2zM19 7h2v2h-2V7zm2-2h-2V3.08c1.1 0 2 .92 2 1.92zm-6-2h2v2h-2V3zm4 8h2v2h-2v-2zM9 21H7v-2h2v2zM5 9H3V7h2v2zm0-5.92V5H3c0-1 1-1.92 2-1.92zM5 17H3v-2h2v2zM9 5H7V3h2v2zm4 0h-2V3h2v2zm0 16h-2v-2h2v2zm-8-8H3v-2h2v2zm0 8.08C3.9 21.08 3 20 3 19h2v2.08z\"></path>\r\n </svg>\r\n </span>\r\n </button>\r\n <button mat-button class=\"merge-button\" (click)=\"done()\">{{vars['editDoneLabel']}}</button>\r\n</div>\r\n", styles: [".toolbar{align-items:center;background-color:#fff;border-radius:2px;box-shadow:rgba(0,0,0,.14) 0 24px 38px 3px,rgba(0,0,0,.12) 0 9px 46px 8px,rgba(0,0,0,.2) 0 11px 15px -7px;cursor:pointer;display:-webkit-inline-flex;flex-direction:row;height:56px;min-width:232px;pointer-events:auto;overflow:visible;position:absolute;margin:0 auto;width:228px;bottom:0;top:25%;left:0;right:0;z-index:999}.move-toolbar{cursor:-webkit-grab;height:56px;padding:0 12px;position:relative}.move-toolbar:active{cursor:-webkit-grabbing}.toggle{display:inline-block;position:relative;height:36px;width:36px}.toggle-decorator{left:0;position:absolute;top:0}.highlight-toggle{align-items:center;background-color:#fff;border:none;box-sizing:border-box;cursor:pointer;display:-webkit-flex;justify-content:center;outline:0;padding:10px;pointer-events:auto;position:relative;height:56px;width:56px}.deepen-color{background-color:#e0e0e0!important}.hide-toggle{align-items:center;background-color:#fff;border:none;box-sizing:border-box;cursor:pointer;display:-webkit-flex;justify-content:center;outline:0;padding:10px;pointer-events:auto;position:relative;height:56px;width:56px}.merge-button{padding:0!important;margin:0 10px!important;min-width:56px;color:#4285f4}"] }] } ]; /** @nocollapse */ FeedbackToolbarComponent.ctorParameters = () => [ { type: ElementRef }, { type: FeedbackService } ]; FeedbackToolbarComponent.propDecorators = { drawColor: [{ type: Input }], manipulate: [{ type: Output }], toggleMoveBtn: [{ type: ViewChild, args: ['toggleMove',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackRectangleComponent { /** * @param {?} feedbackService */ constructor(feedbackService) { this.feedbackService = feedbackService; this.close = new EventEmitter(); this.showCloseTag = false; } /** * @return {?} */ onMouseEnter() { this.showCloseTag = this.noHover === false; } /** * @return {?} */ onMouseLeave() { this.showCloseTag = false; } /** * @return {?} */ onClose() { this.close.emit(); } } FeedbackRectangleComponent.decorators = [ { type: Component, args: [{ selector: 'feedback-rectangle', template: "<div exclude-rect=\"true\" class=\"rect\" [ngStyle]=\"{'left.px': rectangle.startX,\r\n 'top.px': rectangle.startY,\r\n 'width.px': rectangle.width ,\r\n 'height.px': rectangle.height }\"\r\n [ngClass]=\"{'highlight': rectangle.color === feedbackService.highlightedColor,\r\n 'hide': rectangle.color !== feedbackService.highlightedColor,\r\n 'noHover': noHover}\">\r\n <span exclude-rect=\"true\" class=\"close\" *ngIf=\"showCloseTag\" (click)=\"onClose()\">\r\n <svg viewBox=\"0 0 1024 1024\"\r\n width=\"16\" height=\"16\">\r\n <path\r\n d=\"M896 224l-96-96-288 288-288-288-96 96 288 288-288 288 96 96 288-288 288 288 96-96-288-288 288-288z\"></path>\r\n </svg>\r\n </span>\r\n</div>\r\n", styles: [".rect{position:fixed;background:0 0;z-index:3}.highlight:not(.noHover):hover{cursor:default;background:rgba(55,131,249,.2)}.hide{background-color:#000}.hide:not(.noHover):hover{background-color:rgba(31,31,31,.75)}.rect .close{width:24px;height:24px;background:#fff;border-radius:50%;justify-content:center;align-items:center;color:#999;position:absolute;right:-12px;top:-12px;cursor:pointer;display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}"] }] } ]; /** @nocollapse */ FeedbackRectangleComponent.ctorParameters = () => [ { type: FeedbackService } ]; FeedbackRectangleComponent.propDecorators = { rectangle: [{ type: Input }], noHover: [{ type: Input }], close: [{ type: Output }], onMouseEnter: [{ type: HostListener, args: ['mouseenter',] }], onMouseLeave: [{ type: HostListener, args: ['mouseleave',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackDirective { /** * @param {?} dialogRef * @param {?} feedbackService * @param {?} overlay */ constructor(dialogRef, feedbackService, overlay) { this.dialogRef = dialogRef; this.feedbackService = feedbackService; this.title = 'Send feedback'; this.placeholder = 'Describe your issue or share your ideas'; this.editTip = 'Click to highlight or hide info'; this.checkboxLabel = 'Include screenshot'; this.cancelLabel = 'CANCEL'; this.sendLabel = 'SEND'; this.moveToolbarTip = 'move toolbar'; this.drawRectTip = 'Draw using yellow to highlight issues or black to hide sensitive info'; this.highlightTip = 'highlight issues'; this.hideTip = 'hide sensitive info'; this.editDoneLabel = 'DONE'; this.send = new EventEmitter(); this.feedbackService.feedback$.subscribe((feedback) => { this.send.emit(feedback); }); this.overlay = overlay; } /** * @return {?} */ onClick() { this.openFeedbackDialog(); } /** * @return {?} */ openFeedbackDialog() { this.feedbackService.initScreenshotCanvas(); /** @type {?} */ const dialogRef = this.dialogRef.open(FeedbackDialogComponent, { panelClass: 'feedbackDialog', backdropClass: 'dialogBackDrop', disableClose: true, height: 'auto', width: 'auto', scrollStrategy: this.overlay.scrollStrategies.reposition() }); } /** * @return {?} */ ngOnInit() { this.feedbackService.initialVariables = { title: this.title, placeholder: this.placeholder, editTip: this.editTip, checkboxLabel: this.checkboxLabel, cancelLabel: this.cancelLabel, sendLabel: this.sendLabel, moveToolbarTip: this.moveToolbarTip, drawRectTip: this.drawRectTip, highlightTip: this.highlightTip, hideTip: this.hideTip, editDoneLabel: this.editDoneLabel }; } } FeedbackDirective.decorators = [ { type: Directive, args: [{ selector: '[feedback]' },] } ]; /** @nocollapse */ FeedbackDirective.ctorParameters = () => [ { type: MatDialog }, { type: FeedbackService }, { type: Overlay } ]; FeedbackDirective.propDecorators = { title: [{ type: Input }], placeholder: [{ type: Input }], editTip: [{ type: Input }], checkboxLabel: [{ type: Input }], cancelLabel: [{ type: Input }], sendLabel: [{ type: Input }], moveToolbarTip: [{ type: Input }], drawRectTip: [{ type: Input }], highlightTip: [{ type: Input }], hideTip: [{ type: Input }], editDoneLabel: [{ type: Input }], send: [{ type: Output }], onClick: [{ type: HostListener, args: ['click',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FeedbackModule { } FeedbackModule.decorators = [ { type: NgModule, args: [{ declarations: [ FeedbackDialogComponent, FeedbackToolbarComponent, FeedbackRectangleComponent, FeedbackDirective ], imports: [ MatDialogModule, MatButtonModule, MatIconModule, MatInputModule, MatTooltipModule, CommonModule, FormsModule, MatCheckboxModule, MatProgressSpinnerModule ], exports: [ FeedbackDirective ], entryComponents: [ FeedbackDialogComponent ], providers: [ FeedbackService ] },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ export { FeedbackModule, FeedbackDialogComponent as ɵa, FeedbackRectangleComponent as ɵd, FeedbackToolbarComponent as ɵc, FeedbackDirective as ɵe, FeedbackService as ɵb }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmctZmVlZGJhY2suanMubWFwIiwic291cmNlcyI6WyJuZzovL25nLWZlZWRiYWNrL2xpYi9lbnRpdHkvZmVlZGJhY2sudHMiLCJuZzovL25nLWZlZWRiYWNrL2xpYi9mZWVkYmFjay5zZXJ2aWNlLnRzIiwibmc6Ly9uZy1mZWVkYmFjay9saWIvZW50aXR5L3JlY3RhbmdsZS50cyIsIm5nOi8vbmctZmVlZGJhY2svbGliL2ZlZWRiYWNrLWRpYWxvZy9mZWVkYmFjay1kaWFsb2cuY29tcG9uZW50LnRzIiwibmc6Ly9uZy1mZWVkYmFjay9saWIvZmVlZGJhY2stdG9vbGJhci9mZWVkYmFjay10b29sYmFyLmNvbXBvbmVudC50cyIsIm5nOi8vbmctZmVlZGJhY2svbGliL2ZlZWRiYWNrLXJlY3RhbmdsZS9mZWVkYmFjay1yZWN0YW5nbGUuY29tcG9uZW50LnRzIiwibmc6Ly9uZy1mZWVkYmFjay9saWIvZmVlZGJhY2suZGlyZWN0aXZlLnRzIiwibmc6Ly9uZy1mZWVkYmFjay9saWIvZmVlZGJhY2subW9kdWxlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBjbGFzcyBGZWVkYmFjayB7XHJcbiAgcHVibGljIGRlc2NyaXB0aW9uOiBzdHJpbmc7XHJcbiAgcHVibGljIHNjcmVlbnNob3Q6IHN0cmluZztcclxufVxyXG4iLCJpbXBvcnQge0luamVjdGFibGV9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgaHRtbDJjYW52YXMgZnJvbSAnaHRtbDJjYW52YXMnO1xyXG5pbXBvcnQge1N1YmplY3QsIE9ic2VydmFibGV9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQge0ZlZWRiYWNrfSBmcm9tICcuL2VudGl0eS9mZWVkYmFjayc7IC8vIGltcG9ydCBPYnNlcnZhYmxlIHRvIHNvbHZlIGJ1aWxkIGlzc3VlXHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBGZWVkYmFja1NlcnZpY2Uge1xyXG4gIHB1YmxpYyBpbml0aWFsVmFyaWFibGVzOiBvYmplY3QgPSB7fTtcclxuICBwdWJsaWMgaGlnaGxpZ2h0ZWRDb2xvciA9ICd5ZWxsb3cnO1xyXG4gIHB1YmxpYyBoaWRkZW5Db2xvciA9ICdibGFjayc7XHJcbiAgcHJpdmF0ZSBzY3JlZW5zaG90Q2FudmFzU291cmNlID0gbmV3IFN1YmplY3Q8SFRNTENhbnZhc0VsZW1lbnQ+KCk7XHJcbiAgcHVibGljIHNjcmVlbnNob3RDYW52YXMkOiBPYnNlcnZhYmxlPEhUTUxDYW52YXNFbGVtZW50PiA9IHRoaXMuc2NyZWVuc2hvdENhbnZhc1NvdXJjZS5hc09ic2VydmFibGUoKTtcclxuXHJcbiAgcHJpdmF0ZSBmZWVkYmFja1NvdXJjZSA9IG5ldyBTdWJqZWN0PEZlZWRiYWNrPigpO1xyXG4gIHB1YmxpYyBmZWVkYmFjayQ6IE9ic2VydmFibGU8RmVlZGJhY2s+ID0gdGhpcy5mZWVkYmFja1NvdXJjZS5hc09ic2VydmFibGUoKTtcclxuXHJcbiAgcHJpdmF0ZSBpc0RyYWdnaW5nVG9vbGJhclNvdXJjZSA9IG5ldyBTdWJqZWN0PGJvb2xlYW4+KCk7XHJcbiAgcHVibGljIGlzRHJhZ2dpbmdUb29sYmFyJDogT2JzZXJ2YWJsZTxib29sZWFuPiA9IHRoaXMuaXNEcmFnZ2luZ1Rvb2xiYXJTb3VyY2UuYXNPYnNlcnZhYmxlKCk7XHJcblxyXG5cclxuICBwdWJsaWMgaW5pdFNjcmVlbnNob3RDYW52YXMoKSB7XHJcbiAgICBjb25zdCB0aGF0ID0gdGhpcztcclxuICAgIGNvbnN0IGJvZHkgPSBkb2N1bWVudC5ib2R5O1xyXG4gICAgaHRtbDJjYW52YXMoYm9keSwge1xyXG4gICAgICBsb2dnaW5nOiBmYWxzZSxcclxuICAgICAgd2lkdGg6IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGllbnRXaWR0aCxcclxuICAgICAgaGVpZ2h0OiBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xpZW50SGVpZ2h0LFxyXG4gICAgICB4OiBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsTGVmdCxcclxuICAgICAgeTogZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCxcclxuICAgICAgYWxsb3dUYWludCA6IHRydWVcclxuICAgIH0pLnRoZW4oYm9keUNhbnZhcyA9PiB7XHJcbiAgICAgIHRoaXMuc2NyZWVuc2hvdENhbnZhc1NvdXJjZS5uZXh0KGJvZHlDYW52YXMpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgc2V0Q2FudmFzKGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQpOiB2b2lkIHtcclxuICAgIHRoaXMuc2NyZWVuc2hvdENhbnZhc1NvdXJjZS5uZXh0KGNhbnZhcyk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgc2V0RmVlZGJhY2soZmVlZGJhY2s6IEZlZWRiYWNrKTogdm9pZCB7XHJcbiAgICB0aGlzLmZlZWRiYWNrU291cmNlLm5leHQoZmVlZGJhY2spO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIHNldElzRHJhZ2dpbmdUb29sYmFyKGlzRHJhZ2dpbmc6IGJvb2xlYW4pOiB2b2lkIHtcclxuICAgIHRoaXMuaXNEcmFnZ2luZ1Rvb2xiYXJTb3VyY2UubmV4dChpc0RyYWdnaW5nKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBnZXRJbWdFbGUoY2FudmFzKTogSFRNTEVsZW1lbnQge1xyXG4gICAgY29uc3QgaW1nID0gY2FudmFzLnRvRGF0YVVSTCgnaW1hZ2UvcG5nJyksXHJcbiAgICAgICAgICBpbWFnZUVsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG4gICAgaW1hZ2VFbGUuc2V0QXR0cmlidXRlKCdzcmMnLCBpbWcpO1xyXG4gICAgT2JqZWN0LmFzc2lnbihpbWFnZUVsZS5zdHlsZSwge1xyXG4gICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcclxuICAgICAgdG9wOiAnNTAlJyxcclxuICAgICAgcmlnaHQ6ICcwJyxcclxuICAgICAgbGVmdDogJzAnLFxyXG4gICAgICBtYXJnaW46ICcwIGF1dG8nLFxyXG4gICAgICBtYXhIZWlnaHQ6ICcxMDAlJyxcclxuICAgICAgbWF4V2lkdGg6ICcxMDAlJyxcclxuICAgICAgdHJhbnNmb3JtOiAndHJhbnNsYXRlWSgtNTAlKSdcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIGltYWdlRWxlO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIGhpZGVCYWNrRHJvcCgpIHtcclxuICAgIGNvbnN0IGRpYWxvZ0JhY2tEcm9wID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnZGlhbG9nQmFja0Ryb3AnKVswXSBhcyBIVE1MRWxlbWVudDtcclxuICAgIGRpYWxvZ0JhY2tEcm9wLnN0eWxlLmJhY2tncm91bmRDb2xvciA9ICdpbml0aWFsJztcclxuICB9XHJcblxyXG4gIHB1YmxpYyBzaG93QmFja0Ryb3AoKSB7XHJcbiAgICBjb25zdCBkaWFsb2dCYWNrRHJvcCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2RpYWxvZ0JhY2tEcm9wJylbMF0gYXMgSFRNTEVsZW1lbnQ7XHJcbiAgICBpZiAoIWRpYWxvZ0JhY2tEcm9wLmdldEF0dHJpYnV0ZSgnZGF0YS1odG1sMmNhbnZhcy1pZ25vcmUnKSkge1xyXG4gICAgICBkaWFsb2dCYWNrRHJvcC5zZXRBdHRyaWJ1dGUoJ2RhdGEtaHRtbDJjYW52YXMtaWdub3JlJywgJ3RydWUnKTtcclxuICAgIH1cclxuICAgIGRpYWxvZ0JhY2tEcm9wLnN0eWxlLmJhY2tncm91bmRDb2xvciA9ICdyZ2JhKDAsIDAsIDAsIC4yODgpJztcclxuICB9XHJcbn1cclxuIiwiZXhwb3J0IGNsYXNzIFJlY3RhbmdsZSB7XHJcbiAgcHVibGljIHN0YXJ0WDogbnVtYmVyO1xyXG4gIHB1YmxpYyBzdGFydFk6IG51bWJlcjtcclxuICBwdWJsaWMgd2lkdGg6IG51bWJlcjtcclxuICBwdWJsaWMgaGVpZ2h0OiBudW1iZXI7XHJcbiAgcHVibGljIGNvbG9yOiBzdHJpbmc7XHJcbiAgcHVibGljIHdpbmRvd1Njcm9sbFk6IG51bWJlciA9IHdpbmRvdy5zY3JvbGxZO1xyXG4gIHB1YmxpYyB3aW5kb3dTY3JvbGxYOiBudW1iZXIgPSB3aW5kb3cuc2Nyb2xsWDtcclxufVxyXG4iLCJpbXBvcnQge2Zyb20sIGZyb21FdmVudCBhcyBvYnNlcnZhYmxlRnJvbUV2ZW50LCBPYnNlcnZhYmxlLCBTdWJzY3JpcHRpb259IGZyb20gJ3J4anMnO1xyXG5cclxuaW1wb3J0IHt0YWtlVW50aWwsIGZpbmFsaXplLCBtYXAsIG1lcmdlTWFwLCB0aW1lb3V0LCBza2lwV2hpbGUsIGZpbHRlciwgc2NhbiwgZmlyc3R9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuaW1wb3J0IHtDb21wb25lbnQsIEFmdGVyVmlld0luaXQsIFZpZXdDaGlsZCwgRWxlbWVudFJlZiwgQ2hhbmdlRGV0ZWN0b3JSZWYsIEhvc3RMaXN0ZW5lciwgUmVuZGVyZXIyfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHtNYXREaWFsb2dSZWZ9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsJztcclxuaW1wb3J0IHtGZWVkYmFja30gZnJvbSAnLi4vZW50aXR5L2ZlZWRiYWNrJztcclxuaW1wb3J0IHtGZWVkYmFja1NlcnZpY2V9IGZyb20gJy4uL2ZlZWRiYWNrLnNlcnZpY2UnO1xyXG5cclxuaW1wb3J0IHtSZWN0YW5nbGV9IGZyb20gJy4uL2VudGl0eS9yZWN0YW5nbGUnO1xyXG5pbXBvcnQge2VsZW1lbnR9IGZyb20gJ3Byb3RyYWN0b3InO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdmZWVkYmFjay1kaWFsb2cnLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9mZWVkYmFjay1kaWFsb2cuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2ZlZWRiYWNrLWRpYWxvZy5jb21wb25lbnQuY3NzJ11cclxufSlcclxuXHJcbmV4cG9ydCBjbGFzcyBGZWVkYmFja0RpYWxvZ0NvbXBvbmVudCBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQge1xyXG4gIHB1YmxpYyBzaG93VG9vbGJhciA9IGZhbHNlO1xyXG4gIHB1YmxpYyB2YXJzOiBvYmplY3QgPSB7fTtcclxuICBwdWJsaWMgZmVlZGJhY2sgPSBuZXcgRmVlZGJhY2soKTtcclxuICBwdWJsaWMgaW5jbHVkZVNjcmVlbnNob3Q6IGJvb2xlYW4gPSB0cnVlO1xyXG4gIHB1YmxpYyBzaG93U3Bpbm5lciA9IHRydWU7XHJcbiAgcHVibGljIHNjcmVlbnNob3RFbGU6IEhUTUxFbGVtZW50O1xyXG4gIHB1YmxpYyBkcmF3Q2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudDtcclxuICBwdWJsaWMgc2hvd1Rvb2xiYXJUaXBzOiBib29sZWFuID0gdHJ1ZTtcclxuICBAVmlld0NoaWxkKCdzY3JlZW5zaG90UGFyZW50JylcclxuICBwdWJsaWMgc2NyZWVuc2hvdFBhcmVudDogRWxlbWVudFJlZjtcclxuICBwdWJsaWMgZHJhd0NvbG9yOiBzdHJpbmcgPSB0aGlzLmZlZWRiYWNrU2VydmljZS5oaWdobGlnaHRlZENvbG9yO1xyXG4gIHB1YmxpYyByZWN0YW5nbGVzOiBSZWN0YW5nbGVbXSA9IFtdO1xyXG4gIHByaXZhdGUgc2Nyb2xsV2lkdGggPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsV2lkdGg7XHJcbiAgcHJpdmF0ZSBzY3JvbGxIZWlnaHQgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsSGVpZ2h0O1xyXG4gIHByaXZhdGUgZWxDb3VsZEJlSGlnaGxpZ2h0ZWQgPSBbJ2J1dHRvbicsICdhJywgJ3NwYW4nLCAnZW0nLCAnaScsICdoMScsICdoMicsICdoMycsICdoNCcsXHJcbiAgICAnaDUnLCAnaDYnLCAncCcsICdzdHJvbmcnLCAnc21hbGwnLCAnc3ViJywgJ3N1cCcsICdiJywgJ3RpbWUnLCAnaW1nJyxcclxuICAgICd2aWRlbycsICdpbnB1dCcsICdsYWJlbCcsICdzZWxlY3QnLCAndGV4dGFyZWEnLCAnYXJ0aWNsZScsICdzdW1tYXJ5JywgJ3NlY3Rpb24nXTtcclxuICAvLyB0aGUgZmxhZyBmaWVsZCAnaXNNYW51YWxseURyYXdSZWN0JyB0byBzb2x2ZSBjb25mbGljdCBiZXR3ZWVuIG1hbnVhbGx5IGRyYXcgYW5kIGF1dG8gZHJhd1xyXG4gIHByaXZhdGUgbWFudWFsbHlEcmF3UmVjdCQ6IFN1YnNjcmlwdGlvbjtcclxuICBwcml2YXRlIGF1dG9EcmF3UmVjdCQ6IFN1YnNjcmlwdGlvbjtcclxuICBwdWJsaWMgaXNEcmF3aW5nUmVjdDogYm9vbGVhbiA9IGZhbHNlO1xyXG5cclxuICBjb25zdHJ1Y3RvcihwdWJsaWMgZGlhbG9nUmVmOiBNYXREaWFsb2dSZWY8RmVlZGJhY2tEaWFsb2dDb21wb25lbnQ+LFxyXG4gICAgICAgICAgICAgIHByaXZhdGUgZmVlZGJhY2tTZXJ2aWNlOiBGZWVkYmFja1NlcnZpY2UsXHJcbiAgICAgICAgICAgICAgcHJpdmF0ZSBkZXRlY3RvcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXHJcbiAgICAgICAgICAgICAgcHJpdmF0ZSBlbDogRWxlbWVudFJlZikge1xyXG4gICAgdGhpcy5mZWVkYmFjayA9IG5ldyBGZWVkYmFjaygpO1xyXG4gICAgdGhpcy5mZWVkYmFjay5kZXNjcmlwdGlvbiA9ICcnO1xyXG4gICAgdGhpcy52YXJzID0gdGhpcy5mZWVkYmFja1NlcnZpY2UuaW5pdGlhbFZhcmlhYmxlcztcclxuICB9XHJcblxyXG4gIHB1YmxpYyBuZ0FmdGVyVmlld0luaXQoKSB7XHJcbiAgICB0aGlzLmZlZWRiYWNrU2VydmljZS5zY3JlZW5zaG90Q2FudmFzJC5zdWJzY3JpYmUoKGNhbnZhcykgPT4ge1xyXG4gICAgICB0aGlzLnNob3dTcGlubmVyID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuZmVlZGJhY2suc2NyZWVuc2hvdCA9IGNhbnZhcy50b0RhdGFVUkwoJ2ltYWdlL3BuZycpO1xyXG4gICAgICB0aGlzLnNjcmVlbnNob3RFbGUgPSB0aGlzLmZlZWRiYWNrU2VydmljZS5nZXRJbWdFbGUoY2FudmFzKTtcclxuICAgICAgdGhpcy5hcHBlbmRTY3JlZW5zaG90KCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLmZlZWRiYWNrU2VydmljZS5pc0RyYWdnaW5nVG9vbGJhciQuc3Vic2NyaWJlKChpc0RyYWdnaW5nKSA9PiB7XHJcbiAgICAgIGlmIChpc0RyYWdnaW5nKSB7XHJcbiAgICAgICAgdGhpcy5kZXN0cm95Q2FudmFzTGlzdGVuZXJzKCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhpcy5hZGRDYW52YXNMaXN0ZW5lcnMoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5kaWFsb2dSZWYuYWZ0ZXJDbG9zZWQoKS5zdWJzY3JpYmUoKHNlbmROb3cpID0+IHtcclxuICAgICAgaWYgKHNlbmROb3cgPT09IHRydWUpIHtcclxuICAgICAgICB0aGlzLmZlZWRiYWNrU2VydmljZS5zZXRGZWVkYmFjayh0aGlzLmZlZWRiYWNrKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgICB0aGlzLmZlZWRiYWNrU2VydmljZS5zaG93QmFja0Ryb3AoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBleHBhbmREcmF3aW5nQm9hcmQoKSB7XHJcbiAgICB0aGlzLnNob3dUb29sYmFyID0gdHJ1ZTtcclxuICAgIGlmICghdGhpcy5kcmF3Q2FudmFzKSB7XHJcbiAgICAgIHRoaXMuZGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xyXG4gICAgICB0aGlzLmluaXRCYWNrZ3JvdW5kQ2FudmFzKCk7XHJcbiAgICAgIHRoaXMuZmVlZGJhY2tTZXJ2aWNlLmhpZGVCYWNrRHJvcCgpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5hZGRDYW52YXNMaXN0ZW5lcnMoKTtcclxuICAgIHRoaXMuZWwubmF0aXZlRWxlbWVudC5hcHBlbmRDaGlsZCh0aGlzLmRyYXdDYW52YXMpO1xyXG4gICAgdGhpcy5mZWVkYmFja1NlcnZpY2UuaGlkZUJhY2tEcm9wKCk7XHJcbiAgICBjb25zb2xlLmxvZygnZXhwYW5kIHRoZSBib2FyZCcpO1xyXG4gIH1cclxuXHJcbiAgQEhvc3RMaXN0ZW5lcignZG9jdW1lbnQ6a2V5ZG93bi5lc2NhcGUnLCBbJyRldmVudCddKVxyXG4gIHB1YmxpYyBvbkVzY2FwZUtleURvd25IYW5kbGVyKGV2dDogS2V5Ym9hcmRFdmVudCkge1xyXG4gICAgdGhpcy5zaG93VG9vbGJhciA9IGZhbHNlO1xyXG4gICAgdGhpcy5pbmNsdWRlU2NyZWVuc2hvdCA9IHRydWU7XHJcbiAgICB0aGlzLmRldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcclxuICAgIHRoaXMuZGlhbG9nUmVmLmNsb3NlKCdrZXkgZG93biBlc2MgdG8gY2xvc2UnKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBtYW5pcHVsYXRlKG1hbmlwdWxhdGlvbjogc3RyaW5nKSB7XHJcbiAgICBpZiAobWFuaXB1bGF0aW9uID09PSAnZG9uZScpIHtcclxuICAgICAgdGhpcy5zaG93VG9vbGJhclRpcHMgPSBmYWxzZTtcclxuICAgICAgdGhpcy5zaG93U3Bpbm5lciA9IHRydWU7XHJcbiAgICAgIHRoaXMuZGVzdHJveUNhbnZhc0xpc3RlbmVycygpO1xyXG4gICAgICB0aGlzLnNob3dUb29sYmFyID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuZGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xyXG4gICAgICB0aGlzLmZlZWRiYWNrU2VydmljZS5pbml0U2NyZWVuc2hvdENhbnZhcygpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5zdGFydERyYXcobWFuaXB1bGF0aW9uKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHB1YmxpYyBzdGFydERyYXcoY29sb3I6IHN0cmluZykge1xyXG4gICAgdGhpcy5kcmF3Q29sb3IgPSBjb2xvcjtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc0luY2x1ZGVTY3JlZW5zaG90KCkge1xyXG4gICAgaWYgKHRoaXMuaW5jbHVkZVNjcmVlbnNob3QpIHtcclxuICAgICAgdGhpcy5kZXRlY3Rvci5kZXRlY3RDaGFuZ2VzKCk7XHJcbiAgICAgIHRoaXMuc2hvd1NwaW5uZXIgPSBmYWxzZTtcclxuICAgICAgdGhpcy5hcHBlbmRTY3JlZW5zaG90KCk7XHJcbiAgICAgIHRoaXMuZmVlZGJhY2suc2NyZWVuc2hvdCA9IHRoaXMuc2NyZWVuc2hvdEVsZS5nZXRBdHRyaWJ1dGUoJ3NyYycpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgZGVsZXRlIHRoaXMuZmVlZGJhY2tbJ3NjcmVlbnNob3QnXTtcclxuICAgICAgdGhpcy5zaG93U3Bpbm5lciA9IHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGFwcGVuZFNjcmVlbnNob3QoKSB7XHJcbiAgICBpZiAodGhpcy5zY3JlZW5zaG90UGFyZW50KSB7IHRoaXMuc2NyZWVuc2hvdFBhcmVudC5uYXRpdmVFbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuc2NyZWVuc2hvdEVsZSk7IH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgaW5pdEJhY2tncm91bmRDYW52YXMoKSB7XHJcbiAgICB0aGlzLmRyYXdDYW52YXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZHJhdy1jYW52YXMnKSBhcyBIVE1MQ2FudmFzRWxlbWVudDtcclxuICAgIC8vIFRoZSBjYW52YXMgdG8gZHJhdywgbXVzdCB1c2UgdGhpcyB3YXkgdG8gaW5pdGlhbCB0aGUgaGVpZ2h0IGFuZCB3aWR0aFxyXG4gICAgdGhpcy5kcmF3Q2FudmFzLnN0eWxlLmhlaWdodCA9IHRoaXMuc2Nyb2xsS