class PDF(FPDF):
def header(self):
self.set_font('Arial', 'B', 14)
self.cell(0, 10, 'Guía de Equipos de Protección contra Caídas', ln=True, align='C')
self.ln(5)
def chapter_title(self, title):
self.set_font('Arial', 'B', 12)
self.set_text_color(30, 30, 30)
self.cell(0, 10, title, ln=True, align='L')
self.ln(2)
def chapter_body(self, body):
self.set_font('Arial', '', 11)
self.set_text_color(50, 50, 50)
self.multi_cell(0, 7, body)
self.ln()
pdf = PDF()
pdf.add_page()
contenido = [
("1. Introducción a los equipos",
"Los equipos de protección contra caídas son dispositivos diseñados para proteger a los trabajadores que realizan tareas en altura..."),
("2. Descripción de cada componente",
"- Arneses: Sujetan el cuerpo del usuario, distribuyendo la fuerza de una caída.\n"
"- Líneas de vida: Conectan al usuario con el punto de anclaje...\n"
"- Anclajes, cinturones, mosquetones, etc."),
("3. Unidades de medida y resistencia",
"La resistencia se mide principalmente en kilonewtons (kN)...\n"
"- 1 kN ≈ 100 kgf\n- 1 kN ≈ 225 lbf"),
("4. Normas técnicas aplicables",
"Normas importantes:\n- EN 361 (Arnés)\n- EN 362 (Conectores)\n- ANSI Z359...\n- OSHA 1910 / 1926"),
("5. Errores comunes y buenas prácticas",
"- No interpretar bien los valores técnicos.\n- Usar equipos incompatibles...\n- Verificar etiquetas y estado físico."),
("6. Plantilla práctica de revisión",
"Checklist:\n[ ] Arnés certificado\n[ ] Mosquetones seguros\n[ ] Línea de vida correcta\n[ ] Punto de anclaje ≥ 12 kN..."),
("7. Marcas y recursos recomendados",
"Marcas: Petzl, 3M, MSA, Kratos, Skylotec, Honeywell...\nConsulta manuales y videos oficiales.")
]
for titulo, texto in contenido:
pdf.chapter_title(titulo)
pdf.chapter_body(texto)
pdf.output("Guia_Equipos_Proteccion_Caidas.pdf")<?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>
Y2xhc3MgUERGKEZQREYpOgogICAgZGVmIGhlYWRlcihzZWxmKToKICAgICAgICBzZWxmLnNldF9mb250KCdBcmlhbCcsICdCJywgMTQpCiAgICAgICAgc2VsZi5jZWxsKDAsIDEwLCAnR3XDrWEgZGUgRXF1aXBvcyBkZSBQcm90ZWNjacOzbiBjb250cmEgQ2HDrWRhcycsIGxuPVRydWUsIGFsaWduPSdDJykKICAgICAgICBzZWxmLmxuKDUpCgogICAgZGVmIGNoYXB0ZXJfdGl0bGUoc2VsZiwgdGl0bGUpOgogICAgICAgIHNlbGYuc2V0X2ZvbnQoJ0FyaWFsJywgJ0InLCAxMikKICAgICAgICBzZWxmLnNldF90ZXh0X2NvbG9yKDMwLCAzMCwgMzApCiAgICAgICAgc2VsZi5jZWxsKDAsIDEwLCB0aXRsZSwgbG49VHJ1ZSwgYWxpZ249J0wnKQogICAgICAgIHNlbGYubG4oMikKCiAgICBkZWYgY2hhcHRlcl9ib2R5KHNlbGYsIGJvZHkpOgogICAgICAgIHNlbGYuc2V0X2ZvbnQoJ0FyaWFsJywgJycsIDExKQogICAgICAgIHNlbGYuc2V0X3RleHRfY29sb3IoNTAsIDUwLCA1MCkKICAgICAgICBzZWxmLm11bHRpX2NlbGwoMCwgNywgYm9keSkKICAgICAgICBzZWxmLmxuKCkKCnBkZiA9IFBERigpCnBkZi5hZGRfcGFnZSgpCgpjb250ZW5pZG8gPSBbCiAgICAoIjEuIEludHJvZHVjY2nDs24gYSBsb3MgZXF1aXBvcyIsIAogICAgICJMb3MgZXF1aXBvcyBkZSBwcm90ZWNjacOzbiBjb250cmEgY2HDrWRhcyBzb24gZGlzcG9zaXRpdm9zIGRpc2XDsWFkb3MgcGFyYSBwcm90ZWdlciBhIGxvcyB0cmFiYWphZG9yZXMgcXVlIHJlYWxpemFuIHRhcmVhcyBlbiBhbHR1cmEuLi4iKSwKCiAgICAoIjIuIERlc2NyaXBjacOzbiBkZSBjYWRhIGNvbXBvbmVudGUiLCAKICAgICAiLSBBcm5lc2VzOiBTdWpldGFuIGVsIGN1ZXJwbyBkZWwgdXN1YXJpbywgZGlzdHJpYnV5ZW5kbyBsYSBmdWVyemEgZGUgdW5hIGNhw61kYS5cbiIKICAgICAiLSBMw61uZWFzIGRlIHZpZGE6IENvbmVjdGFuIGFsIHVzdWFyaW8gY29uIGVsIHB1bnRvIGRlIGFuY2xhamUuLi5cbiIKICAgICAiLSBBbmNsYWplcywgY2ludHVyb25lcywgbW9zcXVldG9uZXMsIGV0Yy4iKSwKCiAgICAoIjMuIFVuaWRhZGVzIGRlIG1lZGlkYSB5IHJlc2lzdGVuY2lhIiwgCiAgICAgIkxhIHJlc2lzdGVuY2lhIHNlIG1pZGUgcHJpbmNpcGFsbWVudGUgZW4ga2lsb25ld3RvbnMgKGtOKS4uLlxuIgogICAgICItIDEga04g4omIIDEwMCBrZ2Zcbi0gMSBrTiDiiYggMjI1IGxiZiIpLAoKICAgICgiNC4gTm9ybWFzIHTDqWNuaWNhcyBhcGxpY2FibGVzIiwgCiAgICAgIk5vcm1hcyBpbXBvcnRhbnRlczpcbi0gRU4gMzYxIChBcm7DqXMpXG4tIEVOIDM2MiAoQ29uZWN0b3Jlcylcbi0gQU5TSSBaMzU5Li4uXG4tIE9TSEEgMTkxMCAvIDE5MjYiKSwKCiAgICAoIjUuIEVycm9yZXMgY29tdW5lcyB5IGJ1ZW5hcyBwcsOhY3RpY2FzIiwgCiAgICAgIi0gTm8gaW50ZXJwcmV0YXIgYmllbiBsb3MgdmFsb3JlcyB0w6ljbmljb3MuXG4tIFVzYXIgZXF1aXBvcyBpbmNvbXBhdGlibGVzLi4uXG4tIFZlcmlmaWNhciBldGlxdWV0YXMgeSBlc3RhZG8gZsOtc2ljby4iKSwKCiAgICAoIjYuIFBsYW50aWxsYSBwcsOhY3RpY2EgZGUgcmV2aXNpw7NuIiwgCiAgICAgIkNoZWNrbGlzdDpcblsgXSBBcm7DqXMgY2VydGlmaWNhZG9cblsgXSBNb3NxdWV0b25lcyBzZWd1cm9zXG5bIF0gTMOtbmVhIGRlIHZpZGEgY29ycmVjdGFcblsgXSBQdW50byBkZSBhbmNsYWplIOKJpSAxMiBrTi4uLiIpLAoKICAgICgiNy4gTWFyY2FzIHkgcmVjdXJzb3MgcmVjb21lbmRhZG9zIiwgCiAgICAgIk1hcmNhczogUGV0emwsIDNNLCBNU0EsIEtyYXRvcywgU2t5bG90ZWMsIEhvbmV5d2VsbC4uLlxuQ29uc3VsdGEgbWFudWFsZXMgeSB2aWRlb3Mgb2ZpY2lhbGVzLiIpCl0KCmZvciB0aXR1bG8sIHRleHRvIGluIGNvbnRlbmlkbzoKICAgIHBkZi5jaGFwdGVyX3RpdGxlKHRpdHVsbykKICAgIHBkZi5jaGFwdGVyX2JvZHkodGV4dG8pCgpwZGYub3V0cHV0KCJHdWlhX0VxdWlwb3NfUHJvdGVjY2lvbl9DYWlkYXMucGRmIik8P3BocAovKioKICogU2luZ2xlLWZpbGUgRnJlbmNoIFF1b3RlICYgSW52b2ljZSBHZW5lcmF0b3IKICoKICogVGhpcyBzY3JpcHQgaGFuZGxlcyB0d28gdGhpbmdzOgogKiAxLiBJZiBhY2Nlc3NlZCB2aWEgYSBQT1NUIHJlcXVlc3QsIGl0IGdlbmVyYXRlcyBhIFBERi4KICogMi4gSWYgYWNjZXNzZWQgdmlhIGEgR0VUIHJlcXVlc3QsIGl0IGRpc3BsYXlzIHRoZSBIVE1MIGludGVyZmFjZS4KICovCgppZiAoJF9TRVJWRVJbJ1JFUVVFU1RfTUVUSE9EJ10gPT09ICdQT1NUJykgewogICAgLy8gLS0tIE1PREUgMTogUERGIEdFTkVSQVRJT04gLS0tCgogICAgcmVxdWlyZSgnbGliL2ZwZGYvZnBkZi5waHAnKTsKCiAgICAvLyBXZSBuZWVkIGEgY3VzdG9tIGNsYXNzIHRvIGNyZWF0ZSBhIGhlYWRlciBhbmQgZm9vdGVyCiAgICBjbGFzcyBQREYgZXh0ZW5kcyBGUERGCiAgICB7CiAgICAgICAgcHJpdmF0ZSAkZG9jRGF0YTsKCiAgICAgICAgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJG9yaWVudGF0aW9uID0gJ1AnLCAkdW5pdCA9ICdtbScsICRzaXplID0gJ0E0JywgJGRhdGEgPSBbXSkgewogICAgICAgICAgICBwYXJlbnQ6Ol9fY29uc3RydWN0KCRvcmllbnRhdGlvbiwgJHVuaXQsICRzaXplKTsKICAgICAgICAgICAgJHRoaXMtPmRvY0RhdGEgPSAkZGF0YTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIEhlYWRlcigpIHsKICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywgJ0InLCAyMCk7CiAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsIDEwLCB1dGY4X2RlY29kZShzdHJ0b3VwcGVyKCR0aGlzLT5kb2NEYXRhWydkb2NfdHlwZSddKSksIDAsIDEsICdMJyk7CiAgICAgICAgICAgICR0aGlzLT5TZXRGb250KCdBcmlhbCcsICcnLCAxMik7CiAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsIDcsIHV0ZjhfZGVjb2RlKCR0aGlzLT5kb2NEYXRhWydkb2MnXVsnbnVtYmVyJ10pLCAwLCAxLCAnTCcpOwogICAgICAgICAgICAkdGhpcy0+TG4oMTUpOwoKICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywgJ0InLCAxMCk7CiAgICAgICAgICAgICR0aGlzLT5DZWxsKDk1LCA3LCB1dGY4X2RlY29kZSgkdGhpcy0+ZG9jRGF0YVsnY29tcGFueSddWyduYW1lJ10pLCAwLCAwLCAnTCcpOwogICAgICAgICAgICAkdGhpcy0+Q2VsbCg5NSwgNywgdXRmOF9kZWNvZGUoJHRoaXMtPmRvY0RhdGFbJ2NsaWVudCddWyduYW1lJ10pLCAwLCAxLCAnUicpOwoKICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywgJycsIDEwKTsKICAgICAgICAgICAgJHlQb3MgPSAkdGhpcy0+R2V0WSgpOwogICAgICAgICAgICAkdGhpcy0+TXVsdGlDZWxsKDk1LCA1LCB1dGY4X2RlY29kZSgkdGhpcy0+ZG9jRGF0YVsnY29tcGFueSddWydhZGRyZXNzJ10pLCAwLCAnTCcpOwogICAgICAgICAgICAkdGhpcy0+U2V0WFkoMTE1LCAkeVBvcyk7IC8vIFNldCBYIHRvIHRoZSByaWdodCBjb2x1bW4KICAgICAgICAgICAgJHRoaXMtPk11bHRpQ2VsbCg4NSwgNSwgdXRmOF9kZWNvZGUoJHRoaXMtPmRvY0RhdGFbJ2NsaWVudCddWydhZGRyZXNzJ10pLCAwLCAnTCcpOwoKICAgICAgICAgICAgLy8gVXNlIEdldFkgZnJvbSB0aGUgbG9uZ2VzdCBNdWx0aUNlbGwgdG8gc2V0IHRoZSBuZXh0IHBvc2l0aW9uIGNvcnJlY3RseQogICAgICAgICAgICAkeVBvc0FmdGVyQWRkcmVzcyA9ICR0aGlzLT5HZXRZKCk7CiAgICAgICAgICAgIAogICAgICAgICAgICAkdGhpcy0+U2V0WSgkeVBvc0FmdGVyQWRkcmVzcyk7CiAgICAgICAgICAgICR0aGlzLT5MbigyKTsKICAgICAgICAgICAgaWYoIWVtcHR5KCR0aGlzLT5kb2NEYXRhWydjb21wYW55J11bJ3NpcmV0J10pKSAkdGhpcy0+Q2VsbCg5NSwgNSwgdXRmOF9kZWNvZGUoJ1NJUkVUIDogJyAuICR0aGlzLT5kb2NEYXRhWydjb21wYW55J11bJ3NpcmV0J10pLCAwLCAxLCAnTCcpOwogICAgICAgICAgICBpZighZW1wdHkoJHRoaXMtPmRvY0RhdGFbJ2NvbXBhbnknXVsndmF0J10pKSAkdGhpcy0+Q2VsbCg5NSwgNSwgdXRmOF9kZWNvZGUoJ07CsCBUVkEgOiAnIC4gJHRoaXMtPmRvY0RhdGFbJ2NvbXBhbnknXVsndmF0J10pLCAwLCAxLCAnTCcpOwogICAgICAgICAgICAKICAgICAgICAgICAgJHRoaXMtPkxuKDEwKTsKICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywgJycsIDEwKTsKICAgICAgICAgICAgJHRoaXMtPkNlbGwoMCwgNSwgdXRmOF9kZWNvZGUoJ0RhdGUgZFwnw6ltaXNzaW9uIDogJyAuIGRhdGUoImQvbS9ZIiwgc3RydG90aW1lKCR0aGlzLT5kb2NEYXRhWydkb2MnXVsnZGF0ZSddKSkpLCAwLCAxLCAnUicpOwogICAgICAgICAgICBpZiAoIWVtcHR5KCR0aGlzLT5kb2NEYXRhWydkb2MnXVsnZHVlX2RhdGUnXSkpIHsKICAgICAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsIDUsIHV0ZjhfZGVjb2RlKCdEYXRlIGRcJ8OpY2jDqWFuY2UgOiAnIC4gZGF0ZSgiZC9tL1kiLCBzdHJ0b3RpbWUoJHRoaXMtPmRvY0RhdGFbJ2RvYyddWydkdWVfZGF0ZSddKSkpLCAwLCAxLCAnUicpOwogICAgICAgICAgICB9CiAgICAgICAgICAgICR0aGlzLT5MbigxNSk7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBGb290ZXIoKSB7CiAgICAgICAgICAgICR0aGlzLT5TZXRZKC0zMCk7CiAgICAgICAgICAgIGlmICghZW1wdHkoJHRoaXMtPmRvY0RhdGFbJ25vdGVzJ10pKSB7CiAgICAgICAgICAgICAgICAkdGhpcy0+U2V0Rm9udCgnQXJpYWwnLCcnLDkpOwogICAgICAgICAgICAgICAgJHRoaXMtPkNlbGwoMCwgNSwgJ05vdGVzIDonLCAwLCAxLCAnTCcpOwogICAgICAgICAgICAgICAgJHRoaXMtPk11bHRpQ2VsbCgwLCA1LCB1dGY4X2RlY29kZSgkdGhpcy0+ZG9jRGF0YVsnbm90ZXMnXSksIDAsICdMJyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgJHRoaXMtPlNldFkoLTE1KTsKICAgICAgICAgICAgJHRoaXMtPlNldEZvbnQoJ0FyaWFsJywnSScsOCk7CiAgICAgICAgICAgICR0aGlzLT5DZWxsKDAsMTAsICdQYWdlICcuJHRoaXMtPlBhZ2VObygpLicve25ifScsMCwwLCdDJyk7CiAgICAgICAgfQogICAgfQoKICAgICRqc29uID0gZmlsZV9nZXRfY29udGVudHMoJ3BocDovL2lucHV0Jyk7CiAgICAkZGF0YSA9IGpzb25fZGVjb2RlKCRqc29uLCB0cnVlKTsKCiAgICBpZiAoJGRhdGEgPT09IG51bGwpIHsKICAgICAgICBodHRwX3Jlc3BvbnNlX2NvZGUoNDAwKTsKICAgICAgICBkaWUoJ0ludmFsaWQgSlNPTicpOwogICAgfQoKICAgICRwZGYgPSBuZXcgUERGKCdQJywgJ21tJywgJ0E0JywgJGRhdGEpOwogICAgJHBkZi0+QWxpYXNOYlBhZ2VzKCk7CiAgICAkcGRmLT5BZGRQYWdlKCk7CgogICAgJHBkZi0+U2V0Rm9udCgnQXJpYWwnLCAnQicsIDEwKTsKICAgICRwZGYtPlNldEZpbGxDb2xvcigyMzAsIDIzMCwgMjMwKTsKICAgICRwZGYtPkNlbGwoMTAwLCA4LCAnRGVzY3JpcHRpb24nLCAxLCAwLCAnTCcsIHRydWUpOwogICAgJHBkZi0+Q2VsbCgyMCwgOCwgdXRmOF9kZWNvZGUoJ1F0w6knKSwgMSwgMCwgJ0MnLCB0cnVlKTsKICAgICRwZGYtPkNlbGwoMzUsIDgsICdQLlUuIEhUJywgMSwgMCwgJ0MnLCB0cnVlKTsKICAgICRwZGYtPkNlbGwoMzUsIDgsICdUb3RhbCBIVCcsIDEsIDEsICdDJywgdHJ1ZSk7CgogICAgJHBkZi0+U2V0Rm9udCgnQXJpYWwnLCAnJywgMTApOwogICAgJGN1cnJlbmN5U3ltYm9sID0gdXRmOF9kZWNvZGUoJ+KCrCcpOwogICAgZm9yZWFjaCAoJGRhdGFbJ2l0ZW1zJ10gYXMgJGl0ZW0pIHsKICAgICAgICAkcGRmLT5DZWxsKDEwMCwgOCwgdXRmOF9kZWNvZGUoJGl0ZW1bJ2Rlc2NyaXB0aW9uJ10pLCAxLCAwLCAnTCcpOwogICAgICAgICRwZGYtPkNlbGwoMjAsIDgsICRpdGVtWydxdWFudGl0eSddLCAxLCAwLCAnUicpOwogICAgICAgICRwZGYtPkNlbGwoMzUsIDgsIG51bWJlcl9mb3JtYXQoKGZsb2F0KSRpdGVtWydwcmljZSddLCAyLCAnLCcsICcgJykgLiAnICcgLiAkY3VycmVuY3lTeW1ib2wsIDEsIDAsICdSJyk7CiAgICAgICAgJHBkZi0+Q2VsbCgzNSwgOCwgbnVtYmVyX2Zvcm1hdCgoZmxvYXQpJGl0ZW1bJ3RvdGFsJ10sIDIsICcsJywgJyAnKSAuICcgJyAuICRjdXJyZW5jeVN5bWJvbCwgMSwgMSwgJ1InKTsKICAgIH0KCiAgICAkcGRmLT5MbigxMCk7CiAgICAkcGRmLT5TZXRGb250KCdBcmlhbCcsICcnLCAxMCk7CiAgICAkdG90YWxzWCA9IDEyMDsKICAgICR0b3RhbHNMYWJlbFdpZHRoID0gMzU7CiAgICAkdG90YWxzVmFsdWVXaWR0aCA9IDQ1OwoKICAgICRwZGYtPkNlbGwoJHRvdGFsc1gsIDgsICcnLCAwLCAwKTsKICAgICRwZGYtPkNlbGwoJHRvdGFsc0xhYmVsV2lkdGgsIDgsICdUb3RhbCBIVCcsIDEsIDAsICdMJyk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNWYWx1ZVdpZHRoLCA4LCBudW1iZXJfZm9ybWF0KChmbG9hdCkkZGF0YVsndG90YWxzJ11bJ3N1YnRvdGFsJ10sIDIsICcsJywgJyAnKSAuICcgJyAuICRjdXJyZW5jeVN5bWJvbCwgMSwgMSwgJ1InKTsKCiAgICAkcGRmLT5DZWxsKCR0b3RhbHNYLCA4LCAnJywgMCwgMCk7CiAgICAkcGRmLT5DZWxsKCR0b3RhbHNMYWJlbFdpZHRoLCA4LCAnVFZBICgnIC4gJGRhdGFbJ3RvdGFscyddWyd2YXRfcmF0ZSddIC4gJyUpJywgMSwgMCwgJ0wnKTsKICAgICRwZGYtPkNlbGwoJHRvdGFsc1ZhbHVlV2lkdGgsIDgsIG51bWJlcl9mb3JtYXQoKGZsb2F0KSRkYXRhWyd0b3RhbHMnXVsndmF0X3RvdGFsJ10sIDIsICcsJywgJyAnKSAuICcgJyAuICRjdXJyZW5jeVN5bWJvbCwgMSwgMSwgJ1InKTsKCiAgICAkcGRmLT5TZXRGb250KCdBcmlhbCcsICdCJywgMTIpOwogICAgJHBkZi0+Q2VsbCgkdG90YWxzWCwgMTAsICcnLCAwLCAwKTsKICAgICRwZGYtPkNlbGwoJHRvdGFsc0xhYmVsV2lkdGgsIDEwLCAnVG90YWwgVFRDJywgMSwgMCwgJ0wnLCB0cnVlKTsKICAgICRwZGYtPkNlbGwoJHRvdGFsc1ZhbHVlV2lkdGgsIDEwLCBudW1iZXJfZm9ybWF0KChmbG9hdCkkZGF0YVsndG90YWxzJ11bJ3RvdGFsX3R0YyddLCAyLCAnLCcsICcgJykgLiAnICcgLiAkY3VycmVuY3lTeW1ib2wsIDEsIDEsICdSJywgdHJ1ZSk7CgogICAgJGZpbGVuYW1lID0gc3RydG91cHBlcigkZGF0YVsnZG9jX3R5cGUnXSkgLiAnLScgLiBwcmVnX3JlcGxhY2UoJy9bXmEtekEtWjAtOS1dLycsICcnLCAkZGF0YVsnZG9jJ11bJ251bWJlciddKSAuICcucGRmJzsKICAgICRwZGYtPk91dHB1dCgnRCcsICRmaWxlbmFtZSk7CgogICAgLy8gU3RvcCBleGVjdXRpb24gdG8gcHJldmVudCBIVE1MIGZyb20gYmVpbmcgc2VudAogICAgZXhpdDsKfQoKLy8gLS0tIE1PREUgMjogSFRNTCBJTlRFUkZBQ0UgLS0tCj8+CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJmciIgZGF0YS10aGVtZT0ibGlnaHQiPgo8aGVhZD4KICAgIDxtZXRhIGNoYXJzZXQ9IlVURi04Ij4KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wIj4KICAgIDx0aXRsZT5Hw6luw6lyYXRldXIgZGUgRGV2aXMgZXQgRmFjdHVyZXM8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2MuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLnIubmV0L25wbS9AcGljb2Nzcy9waWNvQDEvY3NzL3BpY28ubWluLmNzcyI+CiAgICA8c3R5bGU+CiAgICAgICAgLyogRW1iZWRkZWQgY3VzdG9tIENTUyAqLwogICAgICAgIGJvZHkgewogICAgICAgICAgICBwYWRkaW5nLWJvdHRvbTogNXJlbTsKICAgICAgICB9CiAgICAgICAgLnRhYmxlLWNvbnRhaW5lciB7CiAgICAgICAgICAgIG92ZXJmbG93LXg6IGF1dG87CiAgICAgICAgfQogICAgICAgIHRhYmxlIHRoOmxhc3QtY2hpbGQsCiAgICAgICAgdGFibGUgdGQ6bGFzdC1jaGlsZCB7CiAgICAgICAgICAgIHRleHQtYWxpZ246IHJpZ2h0OwogICAgICAgIH0KICAgICAgICB0YWJsZSBpbnB1dFt0eXBlPSJudW1iZXIiXSB7CiAgICAgICAgICAgIG1pbi13aWR0aDogODBweDsKICAgICAgICAgICAgdGV4dC1hbGlnbjogcmlnaHQ7CiAgICAgICAgfQogICAgICAgIC50b3RhbHMtc2VjdGlvbiB7CiAgICAgICAgICAgIHRleHQtYWxpZ246IHJpZ2h0OwogICAgICAgICAgICBwYWRkaW5nLXRvcDogMXJlbTsKICAgICAgICAgICAgYm9yZGVyLWxlZnQ6IDFweCBzb2xpZCB2YXIoLS1waWNvLW11dGVkLWJvcmRlci1jb2xvcik7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMXJlbTsKICAgICAgICB9CiAgICAgICAgLnRvdGFscy1zZWN0aW9uIHAgewogICAgICAgICAgICBtYXJnaW4tYm90dG9tOiAwLjVyZW07CiAgICAgICAgfQogICAgICAgIC50b3RhbHMtc2VjdGlvbiBzdHJvbmcgewogICAgICAgICAgICBtYXJnaW4tcmlnaHQ6IDFyZW07CiAgICAgICAgfQogICAgICAgIC5ub3Rlcy1zZWN0aW9uIHsKICAgICAgICAgICAgcGFkZGluZy1yaWdodDogMXJlbTsKICAgICAgICB9CiAgICAgICAgLmZvcm0tYWN0aW9ucyB7CiAgICAgICAgICAgIG1hcmdpbi10b3A6IDJyZW07CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIGp1c3RpZnktY29udGVudDogZmxleC1lbmQ7CiAgICAgICAgICAgIGdhcDogMXJlbTsKICAgICAgICB9CiAgICAgICAgLnJlbW92ZS1pdGVtIHsKICAgICAgICAgICAgcGFkZGluZzogMC4yNXJlbSAwLjVyZW07CiAgICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxOwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHk+CiAgICA8bWFpbiBjbGFzcz0iY29udGFpbmVyIj4KICAgICAgICA8aGVhZGVyPgogICAgICAgICAgICA8aDE+R8OpbsOpcmF0ZXVyIGRlIERldmlzICYgRmFjdHVyZXM8L2gxPgogICAgICAgICAgICA8cD5Ub3V0ZXMgbGVzIGRvbm7DqWVzIHNvbnQgc2F1dmVnYXJkw6llcyBsb2NhbGVtZW50IGRhbnMgdm90cmUgbmF2aWdhdGV1ci4gQXVjdW5lIGluZm9ybWF0aW9uIG4nZXN0IGVudm95w6llIMOgIHVuIHNlcnZldXIuPC9wPgogICAgICAgIDwvaGVhZGVyPgoKICAgICAgICA8Zm9ybSBpZD0iaW52b2ljZS1mb3JtIj4KICAgICAgICAgICAgPGZpZWxkc2V0PgogICAgICAgICAgICAgICAgPGxlZ2VuZD5UeXBlIGRlIGRvY3VtZW50PC9sZWdlbmQ+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJkb2MtdHlwZS1xdW90ZSI+CiAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iZG9jLXR5cGUtcXVvdGUiIG5hbWU9ImRvY190eXBlIiB2YWx1ZT0iRGV2aXMiIGNoZWNrZWQ+CiAgICAgICAgICAgICAgICAgICAgRGV2aXMKICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJkb2MtdHlwZS1pbnZvaWNlIj4KICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJkb2MtdHlwZS1pbnZvaWNlIiBuYW1lPSJkb2NfdHlwZSIgdmFsdWU9IkZhY3R1cmUiPgogICAgICAgICAgICAgICAgICAgIEZhY3R1cmUKICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgIDwvZmllbGRzZXQ+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJncmlkIj4KICAgICAgICAgICAgICAgIDxhcnRpY2xlPgogICAgICAgICAgICAgICAgICAgIDxoMyBpZD0iY29tcGFueS10aXRsZSI+Vm90cmUgRW50cmVwcmlzZTwvaDM+CiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iY29tcGFueV9uYW1lIj5Ob20gZGUgbCdlbnRyZXByaXNlPC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgaWQ9ImNvbXBhbnlfbmFtZSIgbmFtZT0iY29tcGFueV9uYW1lIiByZXF1aXJlZD4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJjb21wYW55X2FkZHJlc3MiPkFkcmVzc2U8L2xhYmVsPgogICAgICAgICAgICAgICAgICAgIDx0ZXh0YXJlYSBpZD0iY29tcGFueV9hZGRyZXNzIiBuYW1lPSJjb21wYW55X2FkZHJlc3MiIHJvd3M9IjMiPjwvdGV4dGFyZWE+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZ3JpZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9ImNvbXBhbnlfc2lyZXQiPlNJUkVUIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iY29tcGFueV9zaXJldCIgbmFtZT0iY29tcGFueV9zaXJldCI+PC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iY29tcGFueV92YXQiPk7CsCBUVkEgPGlucHV0IHR5cGU9InRleHQiIGlkPSJjb21wYW55X3ZhdCIgbmFtZT0iY29tcGFueV92YXQiPjwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0ic2F2ZS1jb21wYW55LWluZm8iIGNsYXNzPSJzZWNvbmRhcnkiPkVucmVnaXN0cmVyIG1lcyBpbmZvcm1hdGlvbnM8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDwvYXJ0aWNsZT4KCiAgICAgICAgICAgICAgICA8YXJ0aWNsZT4KICAgICAgICAgICAgICAgICAgICA8aDM+Q2xpZW50PC9oMz4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJjbGllbnRfbmFtZSI+Tm9tIGR1IGNsaWVudDwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJjbGllbnRfbmFtZSIgbmFtZT0iY2xpZW50X25hbWUiIHJlcXVpcmVkPgogICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9ImNsaWVudF9hZGRyZXNzIj5BZHJlc3NlIGR1IGNsaWVudDwvbGFiZWw+CiAgICAgICAgICAgICAgICAgICAgPHRleHRhcmVhIGlkPSJjbGllbnRfYWRkcmVzcyIgbmFtZT0iY2xpZW50X2FkZHJlc3MiIHJvd3M9IjMiPjwvdGV4dGFyZWE+CiAgICAgICAgICAgICAgICA8L2FydGljbGU+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAKICAgICAgICAgICAgPGFydGljbGU+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJncmlkIj4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJkb2NfbnVtYmVyIj48c3BhbiBpZD0iZG9jLXR5cGUtbGFiZWwiPk51bcOpcm8gZGUgRGV2aXM8L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iZG9jX251bWJlciIgbmFtZT0iZG9jX251bWJlciIgcmVxdWlyZWQ+CiAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJkb2NfZGF0ZSI+RGF0ZQogICAgICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0iZGF0ZSIgaWQ9ImRvY19kYXRlIiBuYW1lPSJkb2NfZGF0ZSIgcmVxdWlyZWQ+CiAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJkb2NfZHVlX2RhdGUiPkRhdGUgZCfDqWNow6lhbmNlCiAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJkYXRlIiBpZD0iZG9jX2R1ZV9kYXRlIiBuYW1lPSJkb2NfZHVlX2RhdGUiPgogICAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9hcnRpY2xlPgoKICAgICAgICAgICAgPGFydGljbGU+CiAgICAgICAgICAgICAgICA8aDM+TGlnbmVzIGRlIHByZXN0YXRpb248L2gzPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idGFibGUtY29udGFpbmVyIj4KICAgICAgICAgICAgICAgICAgICA8dGFibGU+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0aGVhZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGg+RGVzY3JpcHRpb248L3RoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0aD5RdMOpPC90aD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGg+UC5VLiBIVDwvdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRoPlRvdGFsIEhUPC90aD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGg+PC90aD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGhlYWQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0Ym9keSBpZD0iaXRlbS1saXN0Ij48L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0iYWRkLWl0ZW0iIGNsYXNzPSJzZWNvbmRhcnkiPkFqb3V0ZXIgdW5lIGxpZ25lPC9idXR0b24+CiAgICAgICAgICAgIDwvYXJ0aWNsZT4KCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImdyaWQiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0ibm90ZXMtc2VjdGlvbiI+CiAgICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9Im5vdGVzIj5Ob3RlcyAvIENvbmRpdGlvbnMgZGUgcGFpZW1lbnQ8L2xhYmVsPgogICAgICAgICAgICAgICAgICAgICA8dGV4dGFyZWEgaWQ9Im5vdGVzIiBuYW1lPSJub3RlcyIgcm93cz0iNCI+UGFpZW1lbnQgw6AgcsOpY2VwdGlvbiBkZSBsYSBmYWN0dXJlLjwvdGV4dGFyZWE+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxhcnRpY2xlIGNsYXNzPSJ0b3RhbHMtc2VjdGlvbiI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZ3JpZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9InZhdF9yYXRlIj5UYXV4IGRlIFRWQSAoJSk8L2xhYmVsPgogICAgICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0ibnVtYmVyIiBpZD0idmF0X3JhdGUiIG5hbWU9InZhdF9yYXRlIiB2YWx1ZT0iMjAiIHN0ZXA9IjAuMSIgcmVxdWlyZWQ+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHA+PHN0cm9uZz5Ub3RhbCBIVCA6PC9zdHJvbmc+IDxzcGFuIGlkPSJzdWJ0b3RhbCI+MC4wMDwvc3Bhbj4g4oKsPC9wPgogICAgICAgICAgICAgICAgICAgIDxwPjxzdHJvbmc+VFZBIDo8L3N0cm9uZz4gPHNwYW4gaWQ9InZhdC10b3RhbCI+MC4wMDwvc3Bhbj4g4oKsPC9wPgogICAgICAgICAgICAgICAgICAgIDxwPjxzdHJvbmc+VG90YWwgVFRDIDo8L3N0cm9uZz4gPHNwYW4gaWQ9InRvdGFsLXR0YyI+MC4wMDwvc3Bhbj4g4oKsPC9wPgogICAgICAgICAgICAgICAgPC9hcnRpY2xlPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDxmb290ZXIgY2xhc3M9ImZvcm0tYWN0aW9ucyI+CiAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9InN1Ym1pdCIgaWQ9ImdlbmVyYXRlLXBkZiI+R8OpbsOpcmVyIGxlIFBERjwvYnV0dG9uPgogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJyZXNldC1mb3JtIiBjbGFzcz0ic2Vjb25kYXJ5IG91dGxpbmUiPlLDqWluaXRpYWxpc2VyPC9idXR0b24+CiAgICAgICAgICAgIDwvZm9vdGVyPgogICAgICAgIDwvZm9ybT4KICAgIDwvbWFpbj4KCiAgICA8c2NyaXB0PgogICAgICAgIC8vIC0tLSBFbWJlZGRlZCBKYXZhU2NyaXB0IC0tLQogICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7CiAgICAgICAgICAgIGNvbnN0IGZvcm0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52b2ljZS1mb3JtJyk7CiAgICAgICAgICAgIGNvbnN0IGl0ZW1MaXN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2l0ZW0tbGlzdCcpOwogICAgICAgICAgICBjb25zdCBhZGRJdGVtQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZC1pdGVtJyk7CiAgICAgICAgICAgIGNvbnN0IHNhdmVDb21wYW55SW5mb0J0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzYXZlLWNvbXBhbnktaW5mbycpOwogICAgICAgICAgICBjb25zdCByZXNldEZvcm1CdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVzZXQtZm9ybScpOwogICAgICAgICAgICBjb25zdCBzdWJ0b3RhbEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3N1YnRvdGFsJyk7CiAgICAgICAgICAgIGNvbnN0IHZhdFRvdGFsRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndmF0LXRvdGFsJyk7CiAgICAgICAgICAgIGNvbnN0IHRvdGFsVHRjRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndG90YWwtdHRjJyk7CiAgICAgICAgICAgIGNvbnN0IGRvY1R5cGVSYWRpb3MgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFtuYW1lPSJkb2NfdHlwZSJdJyk7CiAgICAgICAgICAgIGNvbnN0IGRvY1R5cGVMYWJlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2MtdHlwZS1sYWJlbCcpOwogICAgICAgICAgICBjb25zdCBkb2NOdW1iZXJJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2NfbnVtYmVyJyk7CiAgICAgICAgICAgIGNvbnN0IGNvbXBhbnlUaXRsZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55LXRpdGxlJyk7CgogICAgICAgICAgICBjb25zdCBjYWxjdWxhdGVUb3RhbHMgPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBsZXQgc3VidG90YWwgPSAwOwogICAgICAgICAgICAgICAgaXRlbUxpc3QucXVlcnlTZWxlY3RvckFsbCgndHInKS5mb3JFYWNoKHJvdyA9PiB7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgcXVhbnRpdHkgPSBwYXJzZUZsb2F0KHJvdy5xdWVyeVNlbGVjdG9yKCcucXVhbnRpdHknKS52YWx1ZSkgfHwgMDsKICAgICAgICAgICAgICAgICAgICBjb25zdCBwcmljZSA9IHBhcnNlRmxvYXQocm93LnF1ZXJ5U2VsZWN0b3IoJy5wcmljZScpLnZhbHVlKSB8fCAwOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHJvd1RvdGFsID0gcXVhbnRpdHkgKiBwcmljZTsKICAgICAgICAgICAgICAgICAgICByb3cucXVlcnlTZWxlY3RvcignLnJvdy10b3RhbCcpLnRleHRDb250ZW50ID0gcm93VG90YWwudG9GaXhlZCgyKTsKICAgICAgICAgICAgICAgICAgICBzdWJ0b3RhbCArPSByb3dUb3RhbDsKICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgY29uc3QgdmF0UmF0ZSA9IHBhcnNlRmxvYXQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZhdF9yYXRlJykudmFsdWUpIHx8IDA7CiAgICAgICAgICAgICAgICBjb25zdCB2YXRUb3RhbCA9IHN1YnRvdGFsICogKHZhdFJhdGUgLyAxMDApOwogICAgICAgICAgICAgICAgY29uc3QgdG90YWxUdGMgPSBzdWJ0b3RhbCArIHZhdFRvdGFsOwogICAgICAgICAgICAgICAgc3VidG90YWxFbC50ZXh0Q29udGVudCA9IHN1YnRvdGFsLnRvRml4ZWQoMik7CiAgICAgICAgICAgICAgICB2YXRUb3RhbEVsLnRleHRDb250ZW50ID0gdmF0VG90YWwudG9GaXhlZCgyKTsKICAgICAgICAgICAgICAgIHRvdGFsVHRjRWwudGV4dENvbnRlbnQgPSB0b3RhbFR0Yy50b0ZpeGVkKDIpOwogICAgICAgICAgICB9OwoKICAgICAgICAgICAgY29uc3QgYWRkTGluZUl0ZW0gPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBjb25zdCByb3cgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0cicpOwogICAgICAgICAgICAgICAgcm93LmlubmVySFRNTCA9IGAKICAgICAgICAgICAgICAgICAgICA8dGQ+PGlucHV0IHR5cGU9InRleHQiIGNsYXNzPSJkZXNjcmlwdGlvbiIgcGxhY2Vob2xkZXI9IkRlc2NyaXB0aW9uIGRlIGxhIHByZXN0YXRpb24iPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgPHRkPjxpbnB1dCB0eXBlPSJudW1iZXIiIGNsYXNzPSJxdWFudGl0eSIgdmFsdWU9IjEiIHN0ZXA9ImFueSI+PC90ZD4KICAgICAgICAgICAgICAgICAgICA8dGQ+PGlucHV0IHR5cGU9Im51bWJlciIgY2xhc3M9InByaWNlIiB2YWx1ZT0iMC4wMCIgc3RlcD0iYW55Ij48L3RkPgogICAgICAgICAgICAgICAgICAgIDx0ZD48c3BhbiBjbGFzcz0icm93LXRvdGFsIj4wLjAwPC9zcGFuPiDigqw8L3RkPgogICAgICAgICAgICAgICAgICAgIDx0ZD48YnV0dG9uIHR5cGU9ImJ1dHRvbiIgY2xhc3M9InJlbW92ZS1pdGVtIHNlY29uZGFyeSBvdXRsaW5lIj7DlzwvYnV0dG9uPjwvdGQ+CiAgICAgICAgICAgICAgICBgOwogICAgICAgICAgICAgICAgaXRlbUxpc3QuYXBwZW5kQ2hpbGQocm93KTsKICAgICAgICAgICAgICAgIHJvdy5xdWVyeVNlbGVjdG9yKCcucmVtb3ZlLWl0ZW0nKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICAgICAgICAgICAgICByb3cucmVtb3ZlKCk7CiAgICAgICAgICAgICAgICAgICAgY2FsY3VsYXRlVG90YWxzKCk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZURvY1R5cGUgPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBjb25zdCBzZWxlY3RlZFR5cGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdpbnB1dFtuYW1lPSJkb2NfdHlwZSJdOmNoZWNrZWQnKS52YWx1ZTsKICAgICAgICAgICAgICAgIGRvY1R5cGVMYWJlbC50ZXh0Q29udGVudCA9IGBOdW3DqXJvIGRlICR7c2VsZWN0ZWRUeXBlfWA7CiAgICAgICAgICAgICAgICBjb25zdCBwcmVmaXggPSBzZWxlY3RlZFR5cGUgPT09ICdEZXZpcycgPyAnREUnIDogJ0ZBJzsKICAgICAgICAgICAgICAgIGNvbnN0IGN1cnJlbnRWYWwgPSBkb2NOdW1iZXJJbnB1dC52YWx1ZTsKICAgICAgICAgICAgICAgIGlmICghY3VycmVudFZhbC5zdGFydHNXaXRoKCdERS0nKSAmJiAhY3VycmVudFZhbC5zdGFydHNXaXRoKCdGQS0nKSB8fCBjdXJyZW50VmFsID09PSAnJykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHllYXIgPSBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCk7CiAgICAgICAgICAgICAgICAgICAgZG9jTnVtYmVySW5wdXQudmFsdWUgPSBgJHtwcmVmaXh9LSR7eWVhcn0tMDAxYDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGNvbnN0IHNhdmVDb21wYW55SW5mbyA9ICgpID0+IHsKICAgICAgICAgICAgICAgIGNvbnN0IGNvbXBhbnlJbmZvID0gewogICAgICAgICAgICAgICAgICAgIG5hbWU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X25hbWUnKS52YWx1ZSwKICAgICAgICAgICAgICAgICAgICBhZGRyZXNzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9hZGRyZXNzJykudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgc2lyZXQ6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X3NpcmV0JykudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgdmF0OiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV92YXQnKS52YWx1ZSwKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnY29tcGFueUluZm8nLCBKU09OLnN0cmluZ2lmeShjb21wYW55SW5mbykpOwogICAgICAgICAgICAgICAgY29tcGFueVRpdGxlLnRleHRDb250ZW50ID0gJ1ZvdHJlIEVudHJlcHJpc2UgKEVucmVnaXN0csOpKSc7CiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IGNvbXBhbnlUaXRsZS50ZXh0Q29udGVudCA9ICdWb3RyZSBFbnRyZXByaXNlJywgMjAwMCk7CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBjb25zdCBsb2FkQ29tcGFueUluZm8gPSAoKSA9PiB7CiAgICAgICAgICAgICAgICBjb25zdCBjb21wYW55SW5mbyA9IEpTT04ucGFyc2UobG9jYWxTdG9yYWdlLmdldEl0ZW0oJ2NvbXBhbnlJbmZvJykpOwogICAgICAgICAgICAgICAgaWYgKGNvbXBhbnlJbmZvKSB7CiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfbmFtZScpLnZhbHVlID0gY29tcGFueUluZm8ubmFtZSB8fCAnJzsKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9hZGRyZXNzJykudmFsdWUgPSBjb21wYW55SW5mby5hZGRyZXNzIHx8ICcnOwogICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X3NpcmV0JykudmFsdWUgPSBjb21wYW55SW5mby5zaXJldCB8fCAnJzsKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV92YXQnKS52YWx1ZSA9IGNvbXBhbnlJbmZvLnZhdCB8fCAnJzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKICAgICAgICAgICAgCiAgICAgICAgICAgIGNvbnN0IHJlc2V0Rm9ybSA9ICgpID0+IHsKICAgICAgICAgICAgICAgIGlmKGNvbmZpcm0oIlZvdWxlei12b3VzIHZyYWltZW50IHLDqWluaXRpYWxpc2VyIGxlIGZvcm11bGFpcmUgPyBMZXMgaW5mb3JtYXRpb25zIGRlIHZvdHJlIGVudHJlcHJpc2UgcmVzdGVyb250IGVucmVnaXN0csOpZXMuIikpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCBjb21wYW55SW5mbyA9IEpTT04ucGFyc2UobG9jYWxTdG9yYWdlLmdldEl0ZW0oJ2NvbXBhbnlJbmZvJykpOwogICAgICAgICAgICAgICAgICAgIGZvcm0ucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnY29tcGFueUluZm8nLCBKU09OLnN0cmluZ2lmeShjb21wYW55SW5mbykpOwogICAgICAgICAgICAgICAgICAgIGxvYWRDb21wYW55SW5mbygpOwogICAgICAgICAgICAgICAgICAgIGl0ZW1MaXN0LmlubmVySFRNTCA9ICcnOwogICAgICAgICAgICAgICAgICAgIGFkZExpbmVJdGVtKCk7CiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2RvY19kYXRlJykudmFsdWVBc0RhdGUgPSBuZXcgRGF0ZSgpOwogICAgICAgICAgICAgICAgICAgIHVwZGF0ZURvY1R5cGUoKTsKICAgICAgICAgICAgICAgICAgICBjYWxjdWxhdGVUb3RhbHMoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgY29uc3QgZ2VuZXJhdGVQREYgPSBhc3luYyAoZSkgPT4gewogICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICAgICAgY29uc3QgaXRlbXMgPSBBcnJheS5mcm9tKGl0ZW1MaXN0LnF1ZXJ5U2VsZWN0b3JBbGwoJ3RyJykpLm1hcChyb3cgPT4gKHsKICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogcm93LnF1ZXJ5U2VsZWN0b3IoJy5kZXNjcmlwdGlvbicpLnZhbHVlLAogICAgICAgICAgICAgICAgICAgIHF1YW50aXR5OiByb3cucXVlcnlTZWxlY3RvcignLnF1YW50aXR5JykudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgcHJpY2U6IHJvdy5xdWVyeVNlbGVjdG9yKCcucHJpY2UnKS52YWx1ZSwKICAgICAgICAgICAgICAgICAgICB0b3RhbDogcGFyc2VGbG9hdChyb3cucXVlcnlTZWxlY3RvcignLnJvdy10b3RhbCcpLnRleHRDb250ZW50KQogICAgICAgICAgICAgICAgfSkpOwogICAgICAgICAgICAgICAgY29uc3QgZm9ybURhdGEgPSB7CiAgICAgICAgICAgICAgICAgICAgZG9jX3R5cGU6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ2lucHV0W25hbWU9ImRvY190eXBlIl06Y2hlY2tlZCcpLnZhbHVlLAogICAgICAgICAgICAgICAgICAgIGNvbXBhbnk6IHsgbmFtZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbXBhbnlfbmFtZScpLnZhbHVlLCBhZGRyZXNzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9hZGRyZXNzJykudmFsdWUsIHNpcmV0OiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29tcGFueV9zaXJldCcpLnZhbHVlLCB2YXQ6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb21wYW55X3ZhdCcpLnZhbHVlIH0sCiAgICAgICAgICAgICAgICAgICAgY2xpZW50OiB7IG5hbWU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjbGllbnRfbmFtZScpLnZhbHVlLCBhZGRyZXNzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2xpZW50X2FkZHJlc3MnKS52YWx1ZSB9LAogICAgICAgICAgICAgICAgICAgIGRvYzogeyBudW1iZXI6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2NfbnVtYmVyJykudmFsdWUsIGRhdGU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkb2NfZGF0ZScpLnZhbHVlLCBkdWVfZGF0ZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2RvY19kdWVfZGF0ZScpLnZhbHVlIH0sCiAgICAgICAgICAgICAgICAgICAgaXRlbXM6IGl0ZW1zLAogICAgICAgICAgICAgICAgICAgIHRvdGFsczogeyBzdWJ0b3RhbDogc3VidG90YWxFbC50ZXh0Q29udGVudCwgdmF0X3JhdGU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2YXRfcmF0ZScpLnZhbHVlLCB2YXRfdG90YWw6IHZhdFRvdGFsRWwudGV4dENvbnRlbnQsIHRvdGFsX3R0YzogdG90YWxUdGNFbC50ZXh0Q29udGVudCB9LAogICAgICAgICAgICAgICAgICAgIG5vdGVzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbm90ZXMnKS52YWx1ZQogICAgICAgICAgICAgICAgfTsKICAgICAgICAgICAgICAgIGNvbnN0IHBkZkJ1dHRvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdnZW5lcmF0ZS1wZGYnKTsKICAgICAgICAgICAgICAgIHBkZkJ1dHRvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtYnVzeScsICd0cnVlJyk7CiAgICAgICAgICAgICAgICBwZGZCdXR0b24udGV4dENvbnRlbnQgPSAnR8OpbsOpcmF0aW9uLi4uJzsKICAgICAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgnJywgeyAvLyBQb3N0IHRvIHRoZSBzYW1lIGZpbGUKICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiAnUE9TVCcsCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogICAgICAgICAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShmb3JtRGF0YSkKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB0aHJvdyBuZXcgRXJyb3IoYEVycmV1ciBkdSBzZXJ2ZXVyOiAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgYmxvYiA9IGF3YWl0IHJlc3BvbnNlLmJsb2IoKTsKICAgICAgICAgICAgICAgICAgICBjb25zdCB1cmwgPSB3aW5kb3cuVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBhID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpOwogICAgICAgICAgICAgICAgICAgIGEuc3R5bGUuZGlzcGxheSA9ICdub25lJzsKICAgICAgICAgICAgICAgICAgICBhLmhyZWYgPSB1cmw7CiAgICAgICAgICAgICAgICAgICAgYS5kb3dubG9hZCA9IGAke2Zvcm1EYXRhLmRvY190eXBlLnRvVXBwZXJDYXNlKCl9LSR7Zm9ybURhdGEuZG9jLm51bWJlci5yZXBsYWNlKC9bXmEtekEtWjAtOS1dL2csICcnKX0ucGRmYDsKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGEpOwogICAgICAgICAgICAgICAgICAgIGEuY2xpY2soKTsKICAgICAgICAgICAgICAgICAgICB3aW5kb3cuVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpOwogICAgICAgICAgICAgICAgICAgIGEucmVtb3ZlKCk7CiAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0VycmV1ciBsb3JzIGRlIGxhIGfDqW7DqXJhdGlvbiBkdSBQREY6JywgZXJyb3IpOwogICAgICAgICAgICAgICAgICAgIGFsZXJ0KCdVbmUgZXJyZXVyIGVzdCBzdXJ2ZW51ZSBsb3JzIGRlIGxhIGfDqW7DqXJhdGlvbiBkdSBQREYuJyk7CiAgICAgICAgICAgICAgICB9IGZpbmFsbHkgewogICAgICAgICAgICAgICAgICAgIHBkZkJ1dHRvbi5yZW1vdmVBdHRyaWJ1dGUoJ2FyaWEtYnVzeScpOwogICAgICAgICAgICAgICAgICAgIHBkZkJ1dHRvbi50ZXh0Q29udGVudCA9ICdHw6luw6lyZXIgbGUgUERGJzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGFkZEl0ZW1CdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBhZGRMaW5lSXRlbSk7CiAgICAgICAgICAgIGZvcm0uYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCBjYWxjdWxhdGVUb3RhbHMpOwogICAgICAgICAgICBmb3JtLmFkZEV2ZW50TGlzdGVuZXIoJ3N1Ym1pdCcsIGdlbmVyYXRlUERGKTsKICAgICAgICAgICAgc2F2ZUNvbXBhbnlJbmZvQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgc2F2ZUNvbXBhbnlJbmZvKTsKICAgICAgICAgICAgcmVzZXRGb3JtQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcmVzZXRGb3JtKTsKICAgICAgICAgICAgZG9jVHlwZVJhZGlvcy5mb3JFYWNoKHJhZGlvID0+IHJhZGlvLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHVwZGF0ZURvY1R5cGUpKTsKCiAgICAgICAgICAgIC8vIC0tLSBJbml0aWFsaXNhdGlvbiAtLS0KICAgICAgICAgICAgbG9hZENvbXBhbnlJbmZvKCk7CiAgICAgICAgICAgIGFkZExpbmVJdGVtKCk7CiAgICAgICAgICAgIGNhbGN1bGF0ZVRvdGFscygpOwogICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZG9jX2RhdGUnKS52YWx1ZUFzRGF0ZSA9IG5ldyBEYXRlKCk7CiAgICAgICAgICAgIHVwZGF0ZURvY1R5cGUoKTsKICAgICAgICB9KTsKICAgIDwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4=