ngx-xtroedge-cms 1.3.13 → 1.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- "use strict";var XtroedgeCMSLib=(()=>{var E=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var I=(u,t)=>{for(var e in t)E(u,e,{get:t[e],enumerable:!0})},H=(u,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of C(t))!k.call(u,s)&&s!==e&&E(u,s,{get:()=>t[s],enumerable:!(i=L(t,s))||i.enumerable});return u};var P=u=>H(E({},"__esModule",{value:!0}),u);var N={};I(N,{XtroedgeCMS:()=>f});var T=`
1
+ "use strict";var XtroedgeCMSLib=(()=>{var E=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var I=(u,t)=>{for(var e in t)E(u,e,{get:t[e],enumerable:!0})},H=(u,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of C(t))!k.call(u,s)&&s!==e&&E(u,s,{get:()=>t[s],enumerable:!(i=L(t,s))||i.enumerable});return u};var P=u=>H(E({},"__esModule",{value:!0}),u);var D={};I(D,{XtroedgeCMS:()=>f});var T=`
2
2
  /* ===== XTROEDGE CMS ===== */
3
3
 
4
4
  /* LOADER */
@@ -303,7 +303,7 @@
303
303
  <div class="lcms-core"></div>
304
304
  </div>
305
305
  <span class="lcms-loader-text">Loading</span>
306
- </div>`,this.rootEl.appendChild(this.loaderEl),this.toastEl=this.createElement("div","lcms-toast lcms-hidden"),this.rootEl.appendChild(this.toastEl),this.fabEl=this.createElement("div","lcms-fab"),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px",this.fabEl.addEventListener("mousedown",t=>this.onDragStart(t)),this.fabEl.addEventListener("touchstart",t=>this.onTouchStart(t),{passive:!0}),this.fabBtn=document.createElement("button"),this.fabBtn.className="lcms-fab-btn",this.fabBtn.innerHTML=g.edit,this.fabBtn.addEventListener("click",t=>this.onFabClick(t)),this.badgeEl=this.createElement("span","lcms-badge lcms-hidden"),this.fabBtn.appendChild(this.badgeEl),this.fabEl.appendChild(this.fabBtn),this.panelEl=this.createElement("div","lcms-panel lcms-hidden"),this.buildPanel(),this.fabEl.appendChild(this.panelEl),this.rootEl.appendChild(this.fabEl),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.accept="image/*",this.fileInput.style.display="none",this.fileInput.addEventListener("change",t=>this.onImageFileSelected(t)),this.rootEl.appendChild(this.fileInput),this.imgOverlay=this.createElement("div","lcms-img-upload-overlay lcms-hidden"),this.imgOverlay.innerHTML='<div class="lcms-img-upload-content"><div class="lcms-spinner-lg"></div><span>Uploading image...</span></div>',this.rootEl.appendChild(this.imgOverlay),document.body.appendChild(this.rootEl)}buildPanel(){if(!this.panelEl)return;let t=this.createElement("div","lcms-panel-header"),e=this.createElement("label","lcms-toggle");e.addEventListener("click",c=>c.stopPropagation()),this.editToggle=document.createElement("input"),this.editToggle.type="checkbox",this.editToggle.checked=this.editMode,this.editToggle.addEventListener("change",c=>this.toggleEditMode(c));let i=this.createElement("span","lcms-toggle-slider"),s=this.createElement("span","lcms-toggle-label");s.textContent="Edit",e.appendChild(this.editToggle),e.appendChild(i),e.appendChild(s),t.appendChild(e),this.langSwitchEl=this.createElement("div","lcms-lang-switch lcms-hidden"),this.buildLangButtons(),t.appendChild(this.langSwitchEl);let n=document.createElement("button");n.className="lcms-close-btn",n.innerHTML=g.close,n.addEventListener("click",c=>{c.stopPropagation(),this.togglePanel()}),t.appendChild(n),this.panelEl.appendChild(t),this.siteIdEl=this.createElement("div","lcms-site-id");let r=this.siteIdentifier.includes(".");this.siteIdEl.innerHTML=`<span class="lcms-site-id-icon">${r?"\u{1F310}":"\u{1F511}"}</span><span class="lcms-site-id-text">${r?this.siteIdentifier:this.siteIdentifier.substring(0,8)}</span>`,this.panelEl.appendChild(this.siteIdEl),this.editModeContent=this.createElement("div","lcms-hidden"),this.changesInfoEl=this.createElement("div","lcms-changes-info lcms-hidden"),this.editModeContent.appendChild(this.changesInfoEl);let o=this.createElement("div","lcms-undo-row");this.undoBtn=document.createElement("button"),this.undoBtn.className="lcms-icon-btn",this.undoBtn.innerHTML=g.undo,this.undoBtn.disabled=!0,this.undoBtn.addEventListener("click",c=>{c.stopPropagation(),this.onUndo()}),this.redoBtn=document.createElement("button"),this.redoBtn.className="lcms-icon-btn",this.redoBtn.innerHTML=g.redo,this.redoBtn.disabled=!0,this.redoBtn.addEventListener("click",c=>{c.stopPropagation(),this.onRedo()}),o.appendChild(this.undoBtn),o.appendChild(this.redoBtn),this.editModeContent.appendChild(o);let a=this.createElement("div","lcms-theme-row"),l=this.createElement("span","lcms-theme-label");l.textContent="Theme",a.appendChild(l);let d=this.createElement("div","lcms-theme-colors"),h=["#00C853","#6722FB","#2196F3","#FF5722","#E91E63","#FFD600"];for(let c of h){let y=this.createElement("div","lcms-theme-swatch");y.style.background=c,y.dataset.color=c,c===this.highlightColor&&y.classList.add("active"),y.addEventListener("click",M=>{M.stopPropagation(),this.applyThemeColor(c)}),d.appendChild(y)}let b=this.createElement("div","lcms-theme-custom"),p=this.createElement("div","lcms-theme-custom-preview"),m=document.createElement("input");m.type="color",m.value=this.highlightColor,m.addEventListener("input",c=>{c.stopPropagation(),this.applyThemeColor(c.target.value)}),b.appendChild(p),b.appendChild(m),d.appendChild(b),a.appendChild(d),this.editModeContent.appendChild(a),this.historyBtnEl=document.createElement("button"),this.historyBtnEl.className="lcms-history-btn",this.historyBtnEl.innerHTML=`${g.history} History (7 days)`,this.historyBtnEl.addEventListener("click",c=>{c.stopPropagation(),this.toggleHistory()}),this.editModeContent.appendChild(this.historyBtnEl),this.historyPanelEl=this.createElement("div","lcms-history-panel lcms-hidden");let w=this.createElement("div","lcms-history-title");w.textContent="Edit History",this.historyPanelEl.appendChild(w),this.historyListEl=this.createElement("div","lcms-history-list"),this.historyPanelEl.appendChild(this.historyListEl),this.editModeContent.appendChild(this.historyPanelEl),this.actionsEl=this.createElement("div","lcms-actions"),this.saveBtn=document.createElement("button"),this.saveBtn.className="lcms-btn-save",this.saveBtn.innerHTML=`${g.save} Save Draft`,this.saveBtn.addEventListener("click",c=>{c.stopPropagation(),this.saveChanges()}),this.publishBtn=document.createElement("button"),this.publishBtn.className="lcms-btn-publish",this.publishBtn.innerHTML=`${g.publish} Publish`,this.publishBtn.addEventListener("click",c=>{c.stopPropagation(),this.publishChanges()});let x=document.createElement("button");x.className="lcms-btn-logout",x.innerHTML=`${g.logout} Logout`,x.addEventListener("click",c=>{c.stopPropagation(),this.logout()}),this.actionsEl.appendChild(this.saveBtn),this.actionsEl.appendChild(this.publishBtn),this.actionsEl.appendChild(x),this.editModeContent.appendChild(this.actionsEl),this.panelEl.appendChild(this.editModeContent),this.brandingEl=this.createElement("div","lcms-branding"),this.brandingEl.innerHTML='<div class="lcms-branding-logo"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></div><div class="lcms-branding-text">Powered by <span>XtroEdge</span></div>',this.brandingEl.addEventListener("click",c=>{c.stopPropagation(),window.open("https://xtro.vercel.app/","_blank")}),this.panelEl.appendChild(this.brandingEl)}buildLangButtons(){if(!this.langSwitchEl)return;this.langSwitchEl.innerHTML="";let t=document.createElement("select");t.className="lcms-lang-select";for(let i of this.languages){let s=document.createElement("option");s.value=i,s.textContent=i.toUpperCase(),i===this.currentLang&&(s.selected=!0),t.appendChild(s)}t.addEventListener("change",i=>{i.stopPropagation(),this.switchLang(i.target.value)}),this.langSwitchEl.appendChild(t);let e=document.createElement("span");e.className="lcms-lang-arrow",e.textContent="\u25BC",this.langSwitchEl.appendChild(e)}updateUI(){if(this.fabEl&&(this.fabEl.style.display=this.isEditAllowed?"":"none"),this.fabBtn&&(this.fabBtn.style.display=this.isOpen?"none":"",this.fabBtn.classList.toggle("lcms-fab-active",this.editMode)),this.panelEl&&(this.panelEl.classList.toggle("lcms-hidden",!this.isOpen),this.isOpen)){let t=this.posY>window.innerHeight/2,e=this.posX>window.innerWidth/2;this.panelEl.style.bottom=t?"0":"",this.panelEl.style.top=t?"":"0",this.panelEl.style.right=e?"0":"",this.panelEl.style.left=e?"":"0"}if(this.badgeEl&&(this.badgeEl.classList.toggle("lcms-hidden",this.unsavedChanges===0),this.badgeEl.textContent=String(this.unsavedChanges)),this.editToggle&&(this.editToggle.checked=this.editMode),this.langSwitchEl){this.langSwitchEl.classList.toggle("lcms-hidden",!this.editMode||this.languages.length<=1);let t=this.langSwitchEl.querySelector(".lcms-lang-select");t&&(t.value=this.currentLang)}this.editModeContent&&this.editModeContent.classList.toggle("lcms-hidden",!this.editMode),this.changesInfoEl&&(this.changesInfoEl.classList.toggle("lcms-hidden",this.unsavedChanges===0),this.changesInfoEl.textContent=`${this.unsavedChanges} unsaved change${this.unsavedChanges>1?"s":""}`),this.undoBtn&&(this.undoBtn.disabled=!this.canUndo),this.redoBtn&&(this.redoBtn.disabled=!this.canRedo),this.historyBtnEl&&this.historyBtnEl.classList.toggle("active",this.showHistory),this.historyPanelEl&&this.historyPanelEl.classList.toggle("lcms-hidden",!this.showHistory),this.saveBtn&&(this.saveBtn.disabled=this.isSaving||this.isPublishing||this.unsavedChanges===0,this.saveBtn.innerHTML=this.isSaving?'<span class="lcms-spinner"></span> Saving...':`${g.save} Save Draft`),this.publishBtn&&(this.publishBtn.disabled=this.isSaving||this.isPublishing,this.publishBtn.innerHTML=this.isPublishing?'<span class="lcms-spinner"></span> Publishing...':`${g.publish} Publish`),this.loaderEl&&this.loaderEl.classList.toggle("lcms-hidden",!this.loading),this.imgOverlay&&this.imgOverlay.classList.toggle("lcms-hidden",!this.imageUploading)}autoDetectAndScan(){this.autoDetectElements(),this.scanDOM(),this.scanImages()}resolveContainer(){return this.containerSelector&&document.querySelector(this.containerSelector)||document.body}autoDetectElements(){let t=this.resolveContainer();if(!t)return;for(let a of this.autoDetectedElements)document.contains(a)||this.autoDetectedElements.delete(a);let e=a=>{let l=a.parentElement;for(;l&&l!==document.body;){let d=l.tagName.toLowerCase();if(d==="header"||d.endsWith("-header"))return"/header";if(d==="footer"||d.endsWith("-footer"))return"/footer";l=l.parentElement}return this.currentSlug},i={},s=(a,l)=>{let d=l==="/header"?"header":l==="/footer"?"footer":"page";i[d]||(i[d]={});let h=a.tagName.toLowerCase();i[d][h]||(i[d][h]=0);let p=`${d!=="page"?`${d}_`:""}xcms_${h}_${i[d][h]}`;i[d][h]++,a.setAttribute("data-cms",p),a.setAttribute("data-cms-section",l),this.autoDetectedElements.add(a)},n=this.editableTags.join(",");t.querySelectorAll(n).forEach(a=>{a.hasAttribute("data-cms")||a.closest("#xtroedge-cms-root, script, style, noscript")||a.children.length>3||this.getDirectTextContent(a).trim().length<2||s(a,e(a))}),t.querySelectorAll("[data-editable]").forEach(a=>{a.hasAttribute("data-cms")||a.closest("#xtroedge-cms-root, script, style, noscript")||s(a,e(a))})}scanDOM(){document.querySelectorAll("[data-cms]").forEach(e=>{if(e.closest("#xtroedge-cms-root"))return;let i=e.getAttribute("data-cms");this.registeredKeys.add(i),this.managedElements.has(e)||this.attachElement(e,i)});for(let[e]of this.managedElements)document.contains(e)||this.detachElement(e)}scanImages(){let t=this.resolveContainer();if(!t)return;let e={...this.pageImages},i=!1;t.querySelectorAll("img").forEach(n=>{if(n.closest("#xtroedge-cms-root")||this.managedImages.has(n))return;let r=n.getAttribute("src")||"";!r||r.startsWith("data:")||(this.attachImage(n,r),e[r]||(e[r]=r,i=!0))}),i&&(this.pageImages=e,this.originalImages=JSON.parse(JSON.stringify(e)));for(let[n]of this.managedImages)document.contains(n)||this.detachImage(n)}getDirectTextContent(t){let e="";for(let i=0;i<t.childNodes.length;i++)t.childNodes[i].nodeType===Node.TEXT_NODE&&(e+=t.childNodes[i].textContent||"");return e}attachElement(t,e){let i=()=>{let a=t.textContent?.trim()||"",l=this.getPageText(e);a!==l&&this.onTextChanged(e,a)},s=a=>{a.key==="Enter"&&(a.preventDefault(),t.blur())},n=()=>{let a=t.textContent?.trim()||"",l=this.getPageText(e);a!==l&&this.onTextChanged(e,a)},r=a=>{(t.tagName==="A"||a.target.closest("a"))&&(a.preventDefault(),a.stopPropagation(),t.focus())},o=t.getAttribute("data-cms-section")||this.currentSlug;this.managedElements.set(t,{key:e,sectionSlug:o,blurHandler:i,keydownHandler:s,inputHandler:n,clickHandler:r}),this.editMode&&this.enableElementEdit(t,e,i,s,n,r)}detachElement(t){let e=this.managedElements.get(t);e&&(t.removeEventListener("blur",e.blurHandler),t.removeEventListener("keydown",e.keydownHandler),t.removeEventListener("input",e.inputHandler),t.removeEventListener("click",e.clickHandler,!0),t.removeAttribute("contenteditable"),this.managedElements.delete(t))}enableElementEdit(t,e,i,s,n,r){let o=this.getPageText(e);o&&(t.textContent=o),t.setAttribute("contenteditable","true"),t.style.outline=`2px dashed ${this.highlightColor}`,t.style.outlineOffset="-2px",t.style.cursor="text",t.style.transition="background 0.2s",t.style.minWidth="20px",t.addEventListener("blur",i),t.addEventListener("keydown",s),t.addEventListener("input",n),t.addEventListener("click",r,!0)}disableElementEdit(t,e,i,s,n,r){t.removeAttribute("contenteditable"),t.style.outline="",t.style.outline="",t.style.outlineOffset="",t.style.cursor="",t.style.transition="",t.style.minWidth="";let o=this.getPageText(e);o&&(t.textContent=o),t.removeEventListener("blur",i),t.removeEventListener("keydown",s),t.removeEventListener("input",n),t.removeEventListener("click",r,!0)}applyEditMode(t){for(let[e,i]of this.managedElements)t?this.enableElementEdit(e,i.key,i.blurHandler,i.keydownHandler,i.inputHandler,i.clickHandler):this.disableElementEdit(e,i.key,i.blurHandler,i.keydownHandler,i.inputHandler,i.clickHandler);this.applyImageEditMode(t)}updateElementTexts(){this.observer?.disconnect();for(let[t,e]of this.managedElements){if(document.activeElement===t)continue;let i=this.getPageText(e.key);i&&t.textContent!==i&&(t.textContent=i)}this.observer?.observe(document.body,{childList:!0,subtree:!0})}cleanupManagedElements(){for(let[t]of this.managedElements)this.detachElement(t);this.managedElements.clear();for(let t of this.autoDetectedElements)t.removeAttribute("data-cms");this.autoDetectedElements.clear(),this.cleanupManagedImages()}attachImage(t,e){let i=s=>{this.editMode&&(s.preventDefault(),s.stopPropagation(),this.showImageContextMenu(s.clientX,s.clientY,t))};this.managedImages.set(t,{key:e,ctxHandler:i}),this.editMode&&this.enableImageEdit(t,i)}detachImage(t){let e=this.managedImages.get(t);e&&(t.removeEventListener("contextmenu",e.ctxHandler),t.style.outline="",t.style.cursor="",this.managedImages.delete(t))}cleanupManagedImages(){for(let[t]of this.managedImages)this.detachImage(t);this.managedImages.clear(),this.dismissImageCtxMenu()}applyImageEditMode(t){for(let[e,i]of this.managedImages)t?this.enableImageEdit(e,i.ctxHandler):this.disableImageEdit(e,i.ctxHandler);t||this.dismissImageCtxMenu()}enableImageEdit(t,e){t.dataset.origTitle||(t.dataset.origTitle=t.title||""),t.title="Right-click to change image",t.style.outline=`2px dashed ${this.highlightColor}`,t.style.cursor="context-menu",t.addEventListener("contextmenu",e)}disableImageEdit(t,e){t.title=t.dataset.origTitle||"",delete t.dataset.origTitle,t.style.outline="",t.style.cursor="",t.removeEventListener("contextmenu",e)}showImageContextMenu(t,e,i){this.dismissImageCtxMenu();let s=document.createElement("div");Object.assign(s.style,{position:"fixed",zIndex:"10005",minWidth:"160px",background:"rgba(30,15,60,0.85)",backdropFilter:"blur(16px)",webkitBackdropFilter:"blur(16px)",border:"1px solid rgba(139,92,246,0.3)",borderRadius:"10px",padding:"4px",boxShadow:"0 8px 32px rgba(0,0,0,0.4)",fontFamily:"system-ui, -apple-system, sans-serif",left:Math.min(t,window.innerWidth-180)+"px",top:Math.min(e,window.innerHeight-50)+"px"});let n=document.createElement("button");Object.assign(n.style,{width:"100%",padding:"8px 12px",border:"none",borderRadius:"7px",background:"transparent",color:"white",fontSize:"12px",fontWeight:"500",cursor:"pointer",display:"flex",alignItems:"center",gap:"8px"}),n.innerHTML=`${g.image} Upload Image`,n.addEventListener("mouseenter",()=>n.style.background="rgba(103,34,251,0.3)"),n.addEventListener("mouseleave",()=>n.style.background="transparent"),n.addEventListener("click",()=>{this.activeImageEl=i,this.fileInput&&(this.fileInput.value="",this.fileInput.click()),this.dismissImageCtxMenu()}),s.appendChild(n),document.body.appendChild(s),this.imageCtxMenu=s;let r=o=>{s.contains(o.target)||(this.dismissImageCtxMenu(),document.removeEventListener("mousedown",r))};setTimeout(()=>document.addEventListener("mousedown",r),0)}dismissImageCtxMenu(){this.imageCtxMenu&&(this.imageCtxMenu.remove(),this.imageCtxMenu=null)}onImageFileSelected(t){let i=t.target.files?.[0];if(!i||!this.activeImageEl)return;this.imageUploading=!0,this.updateUI();let s=new FileReader;s.onload=()=>{let n=s.result;this.uploadImageToApi(n)},s.readAsDataURL(i)}async uploadImageToApi(t){try{let e=await this.apiFetch(`${this.config.apiBase}/web/upload-image`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:t})});if(!e.ok)throw new Error(`HTTP ${e.status}`);let i=await e.json(),n=(this.config.imageBaseUrl||(this.config.apiBase?new URL(this.config.apiBase).origin:""))+i.path;if(this.activeImageEl){let r=this.managedImages.get(this.activeImageEl)?.key||"";this.activeImageEl.src=n,this.onImageChanged(r,n)}this.imageUploading=!1,this.activeImageEl=null,this.updateUI(),this.showToast("Image updated!","success")}catch(e){this.imageUploading=!1,this.activeImageEl=null,this.updateUI(),this.showToast("Image upload failed.","error"),this.config.onError?.("upload-image",e)}}applyPageImages(){for(let[t,e]of this.managedImages){let i=this.pageImages[e.key];i&&t.src!==i&&(t.src=i)}}getPageText(t){return this.pageTexts[t]?.[this.currentLang]||""}onTextChanged(t,e){this.undoStack.push(this.createSnapshot()),this.redoStack=[],this.canUndo=!0,this.canRedo=!1,this.pageTexts[t]||(this.pageTexts[t]={}),this.pageTexts[t]={...this.pageTexts[t],[this.currentLang]:e},this.dirtyKeys.add(t),this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size,this.pushHistory(t,this.currentLang),this.updateUI()}onImageChanged(t,e){this.undoStack.push(this.createSnapshot()),this.redoStack=[],this.canUndo=!0,this.canRedo=!1,this.pageImages={...this.pageImages,[t]:e},this.dirtyImageKeys.add(t),this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size,this.pushHistory(t,"img"),this.updateUI()}createSnapshot(){return JSON.stringify({texts:this.pageTexts,images:this.pageImages})}applySnapshot(t){let e=JSON.parse(t);e.texts?(this.pageTexts=e.texts,e.images&&(this.pageImages=e.images)):this.pageTexts=e}onUndo(){this.undoStack.length!==0&&(this.redoStack.push(this.createSnapshot()),this.applySnapshot(this.undoStack.pop()),this.canUndo=this.undoStack.length>0,this.canRedo=!0,this.recalcDirtyKeys(),this.updateElementTexts(),this.applyPageImages(),this.updateUI())}onRedo(){this.redoStack.length!==0&&(this.undoStack.push(this.createSnapshot()),this.applySnapshot(this.redoStack.pop()),this.canUndo=!0,this.canRedo=this.redoStack.length>0,this.recalcDirtyKeys(),this.updateElementTexts(),this.applyPageImages(),this.updateUI())}recalcDirtyKeys(){this.dirtyKeys.clear();for(let t of this.registeredKeys)JSON.stringify(this.pageTexts[t])!==JSON.stringify(this.originalTexts[t])&&this.dirtyKeys.add(t);this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size}resetAll(){this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.undoStack=[],this.redoStack=[],this.canUndo=!1,this.canRedo=!1,this.updateUI()}resetAfterSave(){this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages))}cancelEditing(){this.pageTexts=JSON.parse(JSON.stringify(this.originalTexts)),this.pageImages=JSON.parse(JSON.stringify(this.originalImages)),this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.updateElementTexts(),this.applyPageImages(),this.updateUI()}logout(){localStorage.removeItem("builder_token"),sessionStorage.removeItem("builder_edit_mode"),this.editMode=!1,this.isEditAllowed=!1,this.applyEditMode(!1),this.isOpen=!1,this.updateUI(),this.showToast("Logged out","success")}async loadTranslationsAndInit(t){if(this.config.i18nBasePath)try{let e=this.currentLang,i=await fetch(`${this.config.i18nBasePath}/${e}.json`);if(i.ok){let s=await i.json();this.translationCache.clear(),this.flattenTranslations(s,"",this.getPageSection())}}catch{}this.autoDetectAndScan(),this.editMode&&this.applyEditMode(!0),this.config.apiBase?t?this.loadPageContent("draft"):this.loadPublishedContent():(this.buildDefaultTexts(),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.setLoading(!1)),this.updateUI()}flattenTranslations(t,e,i){for(let s of Object.keys(t)){let n=e?`${e}.${s}`:s;if(typeof t[s]=="string"){let r=t[s].trim();r.length>=2&&(!this.translationCache.get(r)||n.toUpperCase().startsWith(i))&&this.translationCache.set(r,n)}else typeof t[s]=="object"&&t[s]!==null&&!Array.isArray(t[s])&&this.flattenTranslations(t[s],n,i)}}getPageSection(){return this.currentSlug.replace(/^\//,"").replace(/-/g,"_").toUpperCase()}apiFetch(t,e){let i=localStorage.getItem("builder_token")||"",s={...e?.headers||{}};return i&&(s.Authorization=`Bearer ${i}`),fetch(t,{...e,headers:s})}async fetchSection(t,e){try{let i=await this.apiFetch(`${this.config.apiBase}/web-page/get?slug=${encodeURIComponent(t)}&status=${e}&site_identifier=${encodeURIComponent(this.siteIdentifier)}`);return i.ok?await i.json():null}catch{return null}}hasSection(t){return t==="header"?!!(document.querySelector("header")||document.querySelector('[class*="-header"]')||document.querySelector('[tagName$="-header"]')):!!(document.querySelector("footer")||document.querySelector('[class*="-footer"]')||document.querySelector('[tagName$="-footer"]'))}async loadPageContent(t){this.setLoading(!0),this.cleanOldHistory();try{let e=[this.fetchSection(this.currentSlug,t)],i=this.hasSection("header"),s=this.hasSection("footer");i&&e.push(this.fetchSection("/header",t)),s&&e.push(this.fetchSection("/footer",t));let n=await Promise.all(e),r=n[0],o=i?n[1]:null,a=s?n[i?2:1]:null,l=m=>m?.content?.texts||m?.published_content?.texts||null,d=l(r),h=l(o),b=l(a);d||h||b?this.pageTexts={...d||{},...h||{},...b||{}}:this.buildDefaultTexts();let p=r?.content?.images||r?.published_content?.images;p&&(this.pageImages=JSON.parse(JSON.stringify(p)),this.applyPageImages()),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.resetAll(),this.updateElementTexts()}catch{this.buildDefaultTexts(),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.resetAll()}finally{this.setLoading(!1)}}async loadPublishedContent(){this.setLoading(!0);try{let t=[this.fetchSection(this.currentSlug,"published")],e=this.hasSection("header"),i=this.hasSection("footer");e&&t.push(this.fetchSection("/header","published")),i&&t.push(this.fetchSection("/footer","published"));let s=await Promise.all(t),n=s[0],r=e?s[1]:null,o=i?s[e?2:1]:null,a=h=>h?.published_content?.texts||null,l={...a(n)||{},...a(r)||{},...a(o)||{}};Object.keys(l).length>0&&(this.pageTexts=JSON.parse(JSON.stringify(l)),this.updateElementTexts());let d=n?.published_content?.images;d&&Object.keys(d).length>0&&(this.pageImages=JSON.parse(JSON.stringify(d)),this.applyPageImages())}catch{}finally{this.setLoading(!1)}}buildDefaultTexts(){let t={};for(let e of this.registeredKeys){let s=document.querySelector(`[data-cms="${e}"]`)?.textContent?.trim()||"";t[e]={[this.currentLang]:s}}if(this.pageTexts=t,this.config.i18nBasePath){let e=this.languages.filter(i=>i!==this.currentLang);for(let i of e)fetch(`${this.config.i18nBasePath}/${i}.json`).then(s=>s.json()).then(s=>{let n={...this.pageTexts};for(let r of this.registeredKeys){let o=r.split("."),a=s;for(let l of o)a=a?.[l];a&&typeof a=="string"&&(n[r]={...n[r],[i]:a})}this.pageTexts=n,this.originalTexts=JSON.parse(JSON.stringify(n)),this.updateElementTexts()}).catch(()=>{})}}groupTextsBySection(){let t={};for(let[,e]of this.managedElements){let i=e.sectionSlug;t[i]||(t[i]={}),this.pageTexts[e.key]!==void 0&&(t[i][e.key]=this.pageTexts[e.key])}return t}async saveChanges(){if(!this.config.apiBase){this.showToast("No API configured. Set apiBase to enable save.","error");return}this.isSaving=!0,this.updateUI();try{let t=this.groupTextsBySection(),e=Object.entries(t).map(([s,n])=>this.apiFetch(`${this.config.apiBase}/web_page/save`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({slug:s,title:s===this.currentSlug?this.currentTitle:s.replace("/",""),site_identifier:this.siteIdentifier,content:{texts:n,...s===this.currentSlug?{images:this.pageImages}:{}}})}));if((await Promise.all(e)).some(s=>!s.ok))throw new Error("One or more sections failed to save");this.isSaving=!1,this.resetAfterSave(),this.updateUI(),this.showToast("Draft saved successfully!","success"),this.config.onSaved?.()}catch(t){this.isSaving=!1,this.updateUI(),this.showToast("Save failed. Please try again.","error"),this.config.onError?.("save",t)}}async publishChanges(){if(!this.config.apiBase){this.showToast("No API configured. Set apiBase to enable publish.","error");return}this.isPublishing=!0,this.updateUI();try{let t=this.groupTextsBySection(),e=Object.entries(t).map(([s,n])=>this.apiFetch(`${this.config.apiBase}/web_page/publish`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({slug:s,title:s===this.currentSlug?this.currentTitle:s.replace("/",""),site_identifier:this.siteIdentifier,published_content:{texts:n,...s===this.currentSlug?{images:this.pageImages}:{}}})}));if((await Promise.all(e)).some(s=>!s.ok))throw new Error("One or more sections failed to publish");this.isPublishing=!1,this.resetAfterSave(),this.updateUI(),this.showToast("Published successfully!","success"),this.config.onPublished?.()}catch(t){this.isPublishing=!1,this.updateUI(),this.showToast("Publish failed. Please try again.","error"),this.config.onError?.("publish",t)}}async toggleHistory(){this.showHistory=!this.showHistory,this.showHistory&&await this.loadHistory(),this.updateUI()}async loadHistory(){try{let t=await this.openDB(),e=await new Promise((i,s)=>{let a=t.transaction(v,"readonly").objectStore(v).index("slug").getAll(this.currentSlug);a.onsuccess=()=>i(a.result.sort((l,d)=>d.timestamp-l.timestamp).slice(0,30)),a.onerror=()=>s(a.error)});this.historyList=e,this.renderHistoryList()}catch{}}renderHistoryList(){if(this.historyListEl){if(this.historyListEl.innerHTML="",this.historyList.length===0){let t=this.createElement("div","lcms-history-empty");t.textContent="No history yet",this.historyListEl.appendChild(t);return}for(let t of this.historyList){let e=document.createElement("button");e.className="lcms-history-item";let i=this.createElement("div","lcms-history-label");i.textContent=t.label;let s=this.createElement("div","lcms-history-meta"),n=this.createElement("span","lcms-history-lang");n.textContent=t.lang.toUpperCase();let r=document.createTextNode(" "+this.formatDate(t.timestamp));s.appendChild(n),s.appendChild(r),e.appendChild(i),e.appendChild(s),e.addEventListener("click",o=>{o.stopPropagation(),this.restoreHistory(t)}),this.historyListEl.appendChild(e)}}}restoreHistory(t){this.undoStack.push(this.createSnapshot()),this.canUndo=!0,this.applySnapshot(t.snapshot),this.recalcDirtyKeys(),this.showHistory=!1,this.updateElementTexts(),this.applyPageImages(),this.updateUI(),this.showToast("Restored from history","success")}pushHistory(t,e){let i=e==="img",s=i?t.split("/").pop()||"image":t.split("_").pop()||t,n=i?`Changed ${s}`:`Edited ${s}`;this.openDB().then(r=>{r.transaction(v,"readwrite").objectStore(v).add({slug:this.currentSlug,timestamp:Date.now(),label:n,lang:e,snapshot:JSON.stringify({texts:this.pageTexts,images:this.pageImages})})}).catch(()=>{})}async cleanOldHistory(){try{let t=await this.openDB(),e=Date.now()-this.historyRetentionMs,n=t.transaction(v,"readwrite").objectStore(v).index("timestamp").openCursor(IDBKeyRange.upperBound(e));n.onsuccess=()=>{let r=n.result;r&&(r.delete(),r.continue())}}catch{}}openDB(){return this.db?Promise.resolve(this.db):new Promise((t,e)=>{let i=indexedDB.open(B,1);i.onupgradeneeded=()=>{let s=i.result;if(!s.objectStoreNames.contains(v)){let n=s.createObjectStore(v,{keyPath:"id",autoIncrement:!0});n.createIndex("slug","slug",{unique:!1}),n.createIndex("timestamp","timestamp",{unique:!1})}},i.onsuccess=()=>{this.db=i.result,t(this.db)},i.onerror=()=>e(i.error)})}showToast(t,e){if(!this.toastEl)return;this.toastMessage=t,this.toastType=e;let i=e==="success"?g.check:g.error;this.toastEl.className=`lcms-toast lcms-toast-${e}`,this.toastEl.innerHTML=`${i} ${t}`,this.toastTimer&&clearTimeout(this.toastTimer),this.toastTimer=setTimeout(()=>{this.toastEl&&this.toastEl.classList.add("lcms-hidden")},3e3)}onFabClick(t){t.stopPropagation(),!this.hasMoved&&(this.isOpen=!this.isOpen,this.updateUI())}togglePanel(){this.hasMoved||(this.isOpen=!this.isOpen,this.updateUI())}toggleEditMode(t){t.stopPropagation(),t.target.checked?(sessionStorage.setItem("builder_edit_mode","true"),this.editMode=!0,this.currentLang=this.detectCurrentLanguage(),this.applyEditMode(!0),this.loadPageContent("draft")):(sessionStorage.removeItem("builder_edit_mode"),this.editMode=!1,this.applyEditMode(!1)),this.updateUI(),this.config.onEditModeChanged?.(this.editMode)}switchLang(t){this.currentLang=t,localStorage.setItem("selectedLanguage",t),document.documentElement.setAttribute("lang",t),document.documentElement.setAttribute("dir",t==="ar"?"rtl":"ltr"),this.config.onLangChanged?.(t),window.dispatchEvent(new CustomEvent("cms:langChanged",{detail:{lang:t}})),window.location.reload()}onDragStart(t){t.target.closest("button:not(.lcms-fab-btn)")||t.target.closest(".lcms-panel")||(this.isDragging=!0,this.hasMoved=!1,this.dragStartX=t.clientX,this.dragStartY=t.clientY,this.startPosX=this.posX,this.startPosY=this.posY,document.addEventListener("mousemove",this.boundMouseMove),document.addEventListener("mouseup",this.boundMouseUp))}onDragMove(t){if(!this.isDragging||!this.fabEl)return;let e=t.clientX-this.dragStartX,i=t.clientY-this.dragStartY;(Math.abs(e)>3||Math.abs(i)>3)&&(this.hasMoved=!0),this.posX=Math.max(0,Math.min(window.innerWidth-60,this.startPosX+e)),this.posY=Math.max(0,Math.min(window.innerHeight-60,this.startPosY+i)),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px"}onDragEnd(){this.isDragging=!1,document.removeEventListener("mousemove",this.boundMouseMove),document.removeEventListener("mouseup",this.boundMouseUp)}onTouchStart(t){if(t.target.closest("button:not(.lcms-fab-btn)")||t.target.closest(".lcms-panel"))return;let e=t.touches[0];this.isDragging=!0,this.hasMoved=!1,this.dragStartX=e.clientX,this.dragStartY=e.clientY,this.startPosX=this.posX,this.startPosY=this.posY,document.addEventListener("touchmove",this.boundTouchMove,{passive:!1}),document.addEventListener("touchend",this.boundTouchEnd)}onTouchMove(t){if(!this.isDragging||!this.fabEl)return;t.preventDefault();let e=t.touches[0],i=e.clientX-this.dragStartX,s=e.clientY-this.dragStartY;(Math.abs(i)>3||Math.abs(s)>3)&&(this.hasMoved=!0),this.posX=Math.max(0,Math.min(window.innerWidth-60,this.startPosX+i)),this.posY=Math.max(0,Math.min(window.innerHeight-60,this.startPosY+s)),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px"}onTouchEnd(){this.isDragging=!1,document.removeEventListener("touchmove",this.boundTouchMove),document.removeEventListener("touchend",this.boundTouchEnd)}setLoading(t){this.loading=t,this.updateUI()}detectCurrentLanguage(){let t=document.documentElement.getAttribute("lang");if(t&&this.languages.includes(t))return t;let e=localStorage.getItem("selectedLanguage");return e&&this.languages.includes(e)?e:this.defaultLanguage}createElement(t,e){let i=document.createElement(t);return i.className=e,i}formatDate(t){let e=new Date(t),i=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e.getHours(),n=e.getMinutes().toString().padStart(2,"0"),r=s>=12?"PM":"AM",o=s%12||12;return`${i[e.getMonth()]} ${e.getDate()}, ${o}:${n} ${r}`}showLoginModal(){if(this.loginModalEl)return;let t=document.createElement("div");t.className="lcms-login-overlay",t.id="lcms-login-modal",t.innerHTML=`
306
+ </div>`,this.rootEl.appendChild(this.loaderEl),this.toastEl=this.createElement("div","lcms-toast lcms-hidden"),this.rootEl.appendChild(this.toastEl),this.fabEl=this.createElement("div","lcms-fab"),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px",this.fabEl.addEventListener("mousedown",t=>this.onDragStart(t)),this.fabEl.addEventListener("touchstart",t=>this.onTouchStart(t),{passive:!0}),this.fabBtn=document.createElement("button"),this.fabBtn.className="lcms-fab-btn",this.fabBtn.innerHTML=g.edit,this.fabBtn.addEventListener("click",t=>this.onFabClick(t)),this.badgeEl=this.createElement("span","lcms-badge lcms-hidden"),this.fabBtn.appendChild(this.badgeEl),this.fabEl.appendChild(this.fabBtn),this.panelEl=this.createElement("div","lcms-panel lcms-hidden"),this.buildPanel(),this.fabEl.appendChild(this.panelEl),this.rootEl.appendChild(this.fabEl),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.accept="image/*",this.fileInput.style.display="none",this.fileInput.addEventListener("change",t=>this.onImageFileSelected(t)),this.rootEl.appendChild(this.fileInput),this.imgOverlay=this.createElement("div","lcms-img-upload-overlay lcms-hidden"),this.imgOverlay.innerHTML='<div class="lcms-img-upload-content"><div class="lcms-spinner-lg"></div><span>Uploading image...</span></div>',this.rootEl.appendChild(this.imgOverlay),document.body.appendChild(this.rootEl)}buildPanel(){if(!this.panelEl)return;let t=this.createElement("div","lcms-panel-header"),e=this.createElement("label","lcms-toggle");e.addEventListener("click",c=>c.stopPropagation()),this.editToggle=document.createElement("input"),this.editToggle.type="checkbox",this.editToggle.checked=this.editMode,this.editToggle.addEventListener("change",c=>this.toggleEditMode(c));let i=this.createElement("span","lcms-toggle-slider"),s=this.createElement("span","lcms-toggle-label");s.textContent="Edit",e.appendChild(this.editToggle),e.appendChild(i),e.appendChild(s),t.appendChild(e),this.langSwitchEl=this.createElement("div","lcms-lang-switch lcms-hidden"),this.buildLangButtons(),t.appendChild(this.langSwitchEl);let n=document.createElement("button");n.className="lcms-close-btn",n.innerHTML=g.close,n.addEventListener("click",c=>{c.stopPropagation(),this.togglePanel()}),t.appendChild(n),this.panelEl.appendChild(t),this.siteIdEl=this.createElement("div","lcms-site-id");let r=this.siteIdentifier.includes(".");this.siteIdEl.innerHTML=`<span class="lcms-site-id-icon">${r?"\u{1F310}":"\u{1F511}"}</span><span class="lcms-site-id-text">${r?this.siteIdentifier:this.siteIdentifier.substring(0,8)}</span>`,this.panelEl.appendChild(this.siteIdEl),this.editModeContent=this.createElement("div","lcms-hidden"),this.changesInfoEl=this.createElement("div","lcms-changes-info lcms-hidden"),this.editModeContent.appendChild(this.changesInfoEl);let o=this.createElement("div","lcms-undo-row");this.undoBtn=document.createElement("button"),this.undoBtn.className="lcms-icon-btn",this.undoBtn.innerHTML=g.undo,this.undoBtn.disabled=!0,this.undoBtn.addEventListener("click",c=>{c.stopPropagation(),this.onUndo()}),this.redoBtn=document.createElement("button"),this.redoBtn.className="lcms-icon-btn",this.redoBtn.innerHTML=g.redo,this.redoBtn.disabled=!0,this.redoBtn.addEventListener("click",c=>{c.stopPropagation(),this.onRedo()}),o.appendChild(this.undoBtn),o.appendChild(this.redoBtn),this.editModeContent.appendChild(o);let a=this.createElement("div","lcms-theme-row"),l=this.createElement("span","lcms-theme-label");l.textContent="Theme",a.appendChild(l);let d=this.createElement("div","lcms-theme-colors"),h=["#00C853","#6722FB","#2196F3","#FF5722","#E91E63","#FFD600"];for(let c of h){let y=this.createElement("div","lcms-theme-swatch");y.style.background=c,y.dataset.color=c,c===this.highlightColor&&y.classList.add("active"),y.addEventListener("click",M=>{M.stopPropagation(),this.applyThemeColor(c)}),d.appendChild(y)}let b=this.createElement("div","lcms-theme-custom"),p=this.createElement("div","lcms-theme-custom-preview"),m=document.createElement("input");m.type="color",m.value=this.highlightColor,m.addEventListener("input",c=>{c.stopPropagation(),this.applyThemeColor(c.target.value)}),b.appendChild(p),b.appendChild(m),d.appendChild(b),a.appendChild(d),this.editModeContent.appendChild(a),this.historyBtnEl=document.createElement("button"),this.historyBtnEl.className="lcms-history-btn",this.historyBtnEl.innerHTML=`${g.history} History (7 days)`,this.historyBtnEl.addEventListener("click",c=>{c.stopPropagation(),this.toggleHistory()}),this.editModeContent.appendChild(this.historyBtnEl),this.historyPanelEl=this.createElement("div","lcms-history-panel lcms-hidden");let w=this.createElement("div","lcms-history-title");w.textContent="Edit History",this.historyPanelEl.appendChild(w),this.historyListEl=this.createElement("div","lcms-history-list"),this.historyPanelEl.appendChild(this.historyListEl),this.editModeContent.appendChild(this.historyPanelEl),this.actionsEl=this.createElement("div","lcms-actions"),this.saveBtn=document.createElement("button"),this.saveBtn.className="lcms-btn-save",this.saveBtn.innerHTML=`${g.save} Save Draft`,this.saveBtn.addEventListener("click",c=>{c.stopPropagation(),this.saveChanges()}),this.publishBtn=document.createElement("button"),this.publishBtn.className="lcms-btn-publish",this.publishBtn.innerHTML=`${g.publish} Publish`,this.publishBtn.addEventListener("click",c=>{c.stopPropagation(),this.publishChanges()});let x=document.createElement("button");x.className="lcms-btn-logout",x.innerHTML=`${g.logout} Logout`,x.addEventListener("click",c=>{c.stopPropagation(),this.logout()}),this.actionsEl.appendChild(this.saveBtn),this.actionsEl.appendChild(this.publishBtn),this.actionsEl.appendChild(x),this.editModeContent.appendChild(this.actionsEl),this.panelEl.appendChild(this.editModeContent),this.brandingEl=this.createElement("div","lcms-branding"),this.brandingEl.innerHTML='<div class="lcms-branding-logo"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></div><div class="lcms-branding-text">Powered by <span>XtroEdge</span></div>',this.brandingEl.addEventListener("click",c=>{c.stopPropagation(),window.open("https://xtro.vercel.app/","_blank")}),this.panelEl.appendChild(this.brandingEl)}buildLangButtons(){if(!this.langSwitchEl)return;this.langSwitchEl.innerHTML="";let t=document.createElement("select");t.className="lcms-lang-select";for(let i of this.languages){let s=document.createElement("option");s.value=i,s.textContent=i.toUpperCase(),i===this.currentLang&&(s.selected=!0),t.appendChild(s)}t.addEventListener("change",i=>{i.stopPropagation(),this.switchLang(i.target.value)}),this.langSwitchEl.appendChild(t);let e=document.createElement("span");e.className="lcms-lang-arrow",e.textContent="\u25BC",this.langSwitchEl.appendChild(e)}updateUI(){if(this.fabEl&&(this.fabEl.style.display=this.isEditAllowed?"":"none"),this.fabBtn&&(this.fabBtn.style.display=this.isOpen?"none":"",this.fabBtn.classList.toggle("lcms-fab-active",this.editMode)),this.panelEl&&(this.panelEl.classList.toggle("lcms-hidden",!this.isOpen),this.isOpen)){let t=this.posY>window.innerHeight/2,e=this.posX>window.innerWidth/2;this.panelEl.style.bottom=t?"0":"",this.panelEl.style.top=t?"":"0",this.panelEl.style.right=e?"0":"",this.panelEl.style.left=e?"":"0"}if(this.badgeEl&&(this.badgeEl.classList.toggle("lcms-hidden",this.unsavedChanges===0),this.badgeEl.textContent=String(this.unsavedChanges)),this.editToggle&&(this.editToggle.checked=this.editMode),this.langSwitchEl){this.langSwitchEl.classList.toggle("lcms-hidden",!this.editMode||this.languages.length<=1);let t=this.langSwitchEl.querySelector(".lcms-lang-select");t&&(t.value=this.currentLang)}this.editModeContent&&this.editModeContent.classList.toggle("lcms-hidden",!this.editMode),this.changesInfoEl&&(this.changesInfoEl.classList.toggle("lcms-hidden",this.unsavedChanges===0),this.changesInfoEl.textContent=`${this.unsavedChanges} unsaved change${this.unsavedChanges>1?"s":""}`),this.undoBtn&&(this.undoBtn.disabled=!this.canUndo),this.redoBtn&&(this.redoBtn.disabled=!this.canRedo),this.historyBtnEl&&this.historyBtnEl.classList.toggle("active",this.showHistory),this.historyPanelEl&&this.historyPanelEl.classList.toggle("lcms-hidden",!this.showHistory),this.saveBtn&&(this.saveBtn.disabled=this.isSaving||this.isPublishing||this.unsavedChanges===0,this.saveBtn.innerHTML=this.isSaving?'<span class="lcms-spinner"></span> Saving...':`${g.save} Save Draft`),this.publishBtn&&(this.publishBtn.disabled=this.isSaving||this.isPublishing,this.publishBtn.innerHTML=this.isPublishing?'<span class="lcms-spinner"></span> Publishing...':`${g.publish} Publish`),this.loaderEl&&this.loaderEl.classList.toggle("lcms-hidden",!this.loading),this.imgOverlay&&this.imgOverlay.classList.toggle("lcms-hidden",!this.imageUploading)}autoDetectAndScan(){this.autoDetectElements(),this.scanDOM(),this.scanImages()}resolveContainer(){return this.containerSelector&&document.querySelector(this.containerSelector)||document.body}autoDetectElements(){let t=this.resolveContainer();if(!t)return;for(let a of this.autoDetectedElements)document.contains(a)||this.autoDetectedElements.delete(a);let e=a=>{let l=a.parentElement;for(;l&&l!==document.body;){let d=l.tagName.toLowerCase();if(d==="header"||d.endsWith("-header"))return"/header";if(d==="footer"||d.endsWith("-footer"))return"/footer";l=l.parentElement}return this.currentSlug},i={},s=(a,l)=>{let d=l==="/header"?"header":l==="/footer"?"footer":"page";i[d]||(i[d]={});let h=a.tagName.toLowerCase();i[d][h]||(i[d][h]=0);let p=`${d!=="page"?`${d}_`:""}xcms_${h}_${i[d][h]}`;i[d][h]++,a.setAttribute("data-cms",p),a.setAttribute("data-cms-section",l),this.autoDetectedElements.add(a)},n=this.editableTags.join(",");t.querySelectorAll(n).forEach(a=>{a.hasAttribute("data-cms")||a.closest("#xtroedge-cms-root, script, style, noscript")||a.children.length>3||this.getDirectTextContent(a).trim().length<2||s(a,e(a))}),t.querySelectorAll("[data-editable]").forEach(a=>{a.hasAttribute("data-cms")||a.closest("#xtroedge-cms-root, script, style, noscript")||s(a,e(a))})}scanDOM(){document.querySelectorAll("[data-cms]").forEach(e=>{if(e.closest("#xtroedge-cms-root"))return;let i=e.getAttribute("data-cms");this.registeredKeys.add(i),this.managedElements.has(e)||this.attachElement(e,i)});for(let[e]of this.managedElements)document.contains(e)||this.detachElement(e)}scanImages(){let t=this.resolveContainer();if(!t)return;let e={...this.pageImages},i=!1;t.querySelectorAll("img").forEach(n=>{if(n.closest("#xtroedge-cms-root")||this.managedImages.has(n))return;let r=n.getAttribute("src")||"";!r||r.startsWith("data:")||(this.attachImage(n,r),e[r]||(e[r]=r,i=!0))}),i&&(this.pageImages=e,this.originalImages=JSON.parse(JSON.stringify(e)));for(let[n]of this.managedImages)document.contains(n)||this.detachImage(n)}getDirectTextContent(t){let e="";for(let i=0;i<t.childNodes.length;i++)t.childNodes[i].nodeType===Node.TEXT_NODE&&(e+=t.childNodes[i].textContent||"");return e}attachElement(t,e){let i=()=>{let a=t.textContent?.trim()||"",l=this.getPageText(e);a!==l&&this.onTextChanged(e,a)},s=a=>{a.key==="Enter"&&(a.preventDefault(),t.blur())},n=()=>{let a=t.textContent?.trim()||"",l=this.getPageText(e);a!==l&&this.onTextChanged(e,a)},r=a=>{(t.tagName==="A"||a.target.closest("a"))&&(a.preventDefault(),a.stopPropagation(),t.focus())},o=t.getAttribute("data-cms-section")||this.currentSlug;this.managedElements.set(t,{key:e,sectionSlug:o,blurHandler:i,keydownHandler:s,inputHandler:n,clickHandler:r}),this.editMode&&this.enableElementEdit(t,e,i,s,n,r)}detachElement(t){let e=this.managedElements.get(t);e&&(t.removeEventListener("blur",e.blurHandler),t.removeEventListener("keydown",e.keydownHandler),t.removeEventListener("input",e.inputHandler),t.removeEventListener("click",e.clickHandler,!0),t.removeAttribute("contenteditable"),this.managedElements.delete(t))}enableElementEdit(t,e,i,s,n,r){let o=this.getPageText(e);o&&(t.textContent=o),t.setAttribute("contenteditable","true"),t.style.outline=`2px dashed ${this.highlightColor}`,t.style.outlineOffset="-2px",t.style.cursor="text",t.style.transition="background 0.2s",t.style.minWidth="20px",t.addEventListener("blur",i),t.addEventListener("keydown",s),t.addEventListener("input",n),t.addEventListener("click",r,!0)}disableElementEdit(t,e,i,s,n,r){t.removeAttribute("contenteditable"),t.style.outline="",t.style.outline="",t.style.outlineOffset="",t.style.cursor="",t.style.transition="",t.style.minWidth="";let o=this.getPageText(e);o&&(t.textContent=o),t.removeEventListener("blur",i),t.removeEventListener("keydown",s),t.removeEventListener("input",n),t.removeEventListener("click",r,!0)}applyEditMode(t){for(let[e,i]of this.managedElements)t?this.enableElementEdit(e,i.key,i.blurHandler,i.keydownHandler,i.inputHandler,i.clickHandler):this.disableElementEdit(e,i.key,i.blurHandler,i.keydownHandler,i.inputHandler,i.clickHandler);this.applyImageEditMode(t)}updateElementTexts(){this.observer?.disconnect();for(let[t,e]of this.managedElements){if(document.activeElement===t)continue;let i=this.getPageText(e.key);i&&t.textContent!==i&&(t.textContent=i)}this.observer?.observe(document.body,{childList:!0,subtree:!0})}cleanupManagedElements(){for(let[t]of this.managedElements)this.detachElement(t);this.managedElements.clear();for(let t of this.autoDetectedElements)t.removeAttribute("data-cms");this.autoDetectedElements.clear(),this.cleanupManagedImages()}attachImage(t,e){let i=s=>{this.editMode&&(s.preventDefault(),s.stopPropagation(),this.showImageContextMenu(s.clientX,s.clientY,t))};this.managedImages.set(t,{key:e,ctxHandler:i}),this.editMode&&this.enableImageEdit(t,i)}detachImage(t){let e=this.managedImages.get(t);e&&(t.removeEventListener("contextmenu",e.ctxHandler),t.style.outline="",t.style.cursor="",this.managedImages.delete(t))}cleanupManagedImages(){for(let[t]of this.managedImages)this.detachImage(t);this.managedImages.clear(),this.dismissImageCtxMenu()}applyImageEditMode(t){for(let[e,i]of this.managedImages)t?this.enableImageEdit(e,i.ctxHandler):this.disableImageEdit(e,i.ctxHandler);t||this.dismissImageCtxMenu()}enableImageEdit(t,e){t.dataset.origTitle||(t.dataset.origTitle=t.title||""),t.title="Right-click to change image",t.style.outline=`2px dashed ${this.highlightColor}`,t.style.cursor="context-menu",t.addEventListener("contextmenu",e)}disableImageEdit(t,e){t.title=t.dataset.origTitle||"",delete t.dataset.origTitle,t.style.outline="",t.style.cursor="",t.removeEventListener("contextmenu",e)}showImageContextMenu(t,e,i){this.dismissImageCtxMenu();let s=document.createElement("div");Object.assign(s.style,{position:"fixed",zIndex:"10005",minWidth:"160px",background:"rgba(30,15,60,0.85)",backdropFilter:"blur(16px)",webkitBackdropFilter:"blur(16px)",border:"1px solid rgba(139,92,246,0.3)",borderRadius:"10px",padding:"4px",boxShadow:"0 8px 32px rgba(0,0,0,0.4)",fontFamily:"system-ui, -apple-system, sans-serif",left:Math.min(t,window.innerWidth-180)+"px",top:Math.min(e,window.innerHeight-50)+"px"});let n=document.createElement("button");Object.assign(n.style,{width:"100%",padding:"8px 12px",border:"none",borderRadius:"7px",background:"transparent",color:"white",fontSize:"12px",fontWeight:"500",cursor:"pointer",display:"flex",alignItems:"center",gap:"8px"}),n.innerHTML=`${g.image} Upload Image`,n.addEventListener("mouseenter",()=>n.style.background="rgba(103,34,251,0.3)"),n.addEventListener("mouseleave",()=>n.style.background="transparent"),n.addEventListener("click",()=>{this.activeImageEl=i,this.fileInput&&(this.fileInput.value="",this.fileInput.click()),this.dismissImageCtxMenu()}),s.appendChild(n),document.body.appendChild(s),this.imageCtxMenu=s;let r=o=>{s.contains(o.target)||(this.dismissImageCtxMenu(),document.removeEventListener("mousedown",r))};setTimeout(()=>document.addEventListener("mousedown",r),0)}dismissImageCtxMenu(){this.imageCtxMenu&&(this.imageCtxMenu.remove(),this.imageCtxMenu=null)}onImageFileSelected(t){let i=t.target.files?.[0];if(!i||!this.activeImageEl)return;this.imageUploading=!0,this.updateUI();let s=new FileReader;s.onload=()=>{let n=s.result;this.uploadImageToApi(n)},s.readAsDataURL(i)}async uploadImageToApi(t){try{let e=await this.apiFetch(`${this.config.apiBase}/web/upload-image`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:t})});if(!e.ok)throw new Error(`HTTP ${e.status}`);let i=await e.json(),n=(this.config.imageBaseUrl||(this.config.apiBase?new URL(this.config.apiBase).origin:""))+i.path;if(this.activeImageEl){let r=this.managedImages.get(this.activeImageEl)?.key||"";this.activeImageEl.src=n,this.onImageChanged(r,n)}this.imageUploading=!1,this.activeImageEl=null,this.updateUI(),this.showToast("Image updated!","success")}catch(e){this.imageUploading=!1,this.activeImageEl=null,this.updateUI(),this.showToast("Image upload failed.","error"),this.config.onError?.("upload-image",e)}}applyPageImages(){for(let[t,e]of this.managedImages){let i=this.pageImages[e.key];i&&t.src!==i&&(t.src=i)}}getPageText(t){return this.pageTexts[t]?.[this.currentLang]||""}onTextChanged(t,e){this.undoStack.push(this.createSnapshot()),this.redoStack=[],this.canUndo=!0,this.canRedo=!1,this.pageTexts[t]||(this.pageTexts[t]={}),this.pageTexts[t]={...this.pageTexts[t],[this.currentLang]:e},this.dirtyKeys.add(t),this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size,this.pushHistory(t,this.currentLang),this.updateUI()}onImageChanged(t,e){this.undoStack.push(this.createSnapshot()),this.redoStack=[],this.canUndo=!0,this.canRedo=!1,this.pageImages={...this.pageImages,[t]:e},this.dirtyImageKeys.add(t),this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size,this.pushHistory(t,"img"),this.updateUI()}createSnapshot(){return JSON.stringify({texts:this.pageTexts,images:this.pageImages})}applySnapshot(t){let e=JSON.parse(t);e.texts?(this.pageTexts=e.texts,e.images&&(this.pageImages=e.images)):this.pageTexts=e}onUndo(){this.undoStack.length!==0&&(this.redoStack.push(this.createSnapshot()),this.applySnapshot(this.undoStack.pop()),this.canUndo=this.undoStack.length>0,this.canRedo=!0,this.recalcDirtyKeys(),this.updateElementTexts(),this.applyPageImages(),this.updateUI())}onRedo(){this.redoStack.length!==0&&(this.undoStack.push(this.createSnapshot()),this.applySnapshot(this.redoStack.pop()),this.canUndo=!0,this.canRedo=this.redoStack.length>0,this.recalcDirtyKeys(),this.updateElementTexts(),this.applyPageImages(),this.updateUI())}recalcDirtyKeys(){this.dirtyKeys.clear();for(let t of this.registeredKeys)JSON.stringify(this.pageTexts[t])!==JSON.stringify(this.originalTexts[t])&&this.dirtyKeys.add(t);this.unsavedChanges=this.dirtyKeys.size+this.dirtyImageKeys.size}resetAll(){this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.undoStack=[],this.redoStack=[],this.canUndo=!1,this.canRedo=!1,this.updateUI()}resetAfterSave(){this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages))}cancelEditing(){this.pageTexts=JSON.parse(JSON.stringify(this.originalTexts)),this.pageImages=JSON.parse(JSON.stringify(this.originalImages)),this.dirtyKeys.clear(),this.dirtyImageKeys.clear(),this.unsavedChanges=0,this.updateElementTexts(),this.applyPageImages(),this.updateUI()}logout(){localStorage.removeItem("builder_token"),sessionStorage.removeItem("builder_edit_mode"),this.editMode=!1,this.isEditAllowed=!1,this.applyEditMode(!1),this.isOpen=!1,this.updateUI(),this.showToast("Logged out","success")}async loadTranslationsAndInit(t){if(this.config.i18nBasePath)try{let e=this.currentLang,i=await fetch(`${this.config.i18nBasePath}/${e}.json`);if(i.ok){let s=await i.json();this.translationCache.clear(),this.flattenTranslations(s,"",this.getPageSection())}}catch{}this.autoDetectAndScan(),this.editMode&&this.applyEditMode(!0),this.config.apiBase?t?this.loadPageContent("draft"):this.loadPublishedContent():(this.buildDefaultTexts(),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.setLoading(!1)),this.updateUI()}flattenTranslations(t,e,i){for(let s of Object.keys(t)){let n=e?`${e}.${s}`:s;if(typeof t[s]=="string"){let r=t[s].trim();r.length>=2&&(!this.translationCache.get(r)||n.toUpperCase().startsWith(i))&&this.translationCache.set(r,n)}else typeof t[s]=="object"&&t[s]!==null&&!Array.isArray(t[s])&&this.flattenTranslations(t[s],n,i)}}getPageSection(){return this.currentSlug.replace(/^\//,"").replace(/-/g,"_").toUpperCase()}apiFetch(t,e){let i=localStorage.getItem("builder_token")||"",s={...e?.headers||{}};return i&&(s.Authorization=`Bearer ${i}`),fetch(t,{...e,headers:s})}async fetchSection(t,e){try{let i=await this.apiFetch(`${this.config.apiBase}/web-page/get?slug=${encodeURIComponent(t)}&status=${e}&site_identifier=${encodeURIComponent(this.siteIdentifier)}`);return i.ok?await i.json():null}catch{return null}}hasSection(t){let e=t==="header"?"/header":"/footer";for(let[,i]of this.managedElements)if(i.sectionSlug===e)return!0;return!1}async loadPageContent(t){this.setLoading(!0),this.cleanOldHistory();try{let e=[this.fetchSection(this.currentSlug,t)],i=this.hasSection("header"),s=this.hasSection("footer");i&&e.push(this.fetchSection("/header",t)),s&&e.push(this.fetchSection("/footer",t));let n=await Promise.all(e),r=n[0],o=i?n[1]:null,a=s?n[i?2:1]:null,l=m=>m?.content?.texts||m?.published_content?.texts||null,d=l(r),h=l(o),b=l(a);d||h||b?this.pageTexts={...d||{},...h||{},...b||{}}:this.buildDefaultTexts();let p=r?.content?.images||r?.published_content?.images;p&&(this.pageImages=JSON.parse(JSON.stringify(p)),this.applyPageImages()),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.resetAll(),this.updateElementTexts()}catch{this.buildDefaultTexts(),this.originalTexts=JSON.parse(JSON.stringify(this.pageTexts)),this.originalImages=JSON.parse(JSON.stringify(this.pageImages)),this.resetAll()}finally{this.setLoading(!1)}}async loadPublishedContent(){this.setLoading(!0);try{let t=[this.fetchSection(this.currentSlug,"published")],e=this.hasSection("header"),i=this.hasSection("footer");e&&t.push(this.fetchSection("/header","published")),i&&t.push(this.fetchSection("/footer","published"));let s=await Promise.all(t),n=s[0],r=e?s[1]:null,o=i?s[e?2:1]:null,a=h=>h?.published_content?.texts||null,l={...a(n)||{},...a(r)||{},...a(o)||{}};Object.keys(l).length>0&&(this.pageTexts=JSON.parse(JSON.stringify(l)),this.updateElementTexts());let d=n?.published_content?.images;d&&Object.keys(d).length>0&&(this.pageImages=JSON.parse(JSON.stringify(d)),this.applyPageImages())}catch{}finally{this.setLoading(!1)}}buildDefaultTexts(){let t={};for(let e of this.registeredKeys){let s=document.querySelector(`[data-cms="${e}"]`)?.textContent?.trim()||"";t[e]={[this.currentLang]:s}}if(this.pageTexts=t,this.config.i18nBasePath){let e=this.languages.filter(i=>i!==this.currentLang);for(let i of e)fetch(`${this.config.i18nBasePath}/${i}.json`).then(s=>s.json()).then(s=>{let n={...this.pageTexts};for(let r of this.registeredKeys){let o=r.split("."),a=s;for(let l of o)a=a?.[l];a&&typeof a=="string"&&(n[r]={...n[r],[i]:a})}this.pageTexts=n,this.originalTexts=JSON.parse(JSON.stringify(n)),this.updateElementTexts()}).catch(()=>{})}}groupTextsBySection(){let t={};for(let[,e]of this.managedElements){let i=e.sectionSlug;t[i]||(t[i]={}),this.pageTexts[e.key]!==void 0&&(t[i][e.key]=this.pageTexts[e.key])}return t}async saveChanges(){if(!this.config.apiBase){this.showToast("No API configured. Set apiBase to enable save.","error");return}this.isSaving=!0,this.updateUI();try{let t=this.groupTextsBySection(),e=Object.entries(t).map(([s,n])=>this.apiFetch(`${this.config.apiBase}/web_page/save`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({slug:s,title:s===this.currentSlug?this.currentTitle:s.replace("/",""),site_identifier:this.siteIdentifier,content:{texts:n,...s===this.currentSlug?{images:this.pageImages}:{}}})}));if((await Promise.all(e)).some(s=>!s.ok))throw new Error("One or more sections failed to save");this.isSaving=!1,this.resetAfterSave(),this.updateUI(),this.showToast("Draft saved successfully!","success"),this.config.onSaved?.()}catch(t){this.isSaving=!1,this.updateUI(),this.showToast("Save failed. Please try again.","error"),this.config.onError?.("save",t)}}async publishChanges(){if(!this.config.apiBase){this.showToast("No API configured. Set apiBase to enable publish.","error");return}this.isPublishing=!0,this.updateUI();try{let t=this.groupTextsBySection(),e=Object.entries(t).map(([s,n])=>this.apiFetch(`${this.config.apiBase}/web_page/publish`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({slug:s,title:s===this.currentSlug?this.currentTitle:s.replace("/",""),site_identifier:this.siteIdentifier,published_content:{texts:n,...s===this.currentSlug?{images:this.pageImages}:{}}})}));if((await Promise.all(e)).some(s=>!s.ok))throw new Error("One or more sections failed to publish");this.isPublishing=!1,this.resetAfterSave(),this.updateUI(),this.showToast("Published successfully!","success"),this.config.onPublished?.()}catch(t){this.isPublishing=!1,this.updateUI(),this.showToast("Publish failed. Please try again.","error"),this.config.onError?.("publish",t)}}async toggleHistory(){this.showHistory=!this.showHistory,this.showHistory&&await this.loadHistory(),this.updateUI()}async loadHistory(){try{let t=await this.openDB(),e=await new Promise((i,s)=>{let a=t.transaction(v,"readonly").objectStore(v).index("slug").getAll(this.currentSlug);a.onsuccess=()=>i(a.result.sort((l,d)=>d.timestamp-l.timestamp).slice(0,30)),a.onerror=()=>s(a.error)});this.historyList=e,this.renderHistoryList()}catch{}}renderHistoryList(){if(this.historyListEl){if(this.historyListEl.innerHTML="",this.historyList.length===0){let t=this.createElement("div","lcms-history-empty");t.textContent="No history yet",this.historyListEl.appendChild(t);return}for(let t of this.historyList){let e=document.createElement("button");e.className="lcms-history-item";let i=this.createElement("div","lcms-history-label");i.textContent=t.label;let s=this.createElement("div","lcms-history-meta"),n=this.createElement("span","lcms-history-lang");n.textContent=t.lang.toUpperCase();let r=document.createTextNode(" "+this.formatDate(t.timestamp));s.appendChild(n),s.appendChild(r),e.appendChild(i),e.appendChild(s),e.addEventListener("click",o=>{o.stopPropagation(),this.restoreHistory(t)}),this.historyListEl.appendChild(e)}}}restoreHistory(t){this.undoStack.push(this.createSnapshot()),this.canUndo=!0,this.applySnapshot(t.snapshot),this.recalcDirtyKeys(),this.showHistory=!1,this.updateElementTexts(),this.applyPageImages(),this.updateUI(),this.showToast("Restored from history","success")}pushHistory(t,e){let i=e==="img",s=i?t.split("/").pop()||"image":t.split("_").pop()||t,n=i?`Changed ${s}`:`Edited ${s}`;this.openDB().then(r=>{r.transaction(v,"readwrite").objectStore(v).add({slug:this.currentSlug,timestamp:Date.now(),label:n,lang:e,snapshot:JSON.stringify({texts:this.pageTexts,images:this.pageImages})})}).catch(()=>{})}async cleanOldHistory(){try{let t=await this.openDB(),e=Date.now()-this.historyRetentionMs,n=t.transaction(v,"readwrite").objectStore(v).index("timestamp").openCursor(IDBKeyRange.upperBound(e));n.onsuccess=()=>{let r=n.result;r&&(r.delete(),r.continue())}}catch{}}openDB(){return this.db?Promise.resolve(this.db):new Promise((t,e)=>{let i=indexedDB.open(B,1);i.onupgradeneeded=()=>{let s=i.result;if(!s.objectStoreNames.contains(v)){let n=s.createObjectStore(v,{keyPath:"id",autoIncrement:!0});n.createIndex("slug","slug",{unique:!1}),n.createIndex("timestamp","timestamp",{unique:!1})}},i.onsuccess=()=>{this.db=i.result,t(this.db)},i.onerror=()=>e(i.error)})}showToast(t,e){if(!this.toastEl)return;this.toastMessage=t,this.toastType=e;let i=e==="success"?g.check:g.error;this.toastEl.className=`lcms-toast lcms-toast-${e}`,this.toastEl.innerHTML=`${i} ${t}`,this.toastTimer&&clearTimeout(this.toastTimer),this.toastTimer=setTimeout(()=>{this.toastEl&&this.toastEl.classList.add("lcms-hidden")},3e3)}onFabClick(t){t.stopPropagation(),!this.hasMoved&&(this.isOpen=!this.isOpen,this.updateUI())}togglePanel(){this.hasMoved||(this.isOpen=!this.isOpen,this.updateUI())}toggleEditMode(t){t.stopPropagation(),t.target.checked?(sessionStorage.setItem("builder_edit_mode","true"),this.editMode=!0,this.currentLang=this.detectCurrentLanguage(),this.applyEditMode(!0),this.loadPageContent("draft")):(sessionStorage.removeItem("builder_edit_mode"),this.editMode=!1,this.applyEditMode(!1)),this.updateUI(),this.config.onEditModeChanged?.(this.editMode)}switchLang(t){this.currentLang=t,localStorage.setItem("selectedLanguage",t),document.documentElement.setAttribute("lang",t),document.documentElement.setAttribute("dir",t==="ar"?"rtl":"ltr"),this.config.onLangChanged?.(t),window.dispatchEvent(new CustomEvent("cms:langChanged",{detail:{lang:t}})),window.location.reload()}onDragStart(t){t.target.closest("button:not(.lcms-fab-btn)")||t.target.closest(".lcms-panel")||(this.isDragging=!0,this.hasMoved=!1,this.dragStartX=t.clientX,this.dragStartY=t.clientY,this.startPosX=this.posX,this.startPosY=this.posY,document.addEventListener("mousemove",this.boundMouseMove),document.addEventListener("mouseup",this.boundMouseUp))}onDragMove(t){if(!this.isDragging||!this.fabEl)return;let e=t.clientX-this.dragStartX,i=t.clientY-this.dragStartY;(Math.abs(e)>3||Math.abs(i)>3)&&(this.hasMoved=!0),this.posX=Math.max(0,Math.min(window.innerWidth-60,this.startPosX+e)),this.posY=Math.max(0,Math.min(window.innerHeight-60,this.startPosY+i)),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px"}onDragEnd(){this.isDragging=!1,document.removeEventListener("mousemove",this.boundMouseMove),document.removeEventListener("mouseup",this.boundMouseUp)}onTouchStart(t){if(t.target.closest("button:not(.lcms-fab-btn)")||t.target.closest(".lcms-panel"))return;let e=t.touches[0];this.isDragging=!0,this.hasMoved=!1,this.dragStartX=e.clientX,this.dragStartY=e.clientY,this.startPosX=this.posX,this.startPosY=this.posY,document.addEventListener("touchmove",this.boundTouchMove,{passive:!1}),document.addEventListener("touchend",this.boundTouchEnd)}onTouchMove(t){if(!this.isDragging||!this.fabEl)return;t.preventDefault();let e=t.touches[0],i=e.clientX-this.dragStartX,s=e.clientY-this.dragStartY;(Math.abs(i)>3||Math.abs(s)>3)&&(this.hasMoved=!0),this.posX=Math.max(0,Math.min(window.innerWidth-60,this.startPosX+i)),this.posY=Math.max(0,Math.min(window.innerHeight-60,this.startPosY+s)),this.fabEl.style.left=this.posX+"px",this.fabEl.style.top=this.posY+"px"}onTouchEnd(){this.isDragging=!1,document.removeEventListener("touchmove",this.boundTouchMove),document.removeEventListener("touchend",this.boundTouchEnd)}setLoading(t){this.loading=t,this.updateUI()}detectCurrentLanguage(){let t=document.documentElement.getAttribute("lang");if(t&&this.languages.includes(t))return t;let e=localStorage.getItem("selectedLanguage");return e&&this.languages.includes(e)?e:this.defaultLanguage}createElement(t,e){let i=document.createElement(t);return i.className=e,i}formatDate(t){let e=new Date(t),i=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e.getHours(),n=e.getMinutes().toString().padStart(2,"0"),r=s>=12?"PM":"AM",o=s%12||12;return`${i[e.getMonth()]} ${e.getDate()}, ${o}:${n} ${r}`}showLoginModal(){if(this.loginModalEl)return;let t=document.createElement("div");t.className="lcms-login-overlay",t.id="lcms-login-modal",t.innerHTML=`
307
307
  <div class="lcms-login-box">
308
308
  <div class="lcms-login-logo">
309
309
  <div class="lcms-login-logo-icon">
@@ -324,4 +324,4 @@
324
324
  <button class="lcms-login-btn" id="lcms-login-btn">Sign In</button>
325
325
  <div class="lcms-login-error" id="lcms-login-error"></div>
326
326
  </div>
327
- `,document.body.appendChild(t),this.loginModalEl=t;let e=t.querySelector("#lcms-login-btn"),i=t.querySelector("#lcms-login-email"),s=t.querySelector("#lcms-login-password"),n=()=>this.attemptLogin(i.value.trim(),s.value);e.addEventListener("click",n),s.addEventListener("keydown",r=>{r.key==="Enter"&&n()}),setTimeout(()=>i.focus(),50)}hideLoginModal(){this.loginModalEl?.remove(),this.loginModalEl=null}async attemptLogin(t,e){let i=document.getElementById("lcms-login-btn"),s=document.getElementById("lcms-login-error");if(!t||!e){s.textContent="Please enter your username and password.",s.classList.add("visible");return}i.disabled=!0,i.textContent="Signing in...",s.classList.remove("visible");try{let n=this.config.loginUrl||`${this.config.apiBase}/auth/login`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t,password:e})}),o=null;try{o=await r.json()}catch{}if(!r.ok){let l=o?.message||o?.error||null;throw new Error(l||`HTTP ${r.status}`)}let a=o?.token||o?.authToken||o?.auth_token||o?.data?.token;if(!a)throw new Error("Login successful but no token returned.");localStorage.setItem("builder_token",a),this.hideLoginModal(),this.editMode=!0,this.pendingEditMode=!1,sessionStorage.setItem("builder_edit_mode","true"),this.applyEditMode(!0),this.updateUI(),this.loadPageContent("draft")}catch(n){i.disabled=!1,i.textContent="Sign In",s.textContent=n?.message||"Login failed. Please try again.",s.classList.add("visible")}}resolveSiteIdentifier(){let t=window.location.hostname;if(t&&t!=="localhost"&&t!=="127.0.0.1"&&!t.startsWith("192.168."))return t;let e=localStorage.getItem("xtroedge_site_id");return e||(e="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let s=Math.random()*16|0;return(i==="x"?s:s&3|8).toString(16)}),localStorage.setItem("xtroedge_site_id",e)),e}hexToRgb(t){let e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?`${parseInt(e[1],16)},${parseInt(e[2],16)},${parseInt(e[3],16)}`:"0,200,83"}getDarkerColor(t){let e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(!e)return"#2E7D32";let i=Math.max(0,Math.floor(parseInt(e[1],16)*.6)),s=Math.max(0,Math.floor(parseInt(e[2],16)*.6)),n=Math.max(0,Math.floor(parseInt(e[3],16)*.6));return`#${i.toString(16).padStart(2,"0")}${s.toString(16).padStart(2,"0")}${n.toString(16).padStart(2,"0")}`}applyThemeColor(t){if(this.highlightColor=t,document.documentElement.style.setProperty("--lcms-primary",t),document.documentElement.style.setProperty("--lcms-primary-rgb",this.hexToRgb(t)),document.documentElement.style.setProperty("--lcms-primary-dark",this.getDarkerColor(t)),this.editMode){for(let[i]of this.managedElements)i.style.outline=`2px dashed ${t}`;for(let[i]of this.managedImages)i.style.outline=`2px dashed ${t}`}this.panelEl?.querySelectorAll(".lcms-theme-swatch")?.forEach(i=>{i.classList.toggle("active",i.dataset.color===t)}),localStorage.setItem("xtroedge_theme_color",t)}static create(t){let e=new u(t);return e.init(),e}static autoInit(){if(window.__xtroedge_cms_instance__)return;let t=window.__XTROEDGE_CMS_CONFIG__||{},e=u.create(t);window.__xtroedge_cms_instance__=e}};function S(){f.autoInit()}typeof window<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S());return P(N);})();
327
+ `,document.body.appendChild(t),this.loginModalEl=t;let e=t.querySelector("#lcms-login-btn"),i=t.querySelector("#lcms-login-email"),s=t.querySelector("#lcms-login-password"),n=()=>this.attemptLogin(i.value.trim(),s.value);e.addEventListener("click",n),s.addEventListener("keydown",r=>{r.key==="Enter"&&n()}),setTimeout(()=>i.focus(),50)}hideLoginModal(){this.loginModalEl?.remove(),this.loginModalEl=null}async attemptLogin(t,e){let i=document.getElementById("lcms-login-btn"),s=document.getElementById("lcms-login-error");if(!t||!e){s.textContent="Please enter your username and password.",s.classList.add("visible");return}i.disabled=!0,i.textContent="Signing in...",s.classList.remove("visible");try{let n=this.config.loginUrl||`${this.config.apiBase}/auth/login`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t,password:e})}),o=null;try{o=await r.json()}catch{}if(!r.ok){let l=o?.message||o?.error||null;throw new Error(l||`HTTP ${r.status}`)}let a=o?.token||o?.authToken||o?.auth_token||o?.data?.token;if(!a)throw new Error("Login successful but no token returned.");localStorage.setItem("builder_token",a),this.hideLoginModal(),this.editMode=!0,this.pendingEditMode=!1,sessionStorage.setItem("builder_edit_mode","true"),this.applyEditMode(!0),this.updateUI(),this.loadPageContent("draft")}catch(n){i.disabled=!1,i.textContent="Sign In",s.textContent=n?.message||"Login failed. Please try again.",s.classList.add("visible")}}resolveSiteIdentifier(){let t=window.location.hostname;if(t&&t!=="localhost"&&t!=="127.0.0.1"&&!t.startsWith("192.168."))return t;let e=localStorage.getItem("xtroedge_site_id");return e||(e="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let s=Math.random()*16|0;return(i==="x"?s:s&3|8).toString(16)}),localStorage.setItem("xtroedge_site_id",e)),e}hexToRgb(t){let e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?`${parseInt(e[1],16)},${parseInt(e[2],16)},${parseInt(e[3],16)}`:"0,200,83"}getDarkerColor(t){let e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(!e)return"#2E7D32";let i=Math.max(0,Math.floor(parseInt(e[1],16)*.6)),s=Math.max(0,Math.floor(parseInt(e[2],16)*.6)),n=Math.max(0,Math.floor(parseInt(e[3],16)*.6));return`#${i.toString(16).padStart(2,"0")}${s.toString(16).padStart(2,"0")}${n.toString(16).padStart(2,"0")}`}applyThemeColor(t){if(this.highlightColor=t,document.documentElement.style.setProperty("--lcms-primary",t),document.documentElement.style.setProperty("--lcms-primary-rgb",this.hexToRgb(t)),document.documentElement.style.setProperty("--lcms-primary-dark",this.getDarkerColor(t)),this.editMode){for(let[i]of this.managedElements)i.style.outline=`2px dashed ${t}`;for(let[i]of this.managedImages)i.style.outline=`2px dashed ${t}`}this.panelEl?.querySelectorAll(".lcms-theme-swatch")?.forEach(i=>{i.classList.toggle("active",i.dataset.color===t)}),localStorage.setItem("xtroedge_theme_color",t)}static create(t){let e=new u(t);return e.init(),e}static autoInit(){if(window.__xtroedge_cms_instance__)return;let t=window.__XTROEDGE_CMS_CONFIG__||{},e=u.create(t);window.__xtroedge_cms_instance__=e}};function S(){f.autoInit()}typeof window<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S());return P(D);})();
package/dist/index.js CHANGED
@@ -1387,8 +1387,11 @@ var XtroedgeCMS = class _XtroedgeCMS {
1387
1387
  }
1388
1388
  }
1389
1389
  hasSection(type) {
1390
- if (type === "header") return !!(document.querySelector("header") || document.querySelector('[class*="-header"]') || document.querySelector('[tagName$="-header"]'));
1391
- return !!(document.querySelector("footer") || document.querySelector('[class*="-footer"]') || document.querySelector('[tagName$="-footer"]'));
1390
+ const slug = type === "header" ? "/header" : "/footer";
1391
+ for (const [, info] of this.managedElements) {
1392
+ if (info.sectionSlug === slug) return true;
1393
+ }
1394
+ return false;
1392
1395
  }
1393
1396
  async loadPageContent(status) {
1394
1397
  this.setLoading(true);