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
JavaScript
/**
* @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