ngx-word-viewer
Version:
Angular component for viewing Word documents (.docx) with page navigation, zoom.
624 lines (592 loc) • 89 kB
JavaScript
import { Component, Input, ViewChild, Output, EventEmitter, ViewEncapsulation, Inject, PLATFORM_ID } from '@angular/core';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { FormsModule } from '@angular/forms';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@angular/forms";
// Mammoth ko any type se import karo
let mammothConverter;
export class WordViewerComponent {
constructor(platformId) {
this.platformId = platformId;
this.src = null;
this.showToolbar = true;
this.showPageHeader = false;
this.showPageFooter = true;
this.fileName = 'Document';
this.initialZoom = 1;
this.pageHeight = 1000;
this.onDocumentLoad = new EventEmitter();
this.onError = new EventEmitter();
this.pageChange = new EventEmitter();
this.fullContent = '';
this.pages = [];
this.currentPage = 1;
this.totalPages = 0;
this.currentPageContent = '';
this.zoom = 1;
this.isLoading = false;
this.errorMessage = '';
this.pendingSrc = null;
this.A4_WIDTH_PX = 794;
this.A4_HEIGHT_PX = 1123;
this.MAX_IMAGE_WIDTH = 650; // Page content width minus padding
if (isPlatformBrowser(this.platformId)) {
this.initializeMammoth();
}
}
async initializeMammoth() {
if (window.mammoth) {
mammothConverter = window.mammoth;
return;
}
try {
const mammothModule = await import('mammoth');
mammothConverter = mammothModule.default || mammothModule;
}
catch (e) {
console.log('Mammoth not found in node_modules, will load from CDN when needed');
}
}
ngOnChanges(changes) {
if (changes['src'] && this.src) {
this.loadDocument();
}
if (changes['initialZoom']) {
this.zoom = this.initialZoom;
}
}
async loadMammothAndRetry() {
this.errorMessage = '';
this.isLoading = true;
try {
await this.loadMammothFromCDN();
if (this.pendingSrc || this.src) {
await this.loadDocument();
}
}
catch (error) {
this.errorMessage = 'Failed to load converter library from CDN';
this.isLoading = false;
}
}
loadMammothFromCDN() {
return new Promise((resolve, reject) => {
if (window.mammoth) {
mammothConverter = window.mammoth;
resolve();
return;
}
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/mammoth@1.6.0/mammoth.browser.min.js';
script.onload = () => {
mammothConverter = window.mammoth;
resolve();
};
script.onerror = () => reject(new Error('Failed to load mammoth from CDN'));
document.head.appendChild(script);
});
}
async loadDocument() {
if (!isPlatformBrowser(this.platformId)) {
return;
}
this.isLoading = true;
this.errorMessage = '';
this.pages = [];
this.currentPageContent = '';
this.pendingSrc = this.src;
try {
if (!mammothConverter) {
await this.loadMammothFromCDN();
}
if (!mammothConverter) {
throw new Error('Mammoth library could not be loaded. Click retry to load from CDN.');
}
let arrayBuffer;
if (this.src instanceof File) {
this.fileName = this.src.name.replace('.docx', '');
arrayBuffer = await this.fileToArrayBuffer(this.src);
}
else if (this.src instanceof ArrayBuffer) {
arrayBuffer = this.src;
}
else if (typeof this.src === 'string') {
if (this.src.startsWith('data:')) {
arrayBuffer = this.base64ToArrayBuffer(this.src);
}
else {
const urlParts = this.src.split('/');
this.fileName = urlParts[urlParts.length - 1].replace('.docx', '');
arrayBuffer = await this.fetchDocument(this.src);
}
}
else {
throw new Error('Invalid source type');
}
const options = {
arrayBuffer: arrayBuffer,
// Add custom style map for better formatting
styleMap: [
"p[style-name='Title'] => h1:fresh",
"p[style-name='Heading 1'] => h1:fresh",
"p[style-name='Heading 2'] => h2:fresh",
"p[style-name='Heading 3'] => h3:fresh"
],
includeDefaultStyleMap: true
};
// Handle images with better conversion
if (mammothConverter.images && mammothConverter.images.imgElement) {
options.convertImage = mammothConverter.images.imgElement((image) => {
return image.read("base64").then((imageBuffer) => {
// Get image dimensions if available
const width = image.width || 'auto';
const height = image.height || 'auto';
return {
src: "data:" + image.contentType + ";base64," + imageBuffer,
style: `max-width: 100%; width: ${width}px; height: auto;`
};
});
});
}
const result = await mammothConverter.convertToHtml(options);
// Process and fix images in the HTML
this.fullContent = this.processImages(this.enhanceHtmlContent(result.value));
this.processContentIntoPages();
this.onDocumentLoad.emit({
content: this.fullContent,
messages: result.messages,
totalPages: this.totalPages
});
this.pendingSrc = null;
}
catch (error) {
this.errorMessage = error.message || 'Failed to load document';
this.onError.emit(this.errorMessage);
}
finally {
this.isLoading = false;
}
}
processImages(html) {
// Process images to ensure they fit within page bounds
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
// Find all images and set max dimensions
const images = tempDiv.querySelectorAll('img');
images.forEach((img) => {
// Remove any width/height attributes that might cause overflow
img.removeAttribute('width');
img.removeAttribute('height');
// Add style to ensure image fits
const currentStyle = img.getAttribute('style') || '';
img.setAttribute('style', `${currentStyle}; max-width: 100% !important; height: auto !important; display: block; margin: 10px auto;`);
// Add a wrapper div for better control
const wrapper = document.createElement('div');
wrapper.style.cssText = 'text-align: center; margin: 15px 0; page-break-inside: avoid;';
img.parentNode?.insertBefore(wrapper, img);
wrapper.appendChild(img);
});
return tempDiv.innerHTML;
}
enhanceHtmlContent(html) {
let enhancedHtml = html;
// Convert strong paragraphs to headings
enhancedHtml = enhancedHtml.replace(/<p><strong>([^<]{1,100})<\/strong><\/p>/gi, (match, p1) => {
if (p1.length < 100 && (p1 === p1.toUpperCase() || /^[A-Z]/.test(p1))) {
return `<h2>${p1}</h2>`;
}
return match;
});
// Fix table formatting
enhancedHtml = enhancedHtml.replace(/<table>/g, '<table style="width: 100%; margin: 15px 0;">');
return enhancedHtml;
}
processContentIntoPages() {
const tempContainer = document.createElement('div');
tempContainer.style.position = 'absolute';
tempContainer.style.visibility = 'hidden';
tempContainer.style.width = `${this.A4_WIDTH_PX - 120}px`;
tempContainer.style.padding = '40px 60px';
tempContainer.style.fontSize = '12pt';
tempContainer.style.lineHeight = '1.6';
tempContainer.innerHTML = this.fullContent;
document.body.appendChild(tempContainer);
const elements = Array.from(tempContainer.children);
this.pages = [];
let currentPageHtml = '';
let currentHeight = 0;
const effectivePageHeight = this.pageHeight - 100; // Leave some margin
elements.forEach((element) => {
const elementHeight = element.getBoundingClientRect().height;
// Special handling for images
if (element.tagName === 'IMG' || element.querySelector('img')) {
const img = element.tagName === 'IMG' ? element : element.querySelector('img');
if (img) {
// If image is too tall for remaining space, start new page
if (currentHeight + elementHeight > effectivePageHeight && currentPageHtml) {
this.pages.push(currentPageHtml);
currentPageHtml = element.outerHTML;
currentHeight = elementHeight;
}
else {
currentPageHtml += element.outerHTML;
currentHeight += elementHeight;
}
}
}
else {
// Regular content
if (currentHeight + elementHeight > effectivePageHeight && currentPageHtml) {
this.pages.push(currentPageHtml);
currentPageHtml = element.outerHTML;
currentHeight = elementHeight;
}
else {
currentPageHtml += element.outerHTML;
currentHeight += elementHeight;
}
}
});
if (currentPageHtml) {
this.pages.push(currentPageHtml);
}
document.body.removeChild(tempContainer);
if (this.pages.length === 0) {
this.pages = [this.fullContent || '<p>Empty document</p>'];
}
this.totalPages = this.pages.length;
this.currentPage = 1;
this.displayPage(1);
}
displayPage(pageNumber) {
if (pageNumber >= 1 && pageNumber <= this.totalPages) {
this.currentPage = pageNumber;
this.currentPageContent = this.pages[pageNumber - 1];
this.pageChange.emit(this.currentPage);
}
}
goToPage(event) {
const pageNumber = parseInt(event.target.value, 10);
if (!isNaN(pageNumber) && pageNumber >= 1 && pageNumber <= this.totalPages) {
this.displayPage(pageNumber);
}
}
nextPage() {
if (this.currentPage < this.totalPages) {
this.displayPage(this.currentPage + 1);
}
}
previousPage() {
if (this.currentPage > 1) {
this.displayPage(this.currentPage - 1);
}
}
onZoomChange() { }
zoomIn() {
this.zoom = Math.min(this.zoom + 0.25, 3);
}
zoomOut() {
this.zoom = Math.max(this.zoom - 0.25, 0.5);
}
fitToWidth() {
const viewerArea = document.querySelector('.document-viewer-area');
if (viewerArea) {
const viewerWidth = viewerArea.clientWidth - 40;
this.zoom = viewerWidth / this.A4_WIDTH_PX;
this.zoom = Math.min(Math.max(this.zoom, 0.5), 2);
}
}
print() {
const printWindow = window.open('', '_blank');
if (printWindow) {
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>${this.fileName}</title>
<style>
@page { size: A4; margin: 20mm; }
body { font-family: Arial, sans-serif; font-size: 12pt; line-height: 1.6; }
img { max-width: 100% !important; height: auto !important; page-break-inside: avoid; }
.page { page-break-after: always; }
</style>
</head>
<body>
${this.pages.map(page => `<div class="page">${page}</div>`).join('')}
</body>
</html>
`);
printWindow.document.close();
setTimeout(() => printWindow.print(), 250);
}
}
download() {
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${this.fileName}</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
img { max-width: 100%; height: auto; display: block; margin: 15px auto; }
table { width: 100%; border-collapse: collapse; margin: 15px 0; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>${this.fullContent}</body>
</html>
`;
const blob = new Blob([htmlContent], { type: 'text/html' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${this.fileName}.html`;
link.click();
window.URL.revokeObjectURL(url);
}
fileToArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target?.result);
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}
base64ToArrayBuffer(base64) {
const base64Content = base64.split(',')[1];
const binaryString = window.atob(base64Content);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
async fetchDocument(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch document: ${response.statusText}`);
}
return await response.arrayBuffer();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WordViewerComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: WordViewerComponent, isStandalone: true, selector: "ngx-word-viewer", inputs: { src: "src", showToolbar: "showToolbar", showPageHeader: "showPageHeader", showPageFooter: "showPageFooter", fileName: "fileName", initialZoom: "initialZoom", pageHeight: "pageHeight" }, outputs: { onDocumentLoad: "onDocumentLoad", onError: "onError", pageChange: "pageChange" }, viewQueries: [{ propertyName: "documentContainer", first: true, predicate: ["documentContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
<div class="word-viewer-container">
<!-- Toolbar -->
<div class="word-viewer-toolbar" *ngIf="showToolbar">
<button (click)="previousPage()" [disabled]="currentPage === 1" class="toolbar-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
</svg>
Previous
</button>
<div class="page-navigation">
<input
type="number"
[value]="currentPage"
(change)="goToPage($event)"
[min]="1"
[max]="totalPages"
class="page-input"
>
<span class="page-info">/ {{ totalPages }}</span>
</div>
<button (click)="nextPage()" [disabled]="currentPage === totalPages" class="toolbar-btn">
Next
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
</svg>
</button>
<div class="separator"></div>
<button (click)="zoomOut()" class="toolbar-btn zoom-btn" title="Zoom Out">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M19 13H5v-2h14v2z"/>
</svg>
</button>
<select [(ngModel)]="zoom" (change)="onZoomChange()" class="zoom-select">
<option [value]="0.5">50%</option>
<option [value]="0.75">75%</option>
<option [value]="1">100%</option>
<option [value]="1.25">125%</option>
<option [value]="1.5">150%</option>
<option [value]="2">200%</option>
</select>
<button (click)="zoomIn()" class="toolbar-btn zoom-btn" title="Zoom In">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</svg>
</button>
<button (click)="fitToWidth()" class="toolbar-btn" title="Fit to Width">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M4 5v14h16V5H4zm14 12H6V7h12v10z"/>
</svg>
Fit Width
</button>
</div>
<!-- Loading Spinner -->
<div *ngIf="isLoading" class="loading-container">
<div class="loading-spinner">
<div class="spinner"></div>
<p>Loading document...</p>
</div>
</div>
<!-- Error Message -->
<div *ngIf="errorMessage && !isLoading" class="error-container">
<div class="error-message">
<svg width="48" height="48" viewBox="0 0 24 24" fill="#dc3545">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
</svg>
<h3>Error Loading Document</h3>
<p>{{ errorMessage }}</p>
<button *ngIf="errorMessage.includes('Mammoth')" (click)="loadMammothAndRetry()" class="retry-btn">
Load from CDN and Retry
</button>
</div>
</div>
<!-- Document Viewer Area -->
<div class="document-viewer-area" *ngIf="!isLoading && !errorMessage">
<div class="pages-container" [style.transform]="'scale(' + zoom + ')'">
<!-- A4 Page -->
<div class="a4-page" *ngIf="currentPageContent">
<div class="page-header" *ngIf="showPageHeader">
<span>{{ fileName }}</span>
<span>Page {{ currentPage }} of {{ totalPages }}</span>
</div>
<div
class="page-content"
[innerHTML]="currentPageContent">
</div>
<div class="page-footer" *ngIf="showPageFooter">
<span>{{ currentPage }}</span>
</div>
</div>
</div>
</div>
</div>
`, isInline: true, styles: ["*{box-sizing:border-box}.word-viewer-container{width:100%;height:100%;display:flex;flex-direction:column;background:#e5e5e5;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;position:relative}.word-viewer-toolbar{display:flex;align-items:center;gap:8px;padding:8px 16px;background:#2c3e50;border-bottom:1px solid #1a252f;box-shadow:0 2px 4px #0000001a;z-index:100}.toolbar-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;background:transparent;color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s;font-size:13px;font-weight:500}.toolbar-btn:hover:not(:disabled){background:#ffffff1a;border-color:#ffffff4d}.toolbar-btn:disabled{opacity:.5;cursor:not-allowed}.zoom-btn{padding:6px 8px}.page-navigation{display:flex;align-items:center;gap:8px;padding:0 12px}.page-input{width:50px;padding:4px 8px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#ffffff1a;color:#fff;text-align:center;font-size:13px}.page-info{color:#fffc;font-size:13px}.zoom-select{padding:4px 8px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#ffffff1a;color:#fff;font-size:13px;cursor:pointer}.zoom-select option{background:#2c3e50}.separator{width:1px;height:24px;background:#fff3;margin:0 8px}.loading-container{flex:1;display:flex;align-items:center;justify-content:center}.loading-spinner{display:flex;flex-direction:column;align-items:center;gap:16px}.spinner{border:3px solid #f3f3f3;border-top:3px solid #2c3e50;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.error-container{flex:1;display:flex;align-items:center;justify-content:center;padding:20px}.error-message{text-align:center;padding:40px;background:#fff;border-radius:8px;box-shadow:0 2px 10px #0000001a;max-width:400px}.error-message h3{color:#dc3545;margin:16px 0 8px}.retry-btn{margin-top:20px;padding:10px 20px;background:#007bff;color:#fff;border:none;border-radius:4px;cursor:pointer}.retry-btn:hover{background:#0056b3}.document-viewer-area{flex:1;overflow:auto;display:flex;justify-content:center;padding:20px;background:#e5e5e5}.pages-container{transform-origin:top center;transition:transform .2s ease}.a4-page{width:794px;min-height:1123px;background:#fff;box-shadow:0 0 20px #0000001a;margin:0 auto;position:relative;display:flex;flex-direction:column}.page-header{display:flex;justify-content:space-between;padding:20px 40px 10px;font-size:11px;color:#666;border-bottom:1px solid #e0e0e0}.page-content{flex:1;padding:40px 60px;font-size:12pt;line-height:1.6;color:#333;overflow:hidden;word-wrap:break-word;overflow-wrap:break-word}.page-footer{padding:10px 40px 20px;text-align:center;font-size:11px;color:#666;border-top:1px solid #e0e0e0}.page-content h1{font-size:24pt;margin:0 0 12pt;color:#2c3e50;font-weight:700;page-break-after:avoid}.page-content h2{font-size:18pt;margin:12pt 0 10pt;color:#34495e;font-weight:700;page-break-after:avoid}.page-content h3{font-size:14pt;margin:10pt 0 8pt;color:#34495e;font-weight:700;page-break-after:avoid}.page-content p{margin:0 0 10pt;text-align:justify;page-break-inside:avoid}.page-content ul,.page-content ol{margin:10pt 0;padding-left:30pt;page-break-inside:avoid}.page-content li{margin:5pt 0}.page-content table{border-collapse:collapse;width:100%;margin:12pt 0;page-break-inside:avoid}.page-content th,.page-content td{border:1px solid #ddd;padding:8pt;text-align:left}.page-content th{background-color:#f8f9fa;font-weight:700}.page-content img{max-width:100%!important;width:auto!important;height:auto!important;max-height:600px!important;display:block;margin:12pt auto;object-fit:contain;page-break-inside:avoid;box-shadow:0 2px 8px #0000001a;border-radius:4px}.page-content p img{display:inline-block;vertical-align:middle;margin:4pt;max-height:400px!important}.page-content>*{max-width:100%;overflow:hidden}.page-content blockquote{border-left:4px solid #ddd;padding-left:16pt;margin:12pt 0;color:#666;font-style:italic;page-break-inside:avoid}@media (max-width: 900px){.a4-page{width:100%;min-height:auto}.page-content{padding:20px}.page-content img{max-width:100%!important;max-height:400px!important}}@media print{.word-viewer-toolbar{display:none!important}.document-viewer-area{padding:0;background:#fff}.a4-page{box-shadow:none;page-break-after:always;margin:0}.page-header,.page-footer{display:none}.page-content img{max-width:100%!important;page-break-inside:avoid!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WordViewerComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-word-viewer', standalone: true, imports: [CommonModule, FormsModule], template: `
<div class="word-viewer-container">
<!-- Toolbar -->
<div class="word-viewer-toolbar" *ngIf="showToolbar">
<button (click)="previousPage()" [disabled]="currentPage === 1" class="toolbar-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
</svg>
Previous
</button>
<div class="page-navigation">
<input
type="number"
[value]="currentPage"
(change)="goToPage($event)"
[min]="1"
[max]="totalPages"
class="page-input"
>
<span class="page-info">/ {{ totalPages }}</span>
</div>
<button (click)="nextPage()" [disabled]="currentPage === totalPages" class="toolbar-btn">
Next
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
</svg>
</button>
<div class="separator"></div>
<button (click)="zoomOut()" class="toolbar-btn zoom-btn" title="Zoom Out">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M19 13H5v-2h14v2z"/>
</svg>
</button>
<select [(ngModel)]="zoom" (change)="onZoomChange()" class="zoom-select">
<option [value]="0.5">50%</option>
<option [value]="0.75">75%</option>
<option [value]="1">100%</option>
<option [value]="1.25">125%</option>
<option [value]="1.5">150%</option>
<option [value]="2">200%</option>
</select>
<button (click)="zoomIn()" class="toolbar-btn zoom-btn" title="Zoom In">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</svg>
</button>
<button (click)="fitToWidth()" class="toolbar-btn" title="Fit to Width">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M4 5v14h16V5H4zm14 12H6V7h12v10z"/>
</svg>
Fit Width
</button>
</div>
<!-- Loading Spinner -->
<div *ngIf="isLoading" class="loading-container">
<div class="loading-spinner">
<div class="spinner"></div>
<p>Loading document...</p>
</div>
</div>
<!-- Error Message -->
<div *ngIf="errorMessage && !isLoading" class="error-container">
<div class="error-message">
<svg width="48" height="48" viewBox="0 0 24 24" fill="#dc3545">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
</svg>
<h3>Error Loading Document</h3>
<p>{{ errorMessage }}</p>
<button *ngIf="errorMessage.includes('Mammoth')" (click)="loadMammothAndRetry()" class="retry-btn">
Load from CDN and Retry
</button>
</div>
</div>
<!-- Document Viewer Area -->
<div class="document-viewer-area" *ngIf="!isLoading && !errorMessage">
<div class="pages-container" [style.transform]="'scale(' + zoom + ')'">
<!-- A4 Page -->
<div class="a4-page" *ngIf="currentPageContent">
<div class="page-header" *ngIf="showPageHeader">
<span>{{ fileName }}</span>
<span>Page {{ currentPage }} of {{ totalPages }}</span>
</div>
<div
class="page-content"
[innerHTML]="currentPageContent">
</div>
<div class="page-footer" *ngIf="showPageFooter">
<span>{{ currentPage }}</span>
</div>
</div>
</div>
</div>
</div>
`, encapsulation: ViewEncapsulation.None, styles: ["*{box-sizing:border-box}.word-viewer-container{width:100%;height:100%;display:flex;flex-direction:column;background:#e5e5e5;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;position:relative}.word-viewer-toolbar{display:flex;align-items:center;gap:8px;padding:8px 16px;background:#2c3e50;border-bottom:1px solid #1a252f;box-shadow:0 2px 4px #0000001a;z-index:100}.toolbar-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;background:transparent;color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s;font-size:13px;font-weight:500}.toolbar-btn:hover:not(:disabled){background:#ffffff1a;border-color:#ffffff4d}.toolbar-btn:disabled{opacity:.5;cursor:not-allowed}.zoom-btn{padding:6px 8px}.page-navigation{display:flex;align-items:center;gap:8px;padding:0 12px}.page-input{width:50px;padding:4px 8px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#ffffff1a;color:#fff;text-align:center;font-size:13px}.page-info{color:#fffc;font-size:13px}.zoom-select{padding:4px 8px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#ffffff1a;color:#fff;font-size:13px;cursor:pointer}.zoom-select option{background:#2c3e50}.separator{width:1px;height:24px;background:#fff3;margin:0 8px}.loading-container{flex:1;display:flex;align-items:center;justify-content:center}.loading-spinner{display:flex;flex-direction:column;align-items:center;gap:16px}.spinner{border:3px solid #f3f3f3;border-top:3px solid #2c3e50;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.error-container{flex:1;display:flex;align-items:center;justify-content:center;padding:20px}.error-message{text-align:center;padding:40px;background:#fff;border-radius:8px;box-shadow:0 2px 10px #0000001a;max-width:400px}.error-message h3{color:#dc3545;margin:16px 0 8px}.retry-btn{margin-top:20px;padding:10px 20px;background:#007bff;color:#fff;border:none;border-radius:4px;cursor:pointer}.retry-btn:hover{background:#0056b3}.document-viewer-area{flex:1;overflow:auto;display:flex;justify-content:center;padding:20px;background:#e5e5e5}.pages-container{transform-origin:top center;transition:transform .2s ease}.a4-page{width:794px;min-height:1123px;background:#fff;box-shadow:0 0 20px #0000001a;margin:0 auto;position:relative;display:flex;flex-direction:column}.page-header{display:flex;justify-content:space-between;padding:20px 40px 10px;font-size:11px;color:#666;border-bottom:1px solid #e0e0e0}.page-content{flex:1;padding:40px 60px;font-size:12pt;line-height:1.6;color:#333;overflow:hidden;word-wrap:break-word;overflow-wrap:break-word}.page-footer{padding:10px 40px 20px;text-align:center;font-size:11px;color:#666;border-top:1px solid #e0e0e0}.page-content h1{font-size:24pt;margin:0 0 12pt;color:#2c3e50;font-weight:700;page-break-after:avoid}.page-content h2{font-size:18pt;margin:12pt 0 10pt;color:#34495e;font-weight:700;page-break-after:avoid}.page-content h3{font-size:14pt;margin:10pt 0 8pt;color:#34495e;font-weight:700;page-break-after:avoid}.page-content p{margin:0 0 10pt;text-align:justify;page-break-inside:avoid}.page-content ul,.page-content ol{margin:10pt 0;padding-left:30pt;page-break-inside:avoid}.page-content li{margin:5pt 0}.page-content table{border-collapse:collapse;width:100%;margin:12pt 0;page-break-inside:avoid}.page-content th,.page-content td{border:1px solid #ddd;padding:8pt;text-align:left}.page-content th{background-color:#f8f9fa;font-weight:700}.page-content img{max-width:100%!important;width:auto!important;height:auto!important;max-height:600px!important;display:block;margin:12pt auto;object-fit:contain;page-break-inside:avoid;box-shadow:0 2px 8px #0000001a;border-radius:4px}.page-content p img{display:inline-block;vertical-align:middle;margin:4pt;max-height:400px!important}.page-content>*{max-width:100%;overflow:hidden}.page-content blockquote{border-left:4px solid #ddd;padding-left:16pt;margin:12pt 0;color:#666;font-style:italic;page-break-inside:avoid}@media (max-width: 900px){.a4-page{width:100%;min-height:auto}.page-content{padding:20px}.page-content img{max-width:100%!important;max-height:400px!important}}@media print{.word-viewer-toolbar{display:none!important}.document-viewer-area{padding:0;background:#fff}.a4-page{box-shadow:none;page-break-after:always;margin:0}.page-header,.page-footer{display:none}.page-content img{max-width:100%!important;page-break-inside:avoid!important}}\n"] }]
}], ctorParameters: () => [{ type: Object, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }], propDecorators: { documentContainer: [{
type: ViewChild,
args: ['documentContainer']
}], src: [{
type: Input
}], showToolbar: [{
type: Input
}], showPageHeader: [{
type: Input
}], showPageFooter: [{
type: Input
}], fileName: [{
type: Input
}], initialZoom: [{
type: Input
}], pageHeight: [{
type: Input
}], onDocumentLoad: [{
type: Output
}], onError: [{
type: Output
}], pageChange: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29yZC12aWV3ZXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXdvcmQtdmlld2VyL3NyYy9saWIvd29yZC12aWV3ZXIvd29yZC12aWV3ZXIuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUE0QixTQUFTLEVBQWMsTUFBTSxFQUFFLFlBQVksRUFBRSxpQkFBaUIsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hLLE9BQU8sRUFBRSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7QUFFN0MscUNBQXFDO0FBQ3JDLElBQUksZ0JBQXFCLENBQUM7QUF3ZDFCLE1BQU0sT0FBTyxtQkFBbUI7SUE2QjlCLFlBQXlDLFVBQWtCO1FBQWxCLGVBQVUsR0FBVixVQUFVLENBQVE7UUExQmxELFFBQUcsR0FBdUMsSUFBSSxDQUFDO1FBQy9DLGdCQUFXLEdBQVksSUFBSSxDQUFDO1FBQzVCLG1CQUFjLEdBQVksS0FBSyxDQUFDO1FBQ2hDLG1CQUFjLEdBQVksSUFBSSxDQUFDO1FBQy9CLGFBQVEsR0FBVyxVQUFVLENBQUM7UUFDOUIsZ0JBQVcsR0FBVyxDQUFDLENBQUM7UUFDeEIsZUFBVSxHQUFXLElBQUksQ0FBQztRQUV6QixtQkFBYyxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7UUFDekMsWUFBTyxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFDckMsZUFBVSxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFFbEQsZ0JBQVcsR0FBVyxFQUFFLENBQUM7UUFDekIsVUFBSyxHQUFhLEVBQUUsQ0FBQztRQUNyQixnQkFBVyxHQUFXLENBQUMsQ0FBQztRQUN4QixlQUFVLEdBQVcsQ0FBQyxDQUFDO1FBQ3ZCLHVCQUFrQixHQUFXLEVBQUUsQ0FBQztRQUNoQyxTQUFJLEdBQVcsQ0FBQyxDQUFDO1FBQ2pCLGNBQVMsR0FBWSxLQUFLLENBQUM7UUFDM0IsaUJBQVksR0FBVyxFQUFFLENBQUM7UUFDbEIsZUFBVSxHQUFRLElBQUksQ0FBQztRQUVkLGdCQUFXLEdBQUcsR0FBRyxDQUFDO1FBQ2xCLGlCQUFZLEdBQUcsSUFBSSxDQUFDO1FBQ3BCLG9CQUFlLEdBQUcsR0FBRyxDQUFDLENBQUMsbUNBQW1DO1FBR3pFLElBQUksaUJBQWlCLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFDdkMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDM0IsQ0FBQztJQUNILENBQUM7SUFFTyxLQUFLLENBQUMsaUJBQWlCO1FBQzdCLElBQUssTUFBYyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQzVCLGdCQUFnQixHQUFJLE1BQWMsQ0FBQyxPQUFPLENBQUM7WUFDM0MsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCxNQUFNLGFBQWEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxTQUFnQixDQUFDLENBQUM7WUFDckQsZ0JBQWdCLEdBQUcsYUFBYSxDQUFDLE9BQU8sSUFBSSxhQUFhLENBQUM7UUFDNUQsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCxPQUFPLENBQUMsR0FBRyxDQUFDLG1FQUFtRSxDQUFDLENBQUM7UUFDbkYsQ0FBQztJQUNILENBQUM7SUFFRCxXQUFXLENBQUMsT0FBc0I7UUFDaEMsSUFBSSxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQy9CLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUN0QixDQUFDO1FBQ0QsSUFBSSxPQUFPLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQztZQUMzQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDL0IsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsbUJBQW1CO1FBQ3ZCLElBQUksQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBRXRCLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEMsSUFBSSxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDaEMsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDNUIsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLFlBQVksR0FBRywyQ0FBMkMsQ0FBQztZQUNoRSxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztRQUN6QixDQUFDO0lBQ0gsQ0FBQztJQUVPLGtCQUFrQjtRQUN4QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3JDLElBQUssTUFBYyxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUM1QixnQkFBZ0IsR0FBSSxNQUFjLENBQUMsT0FBTyxDQUFDO2dCQUMzQyxPQUFPLEVBQUUsQ0FBQztnQkFDVixPQUFPO1lBQ1QsQ0FBQztZQUVELE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDaEQsTUFBTSxDQUFDLEdBQUcsR0FBRyxtRUFBbUUsQ0FBQztZQUNqRixNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtnQkFDbkIsZ0JBQWdCLEdBQUksTUFBYyxDQUFDLE9BQU8sQ0FBQztnQkFDM0MsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDLENBQUM7WUFDRixNQUFNLENBQUMsT0FBTyxHQUFHLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDLENBQUM7WUFDNUUsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDcEMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVk7UUFDaEIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ3hDLE9BQU87UUFDVCxDQUFDO1FBRUQsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFDdEIsSUFBSSxDQUFDLFlBQVksR0FBRyxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDaEIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLEVBQUUsQ0FBQztRQUM3QixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7UUFFM0IsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7Z0JBQ3RCLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDbEMsQ0FBQztZQUVELElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO2dCQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLG9FQUFvRSxDQUFDLENBQUM7WUFDeEYsQ0FBQztZQUVELElBQUksV0FBd0IsQ0FBQztZQUU3QixJQUFJLElBQUksQ0FBQyxHQUFHLFlBQVksSUFBSSxFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDbkQsV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN2RCxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLEdBQUcsWUFBWSxXQUFXLEVBQUUsQ0FBQztnQkFDM0MsV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7WUFDekIsQ0FBQztpQkFBTSxJQUFJLE9BQU8sSUFBSSxDQUFDLEdBQUcsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDeEMsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO29CQUNqQyxXQUFXLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbkQsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNyQyxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQ25FLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNuRCxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQztZQUN6QyxDQUFDO1lBRUQsTUFBTSxPQUFPLEdBQVE7Z0JBQ25CLFdBQVcsRUFBRSxXQUFXO2dCQUN4Qiw2Q0FBNkM7Z0JBQzdDLFFBQVEsRUFBRTtvQkFDUixtQ0FBbUM7b0JBQ25DLHVDQUF1QztvQkFDdkMsdUNBQXVDO29CQUN2Qyx1Q0FBdUM7aUJBQ3hDO2dCQUNELHNCQUFzQixFQUFFLElBQUk7YUFDN0IsQ0FBQztZQUVGLHVDQUF1QztZQUN2QyxJQUFJLGdCQUFnQixDQUFDLE1BQU0sSUFBSSxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQ2xFLE9BQU8sQ0FBQyxZQUFZLEdBQUcsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLEtBQVUsRUFBRSxFQUFFO29CQUN2RSxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsV0FBbUIsRUFBRSxFQUFFO3dCQUN2RCxvQ0FBb0M7d0JBQ3BDLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLElBQUksTUFBTSxDQUFDO3dCQUNwQyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxJQUFJLE1BQU0sQ0FBQzt3QkFFdEMsT0FBTzs0QkFDTCxHQUFHLEVBQUUsT0FBTyxHQUFHLEtBQUssQ0FBQyxXQUFXLEdBQUcsVUFBVSxHQUFHLFdBQVc7NEJBQzNELEtBQUssRUFBRSwyQkFBMkIsS0FBSyxtQkFBbUI7eUJBQzNELENBQUM7b0JBQ0osQ0FBQyxDQUFDLENBQUM7Z0JBQ0wsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDO1lBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFN0QscUNBQXFDO1lBQ3JDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDN0UsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7WUFDL0IsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUM7Z0JBQ3ZCLE9BQU8sRUFBRSxJQUFJLENBQUMsV0FBVztnQkFDekIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO2dCQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7YUFDNUIsQ0FBQyxDQUFDO1lBRUgsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFFekIsQ0FBQztRQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7WUFDcEIsSUFBSSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUMsT0FBTyxJQUFJLHlCQUF5QixDQUFDO1lBQy9ELElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUN2QyxDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztRQUN6QixDQUFDO0lBQ0gsQ0FBQztJQUVPLGFBQWEsQ0FBQyxJQUFZO1FBQ2hDLHVEQUF1RDtRQUN2RCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzlDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBRXpCLHlDQUF5QztRQUN6QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDL0MsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQXFCLEVBQUUsRUFBRTtZQUN2QywrREFBK0Q7WUFDL0QsR0FBRyxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM3QixHQUFHLENBQUMsZUFBZSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBRTlCLGlDQUFpQztZQUNqQyxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNyRCxHQUFHLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxHQUFHLFlBQVksMkZBQTJGLENBQUMsQ0FBQztZQUV0SSx1Q0FBdUM7WUFDdkMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM5QyxPQUFPLENBQUMsS0FBSyxDQUFDLE9BQU8sR0FBRywrREFBK0QsQ0FBQztZQUN4RixHQUFHLENBQUMsVUFBVSxFQUFFLFlBQVksQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDM0MsT0FBTyxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMzQixDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sT0FBTyxDQUFDLFNBQVMsQ0FBQztJQUMzQixDQUFDO0lBRU8sa0JBQWtCLENBQUMsSUFBWTtRQUNyQyxJQUFJLFlBQVksR0FBRyxJQUFJLENBQUM7UUFFeEIsd0NBQXdDO1FBQ3hDLFlBQVksR0FBRyxZQUFZLENBQUMsT0FBTyxDQUNqQywyQ0FBMkMsRUFDM0MsQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7WUFDWixJQUFJLEVBQUUsQ0FBQyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxXQUFXLEVBQUUsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDdEUsT0FBTyxPQUFPLEVBQUUsT0FBTyxDQUFDO1lBQzFCLENBQUM7WUFDRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUMsQ0FDRixDQUFDO1FBRUYsdUJBQXVCO1FBQ3ZCLFlBQVksR0FBRyxZQUFZLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSw4Q0FBOEMsQ0FBQyxDQUFDO1FBRWhHLE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFTyx1QkFBdUI7UUFDN0IsTUFBTSxhQUFhLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwRCxhQUFhLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUM7UUFDMUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsUUFBUSxDQUFDO1FBQzFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLEdBQUcsSUFBSSxDQUFDLFdBQVcsR0FBRyxHQUFHLElBQUksQ0FBQztRQUMxRCxhQUFhLENBQUMsS0FBSyxDQUFDLE9BQU8sR0FBRyxXQUFXLENBQUM7UUFDMUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO1FBQ3RDLGFBQWEsQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQztRQUN2QyxhQUFhLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDM0MsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFekMsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDaEIsSUFBSSxlQUFlLEdBQUcsRUFBRSxDQUFDO1FBQ3pCLElBQUksYUFBYSxHQUFHLENBQUMsQ0FBQztRQUN0QixNQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQyxVQUFVLEdBQUcsR0FBRyxDQUFDLENBQUMsb0JBQW9CO1FBRXZFLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFZLEVBQUUsRUFBRTtZQUNoQyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFFN0QsOEJBQThCO1lBQzlCLElBQUksT0FBTyxDQUFDLE9BQU8sS0FBSyxLQUFLLElBQUksT0FBTyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUM5RCxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsT0FBTyxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUMvRSxJQUFJLEdBQUcsRUFBRSxDQUFDO29CQUNSLDJEQUEyRDtvQkFDM0QsSUFBSSxhQUFhLEdBQUcsYUFBYSxHQUFHLG1CQUFtQixJQUFJLGVBQWUsRUFBRSxDQUFDO3dCQUMzRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQzt3QkFDakMsZUFBZSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUM7d0JBQ3BDLGFBQWEsR0FBRyxhQUFhLENBQUM7b0JBQ2hDLENBQUM7eUJBQU0sQ0FBQzt3QkFDTixlQUFlLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQzt3QkFDckMsYUFBYSxJQUFJLGFBQWEsQ0FBQztvQkFDakMsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGtCQUFrQjtnQkFDbEIsSUFBSSxhQUFhLEdBQUcsYUFBYSxHQUFHLG1CQUFtQixJQUFJLGVBQWUsRUFBRSxDQUFDO29CQUMzRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztvQkFDakMsZUFBZSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUM7b0JBQ3BDLGFBQWEsR0FBRyxhQUFhLENBQUM7Z0JBQ2hDLENBQUM7cUJBQU0sQ0FBQztvQkFDTixlQUFlLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQztvQkFDckMsYUFBYSxJQUFJLGFBQWEsQ0FBQztnQkFDakMsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVILElBQUksZUFBZSxFQUFFLENBQUM7WUFDcEIsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDbkMsQ0FBQztRQUVELFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBRXpDLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLElBQUksdUJBQXVCLENBQUMsQ0FBQztRQUM3RCxDQUFDO1FBRUQsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQztRQUNwQyxJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3RCLENBQUM7SUFFTyxXQUFXLENBQUMsVUFBa0I7UUFDcEMsSUFBSSxVQUFVLElBQUksQ0FBQyxJQUFJLFVBQVUsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDckQsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUM7WUFDOUIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ3JELElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVELFFBQVEsQ0FBQyxLQUFVO1FBQ2pCLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNwRCxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLFVBQVUsSUFBSSxDQUFDLElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUMzRSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsUUFBUTtRQUNOLElBQUksSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDdkMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLENBQUM7SUFDSCxDQUFDO0lBRUQsWUFBWTtRQUNWLElBQUksSUFBSSxDQUFDLFdBQVcsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QixJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDekMsQ0FBQztJQUNILENBQUM7SUFFRCxZQUFZLEtBQVUsQ0FBQztJQUV2QixNQUFNO1FBQ0osSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFRCxPQUFPO1FBQ0wsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRCxVQUFVO1FBQ1IsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO1FBQ25FLElBQUksVUFBVSxFQUFFLENBQUM7WUFDZixNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztZQUNoRCxJQUFJLENBQUMsSUFBSSxHQUFHLFdBQVcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO1lBQzNDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDcEQsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLO1FBQ0gsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDOUMsSUFBSSxXQUFXLEVBQUUsQ0FBQztZQUNoQixXQUFXLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQzs7OztxQkFJWixJQUFJLENBQUMsUUFBUTs7Ozs7Ozs7O2NBU3BCLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMscUJBQXFCLElBQUksUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzs7O09BR3pFLENBQUMsQ0FBQztZQUNILFdBQVcsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDN0IsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUM3QyxDQUFDO0lBQ0gsQ0FBQztJQUVELFFBQVE7UUFDTixNQUFNLFdBQVcsR0FBRzs7Ozs7bUJBS0wsSUFBSSxDQUFDLFFBQVE7Ozs7Ozs7O2dCQVFoQixJQUFJLENBQUMsV0FBVzs7S0FFM0IsQ0FBQztRQUVGLE1BQU0sSUFBSSxHQUFHLElBQUksSUFBSSxDQUFDLENBQUMsV0FBVyxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLENBQUMsQ0FBQztRQUM1RCxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDO1FBQ2hCLElBQUksQ0FBQyxRQUFRLEdBQUcsR0FBRyxJQUFJLENBQUMsUUFBUSxPQUFPLENBQUM7UUFDeEMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUVPLGlCQUFpQixDQUFDLElBQVU7UUFDbEMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUNyQyxNQUFNLE1BQU0sR0FBRyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ2hDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLE1BQXFCLENBQUMsQ0FBQztZQUNoRSxNQUFNLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztZQUN4QixNQUFNLENBQUMsaUJBQWlCLEN