Changes for page Prepare

Last modified by Veronika Kocher on Friday, 18 Sep 2026 16:47

From version 29.7
edited by Veronika Kocher
on Thursday, 03 Sep 2026 9:18
Change comment: There is no comment for this version
To version 29.8
edited by Veronika Kocher
on Thursday, 03 Sep 2026 9:34
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -487,20 +487,22 @@
487 487  
488 488  {{html clean="false"}}
489 489  <!-- SN_HORIZONTAL_BOX_HARMONIZER_START -->
490 -<style data-sn-horizontal-box-harmonizer="1">
491 -[data-sn-equal-height-managed="1"]{box-sizing:border-box}
492 -@media(max-width:767px){
493 - [data-sn-equal-height-managed="1"]{min-height:0!important}
490 +<style data-sn-horizontal-box-harmonizer="4">
491 +[data-sn-equal-height-managed],
492 +[data-sn-first-section-aligned]{
493 + box-sizing:border-box;
494 494  }
495 495  </style>
496 -<script data-sn-horizontal-box-harmonizer="1">
496 +<script data-sn-horizontal-box-harmonizer="4">
497 497  (function(){
498 498   "use strict";
499 499  
500 500   var MOBILE_MAX=767;
501 - var ROW_TOLERANCE=4;
501 + var ROW_TOLERANCE=6;
502 502   var MIN_W=150;
503 503   var MIN_H=36;
504 + var MAX_PARENT_CHILDREN=16;
505 + var SEARCH_DEPTH=5;
504 504  
505 505   var root=
506 506   document.getElementById("xwikicontent")||
... ... @@ -510,7 +510,10 @@
510 510  
511 511   if(!root)return;
512 512  
513 - var managed=new Set();
515 + var heightManaged=new Set();
516 + var sectionManaged=new Set();
517 + var originalMinHeight=new WeakMap();
518 + var originalMarginTop=new WeakMap();
514 514   var raf=0;
515 515  
516 516   function num(v){
... ... @@ -518,64 +518,181 @@
518 518   return Number.isFinite(n)?n:0;
519 519   }
520 520  
521 - function visible(el){
526 + function visible(el,minW,minH){
522 522   if(!el||el.nodeType!==1)return false;
528 +
523 523   var s=getComputedStyle(el);
524 - if(s.display==="none"||s.visibility==="hidden"||Number(s.opacity)===0)return false;
530 +
531 + if(
532 + s.display==="none"||
533 + s.visibility==="hidden"||
534 + Number(s.opacity)===0
535 + ){
536 + return false;
537 + }
538 +
525 525   var r=el.getBoundingClientRect();
526 - return r.width>=MIN_W&&r.height>=MIN_H;
540 +
541 + return (
542 + r.width>=(minW||1)&&
543 + r.height>=(minH||1)
544 + );
527 527   }
528 528  
529 529   function excluded(el){
530 - return !el.matches||el.matches(
531 - "button,input,select,textarea,option,label,img,svg,canvas,table,thead,tbody,tfoot,tr,td,th,summary,script,style,noscript,template,.btn,.chip,[role=button]"
548 + return (
549 + !el.matches||
550 + el.matches(
551 + "button,input,select,textarea,option,label,img,svg,canvas,table,thead,tbody,tfoot,tr,td,th,summary,script,style,noscript,template,.btn,.chip,[role=button]"
552 + )
532 532   );
533 533   }
534 534  
535 535   function namedLikeBox(el){
536 - var c=typeof el.className==="string"?el.className:"";
537 - return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c);
557 + var c=
558 + typeof el.className==="string"
559 + ? el.className
560 + : "";
561 +
562 + return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c);
538 538   }
539 539  
540 540   function boxLike(el){
541 - if(!visible(el)||excluded(el))return false;
566 + if(!visible(el,MIN_W,MIN_H)||excluded(el))return false;
567 +
542 542   var s=getComputedStyle(el);
543 543   var r=el.getBoundingClientRect();
570 +
544 544   var border=
545 - num(s.borderTopWidth)+num(s.borderRightWidth)+
546 - num(s.borderBottomWidth)+num(s.borderLeftWidth);
572 + num(s.borderTopWidth)+
573 + num(s.borderRightWidth)+
574 + num(s.borderBottomWidth)+
575 + num(s.borderLeftWidth);
576 +
547 547   var radius=Math.max(
548 - num(s.borderTopLeftRadius),num(s.borderTopRightRadius),
549 - num(s.borderBottomLeftRadius),num(s.borderBottomRightRadius)
578 + num(s.borderTopLeftRadius),
579 + num(s.borderTopRightRadius),
580 + num(s.borderBottomLeftRadius),
581 + num(s.borderBottomRightRadius)
550 550   );
551 - var bg=s.backgroundColor&&s.backgroundColor!=="rgba(0, 0, 0, 0)"&&s.backgroundColor!=="transparent";
552 - var shadow=s.boxShadow&&s.boxShadow!=="none";
553 - return namedLikeBox(el)||border>=1||(bg&&radius>=3)||(shadow&&r.width>=180);
583 +
584 + var bg=
585 + s.backgroundColor&&
586 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
587 + s.backgroundColor!=="transparent";
588 +
589 + var shadow=
590 + s.boxShadow&&
591 + s.boxShadow!=="none";
592 +
593 + return (
594 + namedLikeBox(el)||
595 + border>=1||
596 + (bg&&radius>=3)||
597 + (shadow&&r.width>=180)
598 + );
554 554   }
555 555  
601 + function saveOriginal(el,map,property){
602 + if(map.has(el))return;
603 +
604 + map.set(el,{
605 + value:el.style.getPropertyValue(property),
606 + priority:el.style.getPropertyPriority(property)
607 + });
608 + }
609 +
610 + function restoreOriginal(el,map,property){
611 + var original=map.get(el);
612 +
613 + if(original&&original.value){
614 + el.style.setProperty(
615 + property,
616 + original.value,
617 + original.priority||""
618 + );
619 + }else{
620 + el.style.removeProperty(property);
621 + }
622 + }
623 +
624 + function reset(){
625 + heightManaged.forEach(function(el){
626 + restoreOriginal(
627 + el,
628 + originalMinHeight,
629 + "min-height"
630 + );
631 +
632 + el.removeAttribute(
633 + "data-sn-equal-height-managed"
634 + );
635 + });
636 +
637 + sectionManaged.forEach(function(el){
638 + restoreOriginal(
639 + el,
640 + originalMarginTop,
641 + "margin-top"
642 + );
643 +
644 + el.removeAttribute(
645 + "data-sn-first-section-aligned"
646 + );
647 + });
648 +
649 + heightManaged.clear();
650 + sectionManaged.clear();
651 + }
652 +
556 556   function pickTarget(child){
557 - if(!visible(child))return null;
558 - if(boxLike(child))return child;
654 + if(!visible(child,MIN_W,MIN_H))return null;
559 559  
656 + if(boxLike(child)){
657 + return child;
658 + }
659 +
560 560   var cr=child.getBoundingClientRect();
561 561   var q=[];
662 +
562 562   Array.from(child.children||[]).forEach(function(el){
563 563   q.push({el:el,depth:1});
564 564   });
565 565  
566 - var best=null,bestScore=-1;
667 + var best=null;
668 + var bestScore=-Infinity;
567 567  
568 568   while(q.length){
569 569   var item=q.shift();
570 570   var el=item.el;
571 - if(!visible(el))continue;
572 572  
674 + if(!visible(el,MIN_W,MIN_H))continue;
675 +
573 573   var r=el.getBoundingClientRect();
574 574  
575 - if(r.width>=cr.width*.72&&boxLike(el)){
576 - var widthFit=1-Math.min(1,Math.abs(cr.width-r.width)/Math.max(1,cr.width));
577 - var topFit=1-Math.min(1,Math.abs(cr.top-r.top)/80);
578 - var score=widthFit*5+topFit*2+Math.min(r.height,600)/600;
678 + if(
679 + r.width>=cr.width*.68&&
680 + boxLike(el)
681 + ){
682 + var widthFit=
683 + 1-Math.min(
684 + 1,
685 + Math.abs(cr.width-r.width)/
686 + Math.max(1,cr.width)
687 + );
688 +
689 + var topFit=
690 + 1-Math.min(
691 + 1,
692 + Math.abs(cr.top-r.top)/100
693 + );
694 +
695 + var score=
696 + widthFit*6+
697 + topFit*3+
698 + Math.min(r.height,900)/450+
699 + Math.min(r.width*r.height,900000)/900000;
700 +
579 579   if(score>bestScore){
580 580   best=el;
581 581   bestScore=score;
... ... @@ -582,9 +582,12 @@
582 582   }
583 583   }
584 584  
585 - if(item.depth<3){
707 + if(item.depth<SEARCH_DEPTH){
586 586   Array.from(el.children||[]).forEach(function(next){
587 - q.push({el:next,depth:item.depth+1});
709 + q.push({
710 + el:next,
711 + depth:item.depth+1
712 + });
588 588   });
589 589   }
590 590   }
... ... @@ -592,61 +592,392 @@
592 592   return best;
593 593   }
594 594  
595 - function reset(){
596 - managed.forEach(function(el){
597 - el.style.removeProperty("min-height");
598 - el.removeAttribute("data-sn-equal-height-managed");
599 - });
600 - managed.clear();
720 + function groupsByVisualRow(entries){
721 + var groups=[];
722 +
723 + entries
724 + .slice()
725 + .sort(function(a,b){
726 + return (
727 + a.rect.top-b.rect.top||
728 + a.rect.left-b.rect.left
729 + );
730 + })
731 + .forEach(function(entry){
732 + var group=null;
733 +
734 + for(var i=0;i<groups.length;i++){
735 + if(
736 + Math.abs(
737 + groups[i].top-
738 + entry.rect.top
739 + )<=ROW_TOLERANCE
740 + ){
741 + group=groups[i];
742 + break;
743 + }
744 + }
745 +
746 + if(!group){
747 + group={
748 + top:entry.rect.top,
749 + entries:[]
750 + };
751 + groups.push(group);
752 + }
753 +
754 + group.entries.push(entry);
755 + });
756 +
757 + return groups;
601 601   }
602 602  
603 - function horizontalLayout(parent){
604 - if(!visible(parent))return false;
605 - var s=getComputedStyle(parent);
606 - if(s.display==="grid"||s.display==="inline-grid")return true;
607 - if(s.display==="flex"||s.display==="inline-flex"){
608 - return s.flexDirection==="row"||s.flexDirection==="row-reverse";
760 + function validHorizontalGroup(group){
761 + if(group.entries.length<2)return false;
762 +
763 + var lefts=
764 + group.entries
765 + .map(function(e){return e.rect.left;})
766 + .sort(function(a,b){return a-b;});
767 +
768 + if(
769 + lefts[lefts.length-1]-
770 + lefts[0]<
771 + 80
772 + ){
773 + return false;
609 609   }
610 - return false;
775 +
776 + var widths=
777 + group.entries
778 + .map(function(e){return e.rect.width;})
779 + .filter(function(w){return w>0;});
780 +
781 + if(widths.length<2)return false;
782 +
783 + var minWidth=Math.min.apply(Math,widths);
784 + var maxWidth=Math.max.apply(Math,widths);
785 +
786 + return (
787 + maxWidth/
788 + Math.max(1,minWidth)<=
789 + 1.9
790 + );
611 611   }
612 612  
613 - function rowGroups(entries){
614 - var groups=[];
615 - entries.slice().sort(function(a,b){
616 - return a.rect.top-b.rect.top||a.rect.left-b.rect.left;
617 - }).forEach(function(entry){
618 - var group=null;
619 - for(var i=0;i<groups.length;i++){
620 - if(Math.abs(groups[i].top-entry.rect.top)<=ROW_TOLERANCE){
621 - group=groups[i];
622 - break;
623 - }
793 + function normalizeText(text){
794 + return String(text||"")
795 + .replace(/s+/g," ")
796 + .trim()
797 + .toUpperCase();
798 + }
799 +
800 + function exactKnownFirstSection(el){
801 + var text=normalizeText(el.textContent);
802 +
803 + return (
804 + text==="HIER STARTEN"||
805 + text==="ORGANISIEREN & DOKUMENTIEREN"||
806 + text==="ENTSCHEIDEN"||
807 + text==="START HERE"||
808 + text==="GET STARTED"||
809 + text==="ORGANISE & DOCUMENT"||
810 + text==="ORGANIZE & DOCUMENT"||
811 + text==="DECIDE"
812 + );
813 + }
814 +
815 + function visuallyFirstSectionBar(card){
816 + var cardRect=card.getBoundingClientRect();
817 + var candidates=[];
818 +
819 + Array.from(
820 + card.querySelectorAll(
821 + "div,p,span,strong,h3,h4,h5,h6"
822 + )
823 + ).forEach(function(el){
824 + if(!visible(el,20,6))return;
825 + if(excluded(el))return;
826 +
827 + var r=el.getBoundingClientRect();
828 + var relativeTop=r.top-cardRect.top;
829 +
830 + if(
831 + relativeTop<45||
832 + relativeTop>cardRect.height*.72
833 + ){
834 + return;
624 624   }
625 - if(!group){
626 - group={top:entry.rect.top,entries:[]};
627 - groups.push(group);
836 +
837 + var widthRatio=
838 + r.width/
839 + Math.max(1,cardRect.width);
840 +
841 + if(
842 + widthRatio<.55||
843 + widthRatio>1.05||
844 + r.height>54
845 + ){
846 + return;
628 628   }
629 - group.entries.push(entry);
848 +
849 + var text=normalizeText(el.textContent);
850 +
851 + if(
852 + text.length<2||
853 + text.length>90
854 + ){
855 + return;
856 + }
857 +
858 + var style=getComputedStyle(el);
859 +
860 + var bg=
861 + style.backgroundColor&&
862 + style.backgroundColor!=="rgba(0, 0, 0, 0)"&&
863 + style.backgroundColor!=="transparent";
864 +
865 + var border=
866 + num(style.borderTopWidth)+
867 + num(style.borderRightWidth)+
868 + num(style.borderBottomWidth)+
869 + num(style.borderLeftWidth);
870 +
871 + var mostlyUpper=
872 + text.length>=3&&
873 + text===text.toUpperCase();
874 +
875 + var exact=exactKnownFirstSection(el);
876 +
877 + var score=
878 + (exact?20:0)+
879 + (bg?6:0)+
880 + (border>=1?2:0)+
881 + (mostlyUpper?4:0)+
882 + (num(style.fontWeight)>=600?1:0);
883 +
884 + if(score>=7){
885 + candidates.push({
886 + el:el,
887 + relativeTop:relativeTop,
888 + width:r.width,
889 + score:score
890 + });
891 + }
630 630   });
631 - return groups;
893 +
894 + if(!candidates.length)return null;
895 +
896 + candidates.sort(function(a,b){
897 + if(a.score!==b.score)return b.score-a.score;
898 + if(Math.abs(a.relativeTop-b.relativeTop)>3){
899 + return a.relativeTop-b.relativeTop;
900 + }
901 + return b.width-a.width;
902 + });
903 +
904 + // For exact known labels always prefer the earliest exact match.
905 + var exacts=candidates.filter(function(c){
906 + return exactKnownFirstSection(c.el);
907 + });
908 +
909 + if(exacts.length){
910 + exacts.sort(function(a,b){
911 + return a.relativeTop-b.relativeTop||b.width-a.width;
912 + });
913 + return exacts[0].el;
914 + }
915 +
916 + // Generic fallback: earliest high-confidence bar.
917 + candidates.sort(function(a,b){
918 + return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width;
919 + });
920 +
921 + return candidates[0].el;
632 632   }
633 633  
924 + function alignFirstSections(cards){
925 + var rows=
926 + cards.map(function(card){
927 + var bar=
928 + visuallyFirstSectionBar(card);
929 +
930 + if(!bar)return null;
931 +
932 + var cardRect=
933 + card.getBoundingClientRect();
934 +
935 + var barRect=
936 + bar.getBoundingClientRect();
937 +
938 + return {
939 + card:card,
940 + bar:bar,
941 + relativeTop:
942 + barRect.top-cardRect.top,
943 + label:
944 + normalizeText(bar.textContent)
945 + };
946 + });
947 +
948 + // Conservative: only align when every card in the horizontal row
949 + // has a reliable first section bar.
950 + if(
951 + rows.some(function(row){
952 + return !row;
953 + })
954 + ){
955 + return null;
956 + }
957 +
958 + var targetTop=
959 + Math.max.apply(
960 + Math,
961 + rows.map(function(row){
962 + return row.relativeTop;
963 + })
964 + );
965 +
966 + rows.forEach(function(row){
967 + var delta=
968 + targetTop-
969 + row.relativeTop;
970 +
971 + if(delta<=1)return;
972 +
973 + saveOriginal(
974 + row.bar,
975 + originalMarginTop,
976 + "margin-top"
977 + );
978 +
979 + var currentMargin=
980 + num(
981 + getComputedStyle(
982 + row.bar
983 + ).marginTop
984 + );
985 +
986 + row.bar.style.setProperty(
987 + "margin-top",
988 + (
989 + currentMargin+
990 + delta
991 + )+"px"
992 + );
993 +
994 + row.bar.setAttribute(
995 + "data-sn-first-section-aligned",
996 + "4"
997 + );
998 +
999 + sectionManaged.add(row.bar);
1000 + });
1001 +
1002 + return {
1003 + labels:
1004 + rows.map(function(row){
1005 + return row.label;
1006 + }),
1007 + targetRelativeTop:
1008 + Math.round(targetTop)
1009 + };
1010 + }
1011 +
1012 + function applyGroup(group){
1013 + if(!validHorizontalGroup(group)){
1014 + return null;
1015 + }
1016 +
1017 + var cards=[];
1018 + var seen=new Set();
1019 +
1020 + group.entries.forEach(function(entry){
1021 + if(!seen.has(entry.target)){
1022 + seen.add(entry.target);
1023 + cards.push(entry.target);
1024 + }
1025 + });
1026 +
1027 + if(cards.length<2)return null;
1028 +
1029 + var firstSection=
1030 + alignFirstSections(cards);
1031 +
1032 + // The section alignment can change natural card heights.
1033 + // Measure only afterwards.
1034 + var maxHeight=0;
1035 +
1036 + cards.forEach(function(card){
1037 + maxHeight=Math.max(
1038 + maxHeight,
1039 + card.getBoundingClientRect().height
1040 + );
1041 + });
1042 +
1043 + maxHeight=Math.ceil(maxHeight);
1044 +
1045 + if(maxHeight<MIN_H)return null;
1046 +
1047 + cards.forEach(function(card){
1048 + saveOriginal(
1049 + card,
1050 + originalMinHeight,
1051 + "min-height"
1052 + );
1053 +
1054 + card.style.setProperty(
1055 + "min-height",
1056 + maxHeight+"px"
1057 + );
1058 +
1059 + card.setAttribute(
1060 + "data-sn-equal-height-managed",
1061 + "4"
1062 + );
1063 +
1064 + heightManaged.add(card);
1065 + });
1066 +
1067 + return {
1068 + cards:cards.length,
1069 + height:maxHeight,
1070 + firstSection:firstSection
1071 + };
1072 + }
1073 +
634 634   function run(){
635 635   reset();
636 636  
637 - if(window.innerWidth<=MOBILE_MAX)return;
1077 + if(
1078 + window.innerWidth<=MOBILE_MAX
1079 + ){
1080 + return;
1081 + }
638 638  
639 - var parents=Array.from(root.querySelectorAll("*")).filter(function(el){
640 - return el.children&&el.children.length>=2&&horizontalLayout(el);
641 - });
1083 + var parents=
1084 + Array.from(
1085 + root.querySelectorAll("*")
1086 + ).filter(function(parent){
1087 + return (
1088 + visible(parent,MIN_W,MIN_H)&&
1089 + parent.children&&
1090 + parent.children.length>=2&&
1091 + parent.children.length<=MAX_PARENT_CHILDREN
1092 + );
1093 + });
642 642  
1095 + var touched=new Set();
1096 +
643 643   parents.forEach(function(parent){
644 644   var entries=[];
645 645  
646 646   Array.from(parent.children).forEach(function(child){
647 - if(!visible(child))return;
1101 + if(!visible(child,MIN_W,MIN_H))return;
1102 +
648 648   var target=pickTarget(child);
649 649   if(!target)return;
1105 +
650 650   entries.push({
651 651   target:target,
652 652   rect:target.getBoundingClientRect()
... ... @@ -655,37 +655,30 @@
655 655  
656 656   if(entries.length<2)return;
657 657  
658 - rowGroups(entries).forEach(function(group){
659 - if(group.entries.length<2)return;
1114 + groupsByVisualRow(entries).forEach(function(group){
1115 + if(!validHorizontalGroup(group))return;
660 660  
661 - var lefts=group.entries.map(function(e){return e.rect.left;}).sort(function(a,b){return a-b;});
662 - if(lefts[lefts.length-1]-lefts[0]<80)return;
1117 + var signature=
1118 + group.entries.map(function(entry){
1119 + return entry.target;
1120 + });
663 663  
664 - var targets=[];
665 - var seen=new Set();
1122 + if(
1123 + !signature.some(function(el){
1124 + return !touched.has(el);
1125 + })
1126 + ){
1127 + return;
1128 + }
666 666  
667 - group.entries.forEach(function(e){
668 - if(!seen.has(e.target)){
669 - seen.add(e.target);
670 - targets.push(e.target);
671 - }
672 - });
1130 + var applied=
1131 + applyGroup(group);
673 673  
674 - if(targets.length<2)return;
675 -
676 - var max=0;
677 - targets.forEach(function(el){
678 - max=Math.max(max,el.getBoundingClientRect().height);
679 - });
680 -
681 - max=Math.ceil(max);
682 - if(max<MIN_H)return;
683 -
684 - targets.forEach(function(el){
685 - el.style.setProperty("min-height",max+"px");
686 - el.setAttribute("data-sn-equal-height-managed","1");
687 - managed.add(el);
688 - });
1133 + if(applied){
1134 + signature.forEach(function(el){
1135 + touched.add(el);
1136 + });
1137 + }
689 689   });
690 690   });
691 691   }
... ... @@ -692,6 +692,7 @@
692 692  
693 693   function schedule(){
694 694   if(raf)cancelAnimationFrame(raf);
1144 +
695 695   raf=requestAnimationFrame(function(){
696 696   raf=requestAnimationFrame(function(){
697 697   raf=0;
... ... @@ -703,17 +703,28 @@
703 703   addEventListener("resize",schedule,{passive:true});
704 704   addEventListener("load",schedule,{once:true});
705 705  
706 - root.addEventListener("click",function(){
707 - setTimeout(schedule,0);
708 - },true);
1156 + root.addEventListener(
1157 + "click",
1158 + function(){
1159 + setTimeout(schedule,0);
1160 + },
1161 + true
1162 + );
709 709  
1164 + Array.from(root.querySelectorAll("img")).forEach(function(img){
1165 + if(!img.complete){
1166 + img.addEventListener("load",schedule,{once:true});
1167 + }
1168 + });
1169 +
710 710   if(document.fonts&&document.fonts.ready){
711 711   document.fonts.ready.then(schedule);
712 712   }
713 713  
714 714   schedule();
715 - setTimeout(schedule,250);
716 - setTimeout(schedule,900);
1175 + setTimeout(schedule,200);
1176 + setTimeout(schedule,650);
1177 + setTimeout(schedule,1400);
717 717  })();
718 718  </script>
719 719  <!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->