<?php
/**
* Single-file French Quote & Invoice Generator
*
* This script handles two things:
* 1. If accessed via a POST request, it generates a PDF.
* 2. If accessed via a GET request, it displays the HTML interface.
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// --- MODE 1: PDF GENERATION ---
require('lib/fpdf/fpdf.php');
// We need a custom class to create a header and footer
class PDF extends FPDF
{
private $docData;
function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $data = []) {
parent::__construct($orientation, $unit, $size);
$this->docData = $data;
}
$this->SetFont('Arial', 'B', 20);
$this->SetFont('Arial', '', 12);
$this->Cell(0, 7, utf8_decode($this->docData['doc']['number']), 0, 1, 'L'); $this->Ln(15);
$this->SetFont('Arial', 'B', 10);
$this->Cell(95, 7, utf8_decode($this->docData['company']['name']), 0, 0, 'L'); $this->Cell(95, 7, utf8_decode($this->docData['client']['name']), 0, 1, 'R');
$this->SetFont('Arial', '', 10);
$yPos = $this->GetY();
$this->MultiCell(95, 5, utf8_decode($this->docData['company']['address']), 0, 'L'); $this->SetXY(115, $yPos); // Set X to the right column
$this->MultiCell(85, 5, utf8_decode($this->docData['client']['address']), 0, 'L');
// Use GetY from the longest MultiCell to set the next position correctly
$yPosAfterAddress = $this->GetY();
$this->SetY($yPosAfterAddress);
$this->Ln(2);
if(!empty($this->docData['company']['siret'])) $this->Cell(95, 5, utf8_decode('SIRET : ' . $this->docData['company']['siret']), 0, 1, 'L'); if(!empty($this->docData['company']['vat'])) $this->Cell(95, 5, utf8_decode('N° TVA : ' . $this->docData['company']['vat']), 0, 1, 'L');
$this->Ln(10);
$this->SetFont('Arial', '', 10);
$this->Cell(0, 5, utf8_decode('Date d\'émission : ' . date("d/m/Y", strtotime($this->docData['doc']['date']))), 0, 1, 'R'); if (!empty($this->docData['doc']['due_date'])) { $this->Cell(0, 5, utf8_decode('Date d\'échéance : ' . date("d/m/Y", strtotime($this->docData['doc']['due_date']))), 0, 1, 'R'); }
$this->Ln(15);
}
function Footer() {
$this->SetY(-30);
if (!empty($this->docData['notes'])) { $this->SetFont('Arial','',9);
$this->Cell(0, 5, 'Notes :', 0, 1, 'L');
$this->MultiCell(0, 5, utf8_decode($this->docData['notes']), 0, 'L'); }
$this->SetY(-15);
$this->SetFont('Arial','I',8);
$this->Cell(0,10, 'Page '.$this->PageNo().'/{nb}',0,0,'C');
}
}
if ($data === null) {
http_response_code(400);
}
$pdf = new PDF('P', 'mm', 'A4', $data);
$pdf->AliasNbPages();
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 10);
$pdf->SetFillColor(230, 230, 230);
$pdf->Cell(100, 8, 'Description', 1, 0, 'L', true);
$pdf->Cell(20, 8, utf8_decode('Qté'), 1, 0, 'C', true); $pdf->Cell(35, 8, 'P.U. HT', 1, 0, 'C', true);
$pdf->Cell(35, 8, 'Total HT', 1, 1, 'C', true);
$pdf->SetFont('Arial', '', 10);
foreach ($data['items'] as $item) {
$pdf->Cell(100, 8, utf8_decode($item['description']), 1, 0, 'L'); $pdf->Cell(20, 8, $item['quantity'], 1, 0, 'R');
$pdf->Cell(35, 8, number_format((float
)$item['price'], 2, ',', ' ') . ' ' . $currencySymbol, 1, 0, 'R'); $pdf->Cell(35, 8, number_format((float
)$item['total'], 2, ',', ' ') . ' ' . $currencySymbol, 1, 1, 'R'); }
$pdf->Ln(10);
$pdf->SetFont('Arial', '', 10);
$totalsX = 120;
$totalsLabelWidth = 35;
$totalsValueWidth = 45;
$pdf->Cell($totalsX, 8, '', 0, 0);
$pdf->Cell($totalsLabelWidth, 8, 'Total HT', 1, 0, 'L');
$pdf->Cell($totalsValueWidth, 8, number_format((float
)$data['totals']['subtotal'], 2, ',', ' ') . ' ' . $currencySymbol, 1, 1, 'R');
$pdf->Cell($totalsX, 8, '', 0, 0);
$pdf->Cell($totalsLabelWidth, 8, 'TVA (' . $data['totals']['vat_rate'] . '%)', 1, 0, 'L');
$pdf->Cell($totalsValueWidth, 8, number_format((float
)$data['totals']['vat_total'], 2, ',', ' ') . ' ' . $currencySymbol, 1, 1, 'R');
$pdf->SetFont('Arial', 'B', 12);
$pdf->Cell($totalsX, 10, '', 0, 0);
$pdf->Cell($totalsLabelWidth, 10, 'Total TTC', 1, 0, 'L', true);
$pdf->Cell($totalsValueWidth, 10, number_format((float
)$data['totals']['total_ttc'], 2, ',', ' ') . ' ' . $currencySymbol, 1, 1, 'R', true);
$filename = strtoupper($data['doc_type']) . '-' . preg_replace('/[^a-zA-Z0-9-]/', '', $data['doc']['number']) . '.pdf'; $pdf->Output('D', $filename);
// Stop execution to prevent HTML from being sent
}
// --- MODE 2: HTML INTERFACE ---
?>
<!DOCTYPE html>
<html lang="fr" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Générateur de Devis et Factures</title>
<link rel="stylesheet" href="https://c...content-available-to-author-only...r.net/npm/@picocss/pico@1/css/pico.min.css">
<style>
/* Embedded custom CSS */
body {
padding-bottom: 5rem;
}
.table-container {
overflow-x: auto;
}
table th:last-child,
table td:last-child {
text-align: right;
}
table input[type="number"] {
min-width: 80px;
text-align: right;
}
.totals-section {
text-align: right;
padding-top: 1rem;
border-left: 1px solid var(--pico-muted-border-color);
padding-left: 1rem;
}
.totals-section p {
margin-bottom: 0.5rem;
}
.totals-section strong {
margin-right: 1rem;
}
.notes-section {
padding-right: 1rem;
}
.form-actions {
margin-top: 2rem;
display: flex;
justify-content: flex-end;
gap: 1rem;
}
.remove-item {
padding: 0.25rem 0.5rem;
line-height: 1;
}
</style>
</head>
<body>
<main class="container">
<header>
<h1>Générateur de Devis & Factures</h1>
<p>Toutes les données sont sauvegardées localement dans votre navigateur. Aucune information n'est envoyée à un serveur.</p>
</header>
<form id="invoice-form">
<fieldset>
<legend>Type de document</legend>
<label for="doc-type-quote">
<input type="radio" id="doc-type-quote" name="doc_type" value="Devis" checked>
Devis
</label>
<label for="doc-type-invoice">
<input type="radio" id="doc-type-invoice" name="doc_type" value="Facture">
Facture
</label>
</fieldset>
<div class="grid">
<article>
<h3 id="company-title">Votre Entreprise</h3>
<label for="company_name">Nom de l'entreprise</label>
<input type="text" id="company_name" name="company_name" required>
<label for="company_address">Adresse</label>
<textarea id="company_address" name="company_address" rows="3"></textarea>
<div class="grid">
<label for="company_siret">SIRET <input type="text" id="company_siret" name="company_siret"></label>
<label for="company_vat">N° TVA <input type="text" id="company_vat" name="company_vat"></label>
</div>
<button type="button" id="save-company-info" class="secondary">Enregistrer mes informations</button>
</article>
<article>
<h3>Client</h3>
<label for="client_name">Nom du client</label>
<input type="text" id="client_name" name="client_name" required>
<label for="client_address">Adresse du client</label>
<textarea id="client_address" name="client_address" rows="3"></textarea>
</article>
</div>
<article>
<div class="grid">
<label for="doc_number"><span id="doc-type-label">Numéro de Devis</span>
<input type="text" id="doc_number" name="doc_number" required>
</label>
<label for="doc_date">Date
<input type="date" id="doc_date" name="doc_date" required>
</label>
<label for="doc_due_date">Date d'échéance
<input type="date" id="doc_due_date" name="doc_due_date">
</label>
</div>
</article>
<article>
<h3>Lignes de prestation</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Description</th>
<th>Qté</th>
<th>P.U. HT</th>
<th>Total HT</th>
<th></th>
</tr>
</thead>
<tbody id="item-list"></tbody>
</table>
</div>
<button type="button" id="add-item" class="secondary">Ajouter une ligne</button>
</article>
<div class="grid">
<div class="notes-section">
<label for="notes">Notes / Conditions de paiement</label>
<textarea id="notes" name="notes" rows="4">Paiement à réception de la facture.</textarea>
</div>
<article class="totals-section">
<div class="grid">
<label for="vat_rate">Taux de TVA (%)</label>
<input type="number" id="vat_rate" name="vat_rate" value="20" step="0.1" required>
</div>
<p><strong>Total HT :</strong> <span id="subtotal">0.00</span> €</p>
<p><strong>TVA :</strong> <span id="vat-total">0.00</span> €</p>
<p><strong>Total TTC :</strong> <span id="total-ttc">0.00</span> €</p>
</article>
</div>
<footer class="form-actions">
<button type="submit" id="generate-pdf">Générer le PDF</button>
<button type="button" id="reset-form" class="secondary outline">Réinitialiser</button>
</footer>
</form>
</main>
<script>
// --- Embedded JavaScript ---
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('invoice-form');
const itemList = document.getElementById('item-list');
const addItemBtn = document.getElementById('add-item');
const saveCompanyInfoBtn = document.getElementById('save-company-info');
const resetFormBtn = document.getElementById('reset-form');
const subtotalEl = document.getElementById('subtotal');
const vatTotalEl = document.getElementById('vat-total');
const totalTtcEl = document.getElementById('total-ttc');
const docTypeRadios = document.querySelectorAll('input[name="doc_type"]');
const docTypeLabel = document.getElementById('doc-type-label');
const docNumberInput = document.getElementById('doc_number');
const companyTitle = document.getElementById('company-title');
const calculateTotals = () => {
let subtotal = 0;
itemList.querySelectorAll('tr').forEach(row => {
const quantity = parseFloat(row.querySelector('.quantity').value) || 0;
const price = parseFloat(row.querySelector('.price').value) || 0;
const rowTotal = quantity * price;
row.querySelector('.row-total').textContent = rowTotal.toFixed(2);
subtotal += rowTotal;
});
const vatRate = parseFloat(document.getElementById('vat_rate').value) || 0;
const vatTotal = subtotal * (vatRate / 100);
const totalTtc = subtotal + vatTotal;
subtotalEl.textContent = subtotal.toFixed(2);
vatTotalEl.textContent = vatTotal.toFixed(2);
totalTtcEl.textContent = totalTtc.toFixed(2);
};
const addLineItem = () => {
const row = document.createElement('tr');
row.innerHTML = `
<td><input type="text" class="description" placeholder="Description de la prestation"></td>
<td><input type="number" class="quantity" value="1" step="any"></td>
<td><input type="number" class="price" value="0.00" step="any"></td>
<td><span class="row-total">0.00</span> €</td>
<td><button type="button" class="remove-item secondary outline">×</button></td>
`;
itemList.appendChild(row);
row.querySelector('.remove-item').addEventListener('click', () => {
row.remove();
calculateTotals();
});
};
const updateDocType = () => {
const selectedType = document.querySelector('input[name="doc_type"]:checked').value;
docTypeLabel.textContent = `Numéro de ${selectedType}`;
const prefix = selectedType === 'Devis' ? 'DE' : 'FA';
const currentVal = docNumberInput.value;
if (!currentVal.startsWith('DE-') && !currentVal.startsWith('FA-') || currentVal === '') {
const year = new Date().getFullYear();
docNumberInput.value = `${prefix}-${year}-001`;
}
};
const saveCompanyInfo = () => {
const companyInfo = {
name: document.getElementById('company_name').value,
address: document.getElementById('company_address').value,
siret: document.getElementById('company_siret').value,
vat: document.getElementById('company_vat').value,
};
localStorage.setItem('companyInfo', JSON.stringify(companyInfo));
companyTitle.textContent = 'Votre Entreprise (Enregistré)';
setTimeout(() => companyTitle.textContent = 'Votre Entreprise', 2000);
};
const loadCompanyInfo = () => {
const companyInfo = JSON.parse(localStorage.getItem('companyInfo'));
if (companyInfo) {
document.getElementById('company_name').value = companyInfo.name || '';
document.getElementById('company_address').value = companyInfo.address || '';
document.getElementById('company_siret').value = companyInfo.siret || '';
document.getElementById('company_vat').value = companyInfo.vat || '';
}
};
const resetForm = () => {
if(confirm("Voulez-vous vraiment réinitialiser le formulaire ? Les informations de votre entreprise resteront enregistrées.")) {
const companyInfo = JSON.parse(localStorage.getItem('companyInfo'));
form.reset();
localStorage.setItem('companyInfo', JSON.stringify(companyInfo));
loadCompanyInfo();
itemList.innerHTML = '';
addLineItem();
document.getElementById('doc_date').valueAsDate = new Date();
updateDocType();
calculateTotals();
}
}
const generatePDF = async (e) => {
e.preventDefault();
const items = Array.from(itemList.querySelectorAll('tr')).map(row => ({
description: row.querySelector('.description').value,
quantity: row.querySelector('.quantity').value,
price: row.querySelector('.price').value,
total: parseFloat(row.querySelector('.row-total').textContent)
}));
const formData = {
doc_type: document.querySelector('input[name="doc_type"]:checked').value,
company: { name: document.getElementById('company_name').value, address: document.getElementById('company_address').value, siret: document.getElementById('company_siret').value, vat: document.getElementById('company_vat').value },
client: { name: document.getElementById('client_name').value, address: document.getElementById('client_address').value },
doc: { number: document.getElementById('doc_number').value, date: document.getElementById('doc_date').value, due_date: document.getElementById('doc_due_date').value },
items: items,
totals: { subtotal: subtotalEl.textContent, vat_rate: document.getElementById('vat_rate').value, vat_total: vatTotalEl.textContent, total_ttc: totalTtcEl.textContent },
notes: document.getElementById('notes').value
};
const pdfButton = document.getElementById('generate-pdf');
pdfButton.setAttribute('aria-busy', 'true');
pdfButton.textContent = 'Génération...';
try {
const response = await fetch('', { // Post to the same file
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
if (!response.ok) throw new Error(`Erreur du serveur: ${response.statusText}`);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `${formData.doc_type.toUpperCase()}-${formData.doc.number.replace(/[^a-zA-Z0-9-]/g, '')}.pdf`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
a.remove();
} catch (error) {
console.error('Erreur lors de la génération du PDF:', error);
alert('Une erreur est survenue lors de la génération du PDF.');
} finally {
pdfButton.removeAttribute('aria-busy');
pdfButton.textContent = 'Générer le PDF';
}
};
addItemBtn.addEventListener('click', addLineItem);
form.addEventListener('input', calculateTotals);
form.addEventListener('submit', generatePDF);
saveCompanyInfoBtn.addEventListener('click', saveCompanyInfo);
resetFormBtn.addEventListener('click', resetForm);
docTypeRadios.forEach(radio => radio.addEventListener('change', updateDocType));
// --- Initialisation ---
loadCompanyInfo();
addLineItem();
calculateTotals();
document.getElementById('doc_date').valueAsDate = new Date();
updateDocType();
});
</script>
</body>
</html>
PD9waHAKLyoqCiAqIFNpbmdsZS1maWxlIEZyZW5jaCBRdW90ZSAmIEludm9pY2UgR2VuZXJhdG9yCiAqCiAqIFRoaXMgc2NyaXB0IGhhbmRsZXMgdHdvIHRoaW5nczoKICogMS4gSWYgYWNjZXNzZWQgdmlhIGEgUE9TVCByZXF1ZXN0LCBpdCBnZW5lcmF0ZXMgYSBQREYuCiAqIDIuIElmIGFjY2Vzc2VkIHZpYSBhIEdFVCByZXF1ZXN0LCBpdCBkaXNwbGF5cyB0aGUgSFRNTCBpbnRlcmZhY2UuCiAqLwoKaWYgKCRfU0VSVkVSWydSRVFVRVNUX01FVEhPRCddID09PSAnUE9TVCcpIHsKICAgIC8vIC0tLSBNT0RFIDE6IFBERiBHRU5FUkFUSU9OIC0tLQoKICAgIHJlcXVpcmUoJ2xpYi9mcGRmL2ZwZGYucGhwJyk7CgogICAgLy8gV2UgbmVlZCBhIGN1c3RvbSBjbGFzcyB0byBjcmVhdGUgYSBoZWFkZXIgYW5kIGZvb3RlcgogICAgY2xhc3MgUERGIGV4dGVuZHMgRlBERgogICAgewogICAgICAgIHByaXZhdGUgJGRvY0RhdGE7CgogICAgICAgIGZ1bmN0aW9uIF9fY29uc3RydWN0KCRvcmllbnRhdGlvbiA9ICdQJywgJHVuaXQgPSAnbW0nLCAkc2l6ZSA9ICdBNCcsICRkYXRhID0gW10pIHsKICAgICAgICAgICAgcGFyZW50OjpfX2NvbnN0cnVjdCgkb3JpZW50YXRpb24sICR1bml0LCAkc2l6ZSk7CiAgICAgICAgICAgICR0aGlzLT5kb2NEYXRhID0gJGRhdGE7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBIZWFkZXIoKSB7CiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsICdCJywgMjApOwogICAgICAgICAgICAkdGhpcy0+Q2VsbCgwLCAxMCwgdXRmOF9kZWNvZGUoc3RydG91cHBlcigkdGhpcy0+ZG9jRGF0YVsnZG9jX3R5cGUnXSkpLCAwLCAxLCAnTCcpOwogICAgICAgICAgICAkdGhpcy0+U2V0Rm9udCgnQXJpYWwnLCAnJywgMTIpOwogICAgICAgICAgICAkdGhpcy0+Q2VsbCgwLCA3LCB1dGY4X2RlY29kZSgkdGhpcy0+ZG9jRGF0YVsnZG9jJ11bJ251bWJlciddKSwgMCwgMSwgJ0wnKTsKICAgICAgICAgICAgJHRoaXMtPkxuKDE1KTsKCiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsICdCJywgMTApOwogICAgICAgICAgICAkdGhpcy0+Q2VsbCg5NSwgNywgdXRmOF9kZWNvZGUoJHRoaXMtPmRvY0RhdGFbJ2NvbXBhbnknXVsnbmFtZSddKSwgMCwgMCwgJ0wnKTsKICAgICAgICAgICAgJHRoaXMtPkNlbGwoOTUsIDcsIHV0ZjhfZGVjb2RlKCR0aGlzLT5kb2NEYXRhWydjbGllbnQnXVsnbmFtZSddKSwgMCwgMSwgJ1InKTsKCiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsICcnLCAxMCk7CiAgICAgICAgICAgICR5UG9zID0gJHRoaXMtPkdldFkoKTsKICAgICAgICAgICAgJHRoaXMtPk11bHRpQ2VsbCg5NSwgNSwgdXRmOF9kZWNvZGUoJHRoaXMtPmRvY0RhdGFbJ2NvbXBhbnknXVsnYWRkcmVzcyddKSwgMCwgJ0wnKTsKICAgICAgICAgICAgJHRoaXMtPlNldFhZKDExNSwgJHlQb3MpOyAvLyBTZXQgWCB0byB0aGUgcmlnaHQgY29sdW1uCiAgICAgICAgICAgICR0aGlzLT5NdWx0aUNlbGwoODUsIDUsIHV0ZjhfZGVjb2RlKCR0aGlzLT5kb2NEYXRhWydjbGllbnQnXVsnYWRkcmVzcyddKSwgMCwgJ0wnKTsKCiAgICAgICAgICAgIC8vIFVzZSBHZXRZIGZyb20gdGhlIGxvbmdlc3QgTXVsdGlDZWxsIHRvIHNldCB0aGUgbmV4dCBwb3NpdGlvbiBjb3JyZWN0bHkKICAgICAgICAgICAgJHlQb3NBZnRlckFkZHJlc3MgPSAkdGhpcy0+R2V0WSgpOwogICAgICAgICAgICAKICAgICAgICAgICAgJHRoaXMtPlNldFkoJHlQb3NBZnRlckFkZHJlc3MpOwogICAgICAgICAgICAkdGhpcy0+TG4oMik7CiAgICAgICAgICAgIGlmKCFlbXB0eSgkdGhpcy0+ZG9jRGF0YVsnY29tcGFueSddWydzaXJldCddKSkgJHRoaXMtPkNlbGwoOTUsIDUsIHV0ZjhfZGVjb2RlKCdTSVJFVCA6ICcgLiAkdGhpcy0+ZG9jRGF0YVsnY29tcGFueSddWydzaXJldCddKSwgMCwgMSwgJ0wnKTsKICAgICAgICAgICAgaWYoIWVtcHR5KCR0aGlzLT5kb2NEYXRhWydjb21wYW55J11bJ3ZhdCddKSkgJHRoaXMtPkNlbGwoOTUsIDUsIHV0ZjhfZGVjb2RlKCdOwrAgVFZBIDogJyAuICR0aGlzLT5kb2NEYXRhWydjb21wYW55J11bJ3ZhdCddKSwgMCwgMSwgJ0wnKTsKICAgICAgICAgICAgCiAgICAgICAgICAgICR0aGlzLT5MbigxMCk7CiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsICcnLCAxMCk7CiAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsIDUsIHV0ZjhfZGVjb2RlKCdEYXRlIGRcJ8OpbWlzc2lvbiA6ICcgLiBkYXRlKCJkL20vWSIsIHN0cnRvdGltZSgkdGhpcy0+ZG9jRGF0YVsnZG9jJ11bJ2RhdGUnXSkpKSwgMCwgMSwgJ1InKTsKICAgICAgICAgICAgaWYgKCFlbXB0eSgkdGhpcy0+ZG9jRGF0YVsnZG9jJ11bJ2R1ZV9kYXRlJ10pKSB7CiAgICAgICAgICAgICAgICAkdGhpcy0+Q2VsbCgwLCA1LCB1dGY4X2RlY29kZSgnRGF0ZSBkXCfDqWNow6lhbmNlIDogJyAuIGRhdGUoImQvbS9ZIiwgc3RydG90aW1lKCR0aGlzLT5kb2NEYXRhWydkb2MnXVsnZHVlX2RhdGUnXSkpKSwgMCwgMSwgJ1InKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAkdGhpcy0+TG4oMTUpOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gRm9vdGVyKCkgewogICAgICAgICAgICAkdGhpcy0+U2V0WSgtMzApOwogICAgICAgICAgICBpZiAoIWVtcHR5KCR0aGlzLT5kb2NEYXRhWydub3RlcyddKSkgewogICAgICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywnJyw5KTsKICAgICAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsIDUsICdOb3RlcyA6JywgMCwgMSwgJ0wnKTsKICAgICAgICAgICAgICAgICR0aGlzLT5NdWx0aUNlbGwoMCwgNSwgdXRmOF9kZWNvZGUoJHRoaXMtPmRvY0RhdGFbJ25vdGVzJ10pLCAwLCAnTCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgICR0aGlzLT5TZXRZKC0xNSk7CiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsJ0knLDgpOwogICAgICAgICAgICAkdGhpcy0+Q2VsbCgwLDEwLCAnUGFnZSAnLiR0aGlzLT5QYWdlTm8oKS4nL3tuYn0nLDAsMCwnQycpOwogICAgICAgIH0KICAgIH0KCiAgICAkanNvbiA9IGZpbGVfZ2V0X2NvbnRlbnRzKCdwaHA6Ly9pbnB1dCcpOwogICAgJGRhdGEgPSBqc29uX2RlY29kZSgkanNvbiwgdHJ1ZSk7CgogICAgaWYgKCRkYXRhID09PSBudWxsKSB7CiAgICAgICAgaHR0cF9yZXNwb25zZV9jb2RlKDQwMCk7CiAgICAgICAgZGllKCdJbnZhbGlkIEpTT04nKTsKICAgIH0KCiAgICAkcGRmID0gbmV3IFBERignUCcsICdtbScsICdBNCcsICRkYXRhKTsKICAgICRwZGYtPkFsaWFzTmJQYWdlcygpOwogICAgJHBkZi0+QWRkUGFnZSgpOwoKICAgICRwZGYtPlNldEZvbnQoJ0FyaWFsJywgJ0InLCAxMCk7CiAgICAkcGRmLT5TZXRGaWxsQ29sb3IoMjMwLCAyMzAsIDIzMCk7CiAgICAkcGRmLT5DZWxsKDEwMCwgOCwgJ0Rlc2NyaXB0aW9uJywgMSwgMCwgJ0wnLCB0cnVlKTsKICAgICRwZGYtPkNlbGwoMjAsIDgsIHV0ZjhfZGVjb2RlKCdRdMOpJyksIDEsIDAsICdDJywgdHJ1ZSk7CiAgICAkcGRmLT5DZWxsKDM1LCA4LCAnUC5VLiBIVCcsIDEsIDAsICdDJywgdHJ1ZSk7CiAgICAkcGRmLT5DZWxsKDM1LCA4LCAnVG90YWwgSFQnLCAxLCAxLCAnQycsIHRydWUpOwoKICAgICRwZGYtPlNldEZvbnQoJ0FyaWFsJywgJycsIDEwKTsKICAgICRjdXJyZW5jeVN5bWJvbCA9IHV0ZjhfZGVjb2RlKCfigqwnKTsKICAgIGZvcmVhY2ggKCRkYXRhWydpdGVtcyddIGFzICRpdGVtKSB7CiAgICAgICAgJHBkZi0+Q2VsbCgxMDAsIDgsIHV0ZjhfZGVjb2RlKCRpdGVtWydkZXNjcmlwdGlvbiddKSwgMSwgMCwgJ0wnKTsKICAgICAgICAkcGRmLT5DZWxsKDIwLCA4LCAkaXRlbVsncXVhbnRpdHknXSwgMSwgMCwgJ1InKTsKICAgICAgICAkcGRmLT5DZWxsKDM1LCA4LCBudW1iZXJfZm9ybWF0KChmbG9hdCkkaXRlbVsncHJpY2UnXSwgMiwgJywnLCAnICcpIC4gJyAnIC4gJGN1cnJlbmN5U3ltYm9sLCAxLCAwLCAnUicpOwogICAgICAgICRwZGYtPkNlbGwoMzUsIDgsIG51bWJlcl9mb3JtYXQoKGZsb2F0KSRpdGVtWyd0b3RhbCddLCAyLCAnLCcsICcgJykgLiAnICcgLiAkY3VycmVuY3lTeW1ib2wsIDEsIDEsICdSJyk7CiAgICB9CgogICAgJHBkZi0+TG4oMTApOwogICAgJHBkZi0+U2V0Rm9udCgnQXJpYWwnLCAnJywgMTApOwogICAgJHRvdGFsc1ggPSAxMjA7CiAgICAkdG90YWxzTGFiZWxXaWR0aCA9IDM1OwogICAgJHRvdGFsc1ZhbHVlV2lkdGggPSA0NTsKCiAgICAkcGRmLT5DZWxsKCR0b3RhbHNYLCA4LCAnJywgMCwgMCk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNMYWJlbFdpZHRoLCA4LCAnVG90YWwgSFQnLCAxLCAwLCAnTCcpOwogICAgJHBkZi0+Q2VsbCgkdG90YWxzVmFsdWVXaWR0aCwgOCwgbnVtYmVyX2Zvcm1hdCgoZmxvYXQpJGRhdGFbJ3RvdGFscyddWydzdWJ0b3RhbCddLCAyLCAnLCcsICcgJykgLiAnICcgLiAkY3VycmVuY3lTeW1ib2wsIDEsIDEsICdSJyk7CgogICAgJHBkZi0+Q2VsbCgkdG90YWxzWCwgOCwgJycsIDAsIDApOwogICAgJHBkZi0+Q2VsbCgkdG90YWxzTGFiZWxXaWR0aCwgOCwgJ1RWQSAoJyAuICRkYXRhWyd0b3RhbHMnXVsndmF0X3JhdGUnXSAuICclKScsIDEsIDAsICdMJyk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNWYWx1ZVdpZHRoLCA4LCBudW1iZXJfZm9ybWF0KChmbG9hdCkkZGF0YVsndG90YWxzJ11bJ3ZhdF90b3RhbCddLCAyLCAnLCcsICcgJykgLiAnICcgLiAkY3VycmVuY3lTeW1ib2wsIDEsIDEsICdSJyk7CgogICAgJHBkZi0+U2V0Rm9udCgnQXJpYWwnLCAnQicsIDEyKTsKICAgICRwZGYtPkNlbGwoJHRvdGFsc1gsIDEwLCAnJywgMCwgMCk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNMYWJlbFdpZHRoLCAxMCwgJ1RvdGFsIFRUQycsIDEsIDAsICdMJywgdHJ1ZSk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNWYWx1ZVdpZHRoLCAxMCwgbnVtYmVyX2Zvcm1hdCgoZmxvYXQpJGRhdGFbJ3RvdGFscyddWyd0b3RhbF90dGMnXSwgMiwgJywnLCAnICcpIC4gJyAnIC4gJGN1cnJlbmN5U3ltYm9sLCAxLCAxLCAnUicsIHRydWUpOwoKICAgICRmaWxlbmFtZSA9IHN0cnRvdXBwZXIoJGRhdGFbJ2RvY190eXBlJ10pIC4gJy0nIC4gcHJlZ19yZXBsYWNlKCcvW15hLXpBLVowLTktXS8nLCAnJywgJGRhdGFbJ2RvYyddWydudW1iZXInXSkgLiAnLnBkZic7CiAgICAkcGRmLT5PdXRwdXQoJ0QnLCAkZmlsZW5hbWUpOwoKICAgIC8vIFN0b3AgZXhlY3V0aW9uIHRvIHByZXZlbnQgSFRNTCBmcm9tIGJlaW5nIHNlbnQKICAgIGV4aXQ7Cn0KCi8vIC0tLSBNT0RFIDI6IEhUTUwgSU5URVJGQUNFIC0tLQo/Pgo8IURPQ1RZUEUgaHRtbD4KPGh0bWwgbGFuZz0iZnIiIGRhdGEtdGhlbWU9ImxpZ2h0Ij4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICA8dGl0bGU+R8OpbsOpcmF0ZXVyIGRlIERldmlzIGV0IEZhY3R1cmVzPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5yLm5ldC9ucG0vQHBpY29jc3MvcGljb0AxL2Nzcy9waWNvLm1pbi5jc3MiPgogICAgPHN0eWxlPgogICAgICAgIC8qIEVtYmVkZGVkIGN1c3RvbSBDU1MgKi8KICAgICAgICBib2R5IHsKICAgICAgICAgICAgcGFkZGluZy1ib3R0b206IDVyZW07CiAgICAgICAgfQogICAgICAgIC50YWJsZS1jb250YWluZXIgewogICAgICAgICAgICBvdmVyZmxvdy14OiBhdXRvOwogICAgICAgIH0KICAgICAgICB0YWJsZSB0aDpsYXN0LWNoaWxkLAogICAgICAgIHRhYmxlIHRkOmxhc3QtY2hpbGQgewogICAgICAgICAgICB0ZXh0LWFsaWduOiByaWdodDsKICAgICAgICB9CiAgICAgICAgdGFibGUgaW5wdXRbdHlwZT0ibnVtYmVyIl0gewogICAgICAgICAgICBtaW4td2lkdGg6IDgwcHg7CiAgICAgICAgICAgIHRleHQtYWxpZ246IHJpZ2h0OwogICAgICAgIH0KICAgICAgICAudG90YWxzLXNlY3Rpb24gewogICAgICAgICAgICB0ZXh0LWFsaWduOiByaWdodDsKICAgICAgICAgICAgcGFkZGluZy10b3A6IDFyZW07CiAgICAgICAgICAgIGJvcmRlci1sZWZ0OiAxcHggc29saWQgdmFyKC0tcGljby1tdXRlZC1ib3JkZXItY29sb3IpOwogICAgICAgICAgICBwYWRkaW5nLWxlZnQ6IDFyZW07CiAgICAgICAgfQogICAgICAgIC50b3RhbHMtc2VjdGlvbiBwIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMC41cmVtOwogICAgICAgIH0KICAgICAgICAudG90YWxzLXNlY3Rpb24gc3Ryb25nIHsKICAgICAgICAgICAgbWFyZ2luLXJpZ2h0OiAxcmVtOwogICAgICAgIH0KICAgICAgICAubm90ZXMtc2VjdGlvbiB7CiAgICAgICAgICAgIHBhZGRpbmctcmlnaHQ6IDFyZW07CiAgICAgICAgfQogICAgICAgIC5mb3JtLWFjdGlvbnMgewogICAgICAgICAgICBtYXJnaW4tdG9wOiAycmVtOwogICAgICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGZsZXgtZW5kOwogICAgICAgICAgICBnYXA6IDFyZW07CiAgICAgICAgfQogICAgICAgIC5yZW1vdmUtaXRlbSB7CiAgICAgICAgICAgIHBhZGRpbmc6IDAuMjVyZW0gMC41cmVtOwogICAgICAgICAgICBsaW5lLWhlaWdodDogMTsKICAgICAgICB9CiAgICA8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5PgogICAgPG1haW4gY2xhc3M9ImNvbnRhaW5lciI+CiAgICAgICAgPGhlYWRlcj4KICAgICAgICAgICAgPGgxPkfDqW7DqXJhdGV1ciBkZSBEZXZpcyAmIEZhY3R1cmVzPC9oMT4KICAgICAgICAgICAgPHA+VG91dGVzIGxlcyBkb25uw6llcyBzb250IHNhdXZlZ2FyZMOpZXMgbG9jYWxlbWVudCBkYW5zIHZvdHJlIG5hdmlnYXRldXIuIEF1Y3VuZSBpbmZvcm1hdGlvbiBuJ2VzdCBlbnZvecOpZSDDoCB1biBzZXJ2ZXVyLjwvcD4KICAgICAgICA8L2hlYWRlcj4KCiAgICAgICAgPGZvcm0gaWQ9Imludm9pY2UtZm9ybSI+CiAgICAgICAgICAgIDxmaWVsZHNldD4KICAgICAgICAgICAgICAgIDxsZWdlbmQ+VHlwZSBkZSBkb2N1bWVudDwvbGVnZW5kPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iZG9jLXR5cGUtcXVvdGUiPgogICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJyYWRpbyIgaWQ9ImRvYy10eXBlLXF1b3RlIiBuYW1lPSJkb2NfdHlwZSIgdmFsdWU9IkRldmlzIiBjaGVja2VkPgogICAgICAgICAgICAgICAgICAgIERldmlzCiAgICAgICAgICAgICAgICA8L2xhYmVsPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iZG9jLXR5cGUtaW52b2ljZSI+CiAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iZG9jLXR5cGUtaW52b2ljZSIgbmFtZT0iZG9jX3R5cGUiIHZhbHVlPSJGYWN0dXJlIj4KICAgICAgICAgICAgICAgICAgICBGYWN0dXJlCiAgICAgICAgICAgICAgICA8L2xhYmVsPgogICAgICAgICAgICA8L2ZpZWxkc2V0PgoKICAgICAgICAgICAgPGRpdiBjbGFzcz0iZ3JpZCI+CiAgICAgICAgICAgICAgICA8YXJ0aWNsZT4KICAgICAgICAgICAgICAgICAgICA8aDMgaWQ9ImNvbXBhbnktdGl0bGUiPlZvdHJlIEVudHJlcHJpc2U8L2gzPgogICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9ImNvbXBhbnlfbmFtZSI+Tm9tIGRlIGwnZW50cmVwcmlzZTwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJjb21wYW55X25hbWUiIG5hbWU9ImNvbXBhbnlfbmFtZSIgcmVxdWlyZWQ+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iY29tcGFueV9hZGRyZXNzIj5BZHJlc3NlPC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICA8dGV4dGFyZWEgaWQ9ImNvbXBhbnlfYWRkcmVzcyIgbmFtZT0iY29tcGFueV9hZGRyZXNzIiByb3dzPSIzIj48L3RleHRhcmVhPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImdyaWQiPgogICAgICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJjb21wYW55X3NpcmV0Ij5TSVJFVCA8aW5wdXQgdHlwZT0idGV4dCIgaWQ9ImNvbXBhbnlfc2lyZXQiIG5hbWU9ImNvbXBhbnlfc2lyZXQiPjwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9ImNvbXBhbnlfdmF0Ij5OwrAgVFZBIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iY29tcGFueV92YXQiIG5hbWU9ImNvbXBhbnlfdmF0Ij48L2xhYmVsPgogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgaWQ9InNhdmUtY29tcGFueS1pbmZvIiBjbGFzcz0ic2Vjb25kYXJ5Ij5FbnJlZ2lzdHJlciBtZXMgaW5mb3JtYXRpb25zPC9idXR0b24+CiAgICAgICAgICAgICAgICA8L2FydGljbGU+CgogICAgICAgICAgICAgICAgPGFydGljbGU+CiAgICAgICAgICAgICAgICAgICAgPGgzPkNsaWVudDwvaDM+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iY2xpZW50X25hbWUiPk5vbSBkdSBjbGllbnQ8L2xhYmVsPgogICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iY2xpZW50X25hbWUiIG5hbWU9ImNsaWVudF9uYW1lIiByZXF1aXJlZD4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJjbGllbnRfYWRkcmVzcyI+QWRyZXNzZSBkdSBjbGllbnQ8L2xhYmVsPgogICAgICAgICAgICAgICAgICAgIDx0ZXh0YXJlYSBpZD0iY2xpZW50X2FkZHJlc3MiIG5hbWU9ImNsaWVudF9hZGRyZXNzIiByb3dzPSIzIj48L3RleHRhcmVhPgogICAgICAgICAgICAgICAgPC9hcnRpY2xlPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgCiAgICAgICAgICAgIDxhcnRpY2xlPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZ3JpZCI+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iZG9jX251bWJlciI+PHNwYW4gaWQ9ImRvYy10eXBlLWxhYmVsIj5OdW3DqXJvIGRlIERldmlzPC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgaWQ9ImRvY19udW1iZXIiIG5hbWU9ImRvY19udW1iZXIiIHJlcXVpcmVkPgogICAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iZG9jX2RhdGUiPkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9ImRhdGUiIGlkPSJkb2NfZGF0ZSIgbmFtZT0iZG9jX2RhdGUiIHJlcXVpcmVkPgogICAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iZG9jX2R1ZV9kYXRlIj5EYXRlIGQnw6ljaMOpYW5jZQogICAgICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0iZGF0ZSIgaWQ9ImRvY19kdWVfZGF0ZSIgbmFtZT0iZG9jX2R1ZV9kYXRlIj4KICAgICAgICAgICAgICAgICAgICA8L2xhYmVsPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvYXJ0aWNsZT4KCiAgICAgICAgICAgIDxhcnRpY2xlPgogICAgICAgICAgICAgICAgPGgzPkxpZ25lcyBkZSBwcmVzdGF0aW9uPC9oMz4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InRhYmxlLWNvbnRhaW5lciI+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlPgogICAgICAgICAgICAgICAgICAgICAgICA8dGhlYWQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRoPkRlc2NyaXB0aW9uPC90aD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGg+UXTDqTwvdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRoPlAuVS4gSFQ8L3RoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0aD5Ub3RhbCBIVDwvdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRoPjwvdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RoZWFkPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHkgaWQ9Iml0ZW0tbGlzdCI+PC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgaWQ9ImFkZC1pdGVtIiBjbGFzcz0ic2Vjb25kYXJ5Ij5Bam91dGVyIHVuZSBsaWduZTwvYnV0dG9uPgogICAgICAgICAgICA8L2FydGljbGU+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJncmlkIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9Im5vdGVzLXNlY3Rpb24iPgogICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJub3RlcyI+Tm90ZXMgLyBDb25kaXRpb25zIGRlIHBhaWVtZW50PC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICAgPHRleHRhcmVhIGlkPSJub3RlcyIgbmFtZT0ibm90ZXMiIHJvd3M9IjQiPlBhaWVtZW50IMOgIHLDqWNlcHRpb24gZGUgbGEgZmFjdHVyZS48L3RleHRhcmVhPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8YXJ0aWNsZSBjbGFzcz0idG90YWxzLXNlY3Rpb24iPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImdyaWQiPgogICAgICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJ2YXRfcmF0ZSI+VGF1eCBkZSBUVkEgKCUpPC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9Im51bWJlciIgaWQ9InZhdF9yYXRlIiBuYW1lPSJ2YXRfcmF0ZSIgdmFsdWU9IjIwIiBzdGVwPSIwLjEiIHJlcXVpcmVkPgogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDxwPjxzdHJvbmc+VG90YWwgSFQgOjwvc3Ryb25nPiA8c3BhbiBpZD0ic3VidG90YWwiPjAuMDA8L3NwYW4+IOKCrDwvcD4KICAgICAgICAgICAgICAgICAgICA8cD48c3Ryb25nPlRWQSA6PC9zdHJvbmc+IDxzcGFuIGlkPSJ2YXQtdG90YWwiPjAuMDA8L3NwYW4+IOKCrDwvcD4KICAgICAgICAgICAgICAgICAgICA8cD48c3Ryb25nPlRvdGFsIFRUQyA6PC9zdHJvbmc+IDxzcGFuIGlkPSJ0b3RhbC10dGMiPjAuMDA8L3NwYW4+IOKCrDwvcD4KICAgICAgICAgICAgICAgIDwvYXJ0aWNsZT4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICA8Zm9vdGVyIGNsYXNzPSJmb3JtLWFjdGlvbnMiPgogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJzdWJtaXQiIGlkPSJnZW5lcmF0ZS1wZGYiPkfDqW7DqXJlciBsZSBQREY8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0icmVzZXQtZm9ybSIgY2xhc3M9InNlY29uZGFyeSBvdXRsaW5lIj5Sw6lpbml0aWFsaXNlcjwvYnV0dG9uPgogICAgICAgICAgICA8L2Zvb3Rlcj4KICAgICAgICA8L2Zvcm0+CiAgICA8L21haW4+CgogICAgPHNjcmlwdD4KICAgICAgICAvLyAtLS0gRW1iZWRkZWQgSmF2YVNjcmlwdCAtLS0KICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4gewogICAgICAgICAgICBjb25zdCBmb3JtID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ludm9pY2UtZm9ybScpOwogICAgICAgICAgICBjb25zdCBpdGVtTGlzdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdpdGVtLWxpc3QnKTsKICAgICAgICAgICAgY29uc3QgYWRkSXRlbUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdhZGQtaXRlbScpOwogICAgICAgICAgICBjb25zdCBzYXZlQ29tcGFueUluZm9CdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2F2ZS1jb21wYW55LWluZm8nKTsKICAgICAgICAgICAgY29uc3QgcmVzZXRGb3JtQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jlc2V0LWZvcm0nKTsKICAgICAgICAgICAgY29uc3Qgc3VidG90YWxFbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdWJ0b3RhbCcpOwogICAgICAgICAgICBjb25zdCB2YXRUb3RhbEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZhdC10b3RhbCcpOwogICAgICAgICAgICBjb25zdCB0b3RhbFR0Y0VsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RvdGFsLXR0YycpOwogICAgICAgICAgICBjb25zdCBkb2NUeXBlUmFkaW9zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnaW5wdXRbbmFtZT0iZG9jX3R5cGUiXScpOwogICAgICAgICAgICBjb25zdCBkb2NUeXBlTGFiZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZG9jLXR5cGUtbGFiZWwnKTsKICAgICAgICAgICAgY29uc3QgZG9jTnVtYmVySW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZG9jX251bWJlcicpOwogICAgICAgICAgICBjb25zdCBjb21wYW55VGl0bGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueS10aXRsZScpOwoKICAgICAgICAgICAgY29uc3QgY2FsY3VsYXRlVG90YWxzID0gKCkgPT4gewogICAgICAgICAgICAgICAgbGV0IHN1YnRvdGFsID0gMDsKICAgICAgICAgICAgICAgIGl0ZW1MaXN0LnF1ZXJ5U2VsZWN0b3JBbGwoJ3RyJykuZm9yRWFjaChyb3cgPT4gewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHF1YW50aXR5ID0gcGFyc2VGbG9hdChyb3cucXVlcnlTZWxlY3RvcignLnF1YW50aXR5JykudmFsdWUpIHx8IDA7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgcHJpY2UgPSBwYXJzZUZsb2F0KHJvdy5xdWVyeVNlbGVjdG9yKCcucHJpY2UnKS52YWx1ZSkgfHwgMDsKICAgICAgICAgICAgICAgICAgICBjb25zdCByb3dUb3RhbCA9IHF1YW50aXR5ICogcHJpY2U7CiAgICAgICAgICAgICAgICAgICAgcm93LnF1ZXJ5U2VsZWN0b3IoJy5yb3ctdG90YWwnKS50ZXh0Q29udGVudCA9IHJvd1RvdGFsLnRvRml4ZWQoMik7CiAgICAgICAgICAgICAgICAgICAgc3VidG90YWwgKz0gcm93VG90YWw7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGNvbnN0IHZhdFJhdGUgPSBwYXJzZUZsb2F0KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2YXRfcmF0ZScpLnZhbHVlKSB8fCAwOwogICAgICAgICAgICAgICAgY29uc3QgdmF0VG90YWwgPSBzdWJ0b3RhbCAqICh2YXRSYXRlIC8gMTAwKTsKICAgICAgICAgICAgICAgIGNvbnN0IHRvdGFsVHRjID0gc3VidG90YWwgKyB2YXRUb3RhbDsKICAgICAgICAgICAgICAgIHN1YnRvdGFsRWwudGV4dENvbnRlbnQgPSBzdWJ0b3RhbC50b0ZpeGVkKDIpOwogICAgICAgICAgICAgICAgdmF0VG90YWxFbC50ZXh0Q29udGVudCA9IHZhdFRvdGFsLnRvRml4ZWQoMik7CiAgICAgICAgICAgICAgICB0b3RhbFR0Y0VsLnRleHRDb250ZW50ID0gdG90YWxUdGMudG9GaXhlZCgyKTsKICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGNvbnN0IGFkZExpbmVJdGVtID0gKCkgPT4gewogICAgICAgICAgICAgICAgY29uc3Qgcm93ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndHInKTsKICAgICAgICAgICAgICAgIHJvdy5pbm5lckhUTUwgPSBgCiAgICAgICAgICAgICAgICAgICAgPHRkPjxpbnB1dCB0eXBlPSJ0ZXh0IiBjbGFzcz0iZGVzY3JpcHRpb24iIHBsYWNlaG9sZGVyPSJEZXNjcmlwdGlvbiBkZSBsYSBwcmVzdGF0aW9uIj48L3RkPgogICAgICAgICAgICAgICAgICAgIDx0ZD48aW5wdXQgdHlwZT0ibnVtYmVyIiBjbGFzcz0icXVhbnRpdHkiIHZhbHVlPSIxIiBzdGVwPSJhbnkiPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgPHRkPjxpbnB1dCB0eXBlPSJudW1iZXIiIGNsYXNzPSJwcmljZSIgdmFsdWU9IjAuMDAiIHN0ZXA9ImFueSI+PC90ZD4KICAgICAgICAgICAgICAgICAgICA8dGQ+PHNwYW4gY2xhc3M9InJvdy10b3RhbCI+MC4wMDwvc3Bhbj4g4oKsPC90ZD4KICAgICAgICAgICAgICAgICAgICA8dGQ+PGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJyZW1vdmUtaXRlbSBzZWNvbmRhcnkgb3V0bGluZSI+w5c8L2J1dHRvbj48L3RkPgogICAgICAgICAgICAgICAgYDsKICAgICAgICAgICAgICAgIGl0ZW1MaXN0LmFwcGVuZENoaWxkKHJvdyk7CiAgICAgICAgICAgICAgICByb3cucXVlcnlTZWxlY3RvcignLnJlbW92ZS1pdGVtJykuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7CiAgICAgICAgICAgICAgICAgICAgcm93LnJlbW92ZSgpOwogICAgICAgICAgICAgICAgICAgIGNhbGN1bGF0ZVRvdGFscygpOwogICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBjb25zdCB1cGRhdGVEb2NUeXBlID0gKCkgPT4gewogICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRUeXBlID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignaW5wdXRbbmFtZT0iZG9jX3R5cGUiXTpjaGVja2VkJykudmFsdWU7CiAgICAgICAgICAgICAgICBkb2NUeXBlTGFiZWwudGV4dENvbnRlbnQgPSBgTnVtw6lybyBkZSAke3NlbGVjdGVkVHlwZX1gOwogICAgICAgICAgICAgICAgY29uc3QgcHJlZml4ID0gc2VsZWN0ZWRUeXBlID09PSAnRGV2aXMnID8gJ0RFJyA6ICdGQSc7CiAgICAgICAgICAgICAgICBjb25zdCBjdXJyZW50VmFsID0gZG9jTnVtYmVySW5wdXQudmFsdWU7CiAgICAgICAgICAgICAgICBpZiAoIWN1cnJlbnRWYWwuc3RhcnRzV2l0aCgnREUtJykgJiYgIWN1cnJlbnRWYWwuc3RhcnRzV2l0aCgnRkEtJykgfHwgY3VycmVudFZhbCA9PT0gJycpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCB5ZWFyID0gbmV3IERhdGUoKS5nZXRGdWxsWWVhcigpOwogICAgICAgICAgICAgICAgICAgIGRvY051bWJlcklucHV0LnZhbHVlID0gYCR7cHJlZml4fS0ke3llYXJ9LTAwMWA7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBjb25zdCBzYXZlQ29tcGFueUluZm8gPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBjb25zdCBjb21wYW55SW5mbyA9IHsKICAgICAgICAgICAgICAgICAgICBuYW1lOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9uYW1lJykudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgYWRkcmVzczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfYWRkcmVzcycpLnZhbHVlLAogICAgICAgICAgICAgICAgICAgIHNpcmV0OiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9zaXJldCcpLnZhbHVlLAogICAgICAgICAgICAgICAgICAgIHZhdDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfdmF0JykudmFsdWUsCiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ2NvbXBhbnlJbmZvJywgSlNPTi5zdHJpbmdpZnkoY29tcGFueUluZm8pKTsKICAgICAgICAgICAgICAgIGNvbXBhbnlUaXRsZS50ZXh0Q29udGVudCA9ICdWb3RyZSBFbnRyZXByaXNlIChFbnJlZ2lzdHLDqSknOwogICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiBjb21wYW55VGl0bGUudGV4dENvbnRlbnQgPSAnVm90cmUgRW50cmVwcmlzZScsIDIwMDApOwogICAgICAgICAgICB9OwoKICAgICAgICAgICAgY29uc3QgbG9hZENvbXBhbnlJbmZvID0gKCkgPT4gewogICAgICAgICAgICAgICAgY29uc3QgY29tcGFueUluZm8gPSBKU09OLnBhcnNlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdjb21wYW55SW5mbycpKTsKICAgICAgICAgICAgICAgIGlmIChjb21wYW55SW5mbykgewogICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X25hbWUnKS52YWx1ZSA9IGNvbXBhbnlJbmZvLm5hbWUgfHwgJyc7CiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfYWRkcmVzcycpLnZhbHVlID0gY29tcGFueUluZm8uYWRkcmVzcyB8fCAnJzsKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9zaXJldCcpLnZhbHVlID0gY29tcGFueUluZm8uc2lyZXQgfHwgJyc7CiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfdmF0JykudmFsdWUgPSBjb21wYW55SW5mby52YXQgfHwgJyc7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICBjb25zdCByZXNldEZvcm0gPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBpZihjb25maXJtKCJWb3VsZXotdm91cyB2cmFpbWVudCByw6lpbml0aWFsaXNlciBsZSBmb3JtdWxhaXJlID8gTGVzIGluZm9ybWF0aW9ucyBkZSB2b3RyZSBlbnRyZXByaXNlIHJlc3Rlcm9udCBlbnJlZ2lzdHLDqWVzLiIpKSB7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgY29tcGFueUluZm8gPSBKU09OLnBhcnNlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdjb21wYW55SW5mbycpKTsKICAgICAgICAgICAgICAgICAgICBmb3JtLnJlc2V0KCk7CiAgICAgICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ2NvbXBhbnlJbmZvJywgSlNPTi5zdHJpbmdpZnkoY29tcGFueUluZm8pKTsKICAgICAgICAgICAgICAgICAgICBsb2FkQ29tcGFueUluZm8oKTsKICAgICAgICAgICAgICAgICAgICBpdGVtTGlzdC5pbm5lckhUTUwgPSAnJzsKICAgICAgICAgICAgICAgICAgICBhZGRMaW5lSXRlbSgpOwogICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2NfZGF0ZScpLnZhbHVlQXNEYXRlID0gbmV3IERhdGUoKTsKICAgICAgICAgICAgICAgICAgICB1cGRhdGVEb2NUeXBlKCk7CiAgICAgICAgICAgICAgICAgICAgY2FsY3VsYXRlVG90YWxzKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGNvbnN0IGdlbmVyYXRlUERGID0gYXN5bmMgKGUpID0+IHsKICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgICAgIGNvbnN0IGl0ZW1zID0gQXJyYXkuZnJvbShpdGVtTGlzdC5xdWVyeVNlbGVjdG9yQWxsKCd0cicpKS5tYXAocm93ID0+ICh7CiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246IHJvdy5xdWVyeVNlbGVjdG9yKCcuZGVzY3JpcHRpb24nKS52YWx1ZSwKICAgICAgICAgICAgICAgICAgICBxdWFudGl0eTogcm93LnF1ZXJ5U2VsZWN0b3IoJy5xdWFudGl0eScpLnZhbHVlLAogICAgICAgICAgICAgICAgICAgIHByaWNlOiByb3cucXVlcnlTZWxlY3RvcignLnByaWNlJykudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgdG90YWw6IHBhcnNlRmxvYXQocm93LnF1ZXJ5U2VsZWN0b3IoJy5yb3ctdG90YWwnKS50ZXh0Q29udGVudCkKICAgICAgICAgICAgICAgIH0pKTsKICAgICAgICAgICAgICAgIGNvbnN0IGZvcm1EYXRhID0gewogICAgICAgICAgICAgICAgICAgIGRvY190eXBlOiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdpbnB1dFtuYW1lPSJkb2NfdHlwZSJdOmNoZWNrZWQnKS52YWx1ZSwKICAgICAgICAgICAgICAgICAgICBjb21wYW55OiB7IG5hbWU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X25hbWUnKS52YWx1ZSwgYWRkcmVzczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfYWRkcmVzcycpLnZhbHVlLCBzaXJldDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfc2lyZXQnKS52YWx1ZSwgdmF0OiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV92YXQnKS52YWx1ZSB9LAogICAgICAgICAgICAgICAgICAgIGNsaWVudDogeyBuYW1lOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2xpZW50X25hbWUnKS52YWx1ZSwgYWRkcmVzczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NsaWVudF9hZGRyZXNzJykudmFsdWUgfSwKICAgICAgICAgICAgICAgICAgICBkb2M6IHsgbnVtYmVyOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZG9jX251bWJlcicpLnZhbHVlLCBkYXRlOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZG9jX2RhdGUnKS52YWx1ZSwgZHVlX2RhdGU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2NfZHVlX2RhdGUnKS52YWx1ZSB9LAogICAgICAgICAgICAgICAgICAgIGl0ZW1zOiBpdGVtcywKICAgICAgICAgICAgICAgICAgICB0b3RhbHM6IHsgc3VidG90YWw6IHN1YnRvdGFsRWwudGV4dENvbnRlbnQsIHZhdF9yYXRlOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndmF0X3JhdGUnKS52YWx1ZSwgdmF0X3RvdGFsOiB2YXRUb3RhbEVsLnRleHRDb250ZW50LCB0b3RhbF90dGM6IHRvdGFsVHRjRWwudGV4dENvbnRlbnQgfSwKICAgICAgICAgICAgICAgICAgICBub3RlczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25vdGVzJykudmFsdWUKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICBjb25zdCBwZGZCdXR0b24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZ2VuZXJhdGUtcGRmJyk7CiAgICAgICAgICAgICAgICBwZGZCdXR0b24uc2V0QXR0cmlidXRlKCdhcmlhLWJ1c3knLCAndHJ1ZScpOwogICAgICAgICAgICAgICAgcGRmQnV0dG9uLnRleHRDb250ZW50ID0gJ0fDqW7DqXJhdGlvbi4uLic7CiAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goJycsIHsgLy8gUG9zdCB0byB0aGUgc2FtZSBmaWxlCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwKICAgICAgICAgICAgICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoZm9ybURhdGEpCiAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykgdGhyb3cgbmV3IEVycm9yKGBFcnJldXIgZHUgc2VydmV1cjogJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IGJsb2IgPSBhd2FpdCByZXNwb25zZS5ibG9iKCk7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgdXJsID0gd2luZG93LlVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYik7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgYSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTsKICAgICAgICAgICAgICAgICAgICBhLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgICAgICAgICAgICAgICAgYS5ocmVmID0gdXJsOwogICAgICAgICAgICAgICAgICAgIGEuZG93bmxvYWQgPSBgJHtmb3JtRGF0YS5kb2NfdHlwZS50b1VwcGVyQ2FzZSgpfS0ke2Zvcm1EYXRhLmRvYy5udW1iZXIucmVwbGFjZSgvW15hLXpBLVowLTktXS9nLCAnJyl9LnBkZmA7CiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChhKTsKICAgICAgICAgICAgICAgICAgICBhLmNsaWNrKCk7CiAgICAgICAgICAgICAgICAgICAgd2luZG93LlVSTC5yZXZva2VPYmplY3RVUkwodXJsKTsKICAgICAgICAgICAgICAgICAgICBhLnJlbW92ZSgpOwogICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJldXIgbG9ycyBkZSBsYSBnw6luw6lyYXRpb24gZHUgUERGOicsIGVycm9yKTsKICAgICAgICAgICAgICAgICAgICBhbGVydCgnVW5lIGVycmV1ciBlc3Qgc3VydmVudWUgbG9ycyBkZSBsYSBnw6luw6lyYXRpb24gZHUgUERGLicpOwogICAgICAgICAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgICAgICAgICBwZGZCdXR0b24ucmVtb3ZlQXR0cmlidXRlKCdhcmlhLWJ1c3knKTsKICAgICAgICAgICAgICAgICAgICBwZGZCdXR0b24udGV4dENvbnRlbnQgPSAnR8OpbsOpcmVyIGxlIFBERic7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBhZGRJdGVtQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYWRkTGluZUl0ZW0pOwogICAgICAgICAgICBmb3JtLmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgY2FsY3VsYXRlVG90YWxzKTsKICAgICAgICAgICAgZm9ybS5hZGRFdmVudExpc3RlbmVyKCdzdWJtaXQnLCBnZW5lcmF0ZVBERik7CiAgICAgICAgICAgIHNhdmVDb21wYW55SW5mb0J0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHNhdmVDb21wYW55SW5mbyk7CiAgICAgICAgICAgIHJlc2V0Rm9ybUJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHJlc2V0Rm9ybSk7CiAgICAgICAgICAgIGRvY1R5cGVSYWRpb3MuZm9yRWFjaChyYWRpbyA9PiByYWRpby5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB1cGRhdGVEb2NUeXBlKSk7CgogICAgICAgICAgICAvLyAtLS0gSW5pdGlhbGlzYXRpb24gLS0tCiAgICAgICAgICAgIGxvYWRDb21wYW55SW5mbygpOwogICAgICAgICAgICBhZGRMaW5lSXRlbSgpOwogICAgICAgICAgICBjYWxjdWxhdGVUb3RhbHMoKTsKICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2RvY19kYXRlJykudmFsdWVBc0RhdGUgPSBuZXcgRGF0ZSgpOwogICAgICAgICAgICB1cGRhdGVEb2NUeXBlKCk7CiAgICAgICAgfSk7CiAgICA8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+