Add Markdown chapter content
This commit is contained in:
parent
7626417c46
commit
4de130e608
@ -311,6 +311,7 @@
|
|||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
|
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/markdown.js"></script>
|
||||||
<script src="js/blog-post.js"></script>
|
<script src="js/blog-post.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -194,6 +194,70 @@ textarea:focus {
|
|||||||
box-shadow: var(--shadow-3);
|
box-shadow: var(--shadow-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown-content {
|
||||||
|
color: var(--app-text) !important;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h3,
|
||||||
|
.markdown-content h4,
|
||||||
|
.markdown-content h5 {
|
||||||
|
color: var(--app-text) !important;
|
||||||
|
margin: var(--size-3) 0 var(--size-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content p,
|
||||||
|
.markdown-content li {
|
||||||
|
color: var(--app-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content a {
|
||||||
|
color: var(--app-link) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: var(--size-2) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content code {
|
||||||
|
background: var(--surface-3);
|
||||||
|
border: 1px solid var(--app-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
background: var(--surface-3);
|
||||||
|
border: 1px solid var(--app-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: var(--size-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview {
|
||||||
|
margin-top: var(--size-2);
|
||||||
|
padding: var(--size-3);
|
||||||
|
border: 1px solid var(--app-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--app-panel-soft) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-empty {
|
||||||
|
color: var(--app-text-muted) !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile-first approach */
|
/* Mobile-first approach */
|
||||||
.map-container {
|
.map-container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|||||||
@ -381,6 +381,7 @@
|
|||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
|
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/markdown.js"></script>
|
||||||
<script src="js/journey-edit.js"></script>
|
<script src="js/journey-edit.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -32,10 +32,9 @@ function renderJourney() {
|
|||||||
currentJourney.markers.forEach((marker, idx) => {
|
currentJourney.markers.forEach((marker, idx) => {
|
||||||
const title = marker.title || `Chapter ${idx + 1}`;
|
const title = marker.title || `Chapter ${idx + 1}`;
|
||||||
const date = marker.date ? `<span class="marker-date">${new Date(marker.date).toLocaleDateString()}</span>` : '';
|
const date = marker.date ? `<span class="marker-date">${new Date(marker.date).toLocaleDateString()}</span>` : '';
|
||||||
const content = marker.description ? escapeHtml(marker.description).replace(/\n/g, '<br>') : '';
|
const content = marker.description ? renderMarkdown(marker.description) : '';
|
||||||
marker.images = getMarkerImages(marker);
|
marker.images = getMarkerImages(marker);
|
||||||
const images = getMarkerImagesHtml(marker.images, idx, canEdit);
|
const images = getMarkerImagesHtml(marker.images, idx, canEdit);
|
||||||
const video = marker.videoUrl ? `<iframe src="${marker.videoUrl}" frameborder="0" allowfullscreen></iframe>` : '';
|
|
||||||
chaptersHtml += `
|
chaptersHtml += `
|
||||||
<div class="chapter" data-marker-id="${marker.id || idx}">
|
<div class="chapter" data-marker-id="${marker.id || idx}">
|
||||||
<div class="chapter-header">
|
<div class="chapter-header">
|
||||||
@ -44,8 +43,7 @@ function renderJourney() {
|
|||||||
</div>
|
</div>
|
||||||
${images}
|
${images}
|
||||||
${canEdit ? getImageToolsHtml(idx) : ''}
|
${canEdit ? getImageToolsHtml(idx) : ''}
|
||||||
${video}
|
<div class="chapter-content markdown-content">${content}</div>
|
||||||
<div class="chapter-content">${content}</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
@ -63,7 +61,7 @@ function renderJourney() {
|
|||||||
${currentJourney.visibility === 'shared' && !isOwner ? `<span class="badge">${canEdit ? 'Shared edit' : 'Shared'}</span>` : ''}
|
${currentJourney.visibility === 'shared' && !isOwner ? `<span class="badge">${canEdit ? 'Shared edit' : 'Shared'}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
${currentJourney.image ? `<img class="post-image" src="${currentJourney.image}" alt="${currentJourney.title}">` : ''}
|
${currentJourney.image ? `<img class="post-image" src="${currentJourney.image}" alt="${currentJourney.title}">` : ''}
|
||||||
<div class="post-description">${escapeHtml(currentJourney.description).replace(/\n/g, '<br>')}</div>
|
<div class="post-description markdown-content">${renderMarkdown(currentJourney.description)}</div>
|
||||||
${chaptersHtml}
|
${chaptersHtml}
|
||||||
${canEdit || isOwner ? `
|
${canEdit || isOwner ? `
|
||||||
<div style="margin-top: var(--size-4); display: flex; gap: var(--size-2);">
|
<div style="margin-top: var(--size-4); display: flex; gap: var(--size-2);">
|
||||||
|
|||||||
@ -144,6 +144,11 @@ function renderMarkerImages(marker, idx) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMarkdownPreview(markdown) {
|
||||||
|
const html = renderMarkdown(markdown || '');
|
||||||
|
return html || '<p class="markdown-empty">Nothing to preview yet.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
async function uploadMarkerImages(files) {
|
async function uploadMarkerImages(files) {
|
||||||
if (!files.length) return [];
|
if (!files.length) return [];
|
||||||
|
|
||||||
@ -186,17 +191,16 @@ function renderMarkers() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Description</label>
|
<label>Description</label>
|
||||||
<textarea class="marker-description" data-index="${idx}" rows="3" placeholder="Write about this chapter...">${escapeHtml(marker.description || '')}</textarea>
|
<textarea class="marker-description" data-index="${idx}" rows="5" placeholder="Describe this chapter. You can use Markdown like **bold**, [link](https://example.com), or .">${escapeHtml(marker.description || '')}</textarea>
|
||||||
|
<div class="markdown-preview markdown-content" data-preview-index="${idx}">
|
||||||
|
${renderMarkdownPreview(marker.description)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Images</label>
|
<label>Images</label>
|
||||||
<input type="file" class="marker-images" data-index="${idx}" accept="image/*" multiple>
|
<input type="file" class="marker-images" data-index="${idx}" accept="image/*" multiple>
|
||||||
${renderMarkerImages(marker, idx)}
|
${renderMarkerImages(marker, idx)}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label>Video URL</label>
|
|
||||||
<input type="text" class="marker-video" data-index="${idx}" value="${escapeHtml(marker.videoUrl || '')}" placeholder="https://youtu.be/...">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
@ -226,6 +230,8 @@ function renderMarkers() {
|
|||||||
textarea.addEventListener('input', (e) => {
|
textarea.addEventListener('input', (e) => {
|
||||||
const idx = parseInt(textarea.dataset.index);
|
const idx = parseInt(textarea.dataset.index);
|
||||||
markersData[idx].description = textarea.value;
|
markersData[idx].description = textarea.value;
|
||||||
|
const preview = document.querySelector(`.markdown-preview[data-preview-index="${idx}"]`);
|
||||||
|
if (preview) preview.innerHTML = renderMarkdownPreview(textarea.value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.querySelectorAll('.marker-images').forEach(input => {
|
document.querySelectorAll('.marker-images').forEach(input => {
|
||||||
@ -249,12 +255,6 @@ function renderMarkers() {
|
|||||||
renderMarkers();
|
renderMarkers();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.querySelectorAll('.marker-video').forEach(input => {
|
|
||||||
input.addEventListener('input', (e) => {
|
|
||||||
const idx = parseInt(input.dataset.index);
|
|
||||||
markersData[idx].videoUrl = input.value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== LOAD JOURNEY ====================
|
// ==================== LOAD JOURNEY ====================
|
||||||
@ -307,8 +307,7 @@ async function saveJourney(event) {
|
|||||||
title: m.title || '',
|
title: m.title || '',
|
||||||
date: m.date || '',
|
date: m.date || '',
|
||||||
description: m.description || '',
|
description: m.description || '',
|
||||||
images: m.images || [],
|
images: m.images || []
|
||||||
videoUrl: m.videoUrl || ''
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const payload = { title, description, markers };
|
const payload = { title, description, markers };
|
||||||
@ -356,8 +355,7 @@ function addMarker() {
|
|||||||
title: 'New Chapter',
|
title: 'New Chapter',
|
||||||
date: '',
|
date: '',
|
||||||
description: '',
|
description: '',
|
||||||
images: [],
|
images: []
|
||||||
videoUrl: ''
|
|
||||||
});
|
});
|
||||||
renderMarkers();
|
renderMarkers();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,6 @@ function displayJourney(journey) {
|
|||||||
<p class="date">${marker.content.date}</p>
|
<p class="date">${marker.content.date}</p>
|
||||||
<p class="location">${marker.lngLat.lng.toFixed(4)}, ${marker.lngLat.lat.toFixed(4)}</p>
|
<p class="location">${marker.lngLat.lng.toFixed(4)}, ${marker.lngLat.lat.toFixed(4)}</p>
|
||||||
<p>${marker.content.text}</p>
|
<p>${marker.content.text}</p>
|
||||||
${marker.content.videoUrl ? `<iframe src="${marker.content.videoUrl}" frameborder="0"></iframe>` : ''}
|
|
||||||
<div class="images-container">
|
<div class="images-container">
|
||||||
${marker.content.images.map(img => `<img src="${img}" alt="Event image">`).join('')}
|
${marker.content.images.map(img => `<img src="${img}" alt="Event image">`).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -138,6 +138,12 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateMarkerMarkdownPreview(markdown) {
|
||||||
|
const preview = document.getElementById("marker-markdown-preview");
|
||||||
|
if (!preview) return;
|
||||||
|
preview.innerHTML = renderMarkdown(markdown || "") || '<p class="markdown-empty">Nothing to preview yet.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
async function uploadMarkerImages(files) {
|
async function uploadMarkerImages(files) {
|
||||||
if (!files.length) return [];
|
if (!files.length) return [];
|
||||||
|
|
||||||
@ -166,8 +172,7 @@
|
|||||||
content.date || "";
|
content.date || "";
|
||||||
document.getElementById("marker-text").value =
|
document.getElementById("marker-text").value =
|
||||||
content.description || "";
|
content.description || "";
|
||||||
document.getElementById("video-url").value =
|
updateMarkerMarkdownPreview(content.description || "");
|
||||||
content.videoUrl || "";
|
|
||||||
document.getElementById("marker-images").value = "";
|
document.getElementById("marker-images").value = "";
|
||||||
renderMarkerImages(content.images || []);
|
renderMarkerImages(content.images || []);
|
||||||
document.getElementById("marker-coords").value =
|
document.getElementById("marker-coords").value =
|
||||||
@ -189,7 +194,6 @@
|
|||||||
const title = document.getElementById("marker-title").value || "Untitled";
|
const title = document.getElementById("marker-title").value || "Untitled";
|
||||||
const date = document.getElementById("marker-date").value;
|
const date = document.getElementById("marker-date").value;
|
||||||
const description = document.getElementById("marker-text").value;
|
const description = document.getElementById("marker-text").value;
|
||||||
const videoUrl = document.getElementById("video-url").value;
|
|
||||||
const imageInput = document.getElementById("marker-images");
|
const imageInput = document.getElementById("marker-images");
|
||||||
const existingImages = activeMarker._content?.images || [];
|
const existingImages = activeMarker._content?.images || [];
|
||||||
let images = existingImages;
|
let images = existingImages;
|
||||||
@ -207,7 +211,7 @@
|
|||||||
activeMarker.setPopupContent(`<strong>${title}</strong>`);
|
activeMarker.setPopupContent(`<strong>${title}</strong>`);
|
||||||
|
|
||||||
// Store content for saving
|
// Store content for saving
|
||||||
activeMarker._content = { title, date, description, videoUrl, images };
|
activeMarker._content = { title, date, description, images };
|
||||||
|
|
||||||
// Update the list item in the sidebar
|
// Update the list item in the sidebar
|
||||||
const latlng = activeMarker.getLatLng();
|
const latlng = activeMarker.getLatLng();
|
||||||
@ -353,7 +357,6 @@
|
|||||||
title: content.title || "",
|
title: content.title || "",
|
||||||
date: content.date || "",
|
date: content.date || "",
|
||||||
description: content.description || "",
|
description: content.description || "",
|
||||||
videoUrl: content.videoUrl || "",
|
|
||||||
images: content.images || [],
|
images: content.images || [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -436,7 +439,6 @@
|
|||||||
title: m.title,
|
title: m.title,
|
||||||
date: m.date,
|
date: m.date,
|
||||||
description: m.description,
|
description: m.description,
|
||||||
videoUrl: m.videoUrl,
|
|
||||||
images: m.images || [],
|
images: m.images || [],
|
||||||
});
|
});
|
||||||
marker._content = { ...m };
|
marker._content = { ...m };
|
||||||
@ -469,7 +471,6 @@
|
|||||||
title: displayTitle,
|
title: displayTitle,
|
||||||
date: m.date,
|
date: m.date,
|
||||||
description: m.description,
|
description: m.description,
|
||||||
videoUrl: m.videoUrl,
|
|
||||||
images: m.images || [],
|
images: m.images || [],
|
||||||
});
|
});
|
||||||
marker._content = {
|
marker._content = {
|
||||||
@ -652,6 +653,9 @@
|
|||||||
setMode("create");
|
setMode("create");
|
||||||
showToast("Click on the map to add a marker");
|
showToast("Click on the map to add a marker");
|
||||||
});
|
});
|
||||||
|
document
|
||||||
|
.getElementById("marker-text")
|
||||||
|
.addEventListener("input", (e) => updateMarkerMarkdownPreview(e.target.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose functions globally (optional)
|
// Expose functions globally (optional)
|
||||||
|
|||||||
137
js/markdown.js
Normal file
137
js/markdown.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
(function () {
|
||||||
|
const tokenPattern = /%%MDTOKEN(\d+)%%/g;
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value || "").replace(/[&<>"']/g, (char) => ({
|
||||||
|
"&": "&",
|
||||||
|
"<": "<",
|
||||||
|
">": ">",
|
||||||
|
'"': """,
|
||||||
|
"'": "'",
|
||||||
|
})[char]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeUrl(value) {
|
||||||
|
const url = String(value || "").trim().replace(/[\u0000-\u001f\u007f\s]+/g, "");
|
||||||
|
if (/^(https?:\/\/|\/|\.\/|\.\.\/|uploads\/|mailto:|#)/i.test(url)) {
|
||||||
|
return escapeHtml(url);
|
||||||
|
}
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeToken(tokens, html) {
|
||||||
|
const token = `%%MDTOKEN${tokens.length}%%`;
|
||||||
|
tokens.push(html);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderInline(markdown) {
|
||||||
|
const tokens = [];
|
||||||
|
let text = String(markdown || "");
|
||||||
|
|
||||||
|
text = text.replace(/`([^`]+)`/g, (_, code) =>
|
||||||
|
storeToken(tokens, `<code>${escapeHtml(code)}</code>`),
|
||||||
|
);
|
||||||
|
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, url) =>
|
||||||
|
storeToken(
|
||||||
|
tokens,
|
||||||
|
`<img src="${sanitizeUrl(url)}" alt="${escapeHtml(alt)}" loading="lazy">`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) =>
|
||||||
|
storeToken(
|
||||||
|
tokens,
|
||||||
|
`<a href="${sanitizeUrl(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
text = escapeHtml(text)
|
||||||
|
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
||||||
|
.replace(/__([^_]+)__/g, "<strong>$1</strong>")
|
||||||
|
.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
||||||
|
.replace(/_([^_]+)_/g, "<em>$1</em>");
|
||||||
|
|
||||||
|
return text.replace(tokenPattern, (_, index) => tokens[Number(index)] || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderList(lines) {
|
||||||
|
const items = lines
|
||||||
|
.map((line) => line.replace(/^\s*[-*]\s+/, ""))
|
||||||
|
.map((line) => `<li>${renderInline(line)}</li>`)
|
||||||
|
.join("");
|
||||||
|
return `<ul>${items}</ul>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdown(markdown) {
|
||||||
|
const lines = String(markdown || "").replace(/\r\n/g, "\n").split("\n");
|
||||||
|
const blocks = [];
|
||||||
|
let paragraph = [];
|
||||||
|
let list = [];
|
||||||
|
let codeBlock = null;
|
||||||
|
|
||||||
|
function flushParagraph() {
|
||||||
|
if (!paragraph.length) return;
|
||||||
|
blocks.push(`<p>${renderInline(paragraph.join(" "))}</p>`);
|
||||||
|
paragraph = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushList() {
|
||||||
|
if (!list.length) return;
|
||||||
|
blocks.push(renderList(list));
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.forEach((line) => {
|
||||||
|
if (line.trim().startsWith("```")) {
|
||||||
|
if (codeBlock) {
|
||||||
|
blocks.push(`<pre><code>${escapeHtml(codeBlock.join("\n"))}</code></pre>`);
|
||||||
|
codeBlock = null;
|
||||||
|
} else {
|
||||||
|
flushParagraph();
|
||||||
|
flushList();
|
||||||
|
codeBlock = [];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeBlock) {
|
||||||
|
codeBlock.push(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.trim()) {
|
||||||
|
flushParagraph();
|
||||||
|
flushList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const heading = line.match(/^(#{1,3})\s+(.+)$/);
|
||||||
|
if (heading) {
|
||||||
|
flushParagraph();
|
||||||
|
flushList();
|
||||||
|
const level = heading[1].length + 2;
|
||||||
|
blocks.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\s*[-*]\s+/.test(line)) {
|
||||||
|
flushParagraph();
|
||||||
|
list.push(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flushList();
|
||||||
|
paragraph.push(line.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
flushParagraph();
|
||||||
|
flushList();
|
||||||
|
if (codeBlock) {
|
||||||
|
blocks.push(`<pre><code>${escapeHtml(codeBlock.join("\n"))}</code></pre>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.renderMarkdown = renderMarkdown;
|
||||||
|
})();
|
||||||
@ -853,18 +853,12 @@
|
|||||||
<label for="marker-text">Description</label>
|
<label for="marker-text">Description</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="marker-text"
|
id="marker-text"
|
||||||
placeholder="What happened here?"
|
placeholder="Describe this marker. You can use Markdown like **bold**, [link](https://example.com), or ."
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
<div
|
||||||
<div class="form-group">
|
id="marker-markdown-preview"
|
||||||
<label for="video-url"
|
class="markdown-preview markdown-content"
|
||||||
>Video URL (optional)</label
|
></div>
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="video-url"
|
|
||||||
placeholder="https://youtu.be/..."
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="marker-images">Images</label>
|
<label for="marker-images">Images</label>
|
||||||
@ -913,6 +907,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast"><p id="toast-message"></p></div>
|
<div id="toast" class="toast"><p id="toast-message"></p></div>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/markdown.js"></script>
|
||||||
<script src="js/map-page.js"></script>
|
<script src="js/map-page.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user