Änderungen von Dokument Art & Research Services Navigator

Zuletzt geändert von Veronika Kocher am Freitag, 18 Sep. 2026 16:47

Von Version 9.4
bearbeitet von Veronika Kocher
am Donnerstag, 03 Sep. 2026 10:09
Änderungskommentar: Es gibt keinen Kommentar für diese Version
Auf Version 9.3
bearbeitet von Veronika Kocher
am Donnerstag, 03 Sep. 2026 9:34
Änderungskommentar: Es gibt keinen Kommentar für diese Version

Zusammenfassung

Details

Seiteneigenschaften
Inhalt
... ... @@ -635,104 +635,15 @@
635 635  {{/info}}
636 636  )))
637 637  
638 -\n\n
639 -\n\n{{html clean="false"}}
638 +{{html clean="false"}}
640 640  <!-- SN_HORIZONTAL_BOX_HARMONIZER_START -->
641 -
642 -<style data-sn-horizontal-box-harmonizer="7">
643 -/* ---------- Typography: never justify headings ---------- */
644 -#xwikicontent h1,
645 -#xwikicontent h2,
646 -#xwikicontent h3,
647 -#xwikicontent h4,
648 -#xwikicontent h5,
649 -#xwikicontent h6,
650 -#xwikicontent .box-title,
651 -#xwikicontent [role="heading"],
652 -#xwikicontent h1 a,
653 -#xwikicontent h2 a,
654 -#xwikicontent h3 a,
655 -#xwikicontent h4 a,
656 -#xwikicontent h5 a,
657 -#xwikicontent h6 a,
658 -#xwikicontent .box-title a,
659 -#xwikicontent [role="heading"] a{
660 - text-align:left!important;
661 - text-justify:auto!important;
662 - word-spacing:normal!important;
663 -}
664 -
665 -/* ---------- Native XWiki semantic callouts ---------- */
666 -#xwikicontent .box.infomessage,
667 -#xwikicontent .infomessage,
668 -#xwikicontent .box.warningmessage,
669 -#xwikicontent .warningmessage,
670 -#xwikicontent .box.successmessage,
671 -#xwikicontent .successmessage,
672 -#xwikicontent .box.errormessage,
673 -#xwikicontent .errormessage,
674 -#xwikicontent [data-sn-callout-harmonized="1"]{
675 - box-sizing:border-box!important;
676 - border-radius:8px!important;
677 - border-top:1px solid rgba(0,0,0,.08)!important;
678 - border-right:1px solid rgba(0,0,0,.08)!important;
679 - border-bottom:1px solid rgba(0,0,0,.08)!important;
680 - border-left:4px solid var(--sn-callout-accent,#21b6d7)!important;
681 - padding:14px 16px!important;
682 - text-align:left!important;
683 - text-justify:auto!important;
684 - word-spacing:normal!important;
685 - box-shadow:none!important;
686 -}
687 -
688 -#xwikicontent .box.infomessage,
689 -#xwikicontent .infomessage{
690 - --sn-callout-accent:#21b6d7;
691 - background:#eaf7fb!important;
692 -}
693 -
694 -#xwikicontent .box.warningmessage,
695 -#xwikicontent .warningmessage{
696 - --sn-callout-accent:#f0aa00;
697 - background:#fff8e7!important;
698 -}
699 -
700 -#xwikicontent .box.successmessage,
701 -#xwikicontent .successmessage{
702 - --sn-callout-accent:#38b76a;
703 - background:#edf9f1!important;
704 -}
705 -
706 -#xwikicontent .box.errormessage,
707 -#xwikicontent .errormessage{
708 - --sn-callout-accent:#e94b13;
709 - background:#fff1ec!important;
710 -}
711 -
712 -#xwikicontent [data-sn-callout-harmonized="1"] > :first-child,
713 -#xwikicontent .infomessage > :first-child,
714 -#xwikicontent .warningmessage > :first-child,
715 -#xwikicontent .successmessage > :first-child,
716 -#xwikicontent .errormessage > :first-child{
717 - margin-top:0!important;
718 -}
719 -
720 -#xwikicontent [data-sn-callout-harmonized="1"] > :last-child,
721 -#xwikicontent .infomessage > :last-child,
722 -#xwikicontent .warningmessage > :last-child,
723 -#xwikicontent .successmessage > :last-child,
724 -#xwikicontent .errormessage > :last-child{
725 - margin-bottom:0!important;
726 -}
727 -
728 -#xwikicontent [data-sn-equal-height-managed],
729 -#xwikicontent [data-sn-first-section-aligned],
730 -#xwikicontent [data-sn-first-content-aligned]{
640 +<style data-sn-horizontal-box-harmonizer="4">
641 +[data-sn-equal-height-managed],
642 +[data-sn-first-section-aligned]{
731 731   box-sizing:border-box;
732 732  }
733 733  </style>
734 -
735 -<script data-sn-horizontal-box-harmonizer="7">
646 +<script data-sn-horizontal-box-harmonizer="4">
736 736  (function(){
737 737   "use strict";
738 738  
... ... @@ -740,10 +740,8 @@
740 740   var ROW_TOLERANCE=6;
741 741   var MIN_W=150;
742 742   var MIN_H=36;
743 - var MAX_PARENT_CHILDREN=18;
654 + var MAX_PARENT_CHILDREN=16;
744 744   var SEARCH_DEPTH=5;
745 - var RESIZE_DEBOUNCE=220;
746 - var MIN_WIDTH_CHANGE=2;
747 747  
748 748   var root=
749 749   document.getElementById("xwikicontent")||
... ... @@ -755,16 +755,10 @@
755 755  
756 756   var heightManaged=new Set();
757 757   var sectionManaged=new Set();
758 - var contentManaged=new Set();
759 -
760 760   var originalMinHeight=new WeakMap();
761 761   var originalMarginTop=new WeakMap();
669 + var raf=0;
762 762  
763 - var resizeTimer=0;
764 - var lastWidth=-1;
765 - var hasApplied=false;
766 - var applying=false;
767 -
768 768   function num(v){
769 769   var n=parseFloat(v);
770 770   return Number.isFinite(n)?n:0;
... ... @@ -791,13 +791,6 @@
791 791   );
792 792   }
793 793  
794 - function normalizeText(text){
795 - return String(text||"")
796 - .replace(/\s+/g," ")
797 - .trim()
798 - .toUpperCase();
799 - }
800 -
801 801   function excluded(el){
802 802   return (
803 803   !el.matches||
... ... @@ -807,84 +807,57 @@
807 807   );
808 808   }
809 809  
810 - /* ---------- Custom coloured callouts ---------- */
811 -
812 - function nativeCallout(el){
706 + function namedLikeBox(el){
813 813   var c=
814 814   typeof el.className==="string"
815 - ? el.className.toLowerCase()
709 + ? el.className
816 816   : "";
817 817  
818 - return (
819 - /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c)
820 - );
712 + return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c);
821 821   }
822 822  
823 - function customCallout(el){
824 - if(!visible(el,220,42)||excluded(el))return false;
715 + function boxLike(el){
716 + if(!visible(el,MIN_W,MIN_H)||excluded(el))return false;
825 825  
718 + var s=getComputedStyle(el);
826 826   var r=el.getBoundingClientRect();
827 827  
828 - if(r.height>520)return false;
721 + var border=
722 + num(s.borderTopWidth)+
723 + num(s.borderRightWidth)+
724 + num(s.borderBottomWidth)+
725 + num(s.borderLeftWidth);
829 829  
830 - var s=getComputedStyle(el);
727 + var radius=Math.max(
728 + num(s.borderTopLeftRadius),
729 + num(s.borderTopRightRadius),
730 + num(s.borderBottomLeftRadius),
731 + num(s.borderBottomRightRadius)
732 + );
831 831  
832 832   var bg=
833 833   s.backgroundColor&&
834 834   s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
835 - s.backgroundColor!=="transparent"&&
836 - s.backgroundColor!=="rgb(255, 255, 255)";
737 + s.backgroundColor!=="transparent";
837 837  
838 - var left=
839 - num(s.borderLeftWidth);
739 + var shadow=
740 + s.boxShadow&&
741 + s.boxShadow!=="none";
840 840  
841 841   return (
842 - bg&&
843 - left>=2
744 + namedLikeBox(el)||
745 + border>=1||
746 + (bg&&radius>=3)||
747 + (shadow&&r.width>=180)
844 844   );
845 845   }
846 846  
847 - function harmoniseCustomCallouts(){
848 - Array.from(
849 - root.querySelectorAll(
850 - "div,aside,section"
851 - )
852 - ).forEach(function(el){
853 - if(nativeCallout(el))return;
854 - if(!customCallout(el))return;
855 -
856 - var s=getComputedStyle(el);
857 -
858 - el.setAttribute(
859 - "data-sn-callout-harmonized",
860 - "1"
861 - );
862 -
863 - if(
864 - s.borderLeftColor
865 - ){
866 - el.style.setProperty(
867 - "--sn-callout-accent",
868 - s.borderLeftColor
869 - );
870 - }
871 - });
872 - }
873 -
874 - /* ---------- Restore only values V7 itself changed ---------- */
875 -
876 876   function saveOriginal(el,map,property){
877 877   if(map.has(el))return;
878 878  
879 879   map.set(el,{
880 - value:
881 - el.style.getPropertyValue(
882 - property
883 - ),
884 - priority:
885 - el.style.getPropertyPriority(
886 - property
887 - )
755 + value:el.style.getPropertyValue(property),
756 + priority:el.style.getPropertyPriority(property)
888 888   });
889 889   }
890 890  
... ... @@ -898,13 +898,11 @@
898 898   original.priority||""
899 899   );
900 900   }else{
901 - el.style.removeProperty(
902 - property
903 - );
770 + el.style.removeProperty(property);
904 904   }
905 905   }
906 906  
907 - function resetRuntime(){
774 + function reset(){
908 908   heightManaged.forEach(function(el){
909 909   restoreOriginal(
910 910   el,
... ... @@ -929,79 +929,16 @@
929 929   );
930 930   });
931 931  
932 - contentManaged.forEach(function(el){
933 - restoreOriginal(
934 - el,
935 - originalMarginTop,
936 - "margin-top"
937 - );
938 -
939 - el.removeAttribute(
940 - "data-sn-first-content-aligned"
941 - );
942 - });
943 -
944 944   heightManaged.clear();
945 945   sectionManaged.clear();
946 - contentManaged.clear();
947 947   }
948 948  
949 - /* ---------- Card detection ---------- */
950 -
951 - function namedLikeBox(el){
952 - var c=
953 - typeof el.className==="string"
954 - ? el.className
955 - : "";
956 -
957 - return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c);
958 - }
959 -
960 - function boxLike(el){
961 - if(
962 - !visible(el,MIN_W,MIN_H)||
963 - excluded(el)
964 - ){
965 - return false;
966 - }
967 -
968 - var s=getComputedStyle(el);
969 - var r=el.getBoundingClientRect();
970 -
971 - var border=
972 - num(s.borderTopWidth)+
973 - num(s.borderRightWidth)+
974 - num(s.borderBottomWidth)+
975 - num(s.borderLeftWidth);
976 -
977 - var radius=Math.max(
978 - num(s.borderTopLeftRadius),
979 - num(s.borderTopRightRadius),
980 - num(s.borderBottomLeftRadius),
981 - num(s.borderBottomRightRadius)
982 - );
983 -
984 - var bg=
985 - s.backgroundColor&&
986 - s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
987 - s.backgroundColor!=="transparent";
988 -
989 - var shadow=
990 - s.boxShadow&&
991 - s.boxShadow!=="none";
992 -
993 - return (
994 - namedLikeBox(el)||
995 - border>=1||
996 - (bg&&radius>=3)||
997 - (shadow&&r.width>=180)
998 - );
999 - }
1000 -
1001 1001   function pickTarget(child){
1002 1002   if(!visible(child,MIN_W,MIN_H))return null;
1003 1003  
1004 - if(boxLike(child))return child;
806 + if(boxLike(child)){
807 + return child;
808 + }
1005 1005  
1006 1006   var cr=child.getBoundingClientRect();
1007 1007   var q=[];
... ... @@ -1063,7 +1063,7 @@
1063 1063   return best;
1064 1064   }
1065 1065  
1066 - function groupRows(entries){
870 + function groupsByVisualRow(entries){
1067 1067   var groups=[];
1068 1068  
1069 1069   entries
... ... @@ -1103,7 +1103,7 @@
1103 1103   return groups;
1104 1104   }
1105 1105  
1106 - function validRow(group){
910 + function validHorizontalGroup(group){
1107 1107   if(group.entries.length<2)return false;
1108 1108  
1109 1109   var lefts=
... ... @@ -1126,26 +1126,25 @@
1126 1126  
1127 1127   if(widths.length<2)return false;
1128 1128  
1129 - var minW=
1130 - Math.min.apply(Math,widths);
933 + var minWidth=Math.min.apply(Math,widths);
934 + var maxWidth=Math.max.apply(Math,widths);
1131 1131  
1132 - var maxW=
1133 - Math.max.apply(Math,widths);
1134 -
1135 1135   return (
1136 - maxW/
1137 - Math.max(1,minW)<=
937 + maxWidth/
938 + Math.max(1,minWidth)<=
1138 1138   1.9
1139 1139   );
1140 1140   }
1141 1141  
1142 - /* ---------- Known ARSN first section bars ---------- */
943 + function normalizeText(text){
944 + return String(text||"")
945 + .replace(/s+/g," ")
946 + .trim()
947 + .toUpperCase();
948 + }
1143 1143  
1144 - function knownBar(el){
1145 - var text=
1146 - normalizeText(
1147 - el.textContent
1148 - );
950 + function exactKnownFirstSection(el){
951 + var text=normalizeText(el.textContent);
1149 1149  
1150 1150   return (
1151 1151   text==="HIER STARTEN"||
... ... @@ -1159,274 +1159,150 @@
1159 1159   );
1160 1160   }
1161 1161  
1162 - function findKnownBar(card){
1163 - var cardRect=
1164 - card.getBoundingClientRect();
965 + function visuallyFirstSectionBar(card){
966 + var cardRect=card.getBoundingClientRect();
967 + var candidates=[];
1165 1165  
1166 - var rows=
1167 - Array.from(
1168 - card.querySelectorAll(
1169 - "div,p,span,strong,h3,h4,h5,h6"
1170 - )
969 + Array.from(
970 + card.querySelectorAll(
971 + "div,p,span,strong,h3,h4,h5,h6"
1171 1171   )
1172 - .filter(function(el){
1173 - if(!visible(el,20,6))return false;
1174 - if(!knownBar(el))return false;
973 + ).forEach(function(el){
974 + if(!visible(el,20,6))return;
975 + if(excluded(el))return;
1175 1175  
1176 - var r=el.getBoundingClientRect();
977 + var r=el.getBoundingClientRect();
978 + var relativeTop=r.top-cardRect.top;
1177 1177  
1178 - var rel=
1179 - r.top-
1180 - cardRect.top;
980 + if(
981 + relativeTop<45||
982 + relativeTop>cardRect.height*.72
983 + ){
984 + return;
985 + }
1181 1181  
1182 - return (
1183 - rel>=35&&
1184 - rel<=
1185 - cardRect.height*.75
1186 - );
1187 - })
1188 - .sort(function(a,b){
1189 - return (
1190 - a.getBoundingClientRect().top-
1191 - b.getBoundingClientRect().top
1192 - );
1193 - });
987 + var widthRatio=
988 + r.width/
989 + Math.max(1,cardRect.width);
1194 1194  
1195 - return rows[0]||null;
1196 - }
991 + if(
992 + widthRatio<.55||
993 + widthRatio>1.05||
994 + r.height>54
995 + ){
996 + return;
997 + }
1197 1197  
1198 - function alignKnownBars(cards){
1199 - var rows=
1200 - cards.map(function(card){
1201 - var bar=
1202 - findKnownBar(card);
999 + var text=normalizeText(el.textContent);
1203 1203  
1204 - if(!bar)return null;
1001 + if(
1002 + text.length<2||
1003 + text.length>90
1004 + ){
1005 + return;
1006 + }
1205 1205  
1206 - var cr=
1207 - card.getBoundingClientRect();
1008 + var style=getComputedStyle(el);
1208 1208  
1209 - var br=
1210 - bar.getBoundingClientRect();
1010 + var bg=
1011 + style.backgroundColor&&
1012 + style.backgroundColor!=="rgba(0, 0, 0, 0)"&&
1013 + style.backgroundColor!=="transparent";
1211 1211  
1212 - return {
1213 - bar:bar,
1214 - relativeTop:
1215 - br.top-
1216 - cr.top
1217 - };
1218 - });
1015 + var border=
1016 + num(style.borderTopWidth)+
1017 + num(style.borderRightWidth)+
1018 + num(style.borderBottomWidth)+
1019 + num(style.borderLeftWidth);
1219 1219  
1220 - if(
1221 - rows.some(function(row){
1222 - return !row;
1223 - })
1224 - ){
1225 - return false;
1226 - }
1021 + var mostlyUpper=
1022 + text.length>=3&&
1023 + text===text.toUpperCase();
1227 1227  
1228 - var target=
1229 - Math.max.apply(
1230 - Math,
1231 - rows.map(function(row){
1232 - return row.relativeTop;
1233 - })
1234 - );
1025 + var exact=exactKnownFirstSection(el);
1235 1235  
1236 - rows.forEach(function(row){
1237 - var delta=
1238 - target-
1239 - row.relativeTop;
1027 + var score=
1028 + (exact?20:0)+
1029 + (bg?6:0)+
1030 + (border>=1?2:0)+
1031 + (mostlyUpper?4:0)+
1032 + (num(style.fontWeight)>=600?1:0);
1240 1240  
1241 - if(delta<=1)return;
1034 + if(score>=7){
1035 + candidates.push({
1036 + el:el,
1037 + relativeTop:relativeTop,
1038 + width:r.width,
1039 + score:score
1040 + });
1041 + }
1042 + });
1242 1242  
1243 - saveOriginal(
1244 - row.bar,
1245 - originalMarginTop,
1246 - "margin-top"
1247 - );
1044 + if(!candidates.length)return null;
1248 1248  
1249 - var current=
1250 - num(
1251 - getComputedStyle(
1252 - row.bar
1253 - ).marginTop
1254 - );
1046 + candidates.sort(function(a,b){
1047 + if(a.score!==b.score)return b.score-a.score;
1048 + if(Math.abs(a.relativeTop-b.relativeTop)>3){
1049 + return a.relativeTop-b.relativeTop;
1050 + }
1051 + return b.width-a.width;
1052 + });
1255 1255  
1256 - row.bar.style.setProperty(
1257 - "margin-top",
1258 - (
1259 - current+
1260 - delta
1261 - )+"px"
1262 - );
1263 -
1264 - row.bar.setAttribute(
1265 - "data-sn-first-section-aligned",
1266 - "7"
1267 - );
1268 -
1269 - sectionManaged.add(
1270 - row.bar
1271 - );
1054 + // For exact known labels always prefer the earliest exact match.
1055 + var exacts=candidates.filter(function(c){
1056 + return exactKnownFirstSection(c.el);
1272 1272   });
1273 1273  
1274 - return true;
1275 - }
1276 -
1277 - /* ---------- Generic first content after heading ---------- */
1278 -
1279 - function heading(card){
1280 - var cr=
1281 - card.getBoundingClientRect();
1282 -
1283 - var rows=
1284 - Array.from(
1285 - card.querySelectorAll(
1286 - "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]"
1287 - )
1288 - )
1289 - .filter(function(el){
1290 - if(!visible(el,20,8))return false;
1291 -
1292 - var r=el.getBoundingClientRect();
1293 -
1294 - var rel=
1295 - r.top-
1296 - cr.top;
1297 -
1298 - return (
1299 - rel>=0&&
1300 - rel<=
1301 - Math.min(
1302 - 160,
1303 - cr.height*.45
1304 - )
1305 - );
1306 - })
1307 - .sort(function(a,b){
1308 - return (
1309 - a.getBoundingClientRect().top-
1310 - b.getBoundingClientRect().top
1311 - );
1059 + if(exacts.length){
1060 + exacts.sort(function(a,b){
1061 + return a.relativeTop-b.relativeTop||b.width-a.width;
1312 1312   });
1063 + return exacts[0].el;
1064 + }
1313 1313  
1314 - return rows[0]||null;
1315 - }
1066 + // Generic fallback: earliest high-confidence bar.
1067 + candidates.sort(function(a,b){
1068 + return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width;
1069 + });
1316 1316  
1317 - function firstContent(card,h){
1318 - if(!h)return null;
1319 -
1320 - var cr=
1321 - card.getBoundingClientRect();
1322 -
1323 - var hr=
1324 - h.getBoundingClientRect();
1325 -
1326 - var rows=
1327 - Array.from(
1328 - card.querySelectorAll(
1329 - "p,div,ul,ol,dl,blockquote"
1330 - )
1331 - )
1332 - .filter(function(el){
1333 - if(
1334 - el===h||
1335 - h.contains(el)||
1336 - el.contains(h)
1337 - ){
1338 - return false;
1339 - }
1340 -
1341 - if(!visible(el,30,8))return false;
1342 -
1343 - var text=
1344 - String(el.textContent||"")
1345 - .replace(/\s+/g," ")
1346 - .trim();
1347 -
1348 - if(text.length<3)return false;
1349 -
1350 - var r=el.getBoundingClientRect();
1351 -
1352 - if(
1353 - r.top<
1354 - hr.bottom-1
1355 - ){
1356 - return false;
1357 - }
1358 -
1359 - if(
1360 - r.top-cr.top>
1361 - Math.min(
1362 - 280,
1363 - cr.height*.65
1364 - )
1365 - ){
1366 - return false;
1367 - }
1368 -
1369 - if(
1370 - r.height>
1371 - cr.height*.58
1372 - ){
1373 - return false;
1374 - }
1375 -
1376 - return true;
1377 - })
1378 - .sort(function(a,b){
1379 - var ar=
1380 - a.getBoundingClientRect();
1381 -
1382 - var br=
1383 - b.getBoundingClientRect();
1384 -
1385 - return (
1386 - ar.top-br.top||
1387 - ar.height-br.height
1388 - );
1389 - });
1390 -
1391 - return rows[0]||null;
1071 + return candidates[0].el;
1392 1392   }
1393 1393  
1394 - function alignGeneric(cards){
1074 + function alignFirstSections(cards){
1395 1395   var rows=
1396 1396   cards.map(function(card){
1397 - var h=
1398 - heading(card);
1077 + var bar=
1078 + visuallyFirstSectionBar(card);
1399 1399  
1400 - var c=
1401 - firstContent(
1402 - card,
1403 - h
1404 - );
1080 + if(!bar)return null;
1405 1405  
1406 - if(!h||!c)return null;
1407 -
1408 - var cr=
1082 + var cardRect=
1409 1409   card.getBoundingClientRect();
1410 1410  
1411 - var rr=
1412 - c.getBoundingClientRect();
1085 + var barRect=
1086 + bar.getBoundingClientRect();
1413 1413  
1414 1414   return {
1415 - content:c,
1089 + card:card,
1090 + bar:bar,
1416 1416   relativeTop:
1417 - rr.top-cr.top
1092 + barRect.top-cardRect.top,
1093 + label:
1094 + normalizeText(bar.textContent)
1418 1418   };
1419 1419   });
1420 1420  
1098 + // Conservative: only align when every card in the horizontal row
1099 + // has a reliable first section bar.
1421 1421   if(
1422 1422   rows.some(function(row){
1423 1423   return !row;
1424 1424   })
1425 1425   ){
1426 - return false;
1105 + return null;
1427 1427   }
1428 1428  
1429 - var target=
1108 + var targetTop=
1430 1430   Math.max.apply(
1431 1431   Math,
1432 1432   rows.map(function(row){
... ... @@ -1436,47 +1436,54 @@
1436 1436  
1437 1437   rows.forEach(function(row){
1438 1438   var delta=
1439 - target-
1118 + targetTop-
1440 1440   row.relativeTop;
1441 1441  
1442 1442   if(delta<=1)return;
1443 1443  
1444 1444   saveOriginal(
1445 - row.content,
1124 + row.bar,
1446 1446   originalMarginTop,
1447 1447   "margin-top"
1448 1448   );
1449 1449  
1450 - var current=
1129 + var currentMargin=
1451 1451   num(
1452 1452   getComputedStyle(
1453 - row.content
1132 + row.bar
1454 1454   ).marginTop
1455 1455   );
1456 1456  
1457 - row.content.style.setProperty(
1136 + row.bar.style.setProperty(
1458 1458   "margin-top",
1459 1459   (
1460 - current+
1139 + currentMargin+
1461 1461   delta
1462 1462   )+"px"
1463 1463   );
1464 1464  
1465 - row.content.setAttribute(
1466 - "data-sn-first-content-aligned",
1467 - "7"
1144 + row.bar.setAttribute(
1145 + "data-sn-first-section-aligned",
1146 + "4"
1468 1468   );
1469 1469  
1470 - contentManaged.add(
1471 - row.content
1472 - );
1149 + sectionManaged.add(row.bar);
1473 1473   });
1474 1474  
1475 - return true;
1152 + return {
1153 + labels:
1154 + rows.map(function(row){
1155 + return row.label;
1156 + }),
1157 + targetRelativeTop:
1158 + Math.round(targetTop)
1159 + };
1476 1476   }
1477 1477  
1478 - function applyRow(group){
1479 - if(!validRow(group))return false;
1162 + function applyGroup(group){
1163 + if(!validHorizontalGroup(group)){
1164 + return null;
1165 + }
1480 1480  
1481 1481   var cards=[];
1482 1482   var seen=new Set();
... ... @@ -1488,30 +1488,25 @@
1488 1488   }
1489 1489   });
1490 1490  
1491 - if(cards.length<2)return false;
1177 + if(cards.length<2)return null;
1492 1492  
1493 - if(
1494 - !alignKnownBars(cards)
1495 - ){
1496 - alignGeneric(cards);
1497 - }
1179 + var firstSection=
1180 + alignFirstSections(cards);
1498 1498  
1182 + // The section alignment can change natural card heights.
1183 + // Measure only afterwards.
1499 1499   var maxHeight=0;
1500 1500  
1501 1501   cards.forEach(function(card){
1502 - maxHeight=
1503 - Math.max(
1504 - maxHeight,
1505 - card.getBoundingClientRect().height
1506 - );
1187 + maxHeight=Math.max(
1188 + maxHeight,
1189 + card.getBoundingClientRect().height
1190 + );
1507 1507   });
1508 1508  
1509 - maxHeight=
1510 - Math.ceil(
1511 - maxHeight
1512 - );
1193 + maxHeight=Math.ceil(maxHeight);
1513 1513  
1514 - if(maxHeight<MIN_H)return false;
1195 + if(maxHeight<MIN_H)return null;
1515 1515  
1516 1516   cards.forEach(function(card){
1517 1517   saveOriginal(
... ... @@ -1527,228 +1527,125 @@
1527 1527  
1528 1528   card.setAttribute(
1529 1529   "data-sn-equal-height-managed",
1530 - "7"
1211 + "4"
1531 1531   );
1532 1532  
1533 1533   heightManaged.add(card);
1534 1534   });
1535 1535  
1536 - return true;
1217 + return {
1218 + cards:cards.length,
1219 + height:maxHeight,
1220 + firstSection:firstSection
1221 + };
1537 1537   }
1538 1538  
1539 - function calculate(force){
1540 - if(applying)return;
1224 + function run(){
1225 + reset();
1541 1541  
1542 - var width=
1543 - Math.round(
1544 - window.innerWidth
1545 - );
1546 -
1547 - var meaningful=
1548 - lastWidth<0||
1549 - Math.abs(
1550 - width-
1551 - lastWidth
1552 - )>=
1553 - MIN_WIDTH_CHANGE;
1554 -
1555 1555   if(
1556 - hasApplied&&
1557 - !force&&
1558 - !meaningful
1228 + window.innerWidth<=MOBILE_MAX
1559 1559   ){
1560 1560   return;
1561 1561   }
1562 1562  
1563 - applying=true;
1233 + var parents=
1234 + Array.from(
1235 + root.querySelectorAll("*")
1236 + ).filter(function(parent){
1237 + return (
1238 + visible(parent,MIN_W,MIN_H)&&
1239 + parent.children&&
1240 + parent.children.length>=2&&
1241 + parent.children.length<=MAX_PARENT_CHILDREN
1242 + );
1243 + });
1564 1564  
1565 - try{
1566 - harmoniseCustomCallouts();
1245 + var touched=new Set();
1567 1567  
1568 - if(width<=MOBILE_MAX){
1569 - if(hasApplied){
1570 - resetRuntime();
1571 - }
1247 + parents.forEach(function(parent){
1248 + var entries=[];
1572 1572  
1573 - hasApplied=false;
1574 - lastWidth=width;
1575 - return;
1576 - }
1250 + Array.from(parent.children).forEach(function(child){
1251 + if(!visible(child,MIN_W,MIN_H))return;
1577 1577  
1578 - if(hasApplied){
1579 - resetRuntime();
1580 - }
1253 + var target=pickTarget(child);
1254 + if(!target)return;
1581 1581  
1582 - var parents=
1583 - Array.from(
1584 - root.querySelectorAll("*")
1585 - )
1586 - .filter(function(parent){
1587 - return (
1588 - visible(
1589 - parent,
1590 - MIN_W,
1591 - MIN_H
1592 - )&&
1593 - parent.children&&
1594 - parent.children.length>=2&&
1595 - parent.children.length<=
1596 - MAX_PARENT_CHILDREN
1597 - );
1256 + entries.push({
1257 + target:target,
1258 + rect:target.getBoundingClientRect()
1598 1598   });
1260 + });
1599 1599  
1600 - var touched=
1601 - new Set();
1262 + if(entries.length<2)return;
1602 1602  
1603 - parents.forEach(function(parent){
1604 - var entries=[];
1264 + groupsByVisualRow(entries).forEach(function(group){
1265 + if(!validHorizontalGroup(group))return;
1605 1605  
1606 - Array.from(
1607 - parent.children
1608 - ).forEach(function(child){
1609 - if(
1610 - !visible(
1611 - child,
1612 - MIN_W,
1613 - MIN_H
1614 - )
1615 - ){
1616 - return;
1617 - }
1618 -
1619 - var target=
1620 - pickTarget(child);
1621 -
1622 - if(!target)return;
1623 -
1624 - entries.push({
1625 - target:target,
1626 - rect:
1627 - target.getBoundingClientRect()
1267 + var signature=
1268 + group.entries.map(function(entry){
1269 + return entry.target;
1628 1628   });
1629 - });
1630 1630  
1631 - groupRows(
1632 - entries
1633 - ).forEach(function(group){
1634 - if(!validRow(group))return;
1272 + if(
1273 + !signature.some(function(el){
1274 + return !touched.has(el);
1275 + })
1276 + ){
1277 + return;
1278 + }
1635 1635  
1636 - var targets=
1637 - group.entries.map(function(entry){
1638 - return entry.target;
1639 - });
1280 + var applied=
1281 + applyGroup(group);
1640 1640  
1641 - if(
1642 - !targets.some(function(el){
1643 - return !touched.has(el);
1644 - })
1645 - ){
1646 - return;
1647 - }
1648 -
1649 - if(applyRow(group)){
1650 - targets.forEach(function(el){
1651 - touched.add(el);
1652 - });
1653 - }
1654 - });
1283 + if(applied){
1284 + signature.forEach(function(el){
1285 + touched.add(el);
1286 + });
1287 + }
1655 1655   });
1656 -
1657 - lastWidth=width;
1658 - hasApplied=true;
1659 - }finally{
1660 - applying=false;
1661 - }
1289 + });
1662 1662   }
1663 1663  
1664 - function resize(){
1665 - if(resizeTimer){
1666 - clearTimeout(
1667 - resizeTimer
1668 - );
1669 - }
1292 + function schedule(){
1293 + if(raf)cancelAnimationFrame(raf);
1670 1670  
1671 - resizeTimer=
1672 - setTimeout(
1673 - function(){
1674 - resizeTimer=0;
1675 - calculate(false);
1676 - },
1677 - RESIZE_DEBOUNCE
1678 - );
1295 + raf=requestAnimationFrame(function(){
1296 + raf=requestAnimationFrame(function(){
1297 + raf=0;
1298 + run();
1299 + });
1300 + });
1679 1679   }
1680 1680  
1681 - function waitImages(){
1682 - var images=
1683 - Array.from(
1684 - root.querySelectorAll("img")
1685 - );
1303 + addEventListener("resize",schedule,{passive:true});
1304 + addEventListener("load",schedule,{once:true});
1686 1686  
1687 - return Promise.all(
1688 - images.map(function(img){
1689 - if(img.complete){
1690 - return Promise.resolve();
1691 - }
1306 + root.addEventListener(
1307 + "click",
1308 + function(){
1309 + setTimeout(schedule,0);
1310 + },
1311 + true
1312 + );
1692 1692  
1693 - return new Promise(function(resolve){
1694 - var done=function(){
1695 - resolve();
1696 - };
1697 -
1698 - img.addEventListener(
1699 - "load",
1700 - done,
1701 - {once:true}
1702 - );
1703 -
1704 - img.addEventListener(
1705 - "error",
1706 - done,
1707 - {once:true}
1708 - );
1709 - });
1710 - })
1711 - );
1712 - }
1713 -
1714 - async function initial(){
1715 - if(
1716 - document.readyState!==
1717 - "complete"
1718 - ){
1719 - await new Promise(function(resolve){
1720 - addEventListener(
1721 - "load",
1722 - resolve,
1723 - {once:true}
1724 - );
1725 - });
1314 + Array.from(root.querySelectorAll("img")).forEach(function(img){
1315 + if(!img.complete){
1316 + img.addEventListener("load",schedule,{once:true});
1726 1726   }
1318 + });
1727 1727  
1728 - if(
1729 - document.fonts&&
1730 - document.fonts.ready
1731 - ){
1732 - try{
1733 - await document.fonts.ready;
1734 - }catch(error){}
1735 - }
1736 -
1737 - await waitImages();
1738 -
1739 - // Exactly one initial layout calculation.
1740 - calculate(true);
1320 + if(document.fonts&&document.fonts.ready){
1321 + document.fonts.ready.then(schedule);
1741 1741   }
1742 1742  
1743 - addEventListener(
1744 - "resize",
1745 - resize,
1746 - {passive:true}
1747 - );
1748 -
1749 - initial();
1324 + schedule();
1325 + setTimeout(schedule,200);
1326 + setTimeout(schedule,650);
1327 + setTimeout(schedule,1400);
1750 1750  })();
1751 1751  </script>
1752 -
1753 1753  <!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->
1754 -{{/html}}\n
1331 +{{/html}}
1332 +