From 8f5077c4798d66a316a5c08e04c85a7a2ad9e96e Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Tue, 16 Jun 2026 22:42:48 +0200 Subject: [PATCH] [BUGFIX] Sort version switcher correctly for versions like 0.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version switcher sorted entries with parseFloat(), so parseFloat("0.10") === 0.1 and "0.10" sorted together with the 0.x group at the bottom instead of first. It also pre-selected the entry matching data-current-version, which is rendered as "0.1" on the 0.10 page (the version "0.10" is coerced to "0.1" server-side), so the wrong entry was pre-selected. Sort each dotted version component numerically ("main" first, then the highest version) and derive the active version from the page URL — the authoritative source — instead of the coercible data-current-version attribute. resources/public/js/theme.min.js is rebuilt accordingly. Signed-off-by: Sebastian Mendel --- .../typo3-docs-theme/assets/js/versions.js | 54 ++++++++-- .../resources/public/js/theme.min.js | 2 +- .../tests/js/versions.test.js | 98 +++++++++++++++++++ 3 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 packages/typo3-docs-theme/tests/js/versions.test.js diff --git a/packages/typo3-docs-theme/assets/js/versions.js b/packages/typo3-docs-theme/assets/js/versions.js index e9cef4f02..d17260edc 100644 --- a/packages/typo3-docs-theme/assets/js/versions.js +++ b/packages/typo3-docs-theme/assets/js/versions.js @@ -82,16 +82,14 @@ document.addEventListener('DOMContentLoaded', () => { versionSelect.innerHTML = ''; const seen = new Set(); - const currentVersion = versionSelect.getAttribute('data-current-version'); - - const sortedData = versionData.sort((a, b) => { - const priority = (v) => { - if (v === 'main') return Infinity; - const num = parseFloat(v); - return isNaN(num) ? -1 : num; - }; - return priority(b.version) - priority(a.version); - }); + // The current page URL is the authoritative source of the active version + // (e.g. ".../0.10/en-us/..."). Prefer it over the rendered + // data-current-version attribute, which can be wrong for versions like + // "0.10" (server-side numeric coercion turns it into "0.1"). + const currentVersion = getVersionFromUrl(currentURL) + || versionSelect.getAttribute('data-current-version'); + + const sortedData = [...versionData].sort((a, b) => compareVersionsDescending(a.version, b.version)); sortedData.forEach(item => { if (!seen.has(item.version)) { @@ -131,6 +129,42 @@ document.addEventListener('DOMContentLoaded', () => { return match ? match[1].toLowerCase() : ''; } + // The version is the path segment right before the language code, + // e.g. "/p/vendor/pkg/0.10/en-us/Index.html" -> "0.10". + function getVersionFromUrl(url) { + const match = url.match(/\/([^/]+)\/[a-z]{2}-[a-z]{2}(?:\/|$)/i); + return match ? match[1] : ''; + } + + // Compares two version strings for descending order ("main" first, then + // highest version). Each dotted component is compared numerically so that + // "0.10" correctly sorts above "0.9" (parseFloat would treat both as 0.1/0.9). + // A missing or non-numeric component counts as 0, and equal numeric versions + // fall back to a single string tie-break, so the comparator is a consistent + // total order for any input. + function compareVersionsDescending(a, b) { + if (a === 'main' || b === 'main') { + return a === b ? 0 : (a === 'main' ? -1 : 1); + } + + const partsA = a.split('.'); + const partsB = b.split('.'); + const length = Math.max(partsA.length, partsB.length); + + for (let i = 0; i < length; i++) { + const numA = parseInt(partsA[i], 10); + const numB = parseInt(partsB[i], 10); + const valueA = Number.isNaN(numA) ? 0 : numA; + const valueB = Number.isNaN(numB) ? 0 : numB; + + if (valueA !== valueB) { + return valueB - valueA; + } + } + + return b.localeCompare(a); + } + function toAbsoluteUrl(url) { try { const link = document.createElement('a'); diff --git a/packages/typo3-docs-theme/resources/public/js/theme.min.js b/packages/typo3-docs-theme/resources/public/js/theme.min.js index 95088a6fd..6d71e80e8 100644 --- a/packages/typo3-docs-theme/resources/public/js/theme.min.js +++ b/packages/typo3-docs-theme/resources/public/js/theme.min.js @@ -74,4 +74,4 @@ class AllDocumentationsMenuBase extends HTMLElement{MAINMENU_JSON_URL="https://d `+s[t].replace(" at new "," at "),i=n.displayName&&i.includes("")?i.replace("",n.displayName):i}while(1<=t&&0<=c);break}}}finally{Ce=!1,Error.prepareStackTrace=e}return(e=n?n.displayName||n.name:"")?xe(e):""}function _e(e){try{for(var t="",n=null;t+=function(e,t){switch(e.tag){case 26:case 27:case 5:return xe(e.type);case 16:return xe("Lazy");case 13:return e.child!==t&&null!==t?xe("Suspense Fallback"):xe("Suspense");case 19:return xe("SuspenseList");case 0:case 15:return Le(e.type,!1);case 11:return Le(e.type.render,!1);case 1:return Le(e.type,!0);case 31:return xe("Activity");default:return""}}(e,n),e=(n=e).return;);return t}catch(e){return` Error generating stack: `+e.message+` `+e.stack}}var Te=Object.prototype.hasOwnProperty,ze=n.unstable_scheduleCallback,Ne=n.unstable_cancelCallback,Pe=n.unstable_shouldYield,Ae=n.unstable_requestPaint,Oe=n.unstable_now,De=n.unstable_getCurrentPriorityLevel,Me=n.unstable_ImmediatePriority,Re=n.unstable_UserBlockingPriority,Fe=n.unstable_NormalPriority,Ie=n.unstable_LowPriority,je=n.unstable_IdlePriority,He=n.log,$e=n.unstable_setDisableYieldValue,qe=null,Ue=null;function Be(e){if("function"==typeof He&&$e(e),Ue&&"function"==typeof Ue.setStrictMode)try{Ue.setStrictMode(qe,e)}catch{}}var Ve=Math.clz32||function(e){return 0==(e>>>=0)?32:31-(We(e)/Ge|0)|0},We=Math.log,Ge=Math.LN2;var Qe=256,Ye=262144,Xe=4194304;function Ke(e){var t=42&e;if(0!=t)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return 261888&e;case 262144:case 524288:case 1048576:case 2097152:return 3932160&e;case 4194304:case 8388608:case 16777216:case 33554432:return 62914560&e;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ze(e,t,n){var r,a,l,o,i=e.pendingLanes;return 0===i||(a=e.suspendedLanes,l=e.pingedLanes,e=e.warmLanes,(r=0)===(o=134217727&i)?0===(o=i&~a)?0===l?n||0!==(n=i&~e)&&(r=Ke(n)):r=Ke(l):r=Ke(o):0==(i=o&~a)?0==(l&=o)?n||0!==(n=o&~e)&&(r=Ke(n)):r=Ke(l):r=Ke(i),0===r)?0:0!==t&&t!==r&&0==(t&a)&&((n=t&-t)<=(a=r&-r)||32==a&&4194048&n)?t:r}function Je(e,t){return 0==(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)}function et(){var e=Xe;return 62914560&(Xe<<=1)||(Xe=4194304),e}function tt(e){for(var t=[],n=0;n<31;n++)t.push(e);return t}function nt(e,t){e.pendingLanes|=t,268435456!==t&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function rt(e,t,n){e.pendingLanes|=t,e.suspendedLanes&=~t;var r=31-Ve(t);e.entangledLanes|=t,e.entanglements[r]=1073741824|e.entanglements[r]|261930&n}function at(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-Ve(n),a=1<>=r,l-=r,da=1<<32-Ve(t)+l|n<f?(p=d,d=null):p=d.sibling;var m=D(l,d,i[f],s);if(null===m){null===d&&(d=p);break}L&&d&&null===m.alternate&&_(l,d),o=P(m,o,f),null===c?u=m:c.sibling=m,c=m,d=p}if(f===i.length)T(l,d);else if(null===d)for(;fw?(E=S,S=null):E=S.sibling;var C=D(h,S,x.value,v);if(null===C){null===S&&(S=E);break}L&&S&&null===C.alternate&&_(h,S),g=P(C,g,w),null===k?b=C:k.sibling=C,k=C,S=E}if(x.done)T(h,S);else if(null===S)for(;!x.done;w++,x=y.next())null!==(x=O(h,x.value,v))&&(g=P(x,g,w),null===k?b=x:k.sibling=x,k=x);else{for(S=z(S);!x.done;w++,x=y.next())null!==(x=M(S,h,w,x.value,v))&&(L&&null!==x.alternate&&S.delete(null===x.key?w:x.key),g=P(x,g,w),null===k?b=x:k.sibling=x,k=x);L&&S.forEach(function(e){return _(h,e)})}return j&&pa(h,w),b}if("function"==typeof n.then)return R(e,t,ml(n),r);if(n.$$typeof===Z)return R(e,t,ja(e,n),r);gl(0,n)}return"string"==typeof n&&""!==n||"number"==typeof n||"bigint"==typeof n?(n=""+n,(r=null!==t&&6===t.tag?(T(e,t.sibling),N(t,n)):(T(e,t),Jr(n,e.mode,r))).return=e,A(e=r)):T(e,t)}return function(e,t,n,r){try{pl=0;var a=R(e,t,n,r);return fl=null,a}catch(t){if(t===nl||t===al)throw t;n=Gr(29,t,null,e.mode);return n.lanes=r,n.return=e,n}}}var vl=yl(!0),bl=yl(!1),kl=!1;function Sl(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function wl(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function El(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function xl(e,t,n){var r,a=e.updateQueue;return null===a?null:(a=a.shared,2&$?(null===(r=a.pending)?t.next=t:(t.next=r.next,r.next=t),a.pending=t,t=Br(e),Ur(e,null,n),t):(Hr(e,a,t,n),Br(e)))}function Cl(e,t,n){var r;null!==(t=t.updateQueue)&&(t=t.shared,4194048&n)&&(r=t.lanes,r&=e.pendingLanes,at(e,t.lanes=n|=r))}function Ll(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var a=null,l=null;if(null!==(n=n.firstBaseUpdate)){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null}}while(null===l?a=l=o:l=l.next=o,null!==(n=n.next));null===l?a=l=t:l=l.next=t}else a=l=t;n={baseState:r.baseState,firstBaseUpdate:a,lastBaseUpdate:l,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n}else null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var _l=!1;function Tl(){if(_l&&null!==Ya)throw Ya}function zl(e,t,n,r){_l=!1;var a,l,o=e.updateQueue,i=(kl=!1,o.firstBaseUpdate),s=o.lastBaseUpdate;if(null!==(d=o.shared.pending)&&(o.shared.pending=null,l=(a=d).next,(a.next=null)===s?i=l:s.next=l,s=a,null!==(c=e.alternate))&&(d=(c=c.updateQueue).lastBaseUpdate)!==s&&(null===d?c.firstBaseUpdate=l:d.next=l,c.lastBaseUpdate=a),null!==i){for(var u=o.baseState,s=0,c=l=a=null,d=i;;){var f=(h=-536870913&d.lane)!==d.lane;if(f?(B&h)===h:(r&h)===h){0!==h&&h===Qa&&(_l=!0),null!==c&&(c=c.next={lane:0,tag:d.tag,payload:d.payload,callback:null,next:null});e:{var p=e,m=d,h=t,g=n;switch(m.tag){case 1:if("function"==typeof(p=m.payload)){u=p.call(g,u,h);break e}u=p;break e;case 3:p.flags=-65537&p.flags|128;case 0:if(null==(h="function"==typeof(p=m.payload)?p.call(g,u,h):p))break e;u=y({},u,h);break e;case 2:kl=!0}}null!==(h=d.callback)&&(e.flags|=64,f&&(e.flags|=8192),null===(f=o.callbacks)?o.callbacks=[h]:f.push(h))}else f={lane:h,tag:d.tag,payload:d.payload,callback:d.callback,next:null},null===c?(l=c=f,a=u):c=c.next=f,s|=h;if(null===(d=d.next)){if(null===(d=o.shared.pending))break;d=(f=d).next,f.next=null,o.lastBaseUpdate=f,o.shared.pending=null}}null===c&&(a=u),o.baseState=a,o.firstBaseUpdate=l,o.lastBaseUpdate=c,null===i&&(o.shared.lanes=0),eu|=s,e.lanes=s,e.memoizedState=u}}function Nl(e,t){if("function"!=typeof e)throw Error(F(191,e));e.call(t)}function Pl(e,t){var n=e.callbacks;if(null!==n)for(e.callbacks=null,e=0;e title"))),Ac(l,r,n),l[k]=e,S(l),r=l;break e;case"link":var o=wd("link","href",a).get(r+(n.href||""));if(o)for(var i=0;iCd?50:800)+g));return h.unsuspend=e,function(){h.unsuspend=null,clearTimeout(t),clearTimeout(n)}}:null))return hu=l,e.cancelPendingCommit=m($u.bind(null,e,t,l,n,r,a,o,i,s,c,d,null,f,p)),void Lu(e,l,o,!u)}var h,g;$u(e,t,l,n,r,0,o,i,s)}function Lu(e,t,n,r){t=t&~nu&~tu,e.suspendedLanes|=t,e.pingedLanes&=~t,r&&(e.warmLanes|=t),r=e.expirationTimes;for(var a=t;0<\/script>",l=l.removeChild(l.firstChild);break;case"select":l="string"==typeof r.is?o.createElement("select",{is:r.is}):o.createElement("select"),r.multiple?l.multiple=!0:r.size&&(l.size=r.size);break;default:l="string"==typeof r.is?o.createElement(a,{is:r.is}):o.createElement(a)}}l[k]=t,l[ct]=r;e:for(o=t.child;null!==o;){if(5===o.tag||6===o.tag)l.appendChild(o.stateNode);else if(4!==o.tag&&27!==o.tag&&null!==o.child){o=(o.child.return=o).child;continue}if(o===t)break;for(;null===o.sibling;){if(null===o.return||o.return===t)break e;o=o.return}o.sibling.return=o.return,o=o.sibling}switch(Ac(t.stateNode=l,a,r),a){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break;case"img":r=!0;break;default:r=!1}r&&ns(t)}}return h(t),rs(t,t.type,null!==e&&e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&null!=t.stateNode)e.memoizedProps!==r&&ns(t);else{if("string"!=typeof r&&null===t.stateNode)throw Error(F(166));if(e=ve.current,xa(t)){if(e=t.stateNode,n=t.memoizedProps,(r=null)!==(a=w))switch(a.tag){case 27:case 5:r=a.memoizedProps}e[k]=t,(e=!!(e.nodeValue===n||null!==r&&!0===r.suppressHydrationWarning||Nc(e.nodeValue,n)))||Sa(t,!0)}else((e=Rc(e).createTextNode(r))[k]=t).stateNode=e}return h(t),null;case 31:if(n=t.memoizedState,null===e||null!==e.memoizedState){if(r=xa(t),null!==n){if(null===e){if(!r)throw Error(F(318));if(!(e=null===(e=t.memoizedState)?null:e.dehydrated))throw Error(F(557));e[k]=t}else Ca(),128&t.flags||(t.memoizedState=null),t.flags|=4;h(t),e=!1}else n=La(),null!==e&&null!==e.memoizedState&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return 256&t.flags?(Ul(t),t):(Ul(t),null);if(128&t.flags)throw Error(F(558))}return h(t),null;case 13:if(r=t.memoizedState,null===e||null!==e.memoizedState&&null!==e.memoizedState.dehydrated){if(a=xa(t),null!==r&&null!==r.dehydrated){if(null===e){if(!a)throw Error(F(318));if(!(a=null===(a=t.memoizedState)?null:a.dehydrated))throw Error(F(317));a[k]=t}else Ca(),128&t.flags||(t.memoizedState=null),t.flags|=4;h(t),a=!1}else a=La(),null!==e&&null!==e.memoizedState&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return 256&t.flags?(Ul(t),t):(Ul(t),null)}return Ul(t),128&t.flags?(t.lanes=n,t):(e=null!==e&&null!==e.memoizedState,(n=null!==r)&&((a=null)!==(r=t.child).alternate&&null!==r.alternate.memoizedState&&null!==r.alternate.memoizedState.cachePool&&(a=r.alternate.memoizedState.cachePool.pool),(l=(l=null)!==r.memoizedState&&null!==r.memoizedState.cachePool?r.memoizedState.cachePool.pool:l)!==a)&&(r.flags|=2048),n!==e&&n&&(t.child.flags|=8192),ls(t,t.updateQueue),h(t),null);case 4:return Se(),null===e&&kc(t.stateNode.containerInfo),h(t),null;case 10:return Aa(t.type),h(t),null;case 19:if(i(C),null===(r=t.memoizedState))return h(t),null;if(a=0!=(128&t.flags),null===(l=r.rendering))if(a)os(r,!1);else{if(0!==T||null!==e&&128&e.flags)for(e=t.child;null!==e;){if(null!==(l=Bl(e))){for(t.flags|=128,os(r,!1),e=l.updateQueue,t.updateQueue=e,ls(t,e),t.subtreeFlags=0,e=n,n=t.child;null!==n;)Xr(n,e),n=n.sibling;return b(C,1&C.current|2),j&&pa(t,r.treeForkCount),t.child}e=e.sibling}null!==r.tail&&Oe()>cu&&(t.flags|=128,os(r,!(a=!0)),t.lanes=4194304)}else{if(!a)if(null!==(e=Bl(l))){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,ls(t,e),os(r,!0),null===r.tail&&"hidden"===r.tailMode&&!l.alternate&&!j)return h(t),null}else 2*Oe()-r.renderingStartTime>cu&&536870912!==n&&(t.flags|=128,os(r,!(a=!0)),t.lanes=4194304);r.isBackwards?(l.sibling=t.child,t.child=l):(null===(e=r.last)?t.child=l:e.sibling=l,r.last=l)}return null===r.tail?(h(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Oe(),e.sibling=null,n=C.current,b(C,a?1&n|2:1&n),j&&pa(t,r.treeForkCount),e);case 22:case 23:return Ul(t),Rl(),r=null!==t.memoizedState,null===e?r&&(t.flags|=8192):null!==e.memoizedState!==r&&(t.flags|=8192),r?536870912&n&&!(128&t.flags)&&(h(t),6&t.subtreeFlags)&&(t.flags|=8192):h(t),null!==(n=t.updateQueue)&&ls(t,n.retryQueue),(n=null)!==e&&null!==e.memoizedState&&null!==e.memoizedState.cachePool&&(n=e.memoizedState.cachePool.pool),(r=(r=null)!==t.memoizedState&&null!==t.memoizedState.cachePool?t.memoizedState.cachePool.pool:r)!==n&&(t.flags|=2048),null!==e&&i(Za),null;case 24:return(n=null)!==e&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Aa(x),h(t),null;case 25:case 30:return null}throw Error(F(156,t.tag))}(t.alternate,t,Js);if(null!==n)return void(U=n);if(null!==(t=t.sibling))return void(U=t)}while(U=t=e,null!==t);0===T&&(T=5)}function Hu(e,t){do{var n=function(e,t){switch(ga(t),t.tag){case 1:return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 3:return Aa(x),Se(),65536&(e=t.flags)&&!(128&e)?(t.flags=-65537&e|128,t):null;case 26:case 27:case 5:return Ee(t),null;case 31:if(null!==t.memoizedState){if(Ul(t),null===t.alternate)throw Error(F(340));Ca()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 13:if(Ul(t),null!==(e=t.memoizedState)&&null!==e.dehydrated){if(null===t.alternate)throw Error(F(340));Ca()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 19:return i(C),null;case 4:return Se(),null;case 10:return Aa(t.type),null;case 22:case 23:return Ul(t),Rl(),null!==e&&i(Za),65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 24:return Aa(x),null;default:return null}}(e.alternate,e);if(null!==n)return n.flags&=32767,void(U=n);if(null!==(n=e.return)&&(n.flags|=32768,n.subtreeFlags=0,n.deletions=null),!t&&null!==(e=e.sibling))return void(U=e)}while(U=e=n,null!==e);T=6,U=null}function $u(e,t,n,r,a,l,o,i,s){for(e.cancelPendingCommit=null;Wu(),0!==V;);if(6&$)throw Error(F(327));if(null!==t){if(t===e.current)throw Error(F(177));l=t.lanes|t.childLanes;var u=e,c=n,d=l|=Ir,f=o,p=u.pendingLanes,m=(u.pendingLanes=d,u.suspendedLanes=0,u.pingedLanes=0,u.warmLanes=0,u.expiredLanes&=d,u.entangledLanes&=d,u.errorRecoveryDisabledLanes&=d,u.shellSuspendCounter=0,u.entanglements),h=u.expirationTimes,g=u.hiddenUpdates;for(d=p&~d;0 title"):null)}function xd(e){return"stylesheet"!==e.type||3&e.state.loading}var Cd=0;function Ld(){var e;this.count--,0!==this.count||0!==this.imgCount&&this.waitingForImages||(this.stylesheets?Td(this,this.stylesheets):this.unsuspend&&(e=this.unsuspend,this.unsuspend=null,e()))}var _d=null;function Td(e,t){(e.stylesheets=null)!==e.unsuspend&&(e.count++,_d=new Map,t.forEach(zd,e),_d=null,Ld.call(e))}function zd(e,t){if(!(4&t.state.loading)){var n=_d.get(e);if(n)var r=n.get(null);else{n=new Map,_d.set(e,n);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),l=0;l{(function e(){if(!("u"{let[e,t]=(0,_.useState)([]);return(0,_.useEffect)(()=>{let r=[];new URL(window.location.href).searchParams?.forEach((e,t)=>{var n;"scope"===t&&e?(n=(e=decodeURIComponent(e).split("/").filter(Boolean).join("/")).split("/").slice(1,3).join("/"),r.push({type:"manual",title:n,slug:e})):t.startsWith("filters[")&&(n=new RegExp(/filters\[(.*?)\]\[(.*?)\]/),[,e,t]=t.match(n),r.push({type:"optionsaggs"===e?"option":e,title:t}))}),t(r)},[]),[e,t]},y=c(o((e,t)=>{function v(){return d.Date.now()}var r=NaN,a="[object Symbol]",l=/^\s+|\s+$/g,o=/^[-+]0x[0-9a-f]+$/i,i=/^0b[01]+$/i,s=/^0o[0-7]+$/i,u=parseInt,n="object"==typeof global&&global&&global.Object===Object&&global,c="object"==typeof self&&self&&self.Object===Object&&self,d=n||c||Function("return this")(),f=Object.prototype.toString,b=Math.max,k=Math.min;function S(e){var t=typeof e;return e&&("object"==t||"function"==t)}function w(e){if("number"==typeof e)return e;if("symbol"==typeof(n=e)||!!(t=n)&&"object"==typeof t&&f.call(n)==a)return r;var t;if("string"!=typeof(e=S(e)?S(t="function"==typeof e.valueOf?e.valueOf():e)?t+"":t:e))return 0===e?e:+e;e=e.replace(l,"");var n=i.test(e);return n||s.test(e)?u(e.slice(2),n?2:8):o.test(e)?r:+e}t.exports=function(r,n,e){var a,l,o,i,s,u,c=0,d=!1,f=!1,t=!0;if("function"!=typeof r)throw TypeError("Expected a function");function p(e){var t=a,n=l;return a=l=void 0,c=e,i=r.apply(n,t)}function m(e){var t=e-u;return void 0===u||n<=t||t<0||f&&o<=e-c}function h(){var e,t=v();if(m(t))return g(t);s=setTimeout(h,(e=n-((t=t)-u),f?k(e,o-(t-c)):e))}function g(e){return s=void 0,t&&a?p(e):(a=l=void 0,i)}function y(){var e=v(),t=m(e);if(a=arguments,l=this,u=e,t){if(void 0===s)return c=e=u,s=setTimeout(h,n),d?p(e):i;if(f)return s=setTimeout(h,n),p(u)}return void 0===s&&(s=setTimeout(h,n)),i}return n=w(n)||0,S(e)&&(d=!!e.leading,f="maxWait"in e,o=f?b(w(e.maxWait)||0,n):o,t="trailing"in e?!!e.trailing:t),y.cancel=function(){void 0!==s&&clearTimeout(s),a=u=l=s=void(c=0)},y.flush=function(){return void 0===s?i:g(v())},y}})()),b=()=>{let[e,o]=(0,_.useState)([]),[t,i]=(0,_.useState)([]),[n,s]=(0,_.useState)(!1);return{fileSuggestions:e,scopeSuggestions:t,setScopeSuggestions:i,setFileSuggestions:o,isLoading:n,fetchSuggestions:(0,_.useCallback)((0,y.default)((0,_.useCallback)(async(e,t)=>{if(0!==e?.length||t){s(!0);try{var n=await fetch(((e,t)=>{let n=new URL("/search/suggest",T);return e.forEach(e=>{"manual"===e.type?n.searchParams.append("filters[package]",e.title):"vendor"===e.type?n.searchParams.append(`filters[${e.type}]`,e.title):"option"===e.type?n.searchParams.append(`filters[optionsaggs][${e.title}]`,!0):n.searchParams.append(`filters[${e.type}][${e.title}]`,!0)}),n.searchParams.append("q",t),n.href})(e,t),{headers:{"Content-Type":"application/json"}});if(!n.ok)throw Error("Network response error");var r=await n.json(),a=r?.results?.map(e=>({title:e.snippet_title,packageName:e.manual_package,href:`https://docs.typo3.org/${e.manual_slug}/${e.relative_url}#`+e.fragment}))||[],l=Object.entries(r?.suggest?.suggestions??{}).flatMap(([e,t])=>{let n="package"===e.replace("manual_","")?"manual":e.replace("manual_","");return t.map(e=>({type:n,title:"version"===n?e.title.split(".")[0]:e.title,slug:e.slug??null}))});o(a),i(l)}catch(e){console.error(e),o([]),i([])}finally{s(!1)}}else o([]),i([])},[]),300),[])}},x=o(e=>{var l=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(e,t,n){var r=void 0!==n?""+n:null;if(void 0!==t.key&&(r=""+t.key),"key"in t)for(var a in n={},t)"key"!==a&&(n[a]=t[a]);else n=t;return t=n.ref,{$$typeof:l,type:e,key:r,ref:void 0===t?null:t,props:n}}e.Fragment=t,e.jsx=n,e.jsxs=n}),S=o((e,t)=>{t.exports=x()})(),C=({type:e})=>{switch(e){case"search":return(0,S.jsx)("i",{className:"fa-solid fa-magnifying-glass"});case"file":return(0,S.jsx)("i",{className:"fa-regular fa-file-code"});default:return null}},w=({scopes:e,title:t,type:n,packageName:r})=>0(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:t&&t+":"}),e&&(0,S.jsx)("p",{className:"suggest-row__scope-name",children:e})]})),(0,S.jsx)("p",{className:"suggest-row__title",children:t})]})}):(0,S.jsxs)("div",{className:"suggest-row__scope",title:t+(r?` (${r})`:""),children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:n&&n+":"}),(0,S.jsx)("p",{className:"suggest-row__title",dangerouslySetInnerHTML:{__html:t}}),r&&(0,S.jsxs)("p",{className:"suggest-row__description",children:["(",r,")"]})]}),ee=(0,_.forwardRef)(({title:e,packageName:t,scopes:n,tooltip:r,onClick:a,type:l,href:o,isActive:i,icon:s="search"},u)=>(0,S.jsxs)("a",{onClick:e=>{o||(e.preventDefault(),a?.())},ref:u,href:o,className:"suggest-row "+(i?"suggest-row--active":""),children:[(0,S.jsx)("div",{className:"suggest-row__icon",children:(0,S.jsx)(C,{type:s})}),(0,S.jsx)("div",{className:"suggest-row__content",children:(0,S.jsx)(w,{scopes:n,title:e,type:l,packageName:t})}),r&&(0,S.jsx)("p",{className:"suggest-row__tooltip",children:r})]})),te=(ee.displayName="SuggestRow",({isOpen:t,onClose:r})=>{let[a,l]=(0,_.useState)(""),[o,i]=v(),[e,s]=(0,_.useState)([]),[u,c]=(0,_.useState)(-1),d=(0,_.useRef)([]),f=(0,_.useRef)(),p=(0,_.useRef)(),{fileSuggestions:n,scopeSuggestions:m,setScopeSuggestions:h,setFileSuggestions:g,isLoading:y,fetchSuggestions:k}=b(),w=((0,_.useEffect)(()=>{var e,t,n=document.getElementById("searchscope")?.children?.[1]?.value??null;n&&(e=(n=n.split("/").filter(Boolean)).slice(1,3).join("/"),t=n.slice(3,4)[0]?.split(".")[0],s([{type:"manual",title:e,slug:n.join("/")},{type:"version",title:t}]))},[]),(0,_.useEffect)(()=>{var e=new URL(window.location.href).searchParams.get("q");e&&(l(e),k(o,a))},[]),(0,_.useCallback)((e,t)=>{let n=new URL("/search/search",T);return t||1!==e.length||"manual"!==e[0].type?(e.forEach(e=>{"manual"===e.type?n.searchParams.append("scope",`/${e.slug}/`):"vendor"===e.type?n.searchParams.append("vendor",e.title):"option"===e.type?n.searchParams.append(`filters[optionaggs][${e.title}]`,!0):n.searchParams.append(`filters[${e.type}][${e.title}]`,!0)}),n.searchParams.append("q",t),n.href):"/"+e[0].slug},[])),E=(0,_.useMemo)(()=>{var t=[];for(let e=o.length;0{i(e=>{var e=[...e],t=e.findIndex(e=>e.type===r);return-1===t?e.push({type:r,title:n,slug:a}):e[t]={type:r,title:n,slug:a},e}),l(""),c(-1),h([]),g([]),f.current?.focus()},[i]),C=(0,_.useCallback)(e=>{e=e.target.value;l(e),""!==e&&k(o,e)},[o,k]),L=(0,_.useCallback)(e=>{let t=[...E,...m,...n].length;switch(e.key){case"Backspace":0===f.current?.selectionEnd&&i(e=>e.slice(0,-1));break;case"ArrowDown":e.preventDefault(),c(e=>e-1{0<=u&&d.current[u]?.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})},[u]),(0,_.useEffect)(()=>{var e=p.current;e&&(t&&!e.open?e.showModal():!t&&e.open&&e.close())},[t]),(0,_.useEffect)(()=>{let n=p.current;if(n){let e=()=>r(),t=(n.addEventListener("close",e),e=>{e.target===n&&n.close()});return n.addEventListener("click",t),()=>{n.removeEventListener("close",e),n.removeEventListener("click",t)}}},[r]),(0,S.jsx)("dialog",{ref:p,id:"search-modal",className:"search-modal",closedby:"any",children:(0,S.jsxs)("div",{className:"search-modal__content",children:[(0,S.jsx)("div",{className:"search-modal__header",children:(0,S.jsxs)("div",{className:"search-modal__input-wrapper",onClick:()=>c(-1),children:[(0,S.jsx)("i",{className:"fa-solid fa-magnifying-glass search-modal__icon"}),o.map((e,t)=>(0,S.jsxs)("div",{className:"search-modal__scope",children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:e.type&&e.type+":"}),(0,S.jsx)("p",{className:"search-modal__scope-title",children:e.title})]},"scope-"+t)),(0,S.jsx)("input",{ref:f,autoComplete:"off",name:"q",autoFocus:!0,type:"text",className:"search-modal__input",placeholder:0{l(""),i([]),c(-1),f.current?.focus()},children:(0,S.jsx)("i",{className:"fa-solid fa-circle-xmark"})})]})}),(0,S.jsxs)("ul",{className:"search-modal__body",children:[0(0,S.jsx)(ee,{scopes:e.scopes,title:e.title,tooltip:e.tooltip,isActive:u===t,ref:e=>d.current[t]=e,href:e.href},"decomposed-"+t))})}),y?(0,S.jsxs)("div",{className:"search-modal__loading",children:[(0,S.jsx)("div",{className:"search-modal__spinner",children:(0,S.jsx)("i",{className:"fa-solid fa-spinner fa-spin"})}),(0,S.jsx)("p",{children:"Searching..."})]}):(0,S.jsxs)(S.Fragment,{children:[0(0,S.jsx)(ee,{title:e,type:t,isActive:u===r+E.length,ref:e=>d.current[r+E.length]=e,tooltip:"Filter for this",onClick:()=>x(e,t,n)},"scope-"+r))})}),0(0,S.jsx)(ee,{title:e,packageName:t,isActive:u===r+E.length+m.length,href:n,ref:e=>d.current[r+E.length+m.length]=e,tooltip:"Open this page",icon:"file"},"file-"+r))})})]})]})]})})}),ne=({displayInput:e=!1})=>{let[t,n]=(0,_.useState)(!1),[r,a]=(0,_.useState)("");(0,_.useEffect)(()=>{var e=new URL(window.location.href).searchParams.get("q");e&&a(e)},[]);var l=e=>{e.preventDefault(),n(!0)};return(0,_.useEffect)(()=>{var e=document.getElementById("global-search-form");e&&(e.hidden=!0)},[]),(0,S.jsxs)(S.Fragment,{children:[t&&(0,S.jsx)(te,{isOpen:t,onClose:()=>n(!1)}),e?(0,S.jsxs)("div",{class:"input-group mb-3 mt-sm-3",onClick:l,children:[(0,S.jsx)("input",{autocomplete:"off",class:"form-control shadow-none",id:"globalsearchinput",name:"q",placeholder:"TYPO3 documentation...",type:"text",value:r}),(0,S.jsxs)("button",{class:"btn btn-light",children:[(0,S.jsx)("i",{class:"fa-solid fa-magnifying-glass"})," ",(0,S.jsx)("span",{class:"d-none d-md-inline",children:"Search"})]})]}):(0,S.jsxs)("button",{onClick:l,class:"btn btn-light",children:[(0,S.jsx)("i",{class:"fa-solid fa-magnifying-glass"})," ",(0,S.jsx)("span",{class:"d-none d-md-inline",children:"Search"})]})]})},T="https://docs.typo3.org";document.addEventListener("DOMContentLoaded",()=>{var e=document.getElementById("global-search-root"),e=(e&&(0,g.createRoot)(e).render((0,S.jsx)(ne,{})),document.getElementById("global-search-results"));e&&(0,g.createRoot)(e).render((0,S.jsx)(ne,{displayInput:!0}))}),(()=>{"use strict";const e=Array.from(document.querySelectorAll('.nav-item > [role="tab"]'));function l(e){return e.innerHTML.trim()}document.addEventListener("shown.bs.tab",function(r){const a=l(r.target);e.filter(e=>{var t=l(e)===a,n=e===r.target,e="true"===e.getAttribute("aria-selected");return t&&!n&&!e}).forEach(e=>{new bootstrap.Tab(e).show()})})})(),document.addEventListener("DOMContentLoaded",()=>{const t=document.getElementById("options-toggle"),n=document.getElementById("options-panel");t&&n&&(n.addEventListener("toggle",e=>{t.setAttribute("aria-expanded","open"===e.newState)}),n.addEventListener("focusout",e=>{n.contains(e.relatedTarget)||n.hidePopover()}))});class ToggleSection{TOGGLE_CLASS="toggle-section";TOGGLE_ICON_SHOW=["fa-solid","fa-eye"];TOGGLE_ICON_HIDE=["fa-solid","fa-eye-slash"];TOGGLE_TITLE_SHOW="Show Section";TOGGLE_TITLE_HIDE="Hide section";TOGGLE_ALL_SECTIONS_SELECTOR=".toggle-all-sections";TOGGLE_ALL_SECTIONS_TITLE_SHOW="Expand all sections";TOGGLE_ALL_SECTIONS_TITLE_HIDE="Collapse all sections";sections=[];constructor(){this.sectionHeadings=document.querySelectorAll("section > :is(h2,h3,h4,h5,h6)"),this.sectionHeadings.forEach(e=>{var t=this.createToggleSectionButton(),n=e.querySelector("a.headerlink");const r=e.parentElement;this.sections.push(r),e.insertBefore(t,n),t.addEventListener("click",e=>{e.preventDefault(),this.toggleSection(r)})}),this.sectionHeadings&&document.querySelectorAll("a").forEach(n=>{n.hash&&n.addEventListener("click",e=>{var t=document.querySelector(n.hash);t&&this.changeSection(t,!1)})});const e=document.querySelector(this.TOGGLE_ALL_SECTIONS_SELECTOR);e&&e.addEventListener("click",()=>{e.classList.toggle("hide-all");const t=e.classList.contains("hide-all");e.setAttribute("title",t?this.TOGGLE_ALL_SECTIONS_TITLE_SHOW:this.TOGGLE_ALL_SECTIONS_TITLE_HIDE),e.textContent=t?this.TOGGLE_ALL_SECTIONS_TITLE_SHOW:this.TOGGLE_ALL_SECTIONS_TITLE_HIDE,this.sections.forEach(e=>{this.changeSection(e,t)})})}createToggleSectionButton(){var e=document.createElement("a"),t=(e.classList.add(this.TOGGLE_CLASS),e.setAttribute("href","#"),e.setAttribute("title",this.TOGGLE_TITLE_HIDE),document.createElement("i"));return t.classList.add(...this.TOGGLE_ICON_HIDE),e.appendChild(t),e}toggleSection(e){var t=e.classList.contains("section-collapsed");this.changeSection(e,!t)}changeSection(e,r){r?e.classList.add("section-collapsed"):e.classList.remove("section-collapsed");const a=["H2","H3","H4","H5","H6"];e.querySelectorAll(":scope > *").forEach(e=>{var t,n;a.includes(e.nodeName)?(t=(n=e.querySelector("."+this.TOGGLE_CLASS)).querySelector(":scope > i"),n.setAttribute("title",r?this.TOGGLE_TITLE_SHOW:this.TOGGLE_TITLE_HIDE),t.classList.remove(...t.classList),n=r?this.TOGGLE_ICON_SHOW:this.TOGGLE_ICON_HIDE,t.classList.add(...n)):r?e.classList.add("collapsed-section-content"):e.classList.remove("collapsed-section-content")})}}new ToggleSection,document.addEventListener("DOMContentLoaded",()=>{const n=document.getElementById("languageSelect"),a=document.getElementById("versionSelect");let r=document.URL;var e=a.getAttribute("data-override-url-self"),t=a.getAttribute("data-override-url-proxy"),e=(e&&(r=e),(t||"https://docs.typo3.org/services/versionsJson.php?url=")+encodeURIComponent(r));let l={};function o(e){a.innerHTML="";const n=new Set,r=a.getAttribute("data-current-version");e.sort((e,t)=>{var n=e=>{return"main"===e?1/0:(e=parseFloat(e),isNaN(e)?-1:e)};return n(t.version)-n(e.version)}).forEach(e=>{var t;n.has(e.version)||((t=document.createElement("option")).value=i(e.url),t.textContent=e.version,e.version===r&&(t.selected=!0),a.appendChild(t),n.add(e.version))}),0===a.options.length&&((e=document.createElement("option")).textContent="No versions available",e.disabled=!0,a.appendChild(e))}function i(e){try{var t=document.createElement("a");return t.href=e,t.href}catch{return e}}fetch(e).then(e=>{if(e.ok)return e.json();throw new Error("Failed to fetch versions")}).then(e=>{e.forEach(e=>{var t=e.language.toLowerCase();l[t]||(l[t]=[]),l[t].push(e)});var e=Object.keys(l),t=function(e){e=e.match(/\/([a-z]{2}-[a-z]{2})\//i);return e?e[1].toLowerCase():""}(r);e.length<=1&&l["en-us"]?o(l["en-us"]):(n.classList.remove("d-none"),a.innerHTML="",e.sort().forEach(e=>{var t=document.createElement("option");t.value=e,t.textContent={"en-us":"English","de-de":"German","fr-fr":"French","ru-ru":"Russian"}[e=e]||e.toUpperCase(),n.appendChild(t)}),e=e.includes(t)?t:"en-us",n.value=e,o(l[e]),n.addEventListener("change",()=>{var e=n.value,e=l[e];e&&0{console.error(e),a.innerHTML=""}),a.addEventListener("change",e=>{e.target.value&&(window.location.href=e.target.value)})}); \ No newline at end of file +`).replace(Tc,"")}function Nc(e,t){return t=zc(t),zc(e)===t}function N(e,t,n,r,a,l){switch(n){case"children":"string"==typeof r?"body"===t||"textarea"===t&&""===r||Vt(e,r):"number"!=typeof r&&"bigint"!=typeof r||"body"===t||Vt(e,""+r);break;case"className":Nt(e,"class",r);break;case"tabIndex":Nt(e,"tabindex",r);break;case"dir":case"role":case"viewBox":case"width":case"height":Nt(e,n,r);break;case"style":Qt(e,r,l);break;case"data":if("object"!==t){Nt(e,"data",r);break}case"src":case"href":""!==r||"a"===t&&"href"===n?null==r||"function"==typeof r||"symbol"==typeof r||"boolean"==typeof r?e.removeAttribute(n):(r=Zt(""+r),e.setAttribute(n,r)):e.removeAttribute(n);break;case"action":case"formAction":"function"==typeof r?e.setAttribute(n,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"):("function"==typeof l&&("formAction"===n?("input"!==t&&N(e,t,"name",a.name,a,null),N(e,t,"formEncType",a.formEncType,a,null),N(e,t,"formMethod",a.formMethod,a,null),N(e,t,"formTarget",a.formTarget,a,null)):(N(e,t,"encType",a.encType,a,null),N(e,t,"method",a.method,a,null),N(e,t,"target",a.target,a,null))),null==r||"symbol"==typeof r||"boolean"==typeof r?e.removeAttribute(n):(r=Zt(""+r),e.setAttribute(n,r)));break;case"onClick":null!=r&&(e.onclick=Jt);break;case"onScroll":null!=r&&z("scroll",e);break;case"onScrollEnd":null!=r&&z("scrollend",e);break;case"dangerouslySetInnerHTML":if(null!=r){if("object"!=typeof r||!("__html"in r))throw Error(F(61));if(null!=(n=r.__html)){if(null!=a.children)throw Error(F(60));e.innerHTML=n}}break;case"multiple":e.multiple=r&&"function"!=typeof r&&"symbol"!=typeof r;break;case"muted":e.muted=r&&"function"!=typeof r&&"symbol"!=typeof r;break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":case"autoFocus":break;case"xlinkHref":null==r||"function"==typeof r||"boolean"==typeof r||"symbol"==typeof r?e.removeAttribute("xlink:href"):(n=Zt(""+r),e.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",n));break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":null!=r&&"function"!=typeof r&&"symbol"!=typeof r?e.setAttribute(n,""+r):e.removeAttribute(n);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":r&&"function"!=typeof r&&"symbol"!=typeof r?e.setAttribute(n,""):e.removeAttribute(n);break;case"capture":case"download":!0===r?e.setAttribute(n,""):!1!==r&&null!=r&&"function"!=typeof r&&"symbol"!=typeof r?e.setAttribute(n,r):e.removeAttribute(n);break;case"cols":case"rows":case"size":case"span":null!=r&&"function"!=typeof r&&"symbol"!=typeof r&&!isNaN(r)&&1<=r?e.setAttribute(n,r):e.removeAttribute(n);break;case"rowSpan":case"start":null==r||"function"==typeof r||"symbol"==typeof r||isNaN(r)?e.removeAttribute(n):e.setAttribute(n,r);break;case"popover":z("beforetoggle",e),z("toggle",e),zt(e,"popover",r);break;case"xlinkActuate":Pt(e,"http://www.w3.org/1999/xlink","xlink:actuate",r);break;case"xlinkArcrole":Pt(e,"http://www.w3.org/1999/xlink","xlink:arcrole",r);break;case"xlinkRole":Pt(e,"http://www.w3.org/1999/xlink","xlink:role",r);break;case"xlinkShow":Pt(e,"http://www.w3.org/1999/xlink","xlink:show",r);break;case"xlinkTitle":Pt(e,"http://www.w3.org/1999/xlink","xlink:title",r);break;case"xlinkType":Pt(e,"http://www.w3.org/1999/xlink","xlink:type",r);break;case"xmlBase":Pt(e,"http://www.w3.org/XML/1998/namespace","xml:base",r);break;case"xmlLang":Pt(e,"http://www.w3.org/XML/1998/namespace","xml:lang",r);break;case"xmlSpace":Pt(e,"http://www.w3.org/XML/1998/namespace","xml:space",r);break;case"is":zt(e,"is",r);break;case"innerText":case"textContent":break;default:2 title"):null)}function xd(e){return"stylesheet"!==e.type||3&e.state.loading}var Cd=0;function Ld(){var e;this.count--,0!==this.count||0!==this.imgCount&&this.waitingForImages||(this.stylesheets?Td(this,this.stylesheets):this.unsuspend&&(e=this.unsuspend,this.unsuspend=null,e()))}var _d=null;function Td(e,t){(e.stylesheets=null)!==e.unsuspend&&(e.count++,_d=new Map,t.forEach(zd,e),_d=null,Ld.call(e))}function zd(e,t){if(!(4&t.state.loading)){var n=_d.get(e);if(n)var r=n.get(null);else{n=new Map,_d.set(e,n);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),l=0;l{(function e(){if(!("u"{let[e,t]=(0,_.useState)([]);return(0,_.useEffect)(()=>{let r=[];new URL(window.location.href).searchParams?.forEach((e,t)=>{var n;"scope"===t&&e?(n=(e=decodeURIComponent(e).split("/").filter(Boolean).join("/")).split("/").slice(1,3).join("/"),r.push({type:"manual",title:n,slug:e})):t.startsWith("filters[")&&(n=new RegExp(/filters\[(.*?)\]\[(.*?)\]/),[,e,t]=t.match(n),r.push({type:"optionsaggs"===e?"option":e,title:t}))}),t(r)},[]),[e,t]},y=c(o((e,t)=>{function v(){return d.Date.now()}var r=NaN,a="[object Symbol]",l=/^\s+|\s+$/g,o=/^[-+]0x[0-9a-f]+$/i,i=/^0b[01]+$/i,s=/^0o[0-7]+$/i,u=parseInt,n="object"==typeof global&&global&&global.Object===Object&&global,c="object"==typeof self&&self&&self.Object===Object&&self,d=n||c||Function("return this")(),f=Object.prototype.toString,b=Math.max,k=Math.min;function S(e){var t=typeof e;return e&&("object"==t||"function"==t)}function w(e){if("number"==typeof e)return e;if("symbol"==typeof(n=e)||!!(t=n)&&"object"==typeof t&&f.call(n)==a)return r;var t;if("string"!=typeof(e=S(e)?S(t="function"==typeof e.valueOf?e.valueOf():e)?t+"":t:e))return 0===e?e:+e;e=e.replace(l,"");var n=i.test(e);return n||s.test(e)?u(e.slice(2),n?2:8):o.test(e)?r:+e}t.exports=function(r,n,e){var a,l,o,i,s,u,c=0,d=!1,f=!1,t=!0;if("function"!=typeof r)throw TypeError("Expected a function");function p(e){var t=a,n=l;return a=l=void 0,c=e,i=r.apply(n,t)}function m(e){var t=e-u;return void 0===u||n<=t||t<0||f&&o<=e-c}function h(){var e,t=v();if(m(t))return g(t);s=setTimeout(h,(e=n-((t=t)-u),f?k(e,o-(t-c)):e))}function g(e){return s=void 0,t&&a?p(e):(a=l=void 0,i)}function y(){var e=v(),t=m(e);if(a=arguments,l=this,u=e,t){if(void 0===s)return c=e=u,s=setTimeout(h,n),d?p(e):i;if(f)return s=setTimeout(h,n),p(u)}return void 0===s&&(s=setTimeout(h,n)),i}return n=w(n)||0,S(e)&&(d=!!e.leading,f="maxWait"in e,o=f?b(w(e.maxWait)||0,n):o,t="trailing"in e?!!e.trailing:t),y.cancel=function(){void 0!==s&&clearTimeout(s),a=u=l=s=void(c=0)},y.flush=function(){return void 0===s?i:g(v())},y}})()),b=()=>{let[e,o]=(0,_.useState)([]),[t,i]=(0,_.useState)([]),[n,s]=(0,_.useState)(!1);return{fileSuggestions:e,scopeSuggestions:t,setScopeSuggestions:i,setFileSuggestions:o,isLoading:n,fetchSuggestions:(0,_.useCallback)((0,y.default)((0,_.useCallback)(async(e,t)=>{if(0!==e?.length||t){s(!0);try{var n=await fetch(((e,t)=>{let n=new URL("/search/suggest",T);return e.forEach(e=>{"manual"===e.type?n.searchParams.append("filters[package]",e.title):"vendor"===e.type?n.searchParams.append(`filters[${e.type}]`,e.title):"option"===e.type?n.searchParams.append(`filters[optionsaggs][${e.title}]`,!0):n.searchParams.append(`filters[${e.type}][${e.title}]`,!0)}),n.searchParams.append("q",t),n.href})(e,t),{headers:{"Content-Type":"application/json"}});if(!n.ok)throw Error("Network response error");var r=await n.json(),a=r?.results?.map(e=>({title:e.snippet_title,packageName:e.manual_package,href:`https://docs.typo3.org/${e.manual_slug}/${e.relative_url}#`+e.fragment}))||[],l=Object.entries(r?.suggest?.suggestions??{}).flatMap(([e,t])=>{let n="package"===e.replace("manual_","")?"manual":e.replace("manual_","");return t.map(e=>({type:n,title:"version"===n?e.title.split(".")[0]:e.title,slug:e.slug??null}))});o(a),i(l)}catch(e){console.error(e),o([]),i([])}finally{s(!1)}}else o([]),i([])},[]),300),[])}},x=o(e=>{var l=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(e,t,n){var r=void 0!==n?""+n:null;if(void 0!==t.key&&(r=""+t.key),"key"in t)for(var a in n={},t)"key"!==a&&(n[a]=t[a]);else n=t;return t=n.ref,{$$typeof:l,type:e,key:r,ref:void 0===t?null:t,props:n}}e.Fragment=t,e.jsx=n,e.jsxs=n}),S=o((e,t)=>{t.exports=x()})(),C=({type:e})=>{switch(e){case"search":return(0,S.jsx)("i",{className:"fa-solid fa-magnifying-glass"});case"file":return(0,S.jsx)("i",{className:"fa-regular fa-file-code"});default:return null}},w=({scopes:e,title:t,type:n,packageName:r})=>0(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:t&&t+":"}),e&&(0,S.jsx)("p",{className:"suggest-row__scope-name",children:e})]})),(0,S.jsx)("p",{className:"suggest-row__title",children:t})]})}):(0,S.jsxs)("div",{className:"suggest-row__scope",title:t+(r?` (${r})`:""),children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:n&&n+":"}),(0,S.jsx)("p",{className:"suggest-row__title",dangerouslySetInnerHTML:{__html:t}}),r&&(0,S.jsxs)("p",{className:"suggest-row__description",children:["(",r,")"]})]}),ee=(0,_.forwardRef)(({title:e,packageName:t,scopes:n,tooltip:r,onClick:a,type:l,href:o,isActive:i,icon:s="search"},u)=>(0,S.jsxs)("a",{onClick:e=>{o||(e.preventDefault(),a?.())},ref:u,href:o,className:"suggest-row "+(i?"suggest-row--active":""),children:[(0,S.jsx)("div",{className:"suggest-row__icon",children:(0,S.jsx)(C,{type:s})}),(0,S.jsx)("div",{className:"suggest-row__content",children:(0,S.jsx)(w,{scopes:n,title:e,type:l,packageName:t})}),r&&(0,S.jsx)("p",{className:"suggest-row__tooltip",children:r})]})),te=(ee.displayName="SuggestRow",({isOpen:t,onClose:r})=>{let[a,l]=(0,_.useState)(""),[o,i]=v(),[e,s]=(0,_.useState)([]),[u,c]=(0,_.useState)(-1),d=(0,_.useRef)([]),f=(0,_.useRef)(),p=(0,_.useRef)(),{fileSuggestions:n,scopeSuggestions:m,setScopeSuggestions:h,setFileSuggestions:g,isLoading:y,fetchSuggestions:k}=b(),w=((0,_.useEffect)(()=>{var e,t,n=document.getElementById("searchscope")?.children?.[1]?.value??null;n&&(e=(n=n.split("/").filter(Boolean)).slice(1,3).join("/"),t=n.slice(3,4)[0]?.split(".")[0],s([{type:"manual",title:e,slug:n.join("/")},{type:"version",title:t}]))},[]),(0,_.useEffect)(()=>{var e=new URL(window.location.href).searchParams.get("q");e&&(l(e),k(o,a))},[]),(0,_.useCallback)((e,t)=>{let n=new URL("/search/search",T);return t||1!==e.length||"manual"!==e[0].type?(e.forEach(e=>{"manual"===e.type?n.searchParams.append("scope",`/${e.slug}/`):"vendor"===e.type?n.searchParams.append("vendor",e.title):"option"===e.type?n.searchParams.append(`filters[optionaggs][${e.title}]`,!0):n.searchParams.append(`filters[${e.type}][${e.title}]`,!0)}),n.searchParams.append("q",t),n.href):"/"+e[0].slug},[])),E=(0,_.useMemo)(()=>{var t=[];for(let e=o.length;0{i(e=>{var e=[...e],t=e.findIndex(e=>e.type===r);return-1===t?e.push({type:r,title:n,slug:a}):e[t]={type:r,title:n,slug:a},e}),l(""),c(-1),h([]),g([]),f.current?.focus()},[i]),C=(0,_.useCallback)(e=>{e=e.target.value;l(e),""!==e&&k(o,e)},[o,k]),L=(0,_.useCallback)(e=>{let t=[...E,...m,...n].length;switch(e.key){case"Backspace":0===f.current?.selectionEnd&&i(e=>e.slice(0,-1));break;case"ArrowDown":e.preventDefault(),c(e=>e-1{0<=u&&d.current[u]?.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})},[u]),(0,_.useEffect)(()=>{var e=p.current;e&&(t&&!e.open?e.showModal():!t&&e.open&&e.close())},[t]),(0,_.useEffect)(()=>{let n=p.current;if(n){let e=()=>r(),t=(n.addEventListener("close",e),e=>{e.target===n&&n.close()});return n.addEventListener("click",t),()=>{n.removeEventListener("close",e),n.removeEventListener("click",t)}}},[r]),(0,S.jsx)("dialog",{ref:p,id:"search-modal",className:"search-modal",closedby:"any",children:(0,S.jsxs)("div",{className:"search-modal__content",children:[(0,S.jsx)("div",{className:"search-modal__header",children:(0,S.jsxs)("div",{className:"search-modal__input-wrapper",onClick:()=>c(-1),children:[(0,S.jsx)("i",{className:"fa-solid fa-magnifying-glass search-modal__icon"}),o.map((e,t)=>(0,S.jsxs)("div",{className:"search-modal__scope",children:[(0,S.jsx)("p",{className:"suggest-row__scope-type",children:e.type&&e.type+":"}),(0,S.jsx)("p",{className:"search-modal__scope-title",children:e.title})]},"scope-"+t)),(0,S.jsx)("input",{ref:f,autoComplete:"off",name:"q",autoFocus:!0,type:"text",className:"search-modal__input",placeholder:0{l(""),i([]),c(-1),f.current?.focus()},children:(0,S.jsx)("i",{className:"fa-solid fa-circle-xmark"})})]})}),(0,S.jsxs)("ul",{className:"search-modal__body",children:[0(0,S.jsx)(ee,{scopes:e.scopes,title:e.title,tooltip:e.tooltip,isActive:u===t,ref:e=>d.current[t]=e,href:e.href},"decomposed-"+t))})}),y?(0,S.jsxs)("div",{className:"search-modal__loading",children:[(0,S.jsx)("div",{className:"search-modal__spinner",children:(0,S.jsx)("i",{className:"fa-solid fa-spinner fa-spin"})}),(0,S.jsx)("p",{children:"Searching..."})]}):(0,S.jsxs)(S.Fragment,{children:[0(0,S.jsx)(ee,{title:e,type:t,isActive:u===r+E.length,ref:e=>d.current[r+E.length]=e,tooltip:"Filter for this",onClick:()=>x(e,t,n)},"scope-"+r))})}),0(0,S.jsx)(ee,{title:e,packageName:t,isActive:u===r+E.length+m.length,href:n,ref:e=>d.current[r+E.length+m.length]=e,tooltip:"Open this page",icon:"file"},"file-"+r))})})]})]})]})})}),ne=({displayInput:e=!1})=>{let[t,n]=(0,_.useState)(!1),[r,a]=(0,_.useState)("");(0,_.useEffect)(()=>{var e=new URL(window.location.href).searchParams.get("q");e&&a(e)},[]);var l=e=>{e.preventDefault(),n(!0)};return(0,_.useEffect)(()=>{var e=document.getElementById("global-search-form");e&&(e.hidden=!0)},[]),(0,S.jsxs)(S.Fragment,{children:[t&&(0,S.jsx)(te,{isOpen:t,onClose:()=>n(!1)}),e?(0,S.jsxs)("div",{class:"input-group mb-3 mt-sm-3",onClick:l,children:[(0,S.jsx)("input",{autocomplete:"off",class:"form-control shadow-none",id:"globalsearchinput",name:"q",placeholder:"TYPO3 documentation...",type:"text",value:r}),(0,S.jsxs)("button",{class:"btn btn-light",children:[(0,S.jsx)("i",{class:"fa-solid fa-magnifying-glass"})," ",(0,S.jsx)("span",{class:"d-none d-md-inline",children:"Search"})]})]}):(0,S.jsxs)("button",{onClick:l,class:"btn btn-light",children:[(0,S.jsx)("i",{class:"fa-solid fa-magnifying-glass"})," ",(0,S.jsx)("span",{class:"d-none d-md-inline",children:"Search"})]})]})},T="https://docs.typo3.org";document.addEventListener("DOMContentLoaded",()=>{var e=document.getElementById("global-search-root"),e=(e&&(0,g.createRoot)(e).render((0,S.jsx)(ne,{})),document.getElementById("global-search-results"));e&&(0,g.createRoot)(e).render((0,S.jsx)(ne,{displayInput:!0}))}),(()=>{"use strict";const e=Array.from(document.querySelectorAll('.nav-item > [role="tab"]'));function l(e){return e.innerHTML.trim()}document.addEventListener("shown.bs.tab",function(r){const a=l(r.target);e.filter(e=>{var t=l(e)===a,n=e===r.target,e="true"===e.getAttribute("aria-selected");return t&&!n&&!e}).forEach(e=>{new bootstrap.Tab(e).show()})})})(),document.addEventListener("DOMContentLoaded",()=>{const t=document.getElementById("options-toggle"),n=document.getElementById("options-panel");t&&n&&(n.addEventListener("toggle",e=>{t.setAttribute("aria-expanded","open"===e.newState)}),n.addEventListener("focusout",e=>{n.contains(e.relatedTarget)||n.hidePopover()}))});class ToggleSection{TOGGLE_CLASS="toggle-section";TOGGLE_ICON_SHOW=["fa-solid","fa-eye"];TOGGLE_ICON_HIDE=["fa-solid","fa-eye-slash"];TOGGLE_TITLE_SHOW="Show Section";TOGGLE_TITLE_HIDE="Hide section";TOGGLE_ALL_SECTIONS_SELECTOR=".toggle-all-sections";TOGGLE_ALL_SECTIONS_TITLE_SHOW="Expand all sections";TOGGLE_ALL_SECTIONS_TITLE_HIDE="Collapse all sections";sections=[];constructor(){this.sectionHeadings=document.querySelectorAll("section > :is(h2,h3,h4,h5,h6)"),this.sectionHeadings.forEach(e=>{var t=this.createToggleSectionButton(),n=e.querySelector("a.headerlink");const r=e.parentElement;this.sections.push(r),e.insertBefore(t,n),t.addEventListener("click",e=>{e.preventDefault(),this.toggleSection(r)})}),this.sectionHeadings&&document.querySelectorAll("a").forEach(n=>{n.hash&&n.addEventListener("click",e=>{var t=document.querySelector(n.hash);t&&this.changeSection(t,!1)})});const e=document.querySelector(this.TOGGLE_ALL_SECTIONS_SELECTOR);e&&e.addEventListener("click",()=>{e.classList.toggle("hide-all");const t=e.classList.contains("hide-all");e.setAttribute("title",t?this.TOGGLE_ALL_SECTIONS_TITLE_SHOW:this.TOGGLE_ALL_SECTIONS_TITLE_HIDE),e.textContent=t?this.TOGGLE_ALL_SECTIONS_TITLE_SHOW:this.TOGGLE_ALL_SECTIONS_TITLE_HIDE,this.sections.forEach(e=>{this.changeSection(e,t)})})}createToggleSectionButton(){var e=document.createElement("a"),t=(e.classList.add(this.TOGGLE_CLASS),e.setAttribute("href","#"),e.setAttribute("title",this.TOGGLE_TITLE_HIDE),document.createElement("i"));return t.classList.add(...this.TOGGLE_ICON_HIDE),e.appendChild(t),e}toggleSection(e){var t=e.classList.contains("section-collapsed");this.changeSection(e,!t)}changeSection(e,r){r?e.classList.add("section-collapsed"):e.classList.remove("section-collapsed");const a=["H2","H3","H4","H5","H6"];e.querySelectorAll(":scope > *").forEach(e=>{var t,n;a.includes(e.nodeName)?(t=(n=e.querySelector("."+this.TOGGLE_CLASS)).querySelector(":scope > i"),n.setAttribute("title",r?this.TOGGLE_TITLE_SHOW:this.TOGGLE_TITLE_HIDE),t.classList.remove(...t.classList),n=r?this.TOGGLE_ICON_SHOW:this.TOGGLE_ICON_HIDE,t.classList.add(...n)):r?e.classList.add("collapsed-section-content"):e.classList.remove("collapsed-section-content")})}}new ToggleSection,document.addEventListener("DOMContentLoaded",()=>{const n=document.getElementById("languageSelect"),a=document.getElementById("versionSelect");let l=document.URL;var e=a.getAttribute("data-override-url-self"),t=a.getAttribute("data-override-url-proxy"),e=(e&&(l=e),(t||"https://docs.typo3.org/services/versionsJson.php?url=")+encodeURIComponent(l));let r={};function o(e){a.innerHTML="";const n=new Set,r=function(e){e=e.match(/\/([^/]+)\/[a-z]{2}-[a-z]{2}(?:\/|$)/i);return e?e[1]:""}(l)||a.getAttribute("data-current-version");[...e].sort((e,t)=>{e=e.version,t=t.version;if("main"===e||"main"===t)return e===t?0:"main"===e?-1:1;var n=e.split("."),r=t.split("."),a=Math.max(n.length,r.length);for(let e=0;e{var t;n.has(e.version)||((t=document.createElement("option")).value=i(e.url),t.textContent=e.version,e.version===r&&(t.selected=!0),a.appendChild(t),n.add(e.version))}),0===a.options.length&&((e=document.createElement("option")).textContent="No versions available",e.disabled=!0,a.appendChild(e))}function i(e){try{var t=document.createElement("a");return t.href=e,t.href}catch{return e}}fetch(e).then(e=>{if(e.ok)return e.json();throw new Error("Failed to fetch versions")}).then(e=>{e.forEach(e=>{var t=e.language.toLowerCase();r[t]||(r[t]=[]),r[t].push(e)});var e=Object.keys(r),t=function(e){e=e.match(/\/([a-z]{2}-[a-z]{2})\//i);return e?e[1].toLowerCase():""}(l);e.length<=1&&r["en-us"]?o(r["en-us"]):(n.classList.remove("d-none"),a.innerHTML="",e.sort().forEach(e=>{var t=document.createElement("option");t.value=e,t.textContent={"en-us":"English","de-de":"German","fr-fr":"French","ru-ru":"Russian"}[e=e]||e.toUpperCase(),n.appendChild(t)}),e=e.includes(t)?t:"en-us",n.value=e,o(r[e]),n.addEventListener("change",()=>{var e=n.value,e=r[e];e&&0{console.error(e),a.innerHTML=""}),a.addEventListener("change",e=>{e.target.value&&(window.location.href=e.target.value)})}); \ No newline at end of file diff --git a/packages/typo3-docs-theme/tests/js/versions.test.js b/packages/typo3-docs-theme/tests/js/versions.test.js new file mode 100644 index 000000000..4ddd18bd7 --- /dev/null +++ b/packages/typo3-docs-theme/tests/js/versions.test.js @@ -0,0 +1,98 @@ +/** + * Regression test for the version switcher (versions.js). + * + * Two bugs on packages that have both "0.1" and "0.10" (e.g. netresearch/nr-vault): + * 1. Sorting used parseFloat(), so parseFloat("0.10") === 0.1 and "0.10" + * sorted together with the 0.x group at the bottom instead of first. + * 2. Pre-selection used the rendered data-current-version attribute, which is + * "0.1" on the 0.10 page (server-side numeric coercion of "0.10"), so the + * dropdown pre-selected the wrong entry. + * + * The fix sorts each dotted component numerically and derives the active + * version from the page URL. + */ +import { describe, it, expect, beforeEach, vi } from 'vitest'; + +// Importing the script registers its DOMContentLoaded handler once. +import '../../assets/js/versions.js'; + +function versionList(versions) { + return versions.map(version => ({ + version, + language: 'en-us', + url: `/p/netresearch/pkg/${version}/en-us/Index.html`, + })); +} + +// netresearch/nr-vault: has both 0.1 and 0.10. +const NR_VAULT_VERSIONS = versionList(['0.1', '0.10', '0.2', '0.9', 'main']); + +// netresearch/nr-llm: multi-digit minors 0.10, 0.11, 0.12. +const NR_LLM_VERSIONS = versionList( + ['0.9', '0.8', '0.7', '0.6', '0.5', '0.4', '0.3', '0.2', '0.12', '0.11', '0.1', '0.10', 'main']); + +function setup(currentUrl, dataCurrentVersion, versions) { + document.body.replaceChildren(); + + const languageSelect = document.createElement('select'); + languageSelect.id = 'languageSelect'; + + const versionSelect = document.createElement('select'); + versionSelect.id = 'versionSelect'; + versionSelect.setAttribute('data-current-version', dataCurrentVersion); + versionSelect.setAttribute('data-override-url-self', currentUrl); + + document.body.append(languageSelect, versionSelect); + + global.fetch = vi.fn(() => + Promise.resolve({ ok: true, json: () => Promise.resolve(versions) })); + + return versions.length; +} + +async function runAndWait(expectedCount) { + document.dispatchEvent(new Event('DOMContentLoaded')); + await vi.waitFor(() => { + const options = document.querySelectorAll('#versionSelect option'); + expect(options.length).toBe(expectedCount); + }); + return [...document.querySelectorAll('#versionSelect option')]; +} + +describe('version switcher', () => { + it('sorts 0.10 above 0.9 and the 0.x group (not last)', async () => { + // On the 0.10 page the server renders data-current-version="0.1" (coercion). + const count = setup( + 'https://docs.typo3.org/p/netresearch/nr-vault/0.10/en-us/Index.html', '0.1', NR_VAULT_VERSIONS); + const order = (await runAndWait(count)).map(option => option.textContent); + + expect(order).toEqual(['main', '0.10', '0.9', '0.2', '0.1']); + }); + + it('pre-selects the version from the URL (0.10), not the coerced attribute (0.1)', async () => { + const count = setup( + 'https://docs.typo3.org/p/netresearch/nr-vault/0.10/en-us/Index.html', '0.1', NR_VAULT_VERSIONS); + const selected = (await runAndWait(count)).find(option => option.selected); + + expect(selected.textContent).toBe('0.10'); + }); + + it('orders multi-digit minors correctly (nr-llm: 0.12, 0.11, 0.10, 0.9, …)', async () => { + const count = setup( + 'https://docs.typo3.org/p/netresearch/nr-llm/0.12/en-us/Index.html', '0.12', NR_LLM_VERSIONS); + const options = await runAndWait(count); + + expect(options.map(option => option.textContent)).toEqual( + ['main', '0.12', '0.11', '0.10', '0.9', '0.8', '0.7', '0.6', '0.5', '0.4', '0.3', '0.2', '0.1']); + expect(options.find(option => option.selected).textContent).toBe('0.12'); + }); + + it('orders versions with a differing number of components (13.4.21 > 13.4 > 13.3)', async () => { + const versions = versionList(['13.3', '13.4', '13.4.21', '14.0', 'main']); + const count = setup( + 'https://docs.typo3.org/m/typo3/manual/13.4/en-us/Index.html', '13.4', versions); + const order = (await runAndWait(count)).map(option => option.textContent); + + expect(order).toEqual(['main', '14.0', '13.4.21', '13.4', '13.3']); + }); +});