UNPKG

moh-common-lib

Version:

A library of Angular components, services, and styles for B.C. Government Ministry of Health (MoH).

705 lines 71.6 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import * as tslib_1 from "tslib"; import { Component, ElementRef, EventEmitter, Input, NgZone, Output, ViewChild, forwardRef, ViewEncapsulation, } from '@angular/core'; import { NgForm, ControlContainer, } from '@angular/forms'; import { CommonImage, CommonImageError, } from '../../models/images.model'; import { Base } from '../../models/base'; import * as PDFJS_ from 'pdfjs-dist/legacy/build/pdf'; import { pdfJsWorker } from 'pdfjs-dist/legacy/build/pdf.worker.entry'; /** @type {?} */ const PDFJS = ((/** @type {?} */ (PDFJS_))); PDFJS.workerSrc = pdfJsWorker; /** * @record */ export function FileUploaderMsg() { } if (false) { /** @type {?} */ FileUploaderMsg.prototype.required; } /** * @record */ function Size() { } if (false) { /** @type {?} */ Size.prototype.width; /** @type {?} */ Size.prototype.height; } /** @type {?} */ const MAX_IMAGE_SIZE_BYTES = 1048576; /** @type {?} */ const MAX_IMAGE_COUNT = 50; /** @type {?} */ const MAX_IMAGE_WIDTH = 3300; /** @type {?} */ const MAX_IMAGE_HEIGHT = 3300; /** @type {?} */ const MIN_IMAGE_WIDTH = 200; /** @type {?} */ const MIN_IMAGE_HEIGHT = 200; /** @type {?} */ const MIN_IMAGE_SIZE_BYTES = 20000; /** @type {?} */ const JPEG_COMPRESSION = 0.5; /** @type {?} */ const IMAGE_CONTENT_TYPE = 'image/jpeg'; /** @type {?} */ const IMAGE_REDUCTION_SCALE_FACTOR = 0.8; export class FileUploaderComponent extends Base { /** * @param {?} zone * @param {?} controlContainer */ constructor(zone, controlContainer) { super(); this.zone = zone; this.controlContainer = controlContainer; this.images = new Array(0); this.required = false; this.instructionText = 'Please upload required ID documents.'; this.errorMessages = { required: 'File is required.' }; this.imagesChange = new EventEmitter(); this.errorDocument = new EventEmitter(); } /** * Opens the file upload dialog from the browser. * @return {?} */ openFileDialog() { this.browseFileRef.nativeElement.click(); } /** * @param {?} event * @return {?} */ handleDragOver(event) { event.preventDefault(); } /** * @param {?} event * @return {?} */ handleDrop(event) { event.preventDefault(); /** @type {?} */ const files = event.dataTransfer.files; // Don't proceed if no file(s) were selected. if (!files || files.length === 0) { return; } // Clear previous error message. //this.errorMessage = null; // Process each file dropped. for (let i = 0; i < files.length; i++) { this.processFile(files[i]); } } /** * @param {?} event * @return {?} */ handleChangeFile(event) { /** @type {?} */ const files = event.target.files; // Don't proceed if no file(s) were selected. if (!files || files.length === 0) { return; } // Clear previous error message. //this.errorMessage = null; // Process each file selected. for (let i = 0; i < files.length; i++) { this.processFile(files[i]); } // Clear selected files. event.target.value = ''; } /** * @private * @param {?} file * @return {?} */ processFile(file) { return tslib_1.__awaiter(this, void 0, void 0, function* () { switch (file.type) { case 'application/pdf': try { /** @type {?} */ const images = yield this.processPDFFile(file); yield this.addFileImages(file.name, images); } catch (error) { this.handleError(error); } break; default: try { /** @type {?} */ const image = yield this.processImageFile(file); yield this.addFileImages(file.name, [image]); } catch (error) { this.handleError(error); } break; } }); } /** * @private * @param {?} file * @return {?} */ processPDFFile(file) { /** @type {?} */ const reader = new FileReader(); /** @type {?} */ const images = []; return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { reader.onload = (/** * @return {?} */ () => { /** @type {?} */ const docInitParams = { data: reader.result }; PDFJS.getDocument(docInitParams).promise.then((/** * @param {?} pdfDoc * @return {?} */ (pdfDoc) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (pdfDoc.numPages + this.images.length > MAX_IMAGE_COUNT) { reject(CommonImageError.TooBig); return; } for (let pageNumber = 1; pageNumber <= pdfDoc.numPages; pageNumber++) { try { /** @type {?} */ let image = yield this.getPageImage(pdfDoc, pageNumber); // Check image size. if (image.size <= MAX_IMAGE_SIZE_BYTES) { images.push(image); } else { /** @type {?} */ const scaledImage = yield this.scaleImage(image); images.push(scaledImage); } } catch (error) { /** @type {?} */ const message = `Error reading page ${pageNumber} of the PDF.`; console.log(message, error); reject(CommonImageError.CannotOpenPDF); return; } } resolve(images); })), (/** * @return {?} */ () => { reject(CommonImageError.CannotOpenPDF); })); }); reader.readAsArrayBuffer(file); })); } /** * @private * @param {?} pdfDoc * @param {?} pageNumber * @return {?} */ getPageImage(pdfDoc, pageNumber) { /** @type {?} */ const canvas = document.createElement('canvas'); /** @type {?} */ const ctx = canvas.getContext('2d'); return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { pdfDoc.getPage(pageNumber).then((/** * @param {?} page * @return {?} */ (page) => { /** @type {?} */ const viewport = page.getViewport({ scale: 2.0 }); // Sometimes width and height can be NaN, so use viewBox instead. if (viewport.width && viewport.height) { canvas.width = viewport.width; canvas.height = viewport.height; } else { canvas.width = viewport.viewBox[2]; canvas.height = viewport.viewBox[3]; } /** @type {?} */ const renderContext = { canvasContext: ctx, viewport: viewport }; page.render(renderContext).promise.then((/** * @return {?} */ () => tslib_1.__awaiter(this, void 0, void 0, function* () { /** @type {?} */ const dataURL = canvas.toDataURL('image/jpeg', JPEG_COMPRESSION); /** @type {?} */ const image = yield this.createImage(dataURL); resolve(image); })), (/** * @param {?} error * @return {?} */ (error) => { console.log('PDFJS render error:', error); reject(CommonImageError.CannotOpenPDF); })); })).catch((/** * @param {?} error * @return {?} */ (error) => { console.log('PDFJS getPage() error:', error); reject(CommonImageError.CannotOpenPDF); })); })); } /** * @private * @param {?} file * @return {?} */ processImageFile(file) { /** @type {?} */ const reader = new FileReader(); if (this.images.length >= MAX_IMAGE_COUNT) { return Promise.reject(CommonImageError.TooBig); } return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { reader.onload = (/** * @return {?} */ () => tslib_1.__awaiter(this, void 0, void 0, function* () { try { /** @type {?} */ let image = yield this.createImage(reader.result); if (image.size > MAX_IMAGE_SIZE_BYTES) { image = yield this.scaleImage(image); } resolve(image); } catch (_) { reject(CommonImageError.CannotOpen); } })); reader.onerror = (/** * @return {?} */ () => { reject(CommonImageError.CannotOpen); }); reader.readAsDataURL(file); })); } /** * @private * @param {?} image * @return {?} */ scaleImage(image) { return tslib_1.__awaiter(this, void 0, void 0, function* () { return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { // We create an image to receive the Data URI /** @type {?} */ const img = document.createElement('img'); // When the event "onload" is triggered we can resize the image. img.onload = (/** * @return {?} */ () => { // We create a canvas and get its context. /** @type {?} */ const canvas = document.createElement('canvas'); /** @type {?} */ const ctx = canvas.getContext('2d'); /** @type {?} */ const targetWidth = Math.floor(img.width * IMAGE_REDUCTION_SCALE_FACTOR); /** @type {?} */ const targetHeight = Math.floor(img.height * IMAGE_REDUCTION_SCALE_FACTOR); // We set the dimensions at the wanted size. canvas.width = targetWidth; canvas.height = targetHeight; // We resize the image with the canvas method drawImage(); ctx.drawImage(img, 0, 0, targetWidth, targetHeight); canvas.toBlob((/** * @param {?} blob * @return {?} */ (blob) => { /** @type {?} */ const reader = new FileReader(); reader.onload = (/** * @param {?} event * @return {?} */ (event) => tslib_1.__awaiter(this, void 0, void 0, function* () { /** @type {?} */ const scaledImage = image.clone(); scaledImage.fileContent = event.target.result; scaledImage.size = blob.size; scaledImage.naturalWidth = targetWidth; scaledImage.naturalHeight = targetHeight; scaledImage.calculateSize(); if (scaledImage.size > MAX_IMAGE_SIZE_BYTES) { resolve(yield this.scaleImage(scaledImage)); } else { resolve(scaledImage); } })); reader.onerror = (/** * @return {?} */ () => { reject(CommonImageError.CannotOpen); }); reader.readAsDataURL(blob); }), IMAGE_CONTENT_TYPE, JPEG_COMPRESSION); }); img.onerror = (/** * @return {?} */ () => { reject(CommonImageError.CannotOpen); }); // We put the Data URI in the image's src attribute img.src = image.fileContent; })); }); } /** * @private * @param {?} imageDataURL * @return {?} */ createImage(imageDataURL) { /** @type {?} */ const image = new CommonImage(imageDataURL); return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { // We create an image to receive the Data URI /** @type {?} */ const img = document.createElement('img'); // When the event "onload" is triggered we can resize the image. img.onload = (/** * @return {?} */ () => { // We create a canvas and get its context. /** @type {?} */ const canvas = document.createElement('canvas'); /** @type {?} */ const ctx = canvas.getContext('2d'); /** @type {?} */ const size = this.getConstrainedSize(img.width, img.height); if (size.width < MIN_IMAGE_WIDTH || size.height < MIN_IMAGE_HEIGHT) { reject(CommonImageError.TooSmall); } /** @type {?} */ const width = size.width; /** @type {?} */ const height = size.height; // We set the dimensions at the wanted size. canvas.width = width; canvas.height = height; // We resize the image with the canvas method drawImage(); ctx.drawImage(img, 0, 0, width, height); canvas.toBlob((/** * @param {?} blob * @return {?} */ (blob) => tslib_1.__awaiter(this, void 0, void 0, function* () { /** @type {?} */ const reader = new FileReader(); if (blob.size < MIN_IMAGE_SIZE_BYTES) { reject(CommonImageError.TooSmall); } reader.onload = (/** * @param {?} event * @return {?} */ (event) => { image.fileContent = event.target.result; image.size = blob.size; image.naturalWidth = width; image.naturalHeight = height; image.calculateSize(); resolve(image); }); reader.onerror = (/** * @return {?} */ () => { reject(CommonImageError.CannotOpen); }); reader.readAsDataURL(blob); })), IMAGE_CONTENT_TYPE, JPEG_COMPRESSION); }); img.onerror = (/** * @return {?} */ () => { reject(CommonImageError.CannotOpen); }); // We put the Data URI in the image's src attribute img.src = image.fileContent; })); } /** * @private * @param {?} fileName * @param {?} images * @return {?} */ addFileImages(fileName, images) { return tslib_1.__awaiter(this, void 0, void 0, function* () { // Create image objects. for (let i = 0; i < images.length; i++) { /** @type {?} */ const fullFileName = `${fileName}${images.length > 1 ? '.page-' + (i + 1) : ''}`; /** @type {?} */ const image = images[i]; image.name = fullFileName; // image.name has been deprecated. image.id = fullFileName; image.contentType = IMAGE_CONTENT_TYPE; } return new Promise((/** * @param {?} resolve * @param {?} reject * @return {?} */ (resolve, reject) => { // Merge new images with existing images. /** @type {?} */ const imagesToAdd = new Array(); images.forEach((/** * @param {?} image * @return {?} */ (image) => { /** @type {?} */ const existingIndex = this.images.findIndex((/** * @param {?} existingImage * @return {?} */ (existingImage) => existingImage.fileContent === image.fileContent)); // If image doesn't already exist, if (existingIndex === -1) { imagesToAdd.push(image); } })); if (imagesToAdd.length === 0) { reject(CommonImageError.AlreadyExists); return; } this.images = [ ...this.images, ...imagesToAdd, ]; this.imagesChange.emit(this.images); resolve(); })); }); } /** * @param {?} image * @return {?} */ deleteImage(image) { this.resetFileInput(); this.images = this.images.filter((/** * @param {?} x * @return {?} */ x => x.uuid !== image.uuid)); this.imagesChange.emit(this.images); // If there are no images yet, we have to reset the input so it triggers 'required'. if (this.required && this.images.length <= 0) { this.fileControl.setErrors({ required: true }); } } /** * @private * @return {?} */ resetFileInput() { this.browseFileRef.nativeElement.value = ''; } /** * This is created as a workaround to access the form control that binds to * the input[type='file']. We can't access it via the template name bindings * as that isn't working, so instead we access the parent form and then find * the input by name. * @return {?} */ get fileControl() { /** @type {?} */ const INPUT_NAME = `fileUploadBrowse-${this.id}`; // note - should be "this.controlContainer as NgForm" here, which works, // but fails on compiliation due to secondary entries return ((/** @type {?} */ (this.controlContainer))).controls[INPUT_NAME]; } /** * @private * @param {?} error * @param {?=} image * @return {?} */ handleError(error, image) { if (!image) { image = new CommonImage(); } // just add the error to mspImage image.error = error; this.errorDocument.emit(image); } /** * @private * @param {?} width * @param {?} height * @return {?} */ getConstrainedSize(width, height) { /** @type {?} */ let targetWidth = width; /** @type {?} */ let targetHeight = height; if (width >= height) { if (width > MAX_IMAGE_WIDTH) { targetWidth = MAX_IMAGE_WIDTH; targetHeight = height * MAX_IMAGE_WIDTH / width; } } else { if (height > MAX_IMAGE_HEIGHT) { targetWidth = width * MAX_IMAGE_HEIGHT / height; targetHeight = MAX_IMAGE_HEIGHT; } } return { width: Math.floor(targetWidth), height: Math.floor(targetHeight) }; } /** * A special method to force the rendering of this component. This is a workaround * because for some unknown reason, AngularJS2 change detector does not detect the * change of the images Array. * @return {?} */ forceRender() { this.zone.run((/** * @return {?} */ () => { })); } /** * @param {?} event * @return {?} */ handleKeyDownFileBrowse(event) { if (event.key === 'Enter') { this.openFileDialog(); } } } FileUploaderComponent.decorators = [ { type: Component, args: [{ selector: 'common-file-uploader', template: "<div class=\"dropzone\"\r\n (dragover)=\"handleDragOver($event)\"\r\n (drop)=\"handleDrop($event)\"\r\n [ngClass]='{\"has-error\": fileControl?.touched && fileControl?.errors?.required}'>\r\n\r\n <div class=\"instruction-zone d-flex align-items-center flex-wrap flex-sm-nowrap flex-column flex-sm-row\">\r\n <i class=\"fa fa-cloud-upload fa-4x d-inline-block upload-icon\"\r\n aria-hidden=\"true\" (click)='openFileDialog()'></i>\r\n\r\n <input type=\"file\"\r\n id=\"fileUploadBrowse-{{id}}\"\r\n #browseFileRef\r\n ngModel\r\n accept=\"image/*,application/pdf\"\r\n style=\"display:none;\"\r\n tabindex=\"0\"\r\n multiple\r\n name='fileUploadBrowse-{{id}}'\r\n [required]='required && images && images.length === 0'\r\n (change)=\"handleChangeFile($event)\"\r\n autocomplete=\"off\"/>\r\n <label for=\"fileUploadBrowse-{{id}}\"\r\n class=\"file-upload-label d-inline-block ml-3\"\r\n tabindex=\"0\"\r\n (keydown)=\"handleKeyDownFileBrowse($event)\">\r\n <span class='h2 color-body'>Select a file</span>\r\n <span class='d-block description'>{{ instructionText }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"fileControl?.touched\"\r\n role=\"alert\"\r\n class='error-container'\r\n aria-live=\"assertive\">\r\n <common-error-container [displayError]=\"fileControl?.errors?.required\">\r\n {{errorMessages.required}}\r\n </common-error-container>\r\n </div>\r\n\r\n <div class=\"preview-zone\">\r\n <div *ngFor=\"let mspImage of images\"\r\n class=\"preview-item\">\r\n <common-thumbnail [imageObject]=\"mspImage\"\r\n (deleteImage)=\"deleteImage($event)\">\r\n </common-thumbnail>\r\n </div>\r\n\r\n <div class=\"common-thumbnail\">\r\n <div class=\"thumbnail-container\">\r\n <div class=\"image-thumbnail demo-thumbnail\"\r\n (click)='openFileDialog()'>\r\n <i class=\"fa fa-plus fa-3x\"></i>\r\n </div>\r\n <div class=\"action-strip text-primary\">\r\n <a href=\"javascript:void(0)\"\r\n title=\"Add\"\r\n (click)=\"openFileDialog()\">Add</a>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n\r\n</div>\r\n", encapsulation: ViewEncapsulation.None, viewProviders: [{ provide: ControlContainer, useExisting: forwardRef((/** * @return {?} */ () => NgForm)) }], styles: ["common-file-uploader .color-body{color:#494949}common-file-uploader .description{font-weight:400}common-file-uploader .dropzone{border:2px dashed #d3d3d3;margin-bottom:10px;border-radius:8px;padding:2em 4em}common-file-uploader .dropzone.has-error{border-color:#a94442}common-file-uploader .dropzone .instruction-zone{margin:10px 5px 5px;text-align:left}common-file-uploader .dropzone .instruction-zone input{display:none}common-file-uploader .dropzone .instruction-zone .file-upload-label{color:#494949}common-file-uploader .dropzone .instruction-zone .file-upload-label:hover{text-decoration:none;cursor:pointer}common-file-uploader .dropzone .preview-zone{display:flex;flex-wrap:wrap;justify-content:left;align-items:left}common-file-uploader .dropzone .preview-zone .preview-item{position:relative;height:120px;text-align:left;margin-left:6rem;margin-bottom:1.5rem}common-file-uploader .dropzone .preview-zone .preview-item .icon-upload{opacity:.3;margin:0 auto 15px}common-file-uploader .dropzone .preview-zone .preview-item .icon-upload:hover{cursor:pointer;opacity:.6}common-file-uploader .dropzone .preview-zone .preview-item .text-upload:hover{cursor:pointer}common-file-uploader .dropzone .preview-zone .error-style{color:red;font-weight:700}common-file-uploader .dropzone .mobileShow{display:none}@media only screen and (min-device-width:320px) and (max-device-width:480px){common-file-uploader .dropzone .mobileShow{display:inline}}.common-thumbnail .thumbnail-container,common-thumbnail .thumbnail-container{transition:.3s;-webkit-transform:translateY(0);transform:translateY(0)}.common-thumbnail .thumbnail-container .image-thumbnail,.common-thumbnail .thumbnail-container .image-thumbnail-width-priority,common-thumbnail .thumbnail-container .image-thumbnail,common-thumbnail .thumbnail-container .image-thumbnail-width-priority{padding:2px 2px 0;border-radius:5px 5px 0 0}.common-thumbnail .thumbnail-container .image-thumbnail-width-priority:hover,.common-thumbnail .thumbnail-container .image-thumbnail:hover,common-thumbnail .thumbnail-container .image-thumbnail-width-priority:hover,common-thumbnail .thumbnail-container .image-thumbnail:hover{cursor:zoom-in}.common-thumbnail .thumbnail-container .demo-thumbnail,common-thumbnail .thumbnail-container .demo-thumbnail{height:100px!important;width:100px;background-color:#dee2e6;display:flex;justify-content:center;align-items:center}.common-thumbnail .thumbnail-container .demo-thumbnail:hover,common-thumbnail .thumbnail-container .demo-thumbnail:hover{cursor:pointer!important}.common-thumbnail .thumbnail-container .image-thumbnail,common-thumbnail .thumbnail-container .image-thumbnail{max-height:100px;height:auto;max-width:100%}.common-thumbnail .thumbnail-container .image-thumbnail-width-priority,common-thumbnail .thumbnail-container .image-thumbnail-width-priority{max-width:270px;width:auto;max-height:100%}.common-thumbnail .thumbnail-container:hover,common-thumbnail .thumbnail-container:hover{box-shadow:0 15px 10px -10px rgba(0,0,0,.1);-webkit-transform:translateY(-5px);transform:translateY(-5px)}.common-thumbnail .thumbnail-container:hover .action-strip,common-thumbnail .thumbnail-container:hover .action-strip{background:#ececec}.common-thumbnail .thumbnail-container .action-strip,common-thumbnail .thumbnail-container .action-strip{height:2em;border-radius:0 0 5px 5px;text-align:right;font-size:small;margin:0 2px;color:#a94442;padding:.3em;transition:.3s}.common-thumbnail .thumbnail-container .action-strip a,common-thumbnail .thumbnail-container .action-strip a{text-decoration:none}"] }] } ]; /** @nocollapse */ FileUploaderComponent.ctorParameters = () => [ { type: NgZone }, { type: ControlContainer } ]; FileUploaderComponent.propDecorators = { browseFileRef: [{ type: ViewChild, args: ['browseFileRef',] }], images: [{ type: Input }], id: [{ type: Input }], showError: [{ type: Input }], required: [{ type: Input }], instructionText: [{ type: Input }], errorMessages: [{ type: Input }], imagesChange: [{ type: Output }], errorDocument: [{ type: Output }] }; if (false) { /** @type {?} */ FileUploaderComponent.prototype.browseFileRef; /** @type {?} */ FileUploaderComponent.prototype.images; /** @type {?} */ FileUploaderComponent.prototype.id; /** @type {?} */ FileUploaderComponent.prototype.showError; /** @type {?} */ FileUploaderComponent.prototype.required; /** @type {?} */ FileUploaderComponent.prototype.instructionText; /** @type {?} */ FileUploaderComponent.prototype.errorMessages; /** @type {?} */ FileUploaderComponent.prototype.imagesChange; /** @type {?} */ FileUploaderComponent.prototype.errorDocument; /** * @type {?} * @private */ FileUploaderComponent.prototype.zone; /** * @type {?} * @private */ FileUploaderComponent.prototype.controlContainer; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWRlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9tb2gtY29tbW9uLWxpYi8iLCJzb3VyY2VzIjpbImxpYi9jb21wb25lbnRzL2ZpbGUtdXBsb2FkZXIvZmlsZS11cGxvYWRlci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQ0gsU0FBUyxFQUNULFVBQVUsRUFDVixZQUFZLEVBQ1osS0FBSyxFQUNMLE1BQU0sRUFDTixNQUFNLEVBQ04sU0FBUyxFQUNULFVBQVUsRUFDVixpQkFBaUIsR0FFcEIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNILE1BQU0sRUFDTixnQkFBZ0IsR0FDbkIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0gsV0FBVyxFQUNYLGdCQUFnQixHQUNuQixNQUFNLDJCQUEyQixDQUFDO0FBQ25DLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN6QyxPQUFPLEtBQUssTUFBTSxNQUFNLDZCQUE2QixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQzs7TUFFakUsS0FBSyxHQUFHLENBQUMsbUJBQUEsTUFBTSxFQUFPLENBQUM7QUFDN0IsS0FBSyxDQUFDLFNBQVMsR0FBRyxXQUFXLENBQUM7Ozs7QUFFOUIscUNBRUM7OztJQURHLG1DQUFpQjs7Ozs7QUFHckIsbUJBR0M7OztJQUZHLHFCQUFjOztJQUNkLHNCQUFlOzs7TUFHYixvQkFBb0IsR0FBVyxPQUFPOztNQUN0QyxlQUFlLEdBQVcsRUFBRTs7TUFDNUIsZUFBZSxHQUFXLElBQUk7O01BQzlCLGdCQUFnQixHQUFXLElBQUk7O01BQy9CLGVBQWUsR0FBVyxHQUFHOztNQUM3QixnQkFBZ0IsR0FBVyxHQUFHOztNQUM5QixvQkFBb0IsR0FBVyxLQUFLOztNQUNwQyxnQkFBZ0IsR0FBVyxHQUFHOztNQUM5QixrQkFBa0IsR0FBRyxZQUFZ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SyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDO1lBQzlCLE1BQU0sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQztTQUNuQyxDQUFBO0lBQ0wsQ0FBQzs7Ozs7OztJQU9ELFdBQVc7UUFDUCxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUc7OztRQUFDLEdBQUcsRUFBRSxHQUFFLENBQUMsRUFBQyxDQUFDO0lBQzVCLENBQUM7Ozs7O0lBRUQsdUJBQXVCLENBQUMsS0FBb0I7UUFDeEMsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLE9BQU8sRUFBRTtZQUN2QixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7U0FDekI7SUFDTCxDQUFDOzs7WUFsYUosU0FBUyxTQUFDO2dCQUNQLFFBQVEsRUFBRSxzQkFBc0I7Z0JBQ2hDLHN1RUFBNkM7Z0JBRTdDLGFBQWEsRUFBRSxpQkFBaUIsQ0FBQyxJQUFJO2dCQUNyQyxhQUFhLEVBQUUsQ0FBRSxFQUFFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsVUFBVTs7O3dCQUFDLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUU7O2FBQzNGOzs7O1lBaERHLE1BQU07WUFTTixnQkFBZ0I7Ozs0QkF5Q2YsU0FBUyxTQUFDLGVBQWU7cUJBRXpCLEtBQUs7aUJBQ0wsS0FBSzt3QkFDTCxLQUFLO3VCQUNMLEtBQUs7OEJBQ0wsS0FBSzs0QkFDTCxLQUFLOzJCQUVMLE1BQU07NEJBQ04sTUFBTTs7OztJQVZQLDhDQUFzRDs7SUFFdEQsdUNBQWdFOztJQUNoRSxtQ0FBb0I7O0lBQ3BCLDBDQUE0Qjs7SUFDNUIseUNBQW1DOztJQUNuQyxnREFBMEU7O0lBQzFFLDhDQUE0RTs7SUFFNUUsNkNBQWtHOztJQUNsRyw4Q0FBcUY7Ozs7O0lBRXpFLHFDQUFvQjs7Ozs7SUFDcEIsaURBQTBDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcclxuICAgIENvbXBvbmVudCxcclxuICAgIEVsZW1lbnRSZWYsXHJcbiAgICBFdmVudEVtaXR0ZXIsXHJcbiAgICBJbnB1dCxcclxuICAgIE5nWm9uZSxcclxuICAgIE91dHB1dCxcclxuICAgIFZpZXdDaGlsZCxcclxuICAgIGZvcndhcmRSZWYsXHJcbiAgICBWaWV3RW5jYXBzdWxhdGlvbixcclxuICAgIE9uQ2hhbmdlcyxcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHtcclxuICAgIE5nRm9ybSxcclxuICAgIENvbnRyb2xDb250YWluZXIsXHJcbn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xyXG5pbXBvcnQge1xyXG4gICAgQ29tbW9uSW1hZ2UsXHJcbiAgICBDb21tb25JbWFnZUVycm9yLFxyXG59IGZyb20gJy4uLy4uL21vZGVscy9pbWFnZXMubW9kZWwnO1xyXG5pbXBvcnQgeyBCYXNlIH0gZnJvbSAnLi4vLi4vbW9kZWxzL2Jhc2UnO1xyXG5pbXBvcnQgKiBhcyBQREZKU18gZnJvbSAncGRmanMtZGlzdC9sZWdhY3kvYnVpbGQvcGRmJztcclxuaW1wb3J0IHsgcGRmSnNXb3JrZXIgfSBmcm9tICdwZGZqcy1kaXN0L2xlZ2FjeS9idWlsZC9wZGYud29ya2VyLmVudHJ5JztcclxuXHJcbmNvbnN0IFBERkpTID0gKFBERkpTXyBhcyBhbnkpO1xyXG5QREZKUy53b3JrZXJTcmMgPSBwZGZKc1dvcmtlcjtcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgRmlsZVVwbG9hZGVyTXNnIHtcclxuICAgIHJlcXVpcmVkOiBzdHJpbmc7XHJcbn1cclxuXHJcbmludGVyZmFjZSBTaXplIHtcclxuICAgIHdpZHRoOiBudW1iZXIsXHJcbiAgICBoZWlnaHQ6IG51bWJlcixcclxufVxyXG5cclxuY29uc3QgTUFYX0lNQUdFX1NJWkVfQllURVM6IG51bWJlciA9IDEwNDg1NzY7XHJcbmNvbnN0IE1BWF9JTUFHRV9DT1VOVDogbnVtYmVyID0gNTA7XHJcbmNvbnN0IE1BWF9JTUFHRV9XSURUSDogbnVtYmVyID0gMzMwMDtcclxuY29uc3QgTUFYX0lNQUdFX0hFSUdIVDogbnVtYmVyID0gMzMwMDtcclxuY29uc3QgTUlOX0lNQUdFX1dJRFRIOiBudW1iZXIgPSAyMDA7XHJcbmNvbnN0IE1JTl9JTUFHRV9IRUlHSFQ6IG51bWJlciA9IDIwMDtcclxuY29uc3QgTUlOX0lNQUdFX1NJWkVfQllURVM6IG51bWJlciA9IDIwMDAwO1xyXG5jb25zdCBKUEVHX0NPTVBSRVNTSU9OOiBudW1iZXIgPSAwLjU7XHJcbmNvbnN0IElNQUdFX0NPTlRFTlRfVFlQRSA9ICdpbWFnZS9qcGVnJztcclxuY29uc3QgSU1BR0VfUkVEVUNUSU9OX1NDQUxFX0ZBQ1RPUjogbnVtYmVyID0gMC44O1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgICBzZWxlY3RvcjogJ2NvbW1vbi1maWxlLXVwbG9hZGVyJyxcclxuICAgIHRlbXBsYXRlVXJsOiAnLi9maWxlLXVwbG9hZGVyLmNvbXBvbmVudC5odG1sJyxcclxuICAgIHN0eWxlVXJsczogWycuL2ZpbGUtdXBsb2FkZXIuY29tcG9uZW50LnNjc3MnXSxcclxuICAgIGVuY2Fwc3VsYXRpb246IFZpZXdFbmNhcHN1bGF0aW9uLk5vbmUsXHJcbiAgICB2aWV3UHJvdmlkZXJzOiBbIHsgcHJvdmlkZTogQ29udHJvbENvbnRhaW5lciwgdXNlRXhpc3Rpbmc6IGZvcndhcmRSZWYoKCkgPT4gTmdGb3JtICkgfSBdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBGaWxlVXBsb2FkZXJDb21wb25lbnQgZXh0ZW5kcyBCYXNlIHtcclxuICAgIEBWaWV3Q2hpbGQoJ2Jyb3dzZUZpbGVSZWYnKSBicm93c2VGaWxlUmVmOiBFbGVtZW50UmVmO1xyXG5cclxuICAgIEBJbnB1dCgpIGltYWdlczogQXJyYXk8Q29tbW9uSW1hZ2U+ID0gbmV3IEFycmF5PENvbW1vbkltYWdlPigwKTtcclxuICAgIEBJbnB1dCgpIGlkOiBzdHJpbmc7XHJcbiAgICBASW5wdXQoKSBzaG93RXJyb3I6IGJvb2xlYW47XHJcbiAgICBASW5wdXQoKSByZXF1aXJlZDogYm9vbGVhbiA9IGZhbHNlO1xyXG4gICAgQElucHV0KCkgaW5zdHJ1Y3Rpb25UZXh0OiBzdHJpbmcgPSAnUGxlYXNlIHVwbG9hZCByZXF1aXJlZCBJRCBkb2N1bWVudHMuJztcclxuICAgIEBJbnB1dCgpIGVycm9yTWVzc2FnZXM6IEZpbGVVcGxvYWRlck1zZyA9IHsgcmVxdWlyZWQ6ICdGaWxlIGl