Änderungen von Dokument Get Started

Zuletzt geändert von Veronika Kocher am Freitag, 04 Sep. 2026 14:45

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

Zusammenfassung

Details

Seiteneigenschaften
Inhalt
... ... @@ -387,15 +387,104 @@
387 387  </div>
388 388  {{/html}}
389 389  
390 -{{html clean="false"}}
390 +\n\n
391 +\n\n{{html clean="false"}}
391 391  <!-- SN_HORIZONTAL_BOX_HARMONIZER_START -->
392 -<style data-sn-horizontal-box-harmonizer="4">
393 -[data-sn-equal-height-managed],
394 -[data-sn-first-section-aligned]{
393 +
394 +<style data-sn-horizontal-box-harmonizer="7">
395 +/* ---------- Typography: never justify headings ---------- */
396 +#xwikicontent h1,
397 +#xwikicontent h2,
398 +#xwikicontent h3,
399 +#xwikicontent h4,
400 +#xwikicontent h5,
401 +#xwikicontent h6,
402 +#xwikicontent .box-title,
403 +#xwikicontent [role="heading"],
404 +#xwikicontent h1 a,
405 +#xwikicontent h2 a,
406 +#xwikicontent h3 a,
407 +#xwikicontent h4 a,
408 +#xwikicontent h5 a,
409 +#xwikicontent h6 a,
410 +#xwikicontent .box-title a,
411 +#xwikicontent [role="heading"] a{
412 + text-align:left!important;
413 + text-justify:auto!important;
414 + word-spacing:normal!important;
415 +}
416 +
417 +/* ---------- Native XWiki semantic callouts ---------- */
418 +#xwikicontent .box.infomessage,
419 +#xwikicontent .infomessage,
420 +#xwikicontent .box.warningmessage,
421 +#xwikicontent .warningmessage,
422 +#xwikicontent .box.successmessage,
423 +#xwikicontent .successmessage,
424 +#xwikicontent .box.errormessage,
425 +#xwikicontent .errormessage,
426 +#xwikicontent [data-sn-callout-harmonized="1"]{
427 + box-sizing:border-box!important;
428 + border-radius:8px!important;
429 + border-top:1px solid rgba(0,0,0,.08)!important;
430 + border-right:1px solid rgba(0,0,0,.08)!important;
431 + border-bottom:1px solid rgba(0,0,0,.08)!important;
432 + border-left:4px solid var(--sn-callout-accent,#21b6d7)!important;
433 + padding:14px 16px!important;
434 + text-align:left!important;
435 + text-justify:auto!important;
436 + word-spacing:normal!important;
437 + box-shadow:none!important;
438 +}
439 +
440 +#xwikicontent .box.infomessage,
441 +#xwikicontent .infomessage{
442 + --sn-callout-accent:#21b6d7;
443 + background:#eaf7fb!important;
444 +}
445 +
446 +#xwikicontent .box.warningmessage,
447 +#xwikicontent .warningmessage{
448 + --sn-callout-accent:#f0aa00;
449 + background:#fff8e7!important;
450 +}
451 +
452 +#xwikicontent .box.successmessage,
453 +#xwikicontent .successmessage{
454 + --sn-callout-accent:#38b76a;
455 + background:#edf9f1!important;
456 +}
457 +
458 +#xwikicontent .box.errormessage,
459 +#xwikicontent .errormessage{
460 + --sn-callout-accent:#e94b13;
461 + background:#fff1ec!important;
462 +}
463 +
464 +#xwikicontent [data-sn-callout-harmonized="1"] > :first-child,
465 +#xwikicontent .infomessage > :first-child,
466 +#xwikicontent .warningmessage > :first-child,
467 +#xwikicontent .successmessage > :first-child,
468 +#xwikicontent .errormessage > :first-child{
469 + margin-top:0!important;
470 +}
471 +
472 +#xwikicontent [data-sn-callout-harmonized="1"] > :last-child,
473 +#xwikicontent .infomessage > :last-child,
474 +#xwikicontent .warningmessage > :last-child,
475 +#xwikicontent .successmessage > :last-child,
476 +#xwikicontent .errormessage > :last-child{
477 + margin-bottom:0!important;
478 +}
479 +
480 +#xwikicontent [data-sn-equal-height-managed],
481 +#xwikicontent [data-sn-first-section-aligned],
482 +#xwikicontent [data-sn-first-content-aligned]{
395 395   box-sizing:border-box;
396 396  }
397 397  </style>
398 -<script data-sn-horizontal-box-harmonizer="4">
486 +
487 +<script data-sn-horizontal-box-harmonizer="7">
399 399  (function(){
400 400   "use strict";
401 401  
... ... @@ -403,8 +403,10 @@
403 403   var ROW_TOLERANCE=6;
404 404   var MIN_W=150;
405 405   var MIN_H=36;
406 - var MAX_PARENT_CHILDREN=16;
495 + var MAX_PARENT_CHILDREN=18;
407 407   var SEARCH_DEPTH=5;
497 + var RESIZE_DEBOUNCE=220;
498 + var MIN_WIDTH_CHANGE=2;
408 408  
409 409   var root=
410 410   document.getElementById("xwikicontent")||
... ... @@ -416,10 +416,16 @@
416 416  
417 417   var heightManaged=new Set();
418 418   var sectionManaged=new Set();
510 + var contentManaged=new Set();
511 +
419 419   var originalMinHeight=new WeakMap();
420 420   var originalMarginTop=new WeakMap();
421 - var raf=0;
422 422  
515 + var resizeTimer=0;
516 + var lastWidth=-1;
517 + var hasApplied=false;
518 + var applying=false;
519 +
423 423   function num(v){
424 424   var n=parseFloat(v);
425 425   return Number.isFinite(n)?n:0;
... ... @@ -446,6 +446,13 @@
446 446   );
447 447   }
448 448  
546 + function normalizeText(text){
547 + return String(text||"")
548 + .replace(/\s+/g," ")
549 + .trim()
550 + .toUpperCase();
551 + }
552 +
449 449   function excluded(el){
450 450   return (
451 451   !el.matches||
... ... @@ -455,57 +455,84 @@
455 455   );
456 456   }
457 457  
458 - function namedLikeBox(el){
562 + /* ---------- Custom coloured callouts ---------- */
563 +
564 + function nativeCallout(el){
459 459   var c=
460 460   typeof el.className==="string"
461 - ? el.className
567 + ? el.className.toLowerCase()
462 462   : "";
463 463  
464 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c);
570 + return (
571 + /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c)
572 + );
465 465   }
466 466  
467 - function boxLike(el){
468 - if(!visible(el,MIN_W,MIN_H)||excluded(el))return false;
575 + function customCallout(el){
576 + if(!visible(el,220,42)||excluded(el))return false;
469 469  
470 - var s=getComputedStyle(el);
471 471   var r=el.getBoundingClientRect();
472 472  
473 - var border=
474 - num(s.borderTopWidth)+
475 - num(s.borderRightWidth)+
476 - num(s.borderBottomWidth)+
477 - num(s.borderLeftWidth);
580 + if(r.height>520)return false;
478 478  
479 - var radius=Math.max(
480 - num(s.borderTopLeftRadius),
481 - num(s.borderTopRightRadius),
482 - num(s.borderBottomLeftRadius),
483 - num(s.borderBottomRightRadius)
484 - );
582 + var s=getComputedStyle(el);
485 485  
486 486   var bg=
487 487   s.backgroundColor&&
488 488   s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
489 - s.backgroundColor!=="transparent";
587 + s.backgroundColor!=="transparent"&&
588 + s.backgroundColor!=="rgb(255, 255, 255)";
490 490  
491 - var shadow=
492 - s.boxShadow&&
493 - s.boxShadow!=="none";
590 + var left=
591 + num(s.borderLeftWidth);
494 494  
495 495   return (
496 - namedLikeBox(el)||
497 - border>=1||
498 - (bg&&radius>=3)||
499 - (shadow&&r.width>=180)
594 + bg&&
595 + left>=2
500 500   );
501 501   }
502 502  
599 + function harmoniseCustomCallouts(){
600 + Array.from(
601 + root.querySelectorAll(
602 + "div,aside,section"
603 + )
604 + ).forEach(function(el){
605 + if(nativeCallout(el))return;
606 + if(!customCallout(el))return;
607 +
608 + var s=getComputedStyle(el);
609 +
610 + el.setAttribute(
611 + "data-sn-callout-harmonized",
612 + "1"
613 + );
614 +
615 + if(
616 + s.borderLeftColor
617 + ){
618 + el.style.setProperty(
619 + "--sn-callout-accent",
620 + s.borderLeftColor
621 + );
622 + }
623 + });
624 + }
625 +
626 + /* ---------- Restore only values V7 itself changed ---------- */
627 +
503 503   function saveOriginal(el,map,property){
504 504   if(map.has(el))return;
505 505  
506 506   map.set(el,{
507 - value:el.style.getPropertyValue(property),
508 - priority:el.style.getPropertyPriority(property)
632 + value:
633 + el.style.getPropertyValue(
634 + property
635 + ),
636 + priority:
637 + el.style.getPropertyPriority(
638 + property
639 + )
509 509   });
510 510   }
511 511  
... ... @@ -519,11 +519,13 @@
519 519   original.priority||""
520 520   );
521 521   }else{
522 - el.style.removeProperty(property);
653 + el.style.removeProperty(
654 + property
655 + );
523 523   }
524 524   }
525 525  
526 - function reset(){
659 + function resetRuntime(){
527 527   heightManaged.forEach(function(el){
528 528   restoreOriginal(
529 529   el,
... ... @@ -548,16 +548,79 @@
548 548   );
549 549   });
550 550  
684 + contentManaged.forEach(function(el){
685 + restoreOriginal(
686 + el,
687 + originalMarginTop,
688 + "margin-top"
689 + );
690 +
691 + el.removeAttribute(
692 + "data-sn-first-content-aligned"
693 + );
694 + });
695 +
551 551   heightManaged.clear();
552 552   sectionManaged.clear();
698 + contentManaged.clear();
553 553   }
554 554  
701 + /* ---------- Card detection ---------- */
702 +
703 + function namedLikeBox(el){
704 + var c=
705 + typeof el.className==="string"
706 + ? el.className
707 + : "";
708 +
709 + return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c);
710 + }
711 +
712 + function boxLike(el){
713 + if(
714 + !visible(el,MIN_W,MIN_H)||
715 + excluded(el)
716 + ){
717 + return false;
718 + }
719 +
720 + var s=getComputedStyle(el);
721 + var r=el.getBoundingClientRect();
722 +
723 + var border=
724 + num(s.borderTopWidth)+
725 + num(s.borderRightWidth)+
726 + num(s.borderBottomWidth)+
727 + num(s.borderLeftWidth);
728 +
729 + var radius=Math.max(
730 + num(s.borderTopLeftRadius),
731 + num(s.borderTopRightRadius),
732 + num(s.borderBottomLeftRadius),
733 + num(s.borderBottomRightRadius)
734 + );
735 +
736 + var bg=
737 + s.backgroundColor&&
738 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
739 + s.backgroundColor!=="transparent";
740 +
741 + var shadow=
742 + s.boxShadow&&
743 + s.boxShadow!=="none";
744 +
745 + return (
746 + namedLikeBox(el)||
747 + border>=1||
748 + (bg&&radius>=3)||
749 + (shadow&&r.width>=180)
750 + );
751 + }
752 +
555 555   function pickTarget(child){
556 556   if(!visible(child,MIN_W,MIN_H))return null;
557 557  
558 - if(boxLike(child)){
559 - return child;
560 - }
756 + if(boxLike(child))return child;
561 561  
562 562   var cr=child.getBoundingClientRect();
563 563   var q=[];
... ... @@ -619,7 +619,7 @@
619 619   return best;
620 620   }
621 621  
622 - function groupsByVisualRow(entries){
818 + function groupRows(entries){
623 623   var groups=[];
624 624  
625 625   entries
... ... @@ -659,7 +659,7 @@
659 659   return groups;
660 660   }
661 661  
662 - function validHorizontalGroup(group){
858 + function validRow(group){
663 663   if(group.entries.length<2)return false;
664 664  
665 665   var lefts=
... ... @@ -682,25 +682,26 @@
682 682  
683 683   if(widths.length<2)return false;
684 684  
685 - var minWidth=Math.min.apply(Math,widths);
686 - var maxWidth=Math.max.apply(Math,widths);
881 + var minW=
882 + Math.min.apply(Math,widths);
687 687  
884 + var maxW=
885 + Math.max.apply(Math,widths);
886 +
688 688   return (
689 - maxWidth/
690 - Math.max(1,minWidth)<=
888 + maxW/
889 + Math.max(1,minW)<=
691 691   1.9
692 692   );
693 693   }
694 694  
695 - function normalizeText(text){
696 - return String(text||"")
697 - .replace(/s+/g," ")
698 - .trim()
699 - .toUpperCase();
700 - }
894 + /* ---------- Known ARSN first section bars ---------- */
701 701  
702 - function exactKnownFirstSection(el){
703 - var text=normalizeText(el.textContent);
896 + function knownBar(el){
897 + var text=
898 + normalizeText(
899 + el.textContent
900 + );
704 704  
705 705   return (
706 706   text==="HIER STARTEN"||
... ... @@ -714,150 +714,274 @@
714 714   );
715 715   }
716 716  
717 - function visuallyFirstSectionBar(card){
718 - var cardRect=card.getBoundingClientRect();
719 - var candidates=[];
914 + function findKnownBar(card){
915 + var cardRect=
916 + card.getBoundingClientRect();
720 720  
721 - Array.from(
722 - card.querySelectorAll(
723 - "div,p,span,strong,h3,h4,h5,h6"
918 + var rows=
919 + Array.from(
920 + card.querySelectorAll(
921 + "div,p,span,strong,h3,h4,h5,h6"
922 + )
724 724   )
725 - ).forEach(function(el){
726 - if(!visible(el,20,6))return;
727 - if(excluded(el))return;
924 + .filter(function(el){
925 + if(!visible(el,20,6))return false;
926 + if(!knownBar(el))return false;
728 728  
729 - var r=el.getBoundingClientRect();
730 - var relativeTop=r.top-cardRect.top;
928 + var r=el.getBoundingClientRect();
731 731  
732 - if(
733 - relativeTop<45||
734 - relativeTop>cardRect.height*.72
735 - ){
736 - return;
737 - }
930 + var rel=
931 + r.top-
932 + cardRect.top;
738 738  
739 - var widthRatio=
740 - r.width/
741 - Math.max(1,cardRect.width);
934 + return (
935 + rel>=35&&
936 + rel<=
937 + cardRect.height*.75
938 + );
939 + })
940 + .sort(function(a,b){
941 + return (
942 + a.getBoundingClientRect().top-
943 + b.getBoundingClientRect().top
944 + );
945 + });
742 742  
743 - if(
744 - widthRatio<.55||
745 - widthRatio>1.05||
746 - r.height>54
747 - ){
748 - return;
749 - }
947 + return rows[0]||null;
948 + }
750 750  
751 - var text=normalizeText(el.textContent);
950 + function alignKnownBars(cards){
951 + var rows=
952 + cards.map(function(card){
953 + var bar=
954 + findKnownBar(card);
752 752  
753 - if(
754 - text.length<2||
755 - text.length>90
756 - ){
757 - return;
758 - }
956 + if(!bar)return null;
759 759  
760 - var style=getComputedStyle(el);
958 + var cr=
959 + card.getBoundingClientRect();
761 761  
762 - var bg=
763 - style.backgroundColor&&
764 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&&
765 - style.backgroundColor!=="transparent";
961 + var br=
962 + bar.getBoundingClientRect();
766 766  
767 - var border=
768 - num(style.borderTopWidth)+
769 - num(style.borderRightWidth)+
770 - num(style.borderBottomWidth)+
771 - num(style.borderLeftWidth);
964 + return {
965 + bar:bar,
966 + relativeTop:
967 + br.top-
968 + cr.top
969 + };
970 + });
772 772  
773 - var mostlyUpper=
774 - text.length>=3&&
775 - text===text.toUpperCase();
972 + if(
973 + rows.some(function(row){
974 + return !row;
975 + })
976 + ){
977 + return false;
978 + }
776 776  
777 - var exact=exactKnownFirstSection(el);
980 + var target=
981 + Math.max.apply(
982 + Math,
983 + rows.map(function(row){
984 + return row.relativeTop;
985 + })
986 + );
778 778  
779 - var score=
780 - (exact?20:0)+
781 - (bg?6:0)+
782 - (border>=1?2:0)+
783 - (mostlyUpper?4:0)+
784 - (num(style.fontWeight)>=600?1:0);
988 + rows.forEach(function(row){
989 + var delta=
990 + target-
991 + row.relativeTop;
785 785  
786 - if(score>=7){
787 - candidates.push({
788 - el:el,
789 - relativeTop:relativeTop,
790 - width:r.width,
791 - score:score
792 - });
793 - }
794 - });
993 + if(delta<=1)return;
795 795  
796 - if(!candidates.length)return null;
995 + saveOriginal(
996 + row.bar,
997 + originalMarginTop,
998 + "margin-top"
999 + );
797 797  
798 - candidates.sort(function(a,b){
799 - if(a.score!==b.score)return b.score-a.score;
800 - if(Math.abs(a.relativeTop-b.relativeTop)>3){
801 - return a.relativeTop-b.relativeTop;
802 - }
803 - return b.width-a.width;
804 - });
1001 + var current=
1002 + num(
1003 + getComputedStyle(
1004 + row.bar
1005 + ).marginTop
1006 + );
805 805  
806 - // For exact known labels always prefer the earliest exact match.
807 - var exacts=candidates.filter(function(c){
808 - return exactKnownFirstSection(c.el);
1008 + row.bar.style.setProperty(
1009 + "margin-top",
1010 + (
1011 + current+
1012 + delta
1013 + )+"px"
1014 + );
1015 +
1016 + row.bar.setAttribute(
1017 + "data-sn-first-section-aligned",
1018 + "7"
1019 + );
1020 +
1021 + sectionManaged.add(
1022 + row.bar
1023 + );
809 809   });
810 810  
811 - if(exacts.length){
812 - exacts.sort(function(a,b){
813 - return a.relativeTop-b.relativeTop||b.width-a.width;
1026 + return true;
1027 + }
1028 +
1029 + /* ---------- Generic first content after heading ---------- */
1030 +
1031 + function heading(card){
1032 + var cr=
1033 + card.getBoundingClientRect();
1034 +
1035 + var rows=
1036 + Array.from(
1037 + card.querySelectorAll(
1038 + "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]"
1039 + )
1040 + )
1041 + .filter(function(el){
1042 + if(!visible(el,20,8))return false;
1043 +
1044 + var r=el.getBoundingClientRect();
1045 +
1046 + var rel=
1047 + r.top-
1048 + cr.top;
1049 +
1050 + return (
1051 + rel>=0&&
1052 + rel<=
1053 + Math.min(
1054 + 160,
1055 + cr.height*.45
1056 + )
1057 + );
1058 + })
1059 + .sort(function(a,b){
1060 + return (
1061 + a.getBoundingClientRect().top-
1062 + b.getBoundingClientRect().top
1063 + );
814 814   });
815 - return exacts[0].el;
816 - }
817 817  
818 - // Generic fallback: earliest high-confidence bar.
819 - candidates.sort(function(a,b){
820 - return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width;
821 - });
1066 + return rows[0]||null;
1067 + }
822 822  
823 - return candidates[0].el;
1069 + function firstContent(card,h){
1070 + if(!h)return null;
1071 +
1072 + var cr=
1073 + card.getBoundingClientRect();
1074 +
1075 + var hr=
1076 + h.getBoundingClientRect();
1077 +
1078 + var rows=
1079 + Array.from(
1080 + card.querySelectorAll(
1081 + "p,div,ul,ol,dl,blockquote"
1082 + )
1083 + )
1084 + .filter(function(el){
1085 + if(
1086 + el===h||
1087 + h.contains(el)||
1088 + el.contains(h)
1089 + ){
1090 + return false;
1091 + }
1092 +
1093 + if(!visible(el,30,8))return false;
1094 +
1095 + var text=
1096 + String(el.textContent||"")
1097 + .replace(/\s+/g," ")
1098 + .trim();
1099 +
1100 + if(text.length<3)return false;
1101 +
1102 + var r=el.getBoundingClientRect();
1103 +
1104 + if(
1105 + r.top<
1106 + hr.bottom-1
1107 + ){
1108 + return false;
1109 + }
1110 +
1111 + if(
1112 + r.top-cr.top>
1113 + Math.min(
1114 + 280,
1115 + cr.height*.65
1116 + )
1117 + ){
1118 + return false;
1119 + }
1120 +
1121 + if(
1122 + r.height>
1123 + cr.height*.58
1124 + ){
1125 + return false;
1126 + }
1127 +
1128 + return true;
1129 + })
1130 + .sort(function(a,b){
1131 + var ar=
1132 + a.getBoundingClientRect();
1133 +
1134 + var br=
1135 + b.getBoundingClientRect();
1136 +
1137 + return (
1138 + ar.top-br.top||
1139 + ar.height-br.height
1140 + );
1141 + });
1142 +
1143 + return rows[0]||null;
824 824   }
825 825  
826 - function alignFirstSections(cards){
1146 + function alignGeneric(cards){
827 827   var rows=
828 828   cards.map(function(card){
829 - var bar=
830 - visuallyFirstSectionBar(card);
1149 + var h=
1150 + heading(card);
831 831  
832 - if(!bar)return null;
1152 + var c=
1153 + firstContent(
1154 + card,
1155 + h
1156 + );
833 833  
834 - var cardRect=
1158 + if(!h||!c)return null;
1159 +
1160 + var cr=
835 835   card.getBoundingClientRect();
836 836  
837 - var barRect=
838 - bar.getBoundingClientRect();
1163 + var rr=
1164 + c.getBoundingClientRect();
839 839  
840 840   return {
841 - card:card,
842 - bar:bar,
1167 + content:c,
843 843   relativeTop:
844 - barRect.top-cardRect.top,
845 - label:
846 - normalizeText(bar.textContent)
1169 + rr.top-cr.top
847 847   };
848 848   });
849 849  
850 - // Conservative: only align when every card in the horizontal row
851 - // has a reliable first section bar.
852 852   if(
853 853   rows.some(function(row){
854 854   return !row;
855 855   })
856 856   ){
857 - return null;
1178 + return false;
858 858   }
859 859  
860 - var targetTop=
1181 + var target=
861 861   Math.max.apply(
862 862   Math,
863 863   rows.map(function(row){
... ... @@ -867,54 +867,47 @@
867 867  
868 868   rows.forEach(function(row){
869 869   var delta=
870 - targetTop-
1191 + target-
871 871   row.relativeTop;
872 872  
873 873   if(delta<=1)return;
874 874  
875 875   saveOriginal(
876 - row.bar,
1197 + row.content,
877 877   originalMarginTop,
878 878   "margin-top"
879 879   );
880 880  
881 - var currentMargin=
1202 + var current=
882 882   num(
883 883   getComputedStyle(
884 - row.bar
1205 + row.content
885 885   ).marginTop
886 886   );
887 887  
888 - row.bar.style.setProperty(
1209 + row.content.style.setProperty(
889 889   "margin-top",
890 890   (
891 - currentMargin+
1212 + current+
892 892   delta
893 893   )+"px"
894 894   );
895 895  
896 - row.bar.setAttribute(
897 - "data-sn-first-section-aligned",
898 - "4"
1217 + row.content.setAttribute(
1218 + "data-sn-first-content-aligned",
1219 + "7"
899 899   );
900 900  
901 - sectionManaged.add(row.bar);
1222 + contentManaged.add(
1223 + row.content
1224 + );
902 902   });
903 903  
904 - return {
905 - labels:
906 - rows.map(function(row){
907 - return row.label;
908 - }),
909 - targetRelativeTop:
910 - Math.round(targetTop)
911 - };
1227 + return true;
912 912   }
913 913  
914 - function applyGroup(group){
915 - if(!validHorizontalGroup(group)){
916 - return null;
917 - }
1230 + function applyRow(group){
1231 + if(!validRow(group))return false;
918 918  
919 919   var cards=[];
920 920   var seen=new Set();
... ... @@ -926,25 +926,30 @@
926 926   }
927 927   });
928 928  
929 - if(cards.length<2)return null;
1243 + if(cards.length<2)return false;
930 930  
931 - var firstSection=
932 - alignFirstSections(cards);
1245 + if(
1246 + !alignKnownBars(cards)
1247 + ){
1248 + alignGeneric(cards);
1249 + }
933 933  
934 - // The section alignment can change natural card heights.
935 - // Measure only afterwards.
936 936   var maxHeight=0;
937 937  
938 938   cards.forEach(function(card){
939 - maxHeight=Math.max(
940 - maxHeight,
941 - card.getBoundingClientRect().height
942 - );
1254 + maxHeight=
1255 + Math.max(
1256 + maxHeight,
1257 + card.getBoundingClientRect().height
1258 + );
943 943   });
944 944  
945 - maxHeight=Math.ceil(maxHeight);
1261 + maxHeight=
1262 + Math.ceil(
1263 + maxHeight
1264 + );
946 946  
947 - if(maxHeight<MIN_H)return null;
1266 + if(maxHeight<MIN_H)return false;
948 948  
949 949   cards.forEach(function(card){
950 950   saveOriginal(
... ... @@ -960,125 +960,228 @@
960 960  
961 961   card.setAttribute(
962 962   "data-sn-equal-height-managed",
963 - "4"
1282 + "7"
964 964   );
965 965  
966 966   heightManaged.add(card);
967 967   });
968 968  
969 - return {
970 - cards:cards.length,
971 - height:maxHeight,
972 - firstSection:firstSection
973 - };
1288 + return true;
974 974   }
975 975  
976 - function run(){
977 - reset();
1291 + function calculate(force){
1292 + if(applying)return;
978 978  
1294 + var width=
1295 + Math.round(
1296 + window.innerWidth
1297 + );
1298 +
1299 + var meaningful=
1300 + lastWidth<0||
1301 + Math.abs(
1302 + width-
1303 + lastWidth
1304 + )>=
1305 + MIN_WIDTH_CHANGE;
1306 +
979 979   if(
980 - window.innerWidth<=MOBILE_MAX
1308 + hasApplied&&
1309 + !force&&
1310 + !meaningful
981 981   ){
982 982   return;
983 983   }
984 984  
985 - var parents=
986 - Array.from(
987 - root.querySelectorAll("*")
988 - ).filter(function(parent){
989 - return (
990 - visible(parent,MIN_W,MIN_H)&&
991 - parent.children&&
992 - parent.children.length>=2&&
993 - parent.children.length<=MAX_PARENT_CHILDREN
994 - );
995 - });
1315 + applying=true;
996 996  
997 - var touched=new Set();
1317 + try{
1318 + harmoniseCustomCallouts();
998 998  
999 - parents.forEach(function(parent){
1000 - var entries=[];
1320 + if(width<=MOBILE_MAX){
1321 + if(hasApplied){
1322 + resetRuntime();
1323 + }
1001 1001  
1002 - Array.from(parent.children).forEach(function(child){
1003 - if(!visible(child,MIN_W,MIN_H))return;
1325 + hasApplied=false;
1326 + lastWidth=width;
1327 + return;
1328 + }
1004 1004  
1005 - var target=pickTarget(child);
1006 - if(!target)return;
1330 + if(hasApplied){
1331 + resetRuntime();
1332 + }
1007 1007  
1008 - entries.push({
1009 - target:target,
1010 - rect:target.getBoundingClientRect()
1334 + var parents=
1335 + Array.from(
1336 + root.querySelectorAll("*")
1337 + )
1338 + .filter(function(parent){
1339 + return (
1340 + visible(
1341 + parent,
1342 + MIN_W,
1343 + MIN_H
1344 + )&&
1345 + parent.children&&
1346 + parent.children.length>=2&&
1347 + parent.children.length<=
1348 + MAX_PARENT_CHILDREN
1349 + );
1011 1011   });
1012 - });
1013 1013  
1014 - if(entries.length<2)return;
1352 + var touched=
1353 + new Set();
1015 1015  
1016 - groupsByVisualRow(entries).forEach(function(group){
1017 - if(!validHorizontalGroup(group))return;
1355 + parents.forEach(function(parent){
1356 + var entries=[];
1018 1018  
1019 - var signature=
1020 - group.entries.map(function(entry){
1021 - return entry.target;
1022 - });
1358 + Array.from(
1359 + parent.children
1360 + ).forEach(function(child){
1361 + if(
1362 + !visible(
1363 + child,
1364 + MIN_W,
1365 + MIN_H
1366 + )
1367 + ){
1368 + return;
1369 + }
1023 1023  
1024 - if(
1025 - !signature.some(function(el){
1026 - return !touched.has(el);
1027 - })
1028 - ){
1029 - return;
1030 - }
1371 + var target=
1372 + pickTarget(child);
1031 1031  
1032 - var applied=
1033 - applyGroup(group);
1374 + if(!target)return;
1034 1034  
1035 - if(applied){
1036 - signature.forEach(function(el){
1037 - touched.add(el);
1376 + entries.push({
1377 + target:target,
1378 + rect:
1379 + target.getBoundingClientRect()
1038 1038   });
1039 - }
1381 + });
1382 +
1383 + groupRows(
1384 + entries
1385 + ).forEach(function(group){
1386 + if(!validRow(group))return;
1387 +
1388 + var targets=
1389 + group.entries.map(function(entry){
1390 + return entry.target;
1391 + });
1392 +
1393 + if(
1394 + !targets.some(function(el){
1395 + return !touched.has(el);
1396 + })
1397 + ){
1398 + return;
1399 + }
1400 +
1401 + if(applyRow(group)){
1402 + targets.forEach(function(el){
1403 + touched.add(el);
1404 + });
1405 + }
1406 + });
1040 1040   });
1041 - });
1408 +
1409 + lastWidth=width;
1410 + hasApplied=true;
1411 + }finally{
1412 + applying=false;
1413 + }
1042 1042   }
1043 1043  
1044 - function schedule(){
1045 - if(raf)cancelAnimationFrame(raf);
1416 + function resize(){
1417 + if(resizeTimer){
1418 + clearTimeout(
1419 + resizeTimer
1420 + );
1421 + }
1046 1046  
1047 - raf=requestAnimationFrame(function(){
1048 - raf=requestAnimationFrame(function(){
1049 - raf=0;
1050 - run();
1051 - });
1052 - });
1423 + resizeTimer=
1424 + setTimeout(
1425 + function(){
1426 + resizeTimer=0;
1427 + calculate(false);
1428 + },
1429 + RESIZE_DEBOUNCE
1430 + );
1053 1053   }
1054 1054  
1055 - addEventListener("resize",schedule,{passive:true});
1056 - addEventListener("load",schedule,{once:true});
1433 + function waitImages(){
1434 + var images=
1435 + Array.from(
1436 + root.querySelectorAll("img")
1437 + );
1057 1057  
1058 - root.addEventListener(
1059 - "click",
1060 - function(){
1061 - setTimeout(schedule,0);
1062 - },
1063 - true
1064 - );
1439 + return Promise.all(
1440 + images.map(function(img){
1441 + if(img.complete){
1442 + return Promise.resolve();
1443 + }
1065 1065  
1066 - Array.from(root.querySelectorAll("img")).forEach(function(img){
1067 - if(!img.complete){
1068 - img.addEventListener("load",schedule,{once:true});
1445 + return new Promise(function(resolve){
1446 + var done=function(){
1447 + resolve();
1448 + };
1449 +
1450 + img.addEventListener(
1451 + "load",
1452 + done,
1453 + {once:true}
1454 + );
1455 +
1456 + img.addEventListener(
1457 + "error",
1458 + done,
1459 + {once:true}
1460 + );
1461 + });
1462 + })
1463 + );
1464 + }
1465 +
1466 + async function initial(){
1467 + if(
1468 + document.readyState!==
1469 + "complete"
1470 + ){
1471 + await new Promise(function(resolve){
1472 + addEventListener(
1473 + "load",
1474 + resolve,
1475 + {once:true}
1476 + );
1477 + });
1069 1069   }
1070 - });
1071 1071  
1072 - if(document.fonts&&document.fonts.ready){
1073 - document.fonts.ready.then(schedule);
1480 + if(
1481 + document.fonts&&
1482 + document.fonts.ready
1483 + ){
1484 + try{
1485 + await document.fonts.ready;
1486 + }catch(error){}
1487 + }
1488 +
1489 + await waitImages();
1490 +
1491 + // Exactly one initial layout calculation.
1492 + calculate(true);
1074 1074   }
1075 1075  
1076 - schedule();
1077 - setTimeout(schedule,200);
1078 - setTimeout(schedule,650);
1079 - setTimeout(schedule,1400);
1495 + addEventListener(
1496 + "resize",
1497 + resize,
1498 + {passive:true}
1499 + );
1500 +
1501 + initial();
1080 1080  })();
1081 1081  </script>
1082 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->
1083 -{{/html}}
1084 1084  
1505 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->
1506 +{{/html}}\n