Three merge-fallout bugs that combined to leave the services grid empty
and most UI inert:
1. error-handler.js was bundled into onboarding.js (loaded 3rd), but
globals.js in core.js (loaded 1st) does `const errorHandler = new
ErrorHandler()` at top level. ErrorHandler was undefined when core.js
ran -> ReferenceError -> globals.js stopped, so window.APPS,
_showTotpOverlay, loadServices, etc. were never set, and init.js
blew up on every call into core's exports.
Moved error-handler.js to the start of the core.js bundle so the
class is on window before any other script touches it.
2. setup-wizard.js also declared `const errorHandler = new ErrorHandler()`
at top level. Classic scripts share the document's top-level lexical
environment, so this collided with globals.js's declaration ->
redeclaration SyntaxError in features.js. Removed setup-wizard.js's
copy; it picks up the global one.
3. tooltip-definitions.js closed its `(function(window){...})(window);`
IIFE at line ~171 ("Validation module loaded"), then the TOOLTIP_
DEFINITIONS array, getter helpers, window.TooltipDefinitions export,
and final `debug(...)` log all sat at top level — outside the IIFE,
where `debug` was no longer in scope. Removed the early close and
added one at EOF so the whole file is in one IIFE.
201 lines
54 KiB
JavaScript
201 lines
54 KiB
JavaScript
this.driver=this.driver||{},this.driver.js=(function(y){"use strict";let P={};function _(e={}){P={animate:!0,allowClose:!0,overlayOpacity:.7,smoothScroll:!1,disableActiveInteraction:!1,showProgress:!1,stagePadding:10,stageRadius:5,popoverOffset:10,showButtons:["next","previous","close"],disableButtons:[],overlayColor:"#000",...e}}function a(e){return e?P[e]:P}function c(e,t,i,s){return(e/=s/2)<1?i/2*e*e+t:-i/2*(--e*(e-2)-1)+t}function r(e){const t='a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])';return e.flatMap(i=>{const s=i.matches(t),o=Array.from(i.querySelectorAll(t));return[...s?[i]:[],...o]}).filter(i=>getComputedStyle(i).pointerEvents!=="none"&&k(i))}function n(e){if(!e||A(e))return;const t=a("smoothScroll");e.scrollIntoView({behavior:!t||v(e)?"auto":"smooth",inline:"center",block:"center"})}function v(e){if(!e||!e.parentElement)return;const t=e.parentElement;return t.scrollHeight>t.clientHeight}function A(e){const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)}function k(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)}let m={};function C(e,t){m[e]=t}function l(e){return e?m[e]:m}function u(){m={}}let b={};function $(e,t){b[e]=t}function L(e){var t;(t=b[e])==null||t.call(b)}function R(){b={}}function j(e,t,i,s){let o=l("__activeStagePosition");const h=o||i.getBoundingClientRect(),S=s.getBoundingClientRect(),x=c(e,h.x,S.x-h.x,t),p=c(e,h.y,S.y-h.y,t),T=c(e,h.width,S.width-h.width,t),d=c(e,h.height,S.height-h.height,t);o={x,y:p,width:T,height:d},X(o),C("__activeStagePosition",o)}function Q(e){if(!e)return;const t=e.getBoundingClientRect(),i={x:t.x,y:t.y,width:t.width,height:t.height};C("__activeStagePosition",i),X(i)}function le(){const e=l("__activeStagePosition"),t=l("__overlaySvg");if(!e)return;if(!t){console.warn("No stage svg found.");return}const i=window.innerWidth,s=window.innerHeight;t.setAttribute("viewBox",`0 0 ${i} ${s}`)}function de(e){const t=pe(e);document.body.appendChild(t),re(t,i=>{i.target.tagName==="path"&&L("overlayClick")}),C("__overlaySvg",t)}function X(e){const t=l("__overlaySvg");if(!t){de(e);return}const i=t.firstElementChild;if(i?.tagName!=="path")throw new Error("no path element found in stage svg");i.setAttribute("d",Z(e))}function pe(e){const t=window.innerWidth,i=window.innerHeight,s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.classList.add("driver-overlay","driver-overlay-animated"),s.setAttribute("viewBox",`0 0 ${t} ${i}`),s.setAttribute("xmlSpace","preserve"),s.setAttribute("xmlnsXlink","http://www.w3.org/1999/xlink"),s.setAttribute("version","1.1"),s.setAttribute("preserveAspectRatio","xMinYMin slice"),s.style.fillRule="evenodd",s.style.clipRule="evenodd",s.style.strokeLinejoin="round",s.style.strokeMiterlimit="2",s.style.zIndex="10000",s.style.position="fixed",s.style.top="0",s.style.left="0",s.style.width="100%",s.style.height="100%";const o=document.createElementNS("http://www.w3.org/2000/svg","path");return o.setAttribute("d",Z(e)),o.style.fill=a("overlayColor")||"rgb(0,0,0)",o.style.opacity=`${a("overlayOpacity")}`,o.style.pointerEvents="auto",o.style.cursor="auto",s.appendChild(o),s}function Z(e){const t=window.innerWidth,i=window.innerHeight,s=a("stagePadding")||0,o=a("stageRadius")||0,h=e.width+s*2,S=e.height+s*2,x=Math.min(o,h/2,S/2),p=Math.floor(Math.max(x,0)),T=e.x-s+p,d=e.y-s,g=h-p*2,f=S-p*2;return`M${t},0L0,0L0,${i}L${t},${i}L${t},0Z
|
|
M${T},${d} h${g} a${p},${p} 0 0 1 ${p},${p} v${f} a${p},${p} 0 0 1 -${p},${p} h-${g} a${p},${p} 0 0 1 -${p},-${p} v-${f} a${p},${p} 0 0 1 ${p},-${p} z`}function ce(){const e=l("__overlaySvg");e&&e.remove()}function ue(){const e=document.getElementById("driver-dummy-element");if(e)return e;let t=document.createElement("div");return t.id="driver-dummy-element",t.style.width="0",t.style.height="0",t.style.pointerEvents="none",t.style.opacity="0",t.style.position="fixed",t.style.top="50%",t.style.left="50%",document.body.appendChild(t),t}function ee(e){const{element:t}=e;let i=typeof t=="string"?document.querySelector(t):t;i||(i=ue()),he(i,e)}function me(){const e=l("__activeElement"),t=l("__activeStep");e&&(Q(e),le(),ae(e,t))}function he(e,t){const i=Date.now(),s=l("__activeStep"),o=l("__activeElement")||e,h=!o||o===e,S=e.id==="driver-dummy-element",x=o.id==="driver-dummy-element",p=a("animate"),T=t.onHighlightStarted||a("onHighlightStarted"),d=t?.onHighlighted||a("onHighlighted"),g=s?.onDeselected||a("onDeselected"),f=a(),D=l();!h&&g&&g(x?void 0:o,s,{config:f,state:D}),T&&T(S?void 0:e,t,{config:f,state:D});const B=!h&&p;let E=!1;we(),C("previousStep",s),C("previousElement",o),C("activeStep",t),C("activeElement",e);const w=()=>{if(l("__transitionCallback")!==w)return;const I=Date.now()-i,O=400-I<=400/2;t.popover&&O&&!E&&B&&(oe(e,t),E=!0),a("animate")&&I<400?j(I,400,o,e):(Q(e),d&&d(S?void 0:e,t,{config:a(),state:l()}),C("__transitionCallback",void 0),C("__previousStep",s),C("__previousElement",o),C("__activeStep",t),C("__activeElement",e)),window.requestAnimationFrame(w)};C("__transitionCallback",w),window.requestAnimationFrame(w),n(e),!B&&t.popover&&oe(e,t),o.classList.remove("driver-active-element","driver-no-interaction"),o.removeAttribute("aria-haspopup"),o.removeAttribute("aria-expanded"),o.removeAttribute("aria-controls"),a("disableActiveInteraction")&&e.classList.add("driver-no-interaction"),e.classList.add("driver-active-element"),e.setAttribute("aria-haspopup","dialog"),e.setAttribute("aria-expanded","true"),e.setAttribute("aria-controls","driver-popover-content")}function ge(){var e;(e=document.getElementById("driver-dummy-element"))==null||e.remove(),document.querySelectorAll(".driver-active-element").forEach(t=>{t.classList.remove("driver-active-element","driver-no-interaction"),t.removeAttribute("aria-haspopup"),t.removeAttribute("aria-expanded"),t.removeAttribute("aria-controls")})}function U(){const e=l("__resizeTimeout");e&&window.cancelAnimationFrame(e),C("__resizeTimeout",window.requestAnimationFrame(me))}function ve(e){var t;if(!l("isInitialized")||!(e.key==="Tab"||e.keyCode===9))return;const i=l("__activeElement"),s=(t=l("popover"))==null?void 0:t.wrapper,o=r([...s?[s]:[],...i?[i]:[]]),h=o[0],S=o[o.length-1];if(e.preventDefault(),e.shiftKey){const x=o[o.indexOf(document.activeElement)-1]||S;x?.focus()}else{const x=o[o.indexOf(document.activeElement)+1]||h;x?.focus()}}function te(e){var t;((t=a("allowKeyboardControl"))==null||t)&&(e.key==="Escape"?L("escapePress"):e.key==="ArrowRight"?L("arrowRightPress"):e.key==="ArrowLeft"&&L("arrowLeftPress"))}function re(e,t,i){const s=(o,h)=>{const S=o.target;e.contains(S)&&((!i||i(S))&&(o.preventDefault(),o.stopPropagation(),o.stopImmediatePropagation()),h?.(o))};document.addEventListener("pointerdown",s,!0),document.addEventListener("mousedown",s,!0),document.addEventListener("pointerup",s,!0),document.addEventListener("mouseup",s,!0),document.addEventListener("click",o=>{s(o,t)},!0)}function fe(){window.addEventListener("keyup",te,!1),window.addEventListener("keydown",ve,!1),window.addEventListener("resize",U),window.addEventListener("scroll",U)}function ye(){window.removeEventListener("keyup",te),window.removeEventListener("resize",U),window.removeEventListener("scroll",U)}function we(){const e=l("popover");e&&(e.wrapper.style.display="none")}function oe(e,t){var i,s;let o=l("popover");o&&document.body.removeChild(o.wrapper),o=Te(),document.body.appendChild(o.wrapper);const{title:h,description:S,showButtons:x,disableButtons:p,showProgress:T,nextBtnText:d=a("nextBtnText")||"Next →",prevBtnText:g=a("prevBtnText")||"← Previous",progressText:f=a("progressText")||"{current} of {total}"}=t.popover||{};o.nextButton.innerHTML=d,o.previousButton.innerHTML=g,o.progress.innerHTML=f,h?(o.title.innerHTML=h,o.title.style.display="block"):o.title.style.display="none",S?(o.description.innerHTML=S,o.description.style.display="block"):o.description.style.display="none";const D=x||a("showButtons"),B=T||a("showProgress")||!1,E=D?.includes("next")||D?.includes("previous")||B;o.closeButton.style.display=D.includes("close")?"block":"none",E?(o.footer.style.display="flex",o.progress.style.display=B?"block":"none",o.nextButton.style.display=D.includes("next")?"block":"none",o.previousButton.style.display=D.includes("previous")?"block":"none"):o.footer.style.display="none";const w=p||a("disableButtons")||[];w!=null&&w.includes("next")&&(o.nextButton.disabled=!0,o.nextButton.classList.add("driver-popover-btn-disabled")),w!=null&&w.includes("previous")&&(o.previousButton.disabled=!0,o.previousButton.classList.add("driver-popover-btn-disabled")),w!=null&&w.includes("close")&&(o.closeButton.disabled=!0,o.closeButton.classList.add("driver-popover-btn-disabled"));const I=o.wrapper;I.style.display="block",I.style.left="",I.style.top="",I.style.bottom="",I.style.right="",I.id="driver-popover-content",I.setAttribute("role","dialog"),I.setAttribute("aria-labelledby","driver-popover-title"),I.setAttribute("aria-describedby","driver-popover-description");const O=o.arrow;O.className="driver-popover-arrow";const z=((i=t.popover)==null?void 0:i.popoverClass)||a("popoverClass")||"";I.className=`driver-popover ${z}`.trim(),re(o.wrapper,F=>{var V,G,q;const W=F.target,Y=((V=t.popover)==null?void 0:V.onNextClick)||a("onNextClick"),K=((G=t.popover)==null?void 0:G.onPrevClick)||a("onPrevClick"),J=((q=t.popover)==null?void 0:q.onCloseClick)||a("onCloseClick");if(W.classList.contains("driver-popover-next-btn"))return Y?Y(e,t,{config:a(),state:l()}):L("nextClick");if(W.classList.contains("driver-popover-prev-btn"))return K?K(e,t,{config:a(),state:l()}):L("prevClick");if(W.classList.contains("driver-popover-close-btn"))return J?J(e,t,{config:a(),state:l()}):L("closeClick")},F=>!(o!=null&&o.description.contains(F))&&!(o!=null&&o.title.contains(F))&&typeof F.className=="string"&&F.className.includes("driver-popover")),C("popover",o);const N=((s=t.popover)==null?void 0:s.onPopoverRender)||a("onPopoverRender");N&&N(o,{config:a(),state:l()}),ae(e,t),n(I);const M=e.classList.contains("driver-dummy-element"),H=r([I,...M?[]:[e]]);H.length>0&&H[0].focus()}function ie(){const e=l("popover");if(!(e!=null&&e.wrapper))return;const t=e.wrapper.getBoundingClientRect(),i=a("stagePadding")||0,s=a("popoverOffset")||0;return{width:t.width+i+s,height:t.height+i+s,realWidth:t.width,realHeight:t.height}}function ne(e,t){const{elementDimensions:i,popoverDimensions:s,popoverPadding:o,popoverArrowDimensions:h}=t;return e==="start"?Math.max(Math.min(i.top-o,window.innerHeight-s.realHeight-h.width),h.width):e==="end"?Math.max(Math.min(i.top-s?.realHeight+i.height+o,window.innerHeight-s?.realHeight-h.width),h.width):e==="center"?Math.max(Math.min(i.top+i.height/2-s?.realHeight/2,window.innerHeight-s?.realHeight-h.width),h.width):0}function se(e,t){const{elementDimensions:i,popoverDimensions:s,popoverPadding:o,popoverArrowDimensions:h}=t;return e==="start"?Math.max(Math.min(i.left-o,window.innerWidth-s.realWidth-h.width),h.width):e==="end"?Math.max(Math.min(i.left-s?.realWidth+i.width+o,window.innerWidth-s?.realWidth-h.width),h.width):e==="center"?Math.max(Math.min(i.left+i.width/2-s?.realWidth/2,window.innerWidth-s?.realWidth-h.width),h.width):0}function ae(e,t){const i=l("popover");if(!i)return;const{align:s="start",side:o="left"}=t?.popover||{},h=s,S=e.id==="driver-dummy-element"?"over":o,x=a("stagePadding")||0,p=ie(),T=i.arrow.getBoundingClientRect(),d=e.getBoundingClientRect(),g=d.top-p.height;let f=g>=0;const D=window.innerHeight-(d.bottom+p.height);let B=D>=0;const E=d.left-p.width;let w=E>=0;const I=window.innerWidth-(d.right+p.width);let O=I>=0;const z=!f&&!B&&!w&&!O;let N=S;if(S==="top"&&f?O=w=B=!1:S==="bottom"&&B?O=w=f=!1:S==="left"&&w?O=f=B=!1:S==="right"&&O&&(w=f=B=!1),S==="over"){const M=window.innerWidth/2-p.realWidth/2,H=window.innerHeight/2-p.realHeight/2;i.wrapper.style.left=`${M}px`,i.wrapper.style.right="auto",i.wrapper.style.top=`${H}px`,i.wrapper.style.bottom="auto"}else if(z){const M=window.innerWidth/2-p?.realWidth/2,H=10;i.wrapper.style.left=`${M}px`,i.wrapper.style.right="auto",i.wrapper.style.bottom=`${H}px`,i.wrapper.style.top="auto"}else if(w){const M=Math.min(E,window.innerWidth-p?.realWidth-T.width),H=ne(h,{elementDimensions:d,popoverDimensions:p,popoverPadding:x,popoverArrowDimensions:T});i.wrapper.style.left=`${M}px`,i.wrapper.style.top=`${H}px`,i.wrapper.style.bottom="auto",i.wrapper.style.right="auto",N="left"}else if(O){const M=Math.min(I,window.innerWidth-p?.realWidth-T.width),H=ne(h,{elementDimensions:d,popoverDimensions:p,popoverPadding:x,popoverArrowDimensions:T});i.wrapper.style.right=`${M}px`,i.wrapper.style.top=`${H}px`,i.wrapper.style.bottom="auto",i.wrapper.style.left="auto",N="right"}else if(f){const M=Math.min(g,window.innerHeight-p.realHeight-T.width);let H=se(h,{elementDimensions:d,popoverDimensions:p,popoverPadding:x,popoverArrowDimensions:T});i.wrapper.style.top=`${M}px`,i.wrapper.style.left=`${H}px`,i.wrapper.style.bottom="auto",i.wrapper.style.right="auto",N="top"}else if(B){const M=Math.min(D,window.innerHeight-p?.realHeight-T.width);let H=se(h,{elementDimensions:d,popoverDimensions:p,popoverPadding:x,popoverArrowDimensions:T});i.wrapper.style.left=`${H}px`,i.wrapper.style.bottom=`${M}px`,i.wrapper.style.top="auto",i.wrapper.style.right="auto",N="bottom"}z?i.arrow.classList.add("driver-popover-arrow-none"):be(h,N,e)}function be(e,t,i){const s=l("popover");if(!s)return;const o=i.getBoundingClientRect(),h=ie(),S=s.arrow,x=h.width,p=window.innerWidth,T=o.width,d=o.left,g=h.height,f=window.innerHeight,D=o.top,B=o.height;S.className="driver-popover-arrow";let E=t,w=e;t==="top"?(d+T<=0?(E="right",w="end"):d+T-x<=0&&(E="top",w="start"),d>=p?(E="left",w="end"):d+x>=p&&(E="top",w="end")):t==="bottom"?(d+T<=0?(E="right",w="start"):d+T-x<=0&&(E="bottom",w="start"),d>=p?(E="left",w="start"):d+x>=p&&(E="bottom",w="end")):t==="left"?(D+B<=0?(E="bottom",w="end"):D+B-g<=0&&(E="left",w="start"),D>=f?(E="top",w="end"):D+g>=f&&(E="left",w="end")):t==="right"&&(D+B<=0?(E="bottom",w="start"):D+B-g<=0&&(E="right",w="start"),D>=f?(E="top",w="start"):D+g>=f&&(E="right",w="end")),E?(S.classList.add(`driver-popover-arrow-side-${E}`),S.classList.add(`driver-popover-arrow-align-${w}`)):S.classList.add("driver-popover-arrow-none")}function Te(){const e=document.createElement("div");e.classList.add("driver-popover");const t=document.createElement("div");t.classList.add("driver-popover-arrow");const i=document.createElement("header");i.id="driver-popover-title",i.classList.add("driver-popover-title"),i.style.display="none",i.innerText="Popover Title";const s=document.createElement("div");s.id="driver-popover-description",s.classList.add("driver-popover-description"),s.style.display="none",s.innerText="Popover description is here";const o=document.createElement("button");o.type="button",o.classList.add("driver-popover-close-btn"),o.setAttribute("aria-label","Close"),o.innerHTML="×";const h=document.createElement("footer");h.classList.add("driver-popover-footer");const S=document.createElement("span");S.classList.add("driver-popover-progress-text"),S.innerText="";const x=document.createElement("span");x.classList.add("driver-popover-navigation-btns");const p=document.createElement("button");p.type="button",p.classList.add("driver-popover-prev-btn"),p.innerHTML="← Previous";const T=document.createElement("button");return T.type="button",T.classList.add("driver-popover-next-btn"),T.innerHTML="Next →",x.appendChild(p),x.appendChild(T),h.appendChild(S),h.appendChild(x),e.appendChild(o),e.appendChild(t),e.appendChild(i),e.appendChild(s),e.appendChild(h),{wrapper:e,arrow:t,title:i,description:s,footer:h,previousButton:p,nextButton:T,closeButton:o,footerButtons:x,progress:S}}function Se(){var e;const t=l("popover");t&&((e=t.wrapper.parentElement)==null||e.removeChild(t.wrapper))}const De="";function Ce(e={}){_(e);function t(){a("allowClose")&&T()}function i(){const d=l("activeIndex"),g=a("steps")||[];if(typeof d>"u")return;const f=d+1;g[f]?p(f):T()}function s(){const d=l("activeIndex"),g=a("steps")||[];if(typeof d>"u")return;const f=d-1;g[f]?p(f):T()}function o(d){(a("steps")||[])[d]?p(d):T()}function h(){var d;if(l("__transitionCallback"))return;const g=l("activeIndex"),f=l("__activeStep"),D=l("__activeElement");if(typeof g>"u"||typeof f>"u"||typeof l("activeIndex")>"u")return;const B=((d=f.popover)==null?void 0:d.onPrevClick)||a("onPrevClick");if(B)return B(D,f,{config:a(),state:l()});s()}function S(){var d;if(l("__transitionCallback"))return;const g=l("activeIndex"),f=l("__activeStep"),D=l("__activeElement");if(typeof g>"u"||typeof f>"u")return;const B=((d=f.popover)==null?void 0:d.onNextClick)||a("onNextClick");if(B)return B(D,f,{config:a(),state:l()});i()}function x(){l("isInitialized")||(C("isInitialized",!0),document.body.classList.add("driver-active",a("animate")?"driver-fade":"driver-simple"),fe(),$("overlayClick",t),$("escapePress",t),$("arrowLeftPress",h),$("arrowRightPress",S))}function p(d=0){var g,f,D,B,E,w,I,O;const z=a("steps");if(!z){console.error("No steps to drive through"),T();return}if(!z[d]){T();return}C("__activeOnDestroyed",document.activeElement),C("activeIndex",d);const N=z[d],M=z[d+1],H=z[d-1],F=((g=N.popover)==null?void 0:g.doneBtnText)||a("doneBtnText")||"Done",V=a("allowClose"),G=typeof((f=N.popover)==null?void 0:f.showProgress)<"u"?(D=N.popover)==null?void 0:D.showProgress:a("showProgress"),q=(((B=N.popover)==null?void 0:B.progressText)||a("progressText")||"{{current}} of {{total}}").replace("{{current}}",`${d+1}`).replace("{{total}}",`${z.length}`),W=((E=N.popover)==null?void 0:E.showButtons)||a("showButtons"),Y=["next","previous",...V?["close"]:[]].filter(xe=>!(W!=null&&W.length)||W.includes(xe)),K=((w=N.popover)==null?void 0:w.onNextClick)||a("onNextClick"),J=((I=N.popover)==null?void 0:I.onPrevClick)||a("onPrevClick"),ke=((O=N.popover)==null?void 0:O.onCloseClick)||a("onCloseClick");ee({...N,popover:{showButtons:Y,nextBtnText:M?void 0:F,disableButtons:[...H?[]:["previous"]],showProgress:G,progressText:q,onNextClick:K||(()=>{M?p(d+1):T()}),onPrevClick:J||(()=>{p(d-1)}),onCloseClick:ke||(()=>{T()}),...N?.popover||{}}})}function T(d=!0){const g=l("__activeElement"),f=l("__activeStep"),D=l("__activeOnDestroyed"),B=a("onDestroyStarted");if(d&&B){const I=!g||g?.id==="driver-dummy-element";B(I?void 0:g,f,{config:a(),state:l()});return}const E=f?.onDeselected||a("onDeselected"),w=a("onDestroyed");if(document.body.classList.remove("driver-active","driver-fade","driver-simple"),ye(),Se(),ge(),ce(),R(),u(),g&&f){const I=g.id==="driver-dummy-element";E&&E(I?void 0:g,f,{config:a(),state:l()}),w&&w(I?void 0:g,f,{config:a(),state:l()})}D&&D.focus()}return{isActive:()=>l("isInitialized")||!1,refresh:U,drive:(d=0)=>{x(),p(d)},setConfig:_,setSteps:d=>{u(),_({...a(),steps:d})},getConfig:a,getState:l,getActiveIndex:()=>l("activeIndex"),isFirstStep:()=>l("activeIndex")===0,isLastStep:()=>{const d=a("steps")||[],g=l("activeIndex");return g!==void 0&&g===d.length-1},getActiveStep:()=>l("activeStep"),getActiveElement:()=>l("activeElement"),getPreviousElement:()=>l("previousElement"),getPreviousStep:()=>l("previousStep"),moveNext:i,movePrevious:s,moveTo:o,hasNextStep:()=>{const d=a("steps")||[],g=l("activeIndex");return g!==void 0&&d[g+1]},hasPreviousStep:()=>{const d=a("steps")||[],g=l("activeIndex");return g!==void 0&&d[g-1]},highlight:d=>{x(),ee({...d,popover:d.popover?{showButtons:[],showProgress:!1,progressText:"",...d.popover}:void 0})},destroy:()=>{T(!1)}}}return y.driver=Ce,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"}),y})({}),(function(y){"use strict";const P=new ErrorHandler,_=(...c)=>{y.DASHCADDY_DEBUG&&console.log(...c)};class a{constructor(r="dashcaddy_onboarding"){this.storageKey=r,this.storageVersion="1.0",this.installOnboardingCompleted=typeof SITE<"u"&&SITE.onboardingCompleted===!0,this._initializeStorage(),this._updateLastVisit()}_initializeStorage(){const r=this._getStorage();if(!r||r.version!==this.storageVersion){const n={version:this.storageVersion,tourCompleted:!1,completedTooltips:[],currentStep:0,completionTimestamp:null,dnsSetupDeferred:!1,lastVisit:new Date().toISOString()};this._setStorage(n)}}_getStorage(){try{const r=localStorage.getItem(this.storageKey);return r?JSON.parse(r):null}catch(r){return P.logError("[ProgressTracker] Read Storage",r,{function:"_getStorage"}),null}}_setStorage(r){try{localStorage.setItem(this.storageKey,JSON.stringify(r))}catch(n){P.logError("[ProgressTracker] Write Storage",n,{function:"_setStorage"}),this._handleStorageError(n)}}_handleStorageError(r){try{sessionStorage.setItem(this.storageKey,JSON.stringify(this._getStorage())),console.warn("[ProgressTracker] Falling back to session storage")}catch(n){P.logError("[ProgressTracker] Session Storage Unavailable",n,{function:"_handleStorageError"})}}_updateLastVisit(){const r=this._getStorage();r&&(r.lastVisit=new Date().toISOString(),this._setStorage(r))}isTooltipCompleted(r){const n=this._getStorage();return n?n.completedTooltips.includes(r):!1}markTooltipCompleted(r){const n=this._getStorage();n&&(n.completedTooltips.includes(r)||(n.completedTooltips.push(r),n.tooltipTimestamps||(n.tooltipTimestamps={}),n.tooltipTimestamps[r]=new Date().toISOString(),this._setStorage(n)))}isTourCompleted(){const r=this._getStorage();return r?r.tourCompleted===!0:!1}isInstallOnboardingCompleted(){return this.installOnboardingCompleted===!0}async markInstallOnboardingCompleted(){if(!this.installOnboardingCompleted){this.installOnboardingCompleted=!0,typeof SITE<"u"&&(SITE.onboardingCompleted=!0);try{await secureFetch("/api/v1/config",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({onboardingCompleted:!0})})}catch(r){P.logError("[ProgressTracker] Persist Install Onboarding",r,{function:"markInstallOnboardingCompleted"})}}}markTourCompleted(){const r=this._getStorage();r&&(r.tourCompleted=!0,r.completionTimestamp=new Date().toISOString(),this._setStorage(r))}getCurrentStep(){const r=this._getStorage();return r&&r.currentStep||0}setCurrentStep(r){const n=this._getStorage();n&&(n.currentStep=r,this._setStorage(n))}resetProgress(){const r={version:this.storageVersion,tourCompleted:!1,completedTooltips:[],currentStep:0,completionTimestamp:null,dnsSetupDeferred:!1,lastVisit:new Date().toISOString()};this._setStorage(r)}getCompletionTimestamp(){const r=this._getStorage();return!r||!r.completionTimestamp?null:new Date(r.completionTimestamp)}isDnsSetupDeferred(){const r=this._getStorage();return r?r.dnsSetupDeferred===!0:!1}markDnsSetupDeferred(){const r=this._getStorage();r&&(r.dnsSetupDeferred=!0,this._setStorage(r))}getTooltipTimestamp(r){const n=this._getStorage();return!n||!n.tooltipTimestamps||!n.tooltipTimestamps[r]?null:new Date(n.tooltipTimestamps[r])}getCompletedTooltips(){const r=this._getStorage();return r?r.completedTooltips||[]:[]}getLastVisit(){const r=this._getStorage();return!r||!r.lastVisit?null:new Date(r.lastVisit)}}y.ProgressTracker=a,_("[ProgressTracker] Module loaded")})(window),(function(y){"use strict";const P=new ErrorHandler,_=(...r)=>{y.DASHCADDY_DEBUG&&console.log(...r)},a={dark:{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(0, 0, 0, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},light:{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent-strong)",overlayColor:"rgba(0, 0, 0, 0.5)",borderColor:"var(--border)",highlightColor:"var(--accent-strong)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},blue:{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(25, 8, 172, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},nord:{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(46, 52, 64, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},dracula:{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(40, 42, 54, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},"solarized-dark":{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(0, 43, 54, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"},"solarized-light":{backgroundColor:"var(--card-base)",textColor:"var(--fg)",primaryColor:"var(--accent)",overlayColor:"rgba(253, 246, 227, 0.7)",borderColor:"var(--border)",highlightColor:"var(--accent)",fontFamily:"'Sami Grotesk', 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"}};class c{constructor(){this.currentTheme=this.getCurrentTheme(),this.themeChangeCallbacks=[],this._setupThemeChangeListener()}getCurrentTheme(){const n=document.documentElement,v=Array.from(n.classList);return(y.THEMES||[]).filter(m=>m!=="dark").find(m=>v.includes(m))||"dark"}getDriverTheme(){const n=this.getCurrentTheme(),v=a[n]||a.dark,A={};for(const[k,m]of Object.entries(v))if(typeof m=="string"&&m.startsWith("var(")){const C=m.match(/var\((--[^)]+)\)/)?.[1];if(C){const l=getComputedStyle(document.documentElement).getPropertyValue(C).trim();A[k]=l||m}else A[k]=m}else A[k]=m;return A}onThemeChange(n){typeof n=="function"&&this.themeChangeCallbacks.push(n)}_setupThemeChangeListener(){const n=document.documentElement;new MutationObserver(A=>{A.forEach(k=>{if(k.type==="attributes"&&k.attributeName==="class"){const m=this.getCurrentTheme();if(m!==this.currentTheme){const C=this.currentTheme;this.currentTheme=m,this._notifyThemeChange(m,C)}}})}).observe(n,{attributes:!0,attributeFilter:["class"]}),_("[ThemeAdapter] Theme change listener initialized")}_notifyThemeChange(n,v){_(`[ThemeAdapter] Theme changed: ${v} \u2192 ${n}`),this.themeChangeCallbacks.forEach(A=>{try{A(n,v)}catch(k){P.logError("[ThemeAdapter] Theme Change Callback",k,{function:"_notifyThemeChange"})}})}applyTheme(n){if(!n){console.warn("[ThemeAdapter] No driver instance provided");return}const v=this.getDriverTheme();this._injectDriverStyles(v),_("[ThemeAdapter] Theme applied to driver:",this.currentTheme)}_injectDriverStyles(n){const v=document.getElementById("driver-theme-styles");v&&v.remove();const A=document.createElement("style");A.id="driver-theme-styles",A.textContent=`
|
|
.driver-popover {
|
|
background: ${n.backgroundColor} !important;
|
|
color: ${n.textColor} !important;
|
|
border: 1px solid ${n.borderColor} !important;
|
|
border-radius: 12px !important;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4) !important;
|
|
font-family: ${n.fontFamily} !important;
|
|
}
|
|
|
|
.driver-popover-title {
|
|
color: ${n.textColor} !important;
|
|
font-weight: 600 !important;
|
|
font-family: ${n.fontFamily} !important;
|
|
}
|
|
|
|
.driver-popover-description {
|
|
color: ${n.textColor} !important;
|
|
font-family: ${n.fontFamily} !important;
|
|
}
|
|
|
|
.driver-popover-footer button {
|
|
background: ${n.primaryColor} !important;
|
|
color: ${n.backgroundColor} !important;
|
|
border: none !important;
|
|
font-family: ${n.fontFamily} !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
|
|
.driver-popover-footer button:hover {
|
|
opacity: 0.9 !important;
|
|
}
|
|
|
|
.driver-popover-close-btn {
|
|
color: ${n.textColor} !important;
|
|
}
|
|
|
|
.driver-overlay {
|
|
background: ${n.overlayColor} !important;
|
|
}
|
|
|
|
.driver-highlighted-element {
|
|
outline: 2px solid ${n.highlightColor} !important;
|
|
outline-offset: 4px !important;
|
|
}
|
|
|
|
.driver-popover-progress-text {
|
|
color: ${n.textColor} !important;
|
|
opacity: 0.7 !important;
|
|
font-family: ${n.fontFamily} !important;
|
|
}
|
|
`,document.head.appendChild(A)}getAvailableThemes(){return Object.keys(a)}isThemeAvailable(n){return a.hasOwnProperty(n)}}y.ThemeAdapter=c,_("[ThemeAdapter] Module loaded")})(window),(function(y){"use strict";const P=new ErrorHandler,_=(...u)=>{y.DASHCADDY_DEBUG&&console.log(...u)};function a(u){const b=[];if((!u.id||typeof u.id!="string")&&b.push("Tooltip must have a valid string id"),u.element||b.push("Tooltip must have an element selector or HTMLElement"),!u.popover||typeof u.popover!="object")b.push("Tooltip must have a popover object");else{if((!u.popover.title||typeof u.popover.title!="string")&&b.push("Tooltip popover must have a valid string title"),(!u.popover.description||typeof u.popover.description!="string")&&b.push("Tooltip popover must have a valid string description"),u.popover.position){const L=["top","bottom","left","right","center"];L.includes(u.popover.position)||b.push(`Invalid position: ${u.popover.position}. Must be one of: ${L.join(", ")}`)}if(u.popover.align){const L=["start","center","end"];L.includes(u.popover.align)||b.push(`Invalid align: ${u.popover.align}. Must be one of: ${L.join(", ")}`)}u.popover.showButtons&&!Array.isArray(u.popover.showButtons)&&b.push("showButtons must be an array"),["onNext","onPrevious","onClose","onSetupNow","onLater"].forEach(L=>{u.popover[L]&&typeof u.popover[L]!="function"&&b.push(`${L} must be a function`)})}return u.condition&&typeof u.condition!="function"&&b.push("condition must be a function"),u.priority!==void 0&&typeof u.priority!="number"&&b.push("priority must be a number"),{valid:b.length===0,errors:b}}function c(u){if(!Array.isArray(u))return{valid:!1,errors:[{tooltip:null,errors:["tooltips must be an array"]}]};const b=[],$=new Set;return u.forEach((L,R)=>{const j=a(L);j.valid||b.push({tooltip:L.id||`index ${R}`,errors:j.errors}),L.id&&($.has(L.id)&&b.push({tooltip:L.id,errors:[`Duplicate tooltip ID: ${L.id}`]}),$.add(L.id))}),{valid:b.length===0,errors:b}}class r extends Error{constructor(b,$=null){super(b),this.name="TooltipError",this.tooltipId=$}}function n(u){if(!u.valid){const b=u.errors.map($=>`${$.tooltip}: ${$.errors.join(", ")}`).join(`
|
|
`);throw P.logError("[TooltipDefinitions] Validation",b,{function:"validateTooltip"}),new r(`Tooltip validation failed:
|
|
${b}`)}}y.TooltipValidation={validateTooltipDefinition:a,validateTooltipDefinitions:c,handleValidationErrors:n,TooltipError:r},_("[TooltipDefinitions] Validation module loaded");const v=[{id:"welcome",element:"#brand",popover:{title:"Welcome to DashCaddy!",description:`
|
|
<p>Your unified control panel for <strong>Docker</strong>, <strong>Caddy</strong>, and <strong>DNS</strong> — all in one place.</p>
|
|
<p>This tour will walk you through every section so you know exactly where everything is.</p>
|
|
<p style="margin-top: 8px; font-size: 0.85rem; opacity: 0.7;">You can click your logo anytime to customize it.</p>
|
|
`,position:"bottom",align:"start",showButtons:["next"],showProgress:!0},priority:1},{id:"infrastructure",element:".top",popover:{title:"Infrastructure Overview",description:`
|
|
<p>This top row shows the backbone of your setup:</p>
|
|
<ul>
|
|
<li><strong>DNS1 / DNS2 / DNS3</strong> — your Technitium DNS servers</li>
|
|
<li><strong>Internet</strong> — live connectivity check with packet indicators</li>
|
|
<li><strong>Auth</strong> — TOTP authentication status</li>
|
|
<li><strong>DashCA</strong> — your certificate authority for your custom domain</li>
|
|
</ul>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">These core services are always pinned here, separate from your app grid.</p>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:2},{id:"service-cards",element:"#cards",popover:{title:"Your App Grid",description:`
|
|
<p>Every app you deploy appears here as a card. Each one shows:</p>
|
|
<ul>
|
|
<li><strong>Status dot</strong> — green = online, red = offline (pulses when down)</li>
|
|
<li><strong>ON/OFF badge</strong> — current state at a glance</li>
|
|
<li><strong>Response time</strong> — how fast the service responds (color-coded)</li>
|
|
<li><strong>Uptime bar</strong> — historical uptime percentage</li>
|
|
</ul>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">Hover over a card to see action buttons: Logs, Restart, Update, Settings, and Open.</p>
|
|
`,position:"top",showButtons:["previous","next"],showProgress:!0},priority:3},{id:"app-selector",element:"#add-service-btn",popover:{title:"App Selector — Deploy in One Click",description:`
|
|
<p>Browse <strong>50+ self-hosted apps</strong> organized by category:</p>
|
|
<ul>
|
|
<li>Media (Plex, Jellyfin, Emby, Overseerr)</li>
|
|
<li>Downloads (*arr stack, qBittorrent, Transmission)</li>
|
|
<li>Productivity (Nextcloud, Vaultwarden, Gitea)</li>
|
|
<li>Monitoring (Uptime Kuma, Grafana, Prometheus)</li>
|
|
</ul>
|
|
<p>Each app deploys with Docker, Caddy reverse proxy, and DNS — fully configured automatically.</p>
|
|
<p style="margin-top: 6px; padding: 6px 10px; border-radius: 6px; background: rgba(241,196,15,0.12); border: 1px solid rgba(241,196,15,0.25); font-size: 0.85rem;">
|
|
<strong style="color: #f1c40f;">★ Premium:</strong> <strong>Recipes</strong> let you deploy entire stacks (e.g., full media server) in one click with pre-wired configs.
|
|
</p>
|
|
`,position:"top",showButtons:["previous","next"],showProgress:!0},priority:4,condition:()=>document.getElementById("add-service-btn")!==null},{id:"smart-arr",element:"#arr-setup-btn",popover:{title:"Smart Arr Connect",description:`
|
|
<p>Automatically wire up your entire media stack:</p>
|
|
<ul>
|
|
<li>Detects running Arr services (Radarr, Sonarr, Prowlarr, etc.)</li>
|
|
<li>Connects them together with the right API keys</li>
|
|
<li>Links Plex/Jellyfin to Overseerr for request management</li>
|
|
</ul>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">No manual API key copying — DashCaddy handles it all.</p>
|
|
`,position:"top",showButtons:["previous","next"],showProgress:!0},priority:5,condition:()=>document.getElementById("arr-setup-btn")!==null},{id:"add-manual",element:"#add-service",popover:{title:"Add App Manually",description:`
|
|
<p>Already have a service running? Add it to your dashboard manually.</p>
|
|
<p>You can add:</p>
|
|
<ul>
|
|
<li><strong>Docker containers</strong> — auto-detects running containers</li>
|
|
<li><strong>External services</strong> — any URL you want to monitor</li>
|
|
<li><strong>Test services</strong> — try the UI without deploying anything</li>
|
|
</ul>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:6},{id:"theme-selector",element:"#theme",popover:{title:"Themes",description:`
|
|
<p>Switch between <strong>7 built-in themes</strong>:</p>
|
|
<p>Dark, Light, Blue, Nord, Dracula, Solarized Dark, and Solarized Light.</p>
|
|
<p>Your preference is saved automatically. You can also build custom themes with the Theme Builder (in Admin → settings).</p>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:7},{id:"toolbar-status",element:'.tools-section[data-section="status"]',popover:{title:"Status Tools",description:`
|
|
<p>Click <strong>Status</strong> to expand these tools:</p>
|
|
<ul>
|
|
<li><strong>Monitor</strong> — live CPU, memory, and disk usage for every container</li>
|
|
<li><strong>Health</strong> — service health dashboard with uptime history and alerts</li>
|
|
<li><strong>Updates</strong> — check for and apply Docker image updates</li>
|
|
</ul>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">These sections remember whether you left them open or closed.</p>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:8},{id:"toolbar-tools",element:'.tools-section[data-section="tools"]',popover:{title:"Operational Tools",description:`
|
|
<p>Click <strong>Tools</strong> for day-to-day operations:</p>
|
|
<ul>
|
|
<li><strong>Logs</strong> — view error logs from your API server and services</li>
|
|
<li><strong>Alerts</strong> — configure notification rules (email, webhook, etc.)</li>
|
|
<li><strong>Audit</strong> — full audit trail of every action taken in DashCaddy</li>
|
|
</ul>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:9},{id:"toolbar-admin",element:'.tools-section[data-section="admin"]',popover:{title:"Admin & Configuration",description:`
|
|
<p>Click <strong>Admin</strong> for setup and maintenance:</p>
|
|
<ul>
|
|
<li><strong>Tokens</strong> — manage API keys for DNS and other integrations</li>
|
|
<li><strong>Export / Import</strong> — backup and restore your dashboard configuration</li>
|
|
<li><strong>Backup</strong> — full system backup and restore</li>
|
|
<li><strong>License</strong> — activate your license code to unlock premium features</li>
|
|
<li><strong>API</strong> — interactive API documentation (125+ endpoints)</li>
|
|
</ul>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:10},{id:"weather",element:"#weather-widget",popover:{title:"Weather Widget",description:`
|
|
<p>Shows current conditions for your location. Click the <strong>gear icon</strong> to configure your city or switch temperature units.</p>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">Uses Open-Meteo — no API key required.</p>
|
|
`,position:"bottom",showButtons:["previous","next"],showProgress:!0},priority:11,condition:()=>document.getElementById("weather-widget")!==null},{id:"reload-caddy",element:"#reload-caddy-top",popover:{title:"Reload Caddy",description:`
|
|
<p>After changing Caddy configuration (adding reverse proxy rules, SSL settings, etc.), click here to apply the changes live.</p>
|
|
<p style="font-size: 0.85rem; opacity: 0.7;">This is a graceful reload — existing connections are not dropped.</p>
|
|
`,position:"bottom",align:"end",showButtons:["previous","next"],showProgress:!0},priority:12,condition:()=>document.getElementById("reload-caddy-top")!==null},{id:"tour-complete",element:"#brand",popover:{title:"You're All Set!",description:`
|
|
<p>That covers the essentials. A few tips to get the most out of DashCaddy:</p>
|
|
<ul>
|
|
<li><strong>Click any card</strong> to open that service directly</li>
|
|
<li><strong>Keyboard shortcuts</strong> — press <code>?</code> anytime to see them all</li>
|
|
<li><strong>DashCA</strong> — visit your CA page to install the root certificate on any device</li>
|
|
</ul>
|
|
<div style="margin-top: 10px; padding: 10px 12px; border-radius: 8px; background: linear-gradient(135deg, rgba(241,196,15,0.1), rgba(243,156,18,0.08)); border: 1px solid rgba(241,196,15,0.25);">
|
|
<p style="margin: 0 0 6px; font-weight: 600; color: #f1c40f;">★ Unlock Premium</p>
|
|
<p style="margin: 0; font-size: 0.85rem;">Premium adds powerful features for serious homelabbers:</p>
|
|
<ul style="margin: 6px 0 0; font-size: 0.85rem;">
|
|
<li><strong>Auto-Login SSO</strong> — sign into every app automatically, no more password juggling</li>
|
|
<li><strong>Recipes</strong> — deploy full stacks (media server, dev environment) in one click</li>
|
|
<li><strong>Docker Swarm</strong> — orchestrate multi-node clusters from your dashboard</li>
|
|
</ul>
|
|
<p style="margin: 8px 0 0; font-size: 0.82rem; opacity: 0.8;">Activate in <strong>Admin → License</strong></p>
|
|
</div>
|
|
<p style="margin-top: 10px; font-size: 0.85rem; opacity: 0.7;">You can restart this tour anytime from <strong>Admin → Help Tour</strong>.</p>
|
|
`,position:"bottom",align:"start",showButtons:["previous","close"],showProgress:!0},priority:13}];function A(){return v}function k(u){return v.find(b=>b.id===u)||null}function m(){return v.filter(u=>{if(u.condition&&typeof u.condition=="function")try{return u.condition()}catch(b){return P.logError("[TooltipDefinitions] Condition Eval",b,{function:"evaluateCondition",tooltipId:u.id}),!1}return!0})}function C(){return m().sort((b,$)=>{const L=b.priority||999,R=$.priority||999;return L-R})}function l(){return m().filter(b=>b.isNewFeature===!0).sort((b,$)=>{const L=b.priority||999,R=$.priority||999;return L-R})}y.TooltipDefinitions={TOOLTIP_DEFINITIONS:v,getTooltipDefinitions:A,getTooltipById:k,getActiveTooltips:m,getSortedTooltips:C,getNewFeatureTooltips:l},_("[TooltipDefinitions] Definitions loaded:",v.length,"tooltips")})(window),(function(y){"use strict";const P=(...a)=>{y.DASHCADDY_DEBUG&&console.log(...a)};class _{constructor(c){this.progressTracker=c,this.modal=null,this.onTemplateSelected=null,P("[DnsTemplateSelector] Module loaded")}getDnsTemplates(){return[{id:"technitium",name:"Technitium DNS Server",description:"Modern DNS server with web UI for managing private zones",icon:"\u{1F310}",difficulty:"Easy",features:["Web-based management interface","Private zone management for .sami domain","DHCP server integration","DNS-over-HTTPS and DNS-over-TLS support"],recommended:!0},{id:"bind9",name:"BIND9 DNS Server",description:"Industry-standard DNS server - powerful and flexible",icon:"\u{1F527}",difficulty:"Advanced",features:["Industry standard DNS server","Full RFC compliance","Advanced zone management","DNSSEC support"],recommended:!1},{id:"pihole",name:"Pi-hole",description:"Network-wide ad blocker with DNS capabilities",icon:"\u{1F6E1}\uFE0F",difficulty:"Intermediate",features:["Ad blocking at DNS level","Web interface for management","DHCP server included","Query logging and statistics"],recommended:!1},{id:"powerdns",name:"PowerDNS",description:"High-performance DNS server with SQL backend",icon:"\u26A1",difficulty:"Intermediate",features:["SQL database backend","RESTful API for automation","Geographic load balancing","DNSSEC support"],recommended:!1},{id:"coredns",name:"CoreDNS",description:"Cloud-native DNS server - lightweight and flexible",icon:"\u2601\uFE0F",difficulty:"Intermediate",features:["Plugin-based architecture","Kubernetes-native","Lightweight and fast","Prometheus metrics"],recommended:!1}]}showTemplateSelector(){this.modal||this.createModal(),this.populateTemplates(),this.modal.style.display="flex",document.body.style.overflow="hidden"}createModal(){const c=document.createElement("div");c.id="dns-template-modal",c.className="dns-template-modal",c.innerHTML=`
|
|
<div class="dns-template-modal-content">
|
|
<div class="dns-template-header">
|
|
<h2>\u{1F310} Choose a DNS Server</h2>
|
|
<p>Setting up a DNS server is essential for managing your private .sami domain</p>
|
|
<button class="dns-template-close" aria-label="Close">×</button>
|
|
</div>
|
|
<div class="dns-template-grid" id="dns-template-grid">
|
|
<!-- Templates will be inserted here -->
|
|
</div>
|
|
<div class="dns-template-footer">
|
|
<button class="dns-template-later-btn" id="dns-setup-later">Set up later</button>
|
|
</div>
|
|
</div>
|
|
`,document.body.appendChild(c),this.modal=c,c.querySelector(".dns-template-close").addEventListener("click",()=>this.close()),c.querySelector("#dns-setup-later").addEventListener("click",()=>this.handleSetupLater()),c.addEventListener("click",r=>{r.target===c&&this.close()}),document.addEventListener("keydown",r=>{r.key==="Escape"&&c.style.display==="flex"&&this.close()})}populateTemplates(){const c=document.getElementById("dns-template-grid");if(!c)return;const r=this.getDnsTemplates();c.innerHTML="",r.forEach(n=>{const v=this.createTemplateCard(n);c.appendChild(v)})}createTemplateCard(c){const r=document.createElement("div");r.className="dns-template-card",c.recommended&&r.classList.add("recommended");const n=c.difficulty.toLowerCase();return r.innerHTML=`
|
|
${c.recommended?'<div class="recommended-badge">Recommended</div>':""}
|
|
<div class="dns-template-icon">${c.icon}</div>
|
|
<h3>${c.name}</h3>
|
|
<p class="dns-template-description">${c.description}</p>
|
|
<div class="dns-template-difficulty difficulty-${n}">
|
|
${c.difficulty}
|
|
</div>
|
|
<ul class="dns-template-features">
|
|
${c.features.slice(0,3).map(A=>`<li>${A}</li>`).join("")}
|
|
</ul>
|
|
<button class="dns-template-select-btn" data-template-id="${c.id}">
|
|
Select ${c.name}
|
|
</button>
|
|
`,r.querySelector(".dns-template-select-btn").addEventListener("click",()=>this.handleTemplateSelection(c)),r}handleTemplateSelection(c){P(`[DnsTemplateSelector] Template selected: ${c.id}`),this.close(),this.onTemplateSelected?this.onTemplateSelected(c):this.openAppSelector(c.id)}handleSetupLater(){P("[DnsTemplateSelector] DNS setup deferred"),this.progressTracker&&this.progressTracker.markDnsSetupDeferred(),this.close(),this.showNotification("DNS setup deferred. You can set it up later from the App Selector.")}openAppSelector(c){const r=document.querySelector('[onclick*="showAppSelector"]');r?(r.click(),setTimeout(()=>{const n=document.querySelector("#app-search");n&&(n.value=c,n.dispatchEvent(new Event("input",{bubbles:!0})))},300)):this.showNotification(`To deploy ${c}, use the App Selector and search for "${c}"`)}showNotification(c){const r=document.createElement("div");r.className="dns-template-notification",r.textContent=c,r.style.cssText=`
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
background: var(--card-base);
|
|
color: var(--fg);
|
|
padding: 15px 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
z-index: 10001;
|
|
max-width: 300px;
|
|
`,document.body.appendChild(r),setTimeout(()=>{r.style.opacity="0",r.style.transition="opacity 0.3s",setTimeout(()=>r.remove(),300)},3e3)}close(){this.modal&&(this.modal.style.display="none",document.body.style.overflow="")}}y.DnsTemplateSelector=_,P("[DnsTemplateSelector] Module loaded")})(window),(function(y){"use strict";const P=new ErrorHandler,_=(...c)=>{y.DASHCADDY_DEBUG&&console.log(...c)};class a{constructor(r,n,v){this.progressTracker=r,this.themeAdapter=n,this.dnsTemplateSelector=v,this.driver=null,this.currentStepIndex=0,this.isActive=!1,this.resizeHandler=null,this.layoutChangeHandler=null}async initializeDriver(){const r=y.driver?.js?.driver||y.driver?.driver||y.driver;if(typeof r!="function")return P.logError("[TourManager] Driver.js Not Loaded",new Error("Driver.js not loaded or invalid"),{windowDriver:typeof y.driver}),!1;const n=this.themeAdapter.getDriverTheme();return this.driver=r({showProgress:!0,showButtons:["next","previous","close"],allowClose:!0,overlayClickNext:!1,overlayOpacity:.6,stagePadding:12,stageRadius:12,allowKeyboardControl:!0,popoverClass:"dashcaddy-popover",animate:!0,smoothScroll:!0,onDestroyed:()=>this.onTourComplete(),onDestroyStarted:()=>{this.progressTracker.isTourCompleted()||this.onTourSkip()}}),this.themeAdapter.applyTheme(this.driver),this.themeAdapter.onThemeChange(()=>{this.themeAdapter.applyTheme(this.driver)}),this.setupDynamicRepositioning(),!0}shouldAutoStart(){return!this.progressTracker.isInstallOnboardingCompleted()&&!this.progressTracker.isTourCompleted()&&this.progressTracker.getCurrentStep()===0}async startTour(){if(!this.driver&&!await this.initializeDriver())return;const r=y.TooltipDefinitions.getSortedTooltips(),n=this.progressTracker.getCompletedTooltips(),v=r.filter(k=>!n.includes(k.id));if(v.length===0){_("[TourManager] No tooltips to show"),this.progressTracker.markTourCompleted();return}const A=v.map((k,m)=>{const C=m===0,l=m===v.length-1,u={element:k.element,popover:{title:k.popover.title,description:k.popover.description,side:k.popover.position||"bottom",align:k.popover.align||"start",showButtons:this._getButtonsForStep(k,C,l),showProgress:k.popover.showProgress!==!1,onNextClick:()=>{this.progressTracker.markTooltipCompleted(k.id),this.progressTracker.setCurrentStep(m+1),this.currentStepIndex=m+1,this.driver.moveNext()},onPrevClick:()=>{this.progressTracker.setCurrentStep(Math.max(0,m-1)),this.currentStepIndex=Math.max(0,m-1),this.driver.movePrevious()},onCloseClick:()=>{this.skipTour()}}};return k.id==="dns-priority"&&this.dnsTemplateSelector&&(u.popover.onSetupNowClick=()=>{_("[TourManager] Opening DNS template selector"),this.dnsTemplateSelector.showTemplateSelector(),this.progressTracker.markTooltipCompleted(k.id),this.progressTracker.setCurrentStep(m+1),this.currentStepIndex=m+1,this.driver.moveNext()},u.popover.onLaterClick=()=>{_("[TourManager] DNS setup deferred"),this.progressTracker.markDnsSetupDeferred(),this.progressTracker.markTooltipCompleted(k.id),this.progressTracker.setCurrentStep(m+1),this.currentStepIndex=m+1,this.driver.moveNext()}),u});this.isActive=!0,this.driver.setSteps(A),this.driver.drive()}async resumeTour(){this.progressTracker.getCurrentStep()>0?await this.startTour():await this.startTour()}skipTour(){this.driver&&this.driver.destroy(),this.cleanupDynamicRepositioning(),this.isActive=!1}async restartTour(){this.progressTracker.resetProgress(),await this.startTour()}async showTooltip(r){const n=y.TooltipDefinitions.getTooltipById(r);if(!n){P.logError("[TourManager] Tooltip Not Found",new Error(`Tooltip not found: ${r}`),{tooltipId:r});return}this.driver||await this.initializeDriver();const v={element:n.element,popover:{title:n.popover.title,description:n.popover.description,side:n.popover.position||"bottom",align:n.popover.align||"start"}};this.driver.highlight(v)}async showWhatsNew(){if(!this.driver&&!await this.initializeDriver())return;const r=y.TooltipDefinitions.getNewFeatureTooltips();if(r.length===0){_("[TourManager] No new features to show");return}_(`[TourManager] Showing ${r.length} new features`);const n=r.map((v,A)=>{const k=A===0,m=A===r.length-1;return{element:v.element,popover:{title:`\u2728 NEW: ${v.popover.title}`,description:v.popover.description,side:v.popover.position||"bottom",align:v.popover.align||"start",showButtons:this._getButtonsForStep(v,k,m),showProgress:!0,onNextClick:()=>{this.driver.moveNext()},onPrevClick:()=>{this.driver.movePrevious()},onCloseClick:()=>{this.skipTour()}}}});this.isActive=!0,this.driver.setSteps(n),this.driver.drive()}setupDynamicRepositioning(){let r;this.resizeHandler=()=>{clearTimeout(r),r=setTimeout(()=>{this.isActive&&this.driver&&(_("[TourManager] Window resized, repositioning tooltip"),this.driver.refresh())},150)},this.layoutChangeHandler=()=>{this.isActive&&this.driver&&(_("[TourManager] Layout changed, repositioning tooltip"),setTimeout(()=>{this.driver&&this.driver.refresh()},100))},y.addEventListener("resize",this.resizeHandler),this.themeAdapter.onThemeChange(this.layoutChangeHandler)}cleanupDynamicRepositioning(){this.resizeHandler&&y.removeEventListener("resize",this.resizeHandler)}_getButtonsForStep(r,n,v){if(r.popover.showButtons)return r.popover.showButtons;const A=[];return n||A.push("previous"),v?A.push("close"):A.push("next"),A}onTourComplete(){this.progressTracker.markTourCompleted(),this.isActive=!1,_("[TourManager] Tour completed")}onTourSkip(){_("[TourManager] Tour skipped"),this.isActive=!1}}y.TourManager=a,_("[TourManager] Module loaded")})(window),(function(){"use strict";const y=(...m)=>{window.DASHCADDY_DEBUG&&console.log(...m)};let P,_,a,c,r;async function n(){try{if(y("[Onboarding] Initializing system..."),window.__dashcaddySiteConfigLoaded)try{await window.__dashcaddySiteConfigLoaded}catch{}if(r=new ErrorHandler,y("[Onboarding] Error Handler initialized"),P=new ProgressTracker("dashcaddy_onboarding"),y("[Onboarding] Progress Tracker initialized"),_=new ThemeAdapter,y("[Onboarding] Theme Adapter initialized"),c=new DnsTemplateSelector(P),y("[Onboarding] DNS Template Selector initialized"),a=new TourManager(P,_,c),y("[Onboarding] Tour Manager initialized"),a.shouldAutoStart())y("[Onboarding] Auto-starting tour for first-time install"),await P.markInstallOnboardingCompleted(),setTimeout(()=>{a.startTour()},1e3);else{const m=P.isTourCompleted(),C=P.getCurrentStep();y(`[Onboarding] Tour not auto-starting (completed: ${m}, step: ${C})`),!m&&C>0&&y("[Onboarding] Tour in progress, can be resumed manually")}v(),window.DashCaddyOnboarding={startTour:()=>a.startTour(),restartTour:()=>a.restartTour(),showTooltip:m=>a.showTooltip(m),showWhatsNew:()=>a.showWhatsNew(),resetProgress:()=>P.resetProgress(),getErrors:()=>r.getErrors(),getErrorStats:()=>r.getStatistics()},y("[Onboarding] System initialized successfully")}catch(m){r&&r.logError("[Onboarding] Initialization",m),console.warn("[Onboarding] System failed to initialize, dashboard will continue without onboarding")}}function v(){const m=document.querySelector(".tools-primary")||document.querySelector(".tools");if(!m)return;const C=()=>{a?(y("[Onboarding] Starting tour via button click"),a.restartTour()):(r&&r.logError("[Onboarding] Tour Manager Not Initialized",new Error("Tour manager not initialized")),alert(`Tour is not available. Check browser console for errors.
|
|
|
|
Possible issues:
|
|
- Driver.js library failed to load
|
|
- JavaScript errors during initialization`))},l=document.getElementById("restart-tour-btn");if(l){l.onclick=C;return}const u=document.createElement("button");u.id="restart-tour-btn",u.textContent="Help Tour",u.title="Restart the onboarding tour",u.onclick=C,m.appendChild(u)}function A(){return typeof(window.driver?.js?.driver||window.driver?.driver||window.driver)!="function"?(console.warn("[Onboarding] Driver.js not loaded yet, will retry... window.driver:",window.driver),!1):!0}function k(){let m=0;const C=10;function l(){A()?n():(m++,m<C?setTimeout(l,500):r?r.handleDriverLoadFailure():new ErrorHandler().handleDriverLoadFailure())}l()}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",k):k(),y("[Onboarding] System loaded")})();
|