1import{Controller}from"@hotwired/stimulus";import*as bootstrap from"bootstrap";import EditorJS from"https://esm.sh/@editorjs/[email protected]";import{buildEditorTools}from"../lib/build_tools.js";export default class extends Controller{static targets=["root","toggle","container","left","right","reference","localeSelect","localeSelectClipboard","iconOn","iconOff","referenceHeader","metaPretitle","metaSlug","metaTitle","metaSubtitle","metaDescription","diffContainer","referenceContainer","changedBanner","scrollLockBtn","scrollLockIconLocked","scrollLockIconUnlocked","diffToggleBtn","diffToggleBtnShowText","diffToggleBtnHideText","markReviewedBtn"];static values={referenceUrl:String,currentTranslationId:String,blockCodes:Array,tags:Array,pageIdsUrl:String,articleIdsUrl:String,tagIdsUrl:String,fetchImagesUrl:String,fetchFilesUrl:String,fetchAllFilesUrl:String,fetchFileUrl:String,fetchAnchorSnippetsUrl:String,xutimSections:Array,xutimSectionPreviewUrl:String,referenceDiffUrl:String,referenceHasChanged:Boolean};connect(){const e=this.#t("scroll_lock","xutim.splitViewScrollLock");this.scrollLocked=e===null||e==="1";
1const t=this.#t("ref","xutim.splitViewReference");t&&this.#h(t),this.#t("split","xutim.splitView")==="1"?this.enable():this.disable(),this.#o()}async toggle(){this.isOn?this.disable():await this.enable()}async enable(){this.isOn=!0,this.leftTarget.classList.add("col-lg-6"),this.rightTarget.classList.remove("d-none"),this.refEditor||await this.loadReference(),this.iconOnTarget.classList.add("d-none"),this.iconOffTarget.classList.remove("d-none"),this.localeSelectTarget.hidden=!1,this.localeSelectClipboardTarget.hidden=!1,this.hasScrollLockBtnTarget&&(this.scrollLockBtnTarget.hidden=!1),this.#i(),this.#e()}disable(){this.isOn=!1,this.leftTarget.classList.remove("col-lg-6"),this.rightTarget.classList.add("d-none"),this.iconOnTarget.classList.remove("d-none"),this.iconOffTarget.classList.add("d-none"),this.localeSelectTarget.hidden=!0,this.localeSelectClipboardTarget.hidden=!0,this.hasScrollLockBtnTarget&&(this.scrollLockBtnTarget.hidden=!0),this.#e()}async loadReference(){const t=this.localeSelectTarget?.value||this.referenceUrlValue;if(!t)return;const n=await fetch(t,{headers:{Accept:"application/json"}});if(!n.ok)return;const e=await n.json();this.refData=e;const s=e.meta??e.header??{};this.#m(s),this.refEditor?.destroy&&await this.refEditor.destroy();const o=this.buildTools();this.refEditor=new EditorJS({holder:this.referenceTarget,readOnly:!0,data:e,tools:o,onReady:()=>this.decorateBlocksForCopy()}),this.#e()}#t(e,t){const n=new URLSearchParams(window.location.search).get(e);return n??localStorage.getItem(t)}#h(e){if(!this.hasLocaleSelectTarget)return;const t=this.localeSelectTarget.querySelector(`option[data-locale="${CSS.escape(e)}"]`);t&&(this.localeSelectTarget.value=t.value)}#d(){return this.localeSelectTarget?.selectedOptions?.[0]?.dataset.locale??null}#e(){const e=new URL(window.location.href),t=(t,n,s)=>{s===null?(e.searchParams.delete(t),localStorage.removeItem(n)):(e.searchParams.set(t,s),localStorage.setItem(n,s))};t("split","xutim.splitView",this.isOn?"1":"0"),t("scroll_lock","xutim.splitViewScrollLock",this.scrollLocked?"1":"0"),t("ref","xutim.splitViewReference",this.#d()),history.replaceState({},"",e)}buildTools(e={}){return buildEditorTools({pageIdsUrl:this.pageIdsUrlValue,articleIdsUrl:this.articleIdsUrlValue,tagIdsUrl:this.tagIdsUrlValue,fetchImagesUrl:this.fetchImagesUrlValue,fetchFilesUrl:this.fetchFilesUrlValue,fetchAllFilesUrl:this.fetchAllFilesUrlValue,fetchFileUrl:this.fetchFileUrlValue,fetchAnchorSnippetsUrl:this.fetchAnchorSnippetsUrlValue,blockCodes:this.blockCodesValue,tags:this.tagsValue,xutimSections:this.xutimSectionsValue,xutimSectionPreviewUrl:this.#p(this.xutimSectionPreviewUrlValue),...e})}#p(e){return e&&e+(e.includes("?")?"&":"?")+"edit=1"}async decorateBlocksForCopy(){const e=this.referenceTarget.querySelectorAll(".ce-block"),t=this.refData?.blocks??[];e.forEach((e,n)=>{const o=this.#c(e);if(!o)return;const i=t[n];if(!i)return;const a=(o.innerText||"").trim().length===0&&o.querySelectorAll("img,video,figure,iframe,svg,embed").length===0;let s=e.querySelector(":scope > .x-copy-btn");s||(s=document.createElement("button"),s.type="button",s.className="x-copy-btn btn btn-sm btn-link p-0 text-center",s.title="Copy",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" class="w-3 h-3" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2" /><path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" /></svg>',Object.assign(s.style,{position:"absolute",left:"-1.5rem",top:"50%",transform:"translateY(-50%)"}),e.style.position="relative",e.appendChild(s),s.addEventListener("click",async t=>{t.preventDefault(),t.stopPropagation();try{await this.#f(i,e),this.flash("Copied")}catch(e){console.error(e),this.flash("Copy failed")}})),s.style.display=a?"none":"inline-block"})}async copyAllBlocks(){const e=this.refData?.blocks??[];if(!e.length)return;const s=e.map(e=>({id:e.id??this.#n(10),tool:e.type,data:e.data??{},tunes:e.tunes??{},time:typeof e.time=="number"?e.time:.1})),t=JSON.stringify(s),n=this.#s(this.referenceTarget);await this.#r({"text/plain":n.text,"text/html":n.html,"application/x-editor-js":t,"application/editor-js":t}),this.flash("All blocks copied")}async#f(e,t){const n=JSON.stringify([{id:e.id??this.#n(10),tool:e.type,data:e.data??{},tunes:e.tunes??{},time:typeof e.time=="number"?e.time:.1}]),s=this.#s(this.#c(t));await this.#r({"text/plain":s.text,"text/html":s.html,"application/x-editor-js":n,"application/editor-js":n})}flash(e){const t=document.createElement("div");t.className="toast align-items-center text-bg-secondary border-0 position-fixed top-0 end-0 m-3",t.role="alert",t.innerHTML=`<div class="d-flex"> 2 <div class="toast-body">${e}</div> 3 <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button> 4 </div>`,document.body.appendChild(t);const n=new bootstrap.Toast(t,{delay:1500});n.show(),n._element.addEventListener("hidden.bs.toast",()=>t.remove())}#m(e={}){const t=t=>e[t]??"";this.hasMetaPretitleTarget&&(this.metaPretitleTarget.value=t("pretitle")),this.hasMetaSlugTarget&&(this.metaSlugTarget.value=t("slug")),this.hasMetaTitleTarget&&(this.metaTitleTarget.value=t("title")),this.hasMetaSubtitleTarget&&(this.metaSubtitleTarget.value=t("subtitle")),this.hasMetaDescriptionTarget&&(this.metaDescriptionTarget.value=this.#u(t("description")))}#u(e=""){if(e==null)return"";const t=document.createElement("div");return t.innerHTML=String(e),(t.textContent||t.innerText||"").trim()}#c(e){return e.querySelector(".ce-block__content")||e}#l(e){const t=[".ce-toolbar",".ce-block__actions",".ce-settings",".cdx-settings",".cdx-settings-button",".cdx-button","[data-editorjs-ui]","[data-noncontent]",".image-row__add",".image-tool__button",".x-add-image"];
4return e.querySelectorAll(t.join(",")).forEach(e=>e.remove()),e.querySelectorAll("button,a").forEach(e=>{const t=(e.innerText||"").trim();/^\+\s*add\b/i.test(t)&&e.remove()}),e}#s(e){const t=e.cloneNode(!0);this.#l(t);const n=document.createElement("div");return n.appendChild(t),{html:n.innerHTML,text:t.innerText||""}}async#r(e){const t=t=>{t.preventDefault();const n=t.clipboardData;for(const[t,s]of Object.entries(e))n.setData(t,s)};document.addEventListener("copy",t,{once:!0});const n=document.execCommand("copy");if(document.removeEventListener("copy",t),!n)throw new Error("execCommand(copy) failed")}#n(e=10){const t=new Uint8Array(e);crypto.getRandomValues(t);const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";return Array.from(t,e=>n[e%n.length]).join("")}async showDiff(){if(!this.referenceDiffUrlValue)return;if(!this.hasDiffContainerTarget||!this.hasReferenceContainerTarget)return;this.isOn||await this.enable();const e=await fetch(this.referenceDiffUrlValue);if(!e.ok)return;this.diffContainerTarget.innerHTML=await e.text(),this.referenceContainerTarget.classList.add("d-none"),this.diffContainerTarget.classList.remove("d-none"),this.#a(!0)}showCurrent(){if(!this.hasDiffContainerTarget||!this.hasReferenceContainerTarget)return;this.diffContainerTarget.classList.add("d-none"),this.referenceContainerTarget.classList.remove("d-none"),this.#a(!1)}#a(e){this.hasDiffToggleBtnShowTextTarget&&this.diffToggleBtnShowTextTarget.classList.toggle("d-none",e),this.hasDiffToggleBtnHideTextTarget&&this.diffToggleBtnHideTextTarget.classList.toggle("d-none",!e)}async markReviewed(e){const t=e.params.markReviewedUrl;if(!t)return;const n=e.currentTarget;n.disabled=!0;const s=await fetch(t,{method:"POST",headers:{"X-Requested-With":"XMLHttpRequest"}});s.ok?(this.hasChangedBannerTarget&&this.changedBannerTarget.remove(),this.hasDiffContainerTarget&&this.diffContainerTarget.classList.add("d-none"),this.hasReferenceContainerTarget&&this.referenceContainerTarget.classList.remove("d-none"),this.flash("Marked as reviewed")):(n.disabled=!1,this.flash("Failed to mark as reviewed"))}toggleDiff(){if(!this.hasDiffContainerTarget)return;this.diffContainerTarget.classList.contains("d-none")?this.showDiff():this.showCurrent()}toggleScrollLock(){this.scrollLocked=!this.scrollLocked,this.#i(),this.#o(),this.#e()}#i(){if(!this.hasRightTarget)return;this.scrollLocked?(this.rightTarget.style.position="",this.rightTarget.style.top="",this.rightTarget.style.maxHeight="",this.rightTarget.style.overflowY=""):(this.rightTarget.style.position="sticky",this.rightTarget.style.top="0",this.rightTarget.style.maxHeight="100vh",this.rightTarget.style.overflowY="auto")}#o(){if(!this.hasScrollLockBtnTarget)return;this.scrollLocked?this.scrollLockBtnTarget.classList.add("active"):this.scrollLockBtnTarget.classList.remove("active"),this.hasScrollLockIconLockedTarget&&this.scrollLockIconLockedTarget.classList.toggle("d-none",!this.scrollLocked),this.hasScrollLockIconUnlockedTarget&&this.scrollLockIconUnlockedTarget.classList.toggle("d-none",this.scrollLocked)}}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.