|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Dataset Preparation Interface</title>
|
|
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
|
<meta name="viewport" content="width=device-width, initial-scale=5.0, user-scalable=yes">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='mobile.css') }}">
|
|
<meta name="csrf-token" content="{{ session['csrf_token'] }}">
|
|
</head>
|
|
<body data-auth-enabled="{{ 'true' if enable_auth else 'false' }}">
|
|
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
|
<div id="uploadToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="toast-header">
|
|
|
|
<strong class="me-auto">Upload Status</strong>
|
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
<div class="toast-body"></div>
|
|
</div>
|
|
</div>
|
|
<div class="container-fluid px-3">
|
|
<div class="d-flex justify-content-between align-items-center pt-3 pb-3">
|
|
<h4 class="mb-0" style="color: #1a73e8;">COILD Dhravani</h4>
|
|
{% if enable_auth and session.get('user') %}
|
|
<div class="d-flex align-items-center gap-3">
|
|
{% if session.get('user', {}).get('is_moderator') %}
|
|
<a href="{{ url_for('validation.validate') }}" class="btn btn-outline-primary d-none d-md-inline-block">Validate</a>
|
|
{% endif %}
|
|
{% if session.get('user', {}).get('role') == 'admin' %}
|
|
<a href="{{ url_for('admin.admin_interface') }}" class="btn btn-primary d-none d-md-inline-block">Admin Panel</a>
|
|
{% endif %}
|
|
<a href="{{ url_for('logout') }}" class="btn btn-outline-primary">Logout</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
|
|
<div class="row g-4 main-container">
|
|
|
|
<div class="col-9">
|
|
<div id="recordingInterface" class="card h-100">
|
|
<div class="card-body px-3 recording-interface-scroll">
|
|
<div class="d-flex align-items-center justify-content-between recording-header mb-2">
|
|
<h4 style="color: #202124;">Transcript</h4>
|
|
|
|
<button id="settingsToggle" class="btn btn-icon d-md-none" aria-label="Settings">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
<path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="initial-message" id="initialMessage">
|
|
<div class="text-center empty-state">
|
|
{% if enable_auth and not session.get('user') %}
|
|
<img src="{{ url_for('static', filename='microphone-icon.svg') }}" alt="Microphone" width="64" height="64">
|
|
<h5 class="mt-4" style="color: #202124;">Sign In</h5>
|
|
<p class="text-muted">
|
|
to continue to Dataset Recorder
|
|
</p>
|
|
<a href="{{ url_for('login') }}" class="btn btn-primary mt-3">Sign In</a>
|
|
{% else %}
|
|
<img src="{{ url_for('static', filename='microphone-icon.svg') }}" alt="Microphone" width="64" height="64">
|
|
<h5 class="mt-4" style="color: #202124;">Ready to Start Recording</h5>
|
|
<p class="text-muted">
|
|
Please complete the session setup in Settings to begin recording.
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="transcript-container" style="display: none;">
|
|
<div class="transcript-box mb-2">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 style="color: #5f6368;">Current Transcript:
|
|
<span id="recordingStatus" class="badge bg-warning ms-2" style="display: none;">Previously Recorded</span>
|
|
</h5>
|
|
<div class="font-size-control">
|
|
<button class="btn btn-icon" onclick="decreaseFontSize()">A-</button>
|
|
<span id="fontSizeDisplay">16</span>
|
|
<button class="btn btn-icon" onclick="increaseFontSize()">A+</button>
|
|
</div>
|
|
</div>
|
|
<p id="currentTranscript" class="mb-3" style="color: #202124; line-height: 1.6;"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="recording-controls">
|
|
<div class="btn-group mb-4">
|
|
<button id="recordBtn" class="btn btn-primary px-4" title="Start/Stop Recording (R)">Start Recording</button>
|
|
<button id="playBtn" class="btn btn-secondary px-4" title="Play (Space)" disabled>Play</button>
|
|
<button id="saveBtn" class="btn btn-success px-4" title="Save (Enter)" disabled>Save</button>
|
|
<button id="rerecordBtn" class="btn btn-warning px-4" title="Re-record (Backspace)" disabled>Re-record</button>
|
|
</div>
|
|
|
|
<div class="d-flex align-items-center gap-3 navigation-controls">
|
|
<button id="prevBtn" class="btn btn-outline-primary" title="Previous (←)">Previous</button>
|
|
<button id="skipBtn" class="btn btn-outline-warning" title="Skip (→)">Skip</button>
|
|
<div class="ms-auto">
|
|
<p class="mb-0" style="color: #5f6368;">
|
|
<span id="progress" class="fw-medium">0</span>/<span id="total" class="fw-medium">0</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="col-3" id="settingsPanel">
|
|
<div id="setupForm" class="card sticky-card">
|
|
<div class="card-body p-3 setup-form-container">
|
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
|
<h5 style="color: #202124;">Settings</h5>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="badge bg-primary">Required</span>
|
|
<button id="settingsCloseBtn" class="settings-close d-md-none" aria-label="Close settings">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="setup-form-scroll">
|
|
<form id="sessionForm" class="d-flex flex-column" enctype="multipart/form-data">
|
|
|
|
<div class="form-fields-container">
|
|
|
|
<div class="form-group">
|
|
<div class="material-input">
|
|
<input type="text"
|
|
class="form-control"
|
|
id="speakerName"
|
|
name="speakerName"
|
|
{% if session.get('user') and enable_auth %}disabled{% endif %}
|
|
required>
|
|
<label for="speakerName">
|
|
{% if session.get('user') and enable_auth %}
|
|
Speaker Name (Auto-filled)
|
|
{% else %}
|
|
Speaker Name
|
|
{% endif %}
|
|
</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
<div class="form-text text-muted"></div>
|
|
</div>
|
|
|
|
|
|
<div class="form-group">
|
|
<div class="material-input">
|
|
<select class="form-control" id="language" name="language" required>
|
|
<option value="">Select a language</option>
|
|
</select>
|
|
<label for="language">Language</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row g-3 mb-2">
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="domain" name="domain" required>
|
|
|
|
</select>
|
|
<label for="domain">Domain</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="subdomain" name="subdomain" required disabled>
|
|
|
|
<option value="">Loading...</option>
|
|
</select>
|
|
<label for="subdomain">Subdomain</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row g-3 mb-2">
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="gender" required>
|
|
<option value="">Select gender</option>
|
|
<option value="M">Male</option>
|
|
<option value="F">Female</option>
|
|
<option value="O">Other</option>
|
|
</select>
|
|
<label for="gender">Gender</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="age_group" name="age_group" required>
|
|
<option value="">Select age group</option>
|
|
<option value="Teenagers">Teenagers</option>
|
|
<option value="Adults">Adults</option>
|
|
<option value="Elderly">Elderly</option>
|
|
</select>
|
|
<label for="age_group">Age Group</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row g-3 mb-2">
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="country" name="country" required>
|
|
<option value="">Select Country</option>
|
|
</select>
|
|
<label for="country">Country</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="state" name="state" required>
|
|
<option value="">Select State/Province</option>
|
|
</select>
|
|
<label for="state">State/Province</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row g-3 mb-2">
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<input type="text" class="form-control" id="city" required>
|
|
<label for="city">City</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="material-input">
|
|
<select class="form-control" id="accent" name="accent" required>
|
|
<option value="">Select accent</option>
|
|
<option value="Rural">Rural</option>
|
|
<option value="Urban">Urban</option>
|
|
</select>
|
|
<label for="accent">Accent</label>
|
|
<div class="input-line"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="form-group mt-3">
|
|
<div class="material-checkbox mb-3">
|
|
<input type="checkbox" id="consentCheckbox" required>
|
|
<label for="consentCheckbox">
|
|
I agree to the <a href="{{ url_for('privacy') }}" target="_blank">Privacy Policy and Terms of Service</a>
|
|
</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100" disabled id="startSessionBtn">
|
|
Start Session
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="{{ url_for('static', filename='recorder.js') }}"></script>
|
|
|
|
|
|
<script src="{{ url_for('static', filename='country-states.js') }}"></script>
|
|
|
|
|
|
<div id="authCheck" class="d-none" data-authenticated="{{ 'true' if session.get('user') or not enable_auth else 'false' }}"></div>
|
|
|
|
<script>
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
const authEnabled = document.body.getAttribute('data-auth-enabled') === 'true';
|
|
const isAuthenticated = document.getElementById('authCheck').getAttribute('data-authenticated') === 'true';
|
|
|
|
|
|
if (authEnabled && !isAuthenticated) {
|
|
console.log('User not authenticated, redirecting to login');
|
|
|
|
const recordingInterface = document.getElementById('recordingInterface');
|
|
if (recordingInterface) {
|
|
recordingInterface.style.display = 'none';
|
|
}
|
|
|
|
|
|
const initialMessage = document.getElementById('initialMessage');
|
|
if (initialMessage) {
|
|
initialMessage.style.display = 'block';
|
|
}
|
|
|
|
|
|
const settingsPanel = document.getElementById('settingsPanel');
|
|
if (settingsPanel) {
|
|
settingsPanel.style.display = 'none';
|
|
}
|
|
|
|
|
|
setTimeout(() => {
|
|
if (!document.querySelector('.btn-primary[href*="login"]')) {
|
|
window.location.href = '{{ url_for("login") }}';
|
|
}
|
|
}, 1500);
|
|
}
|
|
});
|
|
|
|
async function loadLanguages() {
|
|
try {
|
|
const response = await fetch('/languages');
|
|
const data = await response.json();
|
|
|
|
const languageSelect = document.getElementById('language');
|
|
const userLanguage = languageSelect.getAttribute('data-selected') || '{{ session.user.language|default("") }}';
|
|
|
|
|
|
languageSelect.innerHTML = '<option value="">Select a language</option>';
|
|
|
|
if (data.status === 'success' && data.languages && data.languages.length > 0) {
|
|
const languages = data.languages.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
languages.forEach(lang => {
|
|
const option = document.createElement('option');
|
|
option.value = lang.code;
|
|
option.textContent = `${lang.name} (${lang.native_name})`;
|
|
|
|
if (lang.code === userLanguage) {
|
|
option.selected = true;
|
|
languageSelect.parentElement.classList.add('is-filled');
|
|
}
|
|
languageSelect.appendChild(option);
|
|
});
|
|
} else {
|
|
|
|
const option = document.createElement('option');
|
|
option.disabled = true;
|
|
option.textContent = 'No languages available';
|
|
languageSelect.appendChild(option);
|
|
|
|
|
|
showToast('No languages available with transcriptions', 'warning');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading languages:', error);
|
|
showToast('Error loading languages. Please refresh the page.', 'error');
|
|
}
|
|
}
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', loadLanguages);
|
|
|
|
|
|
function loadUserProfile() {
|
|
const userDataElem = document.getElementById('userData');
|
|
if (!userDataElem) return;
|
|
|
|
try {
|
|
const userData = JSON.parse(userDataElem.textContent);
|
|
const languageSelect = document.getElementById('language');
|
|
|
|
|
|
if (userData.language) {
|
|
languageSelect.setAttribute('data-selected', userData.language);
|
|
}
|
|
|
|
|
|
const fields = ['gender', 'age_group', 'country', 'state', 'city', 'accent'];
|
|
fields.forEach(field => {
|
|
const elem = document.getElementById(field);
|
|
if (elem && userData[field]) {
|
|
elem.value = userData[field];
|
|
elem.parentElement.classList.add('is-filled');
|
|
|
|
|
|
if (field === 'country' && userData['state']) {
|
|
const event = new Event('change');
|
|
elem.dispatchEvent(event);
|
|
|
|
setTimeout(() => {
|
|
const stateElem = document.getElementById('state');
|
|
if (stateElem) {
|
|
stateElem.value = userData['state'];
|
|
stateElem.parentElement.classList.add('is-filled');
|
|
}
|
|
}, 100);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
if (userData.domain) {
|
|
const domainElem = document.getElementById('domain');
|
|
domainElem.value = userData.domain;
|
|
domainElem.parentElement.classList.add('is-filled');
|
|
|
|
|
|
loadSubdomains(userData.domain).then(() => {
|
|
if (userData.subdomain) {
|
|
setTimeout(() => {
|
|
const subdomainElem = document.getElementById('subdomain');
|
|
if (subdomainElem) {
|
|
subdomainElem.value = userData.subdomain;
|
|
subdomainElem.parentElement.classList.add('is-filled');
|
|
}
|
|
}, 300);
|
|
}
|
|
});
|
|
}
|
|
} catch (e) {
|
|
console.error('Error loading user profile:', e);
|
|
}
|
|
}
|
|
|
|
|
|
async function loadDomains() {
|
|
try {
|
|
const domainSelect = document.getElementById('domain');
|
|
domainSelect.innerHTML = '<option value="">Loading available domains...</option>';
|
|
domainSelect.disabled = true;
|
|
|
|
const response = await fetch('/domains');
|
|
const data = await response.json();
|
|
|
|
domainSelect.innerHTML = '';
|
|
|
|
if (data.status === 'success' && data.domains) {
|
|
|
|
const domainCount = Object.keys(data.domains).length;
|
|
|
|
if (domainCount === 0) {
|
|
domainSelect.innerHTML = '<option value="" disabled>No domains available</option>';
|
|
domainSelect.disabled = true;
|
|
return;
|
|
}
|
|
|
|
|
|
Object.entries(data.domains).forEach(([code, name]) => {
|
|
const option = document.createElement('option');
|
|
option.value = code;
|
|
option.textContent = `${name} (${code})`;
|
|
|
|
|
|
if (domainSelect.options.length === 0) {
|
|
option.selected = true;
|
|
domainSelect.parentElement.classList.add('is-filled');
|
|
}
|
|
|
|
domainSelect.appendChild(option);
|
|
});
|
|
|
|
|
|
domainSelect.disabled = false;
|
|
|
|
|
|
if (domainSelect.value) {
|
|
await loadSubdomains(domainSelect.value);
|
|
}
|
|
} else {
|
|
domainSelect.innerHTML = '<option value="" disabled>No domains available</option>';
|
|
domainSelect.disabled = true;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading domains:', error);
|
|
const domainSelect = document.getElementById('domain');
|
|
domainSelect.innerHTML = '<option value="" disabled>Error loading domains</option>';
|
|
domainSelect.disabled = true;
|
|
}
|
|
}
|
|
|
|
|
|
async function loadSubdomains(domainCode) {
|
|
try {
|
|
const subdomainSelect = document.getElementById('subdomain');
|
|
subdomainSelect.innerHTML = '<option value="">Loading available subdomains...</option>';
|
|
subdomainSelect.disabled = true;
|
|
|
|
if (!domainCode) {
|
|
subdomainSelect.innerHTML = '<option value="" disabled>Please select a domain first</option>';
|
|
subdomainSelect.disabled = true;
|
|
return;
|
|
}
|
|
|
|
const response = await fetch(`/domains/${domainCode}/subdomains`);
|
|
const data = await response.json();
|
|
|
|
subdomainSelect.innerHTML = '';
|
|
|
|
if (data.status === 'success' && data.subdomains) {
|
|
|
|
if (data.subdomains.length === 0) {
|
|
subdomainSelect.innerHTML = '<option value="" disabled>No subdomains available</option>';
|
|
subdomainSelect.disabled = true;
|
|
return;
|
|
}
|
|
|
|
|
|
data.subdomains.forEach(subdomain => {
|
|
const option = document.createElement('option');
|
|
option.value = subdomain.mnemonic;
|
|
option.textContent = `${subdomain.name} (${subdomain.mnemonic})`;
|
|
|
|
|
|
if (subdomainSelect.options.length === 0) {
|
|
option.selected = true;
|
|
}
|
|
|
|
subdomainSelect.appendChild(option);
|
|
});
|
|
|
|
subdomainSelect.disabled = false;
|
|
subdomainSelect.parentElement.classList.add('is-filled');
|
|
|
|
|
|
if (!subdomainSelect.value && subdomainSelect.options.length > 0) {
|
|
subdomainSelect.options[0].selected = true;
|
|
}
|
|
} else {
|
|
|
|
subdomainSelect.innerHTML = '<option value="" disabled>No subdomains available</option>';
|
|
subdomainSelect.disabled = true;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading subdomains:', error);
|
|
const subdomainSelect = document.getElementById('subdomain');
|
|
subdomainSelect.innerHTML = '<option value="" disabled>Error loading subdomains</option>';
|
|
subdomainSelect.disabled = true;
|
|
}
|
|
}
|
|
|
|
|
|
document.getElementById('domain').addEventListener('change', function() {
|
|
loadSubdomains(this.value);
|
|
});
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
|
|
loadDomains();
|
|
});
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
loadUserProfile();
|
|
});
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
|
|
|
|
const originalFetch = window.fetch;
|
|
window.fetch = function(url, options = {}) {
|
|
options = options || {};
|
|
options.headers = options.headers || {};
|
|
|
|
|
|
if (options.method && ['POST', 'PUT', 'DELETE'].includes(options.method.toUpperCase())) {
|
|
options.headers['X-CSRF-Token'] = csrfToken;
|
|
}
|
|
|
|
return originalFetch(url, options);
|
|
};
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
{% if session.get('user') %}
|
|
<script type="application/json" id="userData">
|
|
{
|
|
"gender": "{{ session.user.gender|default('') }}",
|
|
"age_group": "{{ session.user.age_group|default('') }}",
|
|
"country": "{{ session.user.country|default('') }}",
|
|
"state": "{{ session.user.state_province|default('') }}",
|
|
"city": "{{ session.user.city|default('') }}",
|
|
"accent": "{{ session.user.accent|default('') }}",
|
|
"language": "{{ session.user.language|default('') }}",
|
|
"domain": "{{ session.user.domain|default('GEN') }}",
|
|
"subdomain": "{{ session.user.subdomain|default('GEN') }}"
|
|
}
|
|
</script>
|
|
{% endif %}
|
|
</body>
|
|
</html>
|
|
|