/* widget: Foto & video */ /* ========================================================= Media widget — photos and videos from external sites ==========================================================*/ function mountMedia(root, initState){ let cur = null; root.innerHTML = `
`; const urlInp = root.querySelector(".m-url"), goBtn = root.querySelector(".m-go"), extBtn = root.querySelector(".m-ext"), stage = root.querySelector(".media-stage"); function labels(){ urlInp.placeholder = T("mediaPh"); goBtn.textContent = T("mediaShow"); extBtn.title = T("mediaOpen"); const msg = stage.querySelector(".media-msg"); if(msg) msg.textContent = T(msg.dataset.err ? "mediaErr" : "mediaEmpty"); } function showMsg(err){ stage.innerHTML = ""; const m = document.createElement("div"); m.className = "media-msg"; if(err) m.dataset.err = "1"; m.textContent = T(err ? "mediaErr" : "mediaEmpty"); stage.appendChild(m); } function show(raw){ let url = raw.trim(); if(!url){ showMsg(false); return; } if(!/^https?:\/\//i.test(url)) url = "https://"+url; let node = null; const yt = url.match(/(?:youtube\.com\/(?:watch\?[^#]*v=|shorts\/|embed\/|live\/)|youtu\.be\/)([\w-]{6,})/); const vi = url.match(/vimeo\.com\/(?:video\/)?(\d+)/); if(yt){ /* show the thumbnail first (always works); play attempts the embed */ const id = yt[1]; stage.innerHTML = ""; const wrap = document.createElement("div"); wrap.className = "media-yt"; const im = document.createElement("img"); im.src = "https://i.ytimg.com/vi/"+id+"/hqdefault.jpg"; im.alt = ""; const pb = document.createElement("button"); pb.className = "media-play"; pb.textContent = "▶"; pb.addEventListener("click", ()=>{ const f = document.createElement("iframe"); f.src = "https://www.youtube.com/embed/"+id+"?rel=0&autoplay=1"; f.referrerPolicy = "strict-origin-when-cross-origin"; f.allow = "autoplay; encrypted-media; picture-in-picture"; f.allowFullscreen = true; stage.innerHTML = ""; stage.appendChild(f); }); wrap.appendChild(im); wrap.appendChild(pb); stage.appendChild(wrap); cur = url; urlInp.value = url; return; } if(vi){ node = document.createElement("iframe"); node.src = "https://player.vimeo.com/video/"+vi[1]; node.allow = "autoplay; fullscreen; picture-in-picture"; node.allowFullscreen = true; }else if(/\.(jpe?g|png|gif|webp|svg|avif)([?#]|$)/i.test(url)){ node = document.createElement("img"); node.src = url; node.alt = ""; node.addEventListener("error", ()=>showMsg(true)); }else if(/\.(mp4|webm|ogg|mov)([?#]|$)/i.test(url)){ node = document.createElement("video"); node.src = url; node.controls = true; node.addEventListener("error", ()=>showMsg(true)); } if(node){ stage.innerHTML = ""; stage.appendChild(node); cur = url; urlInp.value = url; }else{ showMsg(true); } } goBtn.addEventListener("click", ()=>show(urlInp.value)); extBtn.addEventListener("click", ()=>{ let u = (cur || urlInp.value).trim(); if(!u) return; if(!/^https?:\/\//i.test(u)) u = "https://"+u; window.open(u, "_blank"); }); urlInp.addEventListener("keydown", e=>{ e.stopPropagation(); if(e.key==="Enter") show(urlInp.value); }); document.addEventListener("langchange", labels); if(initState && initState.url){ urlInp.value = initState.url; show(initState.url); } else showMsg(false); labels(); return { getState: ()=>({url: cur || ""}) }; }