feat(embeds): geo/gpx/3D/YouTube/media keyword system + docs, sanitized mirrors
- new snippet geo_map_render.js: [map] inline geomaps (tree order, tint, theme-following), [gpx] stats card + elevation profile with hover cursor (km from start, d-height, vertical guide), [yt] privacy click-to-load facade, [video]/[audio] inline players fed from the new geoData indexes - 3d_model_viewer.js: [cad] keyword + title/ID resolution via geoData.cad, sizes/captions, real occt-wasm ESM pair (fixes 'STEP worker error'), code-span-safe keyword scan (docs can show examples) - blog_generator.js: buildGeoData() v6 – maps (geolocation + geoShape point/line/polygon + child GPX tracks), gpx/cad/media indexes, branch-change rebuild for route reordering - generated/geo_data.js placeholder; docs/embed-keywords.md (new) + embed-3d-model Option D; README layout/wiring/notes tables updated - license: runtime components (MapLibre ISC, VersaTiles MIT/ODbL, YouTube) - hygiene: removed leaked note IDs and instance domain from theme docs
This commit is contained in:
@@ -442,5 +442,67 @@
|
||||
a.replaceWith(el);
|
||||
initViewer(el, "api/notes/" + art.id + "/download", mm[0]);
|
||||
});
|
||||
|
||||
// ---- [cad <key>] keyword: inline viewer, key = title or noteId of an .stl/.step/.stp file note.
|
||||
// Optional pipe suffixes: |small|medium|large for height, free text for a custom caption.
|
||||
var RE_CAD = /\[cad\s+([^\]]+)\]/i;
|
||||
var CAD_HEIGHTS = { small: "280px", medium: "440px", large: "620px" };
|
||||
function scanCadKeywords() {
|
||||
var content = document.getElementById("content");
|
||||
if (!content) return;
|
||||
var list = (window.geoData && window.geoData.cad) || [];
|
||||
if (!list.length) return;
|
||||
// Block text WITHOUT inline <code>/<pre> content (keyword examples stay literal).
|
||||
function textExcludingCode(block) {
|
||||
var out = "";
|
||||
var w = document.createTreeWalker(block, NodeFilter.SHOW_TEXT, {
|
||||
acceptNode: function (n) {
|
||||
var p = n.parentElement;
|
||||
while (p && p !== block) {
|
||||
if (p.tagName === "CODE" || p.tagName === "PRE") return NodeFilter.FILTER_REJECT;
|
||||
p = p.parentElement;
|
||||
}
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
}
|
||||
});
|
||||
while (w.nextNode()) out += w.currentNode.nodeValue;
|
||||
return out;
|
||||
}
|
||||
content.querySelectorAll("p, h1, h2, h3, h4, h5, h6, li, blockquote").forEach(function (block) {
|
||||
var m = textExcludingCode(block).match(RE_CAD);
|
||||
if (!m) return;
|
||||
var parts = m[1].split("|");
|
||||
var key = parts[0].trim();
|
||||
var size = "", title = "";
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
var p = parts[i].trim(), pl = p.toLowerCase();
|
||||
if (pl === "small" || pl === "medium" || pl === "large") size = pl;
|
||||
else if (p && !title) title = p;
|
||||
}
|
||||
if (!key) return;
|
||||
var hit = null;
|
||||
var kl = key.toLowerCase();
|
||||
list.forEach(function (c) {
|
||||
if (c.id === key || c.title === key || c.title.toLowerCase() === kl || (c.title + "." + c.format).toLowerCase() === kl) hit = c;
|
||||
});
|
||||
if (!hit) {
|
||||
block.textContent = 'CAD-Modell \"' + key + '\" nicht gefunden.';
|
||||
return;
|
||||
}
|
||||
var el = makeViewer(title || hit.title);
|
||||
el.classList.add("model-viewer-inline");
|
||||
if (size) {
|
||||
var canvas = el.querySelector(".model-viewer-canvas");
|
||||
if (canvas) canvas.style.height = CAD_HEIGHTS[size];
|
||||
}
|
||||
block.replaceWith(el);
|
||||
initViewer(el, "api/notes/" + hit.id + "/download", "." + hit.format);
|
||||
});
|
||||
}
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", scanCadKeywords);
|
||||
} else {
|
||||
scanCadKeywords();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user