Changes for page Service Navigator
Last modified by Veronika Kocher on Thursday, 03 Sep 2026 14:15
From version 22.15
edited by Veronika Kocher
on Thursday, 03 Sep 2026 10:09
on Thursday, 03 Sep 2026 10:09
Change comment:
There is no comment for this version
To version 22.14
edited by Veronika Kocher
on Thursday, 03 Sep 2026 9:34
on Thursday, 03 Sep 2026 9:34
Change comment:
There is no comment for this version
Summary
-
Page properties (1 modified, 0 added, 0 removed)
Details
- Page properties
-
- Content
-
... ... @@ -285,104 +285,15 @@ 285 285 </div> 286 286 {{/html}} 287 287 288 -\n\n 289 -\n\n{{html clean="false"}} 288 +{{html clean="false"}} 290 290 <!-- SN_HORIZONTAL_BOX_HARMONIZER_START --> 291 - 292 -<style data-sn-horizontal-box-harmonizer="7"> 293 -/* ---------- Typography: never justify headings ---------- */ 294 -#xwikicontent h1, 295 -#xwikicontent h2, 296 -#xwikicontent h3, 297 -#xwikicontent h4, 298 -#xwikicontent h5, 299 -#xwikicontent h6, 300 -#xwikicontent .box-title, 301 -#xwikicontent [role="heading"], 302 -#xwikicontent h1 a, 303 -#xwikicontent h2 a, 304 -#xwikicontent h3 a, 305 -#xwikicontent h4 a, 306 -#xwikicontent h5 a, 307 -#xwikicontent h6 a, 308 -#xwikicontent .box-title a, 309 -#xwikicontent [role="heading"] a{ 310 - text-align:left!important; 311 - text-justify:auto!important; 312 - word-spacing:normal!important; 313 -} 314 - 315 -/* ---------- Native XWiki semantic callouts ---------- */ 316 -#xwikicontent .box.infomessage, 317 -#xwikicontent .infomessage, 318 -#xwikicontent .box.warningmessage, 319 -#xwikicontent .warningmessage, 320 -#xwikicontent .box.successmessage, 321 -#xwikicontent .successmessage, 322 -#xwikicontent .box.errormessage, 323 -#xwikicontent .errormessage, 324 -#xwikicontent [data-sn-callout-harmonized="1"]{ 325 - box-sizing:border-box!important; 326 - border-radius:8px!important; 327 - border-top:1px solid rgba(0,0,0,.08)!important; 328 - border-right:1px solid rgba(0,0,0,.08)!important; 329 - border-bottom:1px solid rgba(0,0,0,.08)!important; 330 - border-left:4px solid var(--sn-callout-accent,#21b6d7)!important; 331 - padding:14px 16px!important; 332 - text-align:left!important; 333 - text-justify:auto!important; 334 - word-spacing:normal!important; 335 - box-shadow:none!important; 336 -} 337 - 338 -#xwikicontent .box.infomessage, 339 -#xwikicontent .infomessage{ 340 - --sn-callout-accent:#21b6d7; 341 - background:#eaf7fb!important; 342 -} 343 - 344 -#xwikicontent .box.warningmessage, 345 -#xwikicontent .warningmessage{ 346 - --sn-callout-accent:#f0aa00; 347 - background:#fff8e7!important; 348 -} 349 - 350 -#xwikicontent .box.successmessage, 351 -#xwikicontent .successmessage{ 352 - --sn-callout-accent:#38b76a; 353 - background:#edf9f1!important; 354 -} 355 - 356 -#xwikicontent .box.errormessage, 357 -#xwikicontent .errormessage{ 358 - --sn-callout-accent:#e94b13; 359 - background:#fff1ec!important; 360 -} 361 - 362 -#xwikicontent [data-sn-callout-harmonized="1"] > :first-child, 363 -#xwikicontent .infomessage > :first-child, 364 -#xwikicontent .warningmessage > :first-child, 365 -#xwikicontent .successmessage > :first-child, 366 -#xwikicontent .errormessage > :first-child{ 367 - margin-top:0!important; 368 -} 369 - 370 -#xwikicontent [data-sn-callout-harmonized="1"] > :last-child, 371 -#xwikicontent .infomessage > :last-child, 372 -#xwikicontent .warningmessage > :last-child, 373 -#xwikicontent .successmessage > :last-child, 374 -#xwikicontent .errormessage > :last-child{ 375 - margin-bottom:0!important; 376 -} 377 - 378 -#xwikicontent [data-sn-equal-height-managed], 379 -#xwikicontent [data-sn-first-section-aligned], 380 -#xwikicontent [data-sn-first-content-aligned]{ 290 +<style data-sn-horizontal-box-harmonizer="4"> 291 +[data-sn-equal-height-managed], 292 +[data-sn-first-section-aligned]{ 381 381 box-sizing:border-box; 382 382 } 383 383 </style> 384 - 385 -<script data-sn-horizontal-box-harmonizer="7"> 296 +<script data-sn-horizontal-box-harmonizer="4"> 386 386 (function(){ 387 387 "use strict"; 388 388 ... ... @@ -390,10 +390,8 @@ 390 390 var ROW_TOLERANCE=6; 391 391 var MIN_W=150; 392 392 var MIN_H=36; 393 - var MAX_PARENT_CHILDREN=1 8;304 + var MAX_PARENT_CHILDREN=16; 394 394 var SEARCH_DEPTH=5; 395 - var RESIZE_DEBOUNCE=220; 396 - var MIN_WIDTH_CHANGE=2; 397 397 398 398 var root= 399 399 document.getElementById("xwikicontent")|| ... ... @@ -405,16 +405,10 @@ 405 405 406 406 var heightManaged=new Set(); 407 407 var sectionManaged=new Set(); 408 - var contentManaged=new Set(); 409 - 410 410 var originalMinHeight=new WeakMap(); 411 411 var originalMarginTop=new WeakMap(); 319 + var raf=0; 412 412 413 - var resizeTimer=0; 414 - var lastWidth=-1; 415 - var hasApplied=false; 416 - var applying=false; 417 - 418 418 function num(v){ 419 419 var n=parseFloat(v); 420 420 return Number.isFinite(n)?n:0; ... ... @@ -441,13 +441,6 @@ 441 441 ); 442 442 } 443 443 444 - function normalizeText(text){ 445 - return String(text||"") 446 - .replace(/\s+/g," ") 447 - .trim() 448 - .toUpperCase(); 449 - } 450 - 451 451 function excluded(el){ 452 452 return ( 453 453 !el.matches|| ... ... @@ -457,84 +457,57 @@ 457 457 ); 458 458 } 459 459 460 - /* ---------- Custom coloured callouts ---------- */ 461 - 462 - function nativeCallout(el){ 356 + function namedLikeBox(el){ 463 463 var c= 464 464 typeof el.className==="string" 465 - ? el.className .toLowerCase()359 + ? el.className 466 466 : ""; 467 467 468 - return ( 469 - /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c) 470 - ); 362 + return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c); 471 471 } 472 472 473 - function customCallout(el){474 - if(!visible(el, 220,42)||excluded(el))return false;365 + function boxLike(el){ 366 + if(!visible(el,MIN_W,MIN_H)||excluded(el))return false; 475 475 368 + var s=getComputedStyle(el); 476 476 var r=el.getBoundingClientRect(); 477 477 478 - if(r.height>520)return false; 371 + var border= 372 + num(s.borderTopWidth)+ 373 + num(s.borderRightWidth)+ 374 + num(s.borderBottomWidth)+ 375 + num(s.borderLeftWidth); 479 479 480 - var s=getComputedStyle(el); 377 + var radius=Math.max( 378 + num(s.borderTopLeftRadius), 379 + num(s.borderTopRightRadius), 380 + num(s.borderBottomLeftRadius), 381 + num(s.borderBottomRightRadius) 382 + ); 481 481 482 482 var bg= 483 483 s.backgroundColor&& 484 484 s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 485 - s.backgroundColor!=="transparent"&& 486 - s.backgroundColor!=="rgb(255, 255, 255)"; 387 + s.backgroundColor!=="transparent"; 487 487 488 - var left= 489 - num(s.borderLeftWidth); 389 + var shadow= 390 + s.boxShadow&& 391 + s.boxShadow!=="none"; 490 490 491 491 return ( 492 - bg&& 493 - left>=2 394 + namedLikeBox(el)|| 395 + border>=1|| 396 + (bg&&radius>=3)|| 397 + (shadow&&r.width>=180) 494 494 ); 495 495 } 496 496 497 - function harmoniseCustomCallouts(){ 498 - Array.from( 499 - root.querySelectorAll( 500 - "div,aside,section" 501 - ) 502 - ).forEach(function(el){ 503 - if(nativeCallout(el))return; 504 - if(!customCallout(el))return; 505 - 506 - var s=getComputedStyle(el); 507 - 508 - el.setAttribute( 509 - "data-sn-callout-harmonized", 510 - "1" 511 - ); 512 - 513 - if( 514 - s.borderLeftColor 515 - ){ 516 - el.style.setProperty( 517 - "--sn-callout-accent", 518 - s.borderLeftColor 519 - ); 520 - } 521 - }); 522 - } 523 - 524 - /* ---------- Restore only values V7 itself changed ---------- */ 525 - 526 526 function saveOriginal(el,map,property){ 527 527 if(map.has(el))return; 528 528 529 529 map.set(el,{ 530 - value: 531 - el.style.getPropertyValue( 532 - property 533 - ), 534 - priority: 535 - el.style.getPropertyPriority( 536 - property 537 - ) 405 + value:el.style.getPropertyValue(property), 406 + priority:el.style.getPropertyPriority(property) 538 538 }); 539 539 } 540 540 ... ... @@ -548,13 +548,11 @@ 548 548 original.priority||"" 549 549 ); 550 550 }else{ 551 - el.style.removeProperty( 552 - property 553 - ); 420 + el.style.removeProperty(property); 554 554 } 555 555 } 556 556 557 - function reset Runtime(){424 + function reset(){ 558 558 heightManaged.forEach(function(el){ 559 559 restoreOriginal( 560 560 el, ... ... @@ -579,79 +579,16 @@ 579 579 ); 580 580 }); 581 581 582 - contentManaged.forEach(function(el){ 583 - restoreOriginal( 584 - el, 585 - originalMarginTop, 586 - "margin-top" 587 - ); 588 - 589 - el.removeAttribute( 590 - "data-sn-first-content-aligned" 591 - ); 592 - }); 593 - 594 594 heightManaged.clear(); 595 595 sectionManaged.clear(); 596 - contentManaged.clear(); 597 597 } 598 598 599 - /* ---------- Card detection ---------- */ 600 - 601 - function namedLikeBox(el){ 602 - var c= 603 - typeof el.className==="string" 604 - ? el.className 605 - : ""; 606 - 607 - return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c); 608 - } 609 - 610 - function boxLike(el){ 611 - if( 612 - !visible(el,MIN_W,MIN_H)|| 613 - excluded(el) 614 - ){ 615 - return false; 616 - } 617 - 618 - var s=getComputedStyle(el); 619 - var r=el.getBoundingClientRect(); 620 - 621 - var border= 622 - num(s.borderTopWidth)+ 623 - num(s.borderRightWidth)+ 624 - num(s.borderBottomWidth)+ 625 - num(s.borderLeftWidth); 626 - 627 - var radius=Math.max( 628 - num(s.borderTopLeftRadius), 629 - num(s.borderTopRightRadius), 630 - num(s.borderBottomLeftRadius), 631 - num(s.borderBottomRightRadius) 632 - ); 633 - 634 - var bg= 635 - s.backgroundColor&& 636 - s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 637 - s.backgroundColor!=="transparent"; 638 - 639 - var shadow= 640 - s.boxShadow&& 641 - s.boxShadow!=="none"; 642 - 643 - return ( 644 - namedLikeBox(el)|| 645 - border>=1|| 646 - (bg&&radius>=3)|| 647 - (shadow&&r.width>=180) 648 - ); 649 - } 650 - 651 651 function pickTarget(child){ 652 652 if(!visible(child,MIN_W,MIN_H))return null; 653 653 654 - if(boxLike(child))return child; 456 + if(boxLike(child)){ 457 + return child; 458 + } 655 655 656 656 var cr=child.getBoundingClientRect(); 657 657 var q=[]; ... ... @@ -713,7 +713,7 @@ 713 713 return best; 714 714 } 715 715 716 - function groupRow s(entries){520 + function groupsByVisualRow(entries){ 717 717 var groups=[]; 718 718 719 719 entries ... ... @@ -753,7 +753,7 @@ 753 753 return groups; 754 754 } 755 755 756 - function valid Row(group){560 + function validHorizontalGroup(group){ 757 757 if(group.entries.length<2)return false; 758 758 759 759 var lefts= ... ... @@ -776,26 +776,25 @@ 776 776 777 777 if(widths.length<2)return false; 778 778 779 - var minW= 780 - Math.min.apply(Math,widths);583 + var minWidth=Math.min.apply(Math,widths); 584 + var maxWidth=Math.max.apply(Math,widths); 781 781 782 - var maxW= 783 - Math.max.apply(Math,widths); 784 - 785 785 return ( 786 - maxW/ 787 - Math.max(1,minW)<= 587 + maxWidth/ 588 + Math.max(1,minWidth)<= 788 788 1.9 789 789 ); 790 790 } 791 791 792 - /* ---------- Known ARSN first section bars ---------- */ 593 + function normalizeText(text){ 594 + return String(text||"") 595 + .replace(/s+/g," ") 596 + .trim() 597 + .toUpperCase(); 598 + } 793 793 794 - function knownBar(el){ 795 - var text= 796 - normalizeText( 797 - el.textContent 798 - ); 600 + function exactKnownFirstSection(el){ 601 + var text=normalizeText(el.textContent); 799 799 800 800 return ( 801 801 text==="HIER STARTEN"|| ... ... @@ -809,274 +809,150 @@ 809 809 ); 810 810 } 811 811 812 - function findKnownBar(card){813 - var cardRect= 814 - card.getBoundingClientRect();615 + function visuallyFirstSectionBar(card){ 616 + var cardRect=card.getBoundingClientRect(); 617 + var candidates=[]; 815 815 816 - var rows= 817 - Array.from( 818 - card.querySelectorAll( 819 - "div,p,span,strong,h3,h4,h5,h6" 820 - ) 619 + Array.from( 620 + card.querySelectorAll( 621 + "div,p,span,strong,h3,h4,h5,h6" 821 821 ) 822 - .filter(function(el){823 - if(!visible(el,20,6))returnfalse;824 - if(!knownBar(el))returnfalse;623 + ).forEach(function(el){ 624 + if(!visible(el,20,6))return; 625 + if(excluded(el))return; 825 825 826 - var r=el.getBoundingClientRect(); 627 + var r=el.getBoundingClientRect(); 628 + var relativeTop=r.top-cardRect.top; 827 827 828 - var rel= 829 - r.top- 830 - cardRect.top; 630 + if( 631 + relativeTop<45|| 632 + relativeTop>cardRect.height*.72 633 + ){ 634 + return; 635 + } 831 831 832 - return ( 833 - rel>=35&& 834 - rel<= 835 - cardRect.height*.75 836 - ); 837 - }) 838 - .sort(function(a,b){ 839 - return ( 840 - a.getBoundingClientRect().top- 841 - b.getBoundingClientRect().top 842 - ); 843 - }); 637 + var widthRatio= 638 + r.width/ 639 + Math.max(1,cardRect.width); 844 844 845 - return rows[0]||null; 846 - } 641 + if( 642 + widthRatio<.55|| 643 + widthRatio>1.05|| 644 + r.height>54 645 + ){ 646 + return; 647 + } 847 847 848 - function alignKnownBars(cards){ 849 - var rows= 850 - cards.map(function(card){ 851 - var bar= 852 - findKnownBar(card); 649 + var text=normalizeText(el.textContent); 853 853 854 - if(!bar)return null; 651 + if( 652 + text.length<2|| 653 + text.length>90 654 + ){ 655 + return; 656 + } 855 855 856 - var cr= 857 - card.getBoundingClientRect(); 658 + var style=getComputedStyle(el); 858 858 859 - var br= 860 - bar.getBoundingClientRect(); 660 + var bg= 661 + style.backgroundColor&& 662 + style.backgroundColor!=="rgba(0, 0, 0, 0)"&& 663 + style.backgroundColor!=="transparent"; 861 861 862 - return { 863 - bar:bar, 864 - relativeTop: 865 - br.top- 866 - cr.top 867 - }; 868 - }); 665 + var border= 666 + num(style.borderTopWidth)+ 667 + num(style.borderRightWidth)+ 668 + num(style.borderBottomWidth)+ 669 + num(style.borderLeftWidth); 869 869 870 - if( 871 - rows.some(function(row){ 872 - return !row; 873 - }) 874 - ){ 875 - return false; 876 - } 671 + var mostlyUpper= 672 + text.length>=3&& 673 + text===text.toUpperCase(); 877 877 878 - var target= 879 - Math.max.apply( 880 - Math, 881 - rows.map(function(row){ 882 - return row.relativeTop; 883 - }) 884 - ); 675 + var exact=exactKnownFirstSection(el); 885 885 886 - rows.forEach(function(row){ 887 - var delta= 888 - target- 889 - row.relativeTop; 677 + var score= 678 + (exact?20:0)+ 679 + (bg?6:0)+ 680 + (border>=1?2:0)+ 681 + (mostlyUpper?4:0)+ 682 + (num(style.fontWeight)>=600?1:0); 890 890 891 - if(delta<=1)return; 684 + if(score>=7){ 685 + candidates.push({ 686 + el:el, 687 + relativeTop:relativeTop, 688 + width:r.width, 689 + score:score 690 + }); 691 + } 692 + }); 892 892 893 - saveOriginal( 894 - row.bar, 895 - originalMarginTop, 896 - "margin-top" 897 - ); 694 + if(!candidates.length)return null; 898 898 899 - var current= 900 - num( 901 - getComputedStyle( 902 - row.bar 903 - ).marginTop 904 - ); 696 + candidates.sort(function(a,b){ 697 + if(a.score!==b.score)return b.score-a.score; 698 + if(Math.abs(a.relativeTop-b.relativeTop)>3){ 699 + return a.relativeTop-b.relativeTop; 700 + } 701 + return b.width-a.width; 702 + }); 905 905 906 - row.bar.style.setProperty( 907 - "margin-top", 908 - ( 909 - current+ 910 - delta 911 - )+"px" 912 - ); 913 - 914 - row.bar.setAttribute( 915 - "data-sn-first-section-aligned", 916 - "7" 917 - ); 918 - 919 - sectionManaged.add( 920 - row.bar 921 - ); 704 + // For exact known labels always prefer the earliest exact match. 705 + var exacts=candidates.filter(function(c){ 706 + return exactKnownFirstSection(c.el); 922 922 }); 923 923 924 - return true; 925 - } 926 - 927 - /* ---------- Generic first content after heading ---------- */ 928 - 929 - function heading(card){ 930 - var cr= 931 - card.getBoundingClientRect(); 932 - 933 - var rows= 934 - Array.from( 935 - card.querySelectorAll( 936 - "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]" 937 - ) 938 - ) 939 - .filter(function(el){ 940 - if(!visible(el,20,8))return false; 941 - 942 - var r=el.getBoundingClientRect(); 943 - 944 - var rel= 945 - r.top- 946 - cr.top; 947 - 948 - return ( 949 - rel>=0&& 950 - rel<= 951 - Math.min( 952 - 160, 953 - cr.height*.45 954 - ) 955 - ); 956 - }) 957 - .sort(function(a,b){ 958 - return ( 959 - a.getBoundingClientRect().top- 960 - b.getBoundingClientRect().top 961 - ); 709 + if(exacts.length){ 710 + exacts.sort(function(a,b){ 711 + return a.relativeTop-b.relativeTop||b.width-a.width; 962 962 }); 713 + return exacts[0].el; 714 + } 963 963 964 - return rows[0]||null; 965 - } 716 + // Generic fallback: earliest high-confidence bar. 717 + candidates.sort(function(a,b){ 718 + return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width; 719 + }); 966 966 967 - function firstContent(card,h){ 968 - if(!h)return null; 969 - 970 - var cr= 971 - card.getBoundingClientRect(); 972 - 973 - var hr= 974 - h.getBoundingClientRect(); 975 - 976 - var rows= 977 - Array.from( 978 - card.querySelectorAll( 979 - "p,div,ul,ol,dl,blockquote" 980 - ) 981 - ) 982 - .filter(function(el){ 983 - if( 984 - el===h|| 985 - h.contains(el)|| 986 - el.contains(h) 987 - ){ 988 - return false; 989 - } 990 - 991 - if(!visible(el,30,8))return false; 992 - 993 - var text= 994 - String(el.textContent||"") 995 - .replace(/\s+/g," ") 996 - .trim(); 997 - 998 - if(text.length<3)return false; 999 - 1000 - var r=el.getBoundingClientRect(); 1001 - 1002 - if( 1003 - r.top< 1004 - hr.bottom-1 1005 - ){ 1006 - return false; 1007 - } 1008 - 1009 - if( 1010 - r.top-cr.top> 1011 - Math.min( 1012 - 280, 1013 - cr.height*.65 1014 - ) 1015 - ){ 1016 - return false; 1017 - } 1018 - 1019 - if( 1020 - r.height> 1021 - cr.height*.58 1022 - ){ 1023 - return false; 1024 - } 1025 - 1026 - return true; 1027 - }) 1028 - .sort(function(a,b){ 1029 - var ar= 1030 - a.getBoundingClientRect(); 1031 - 1032 - var br= 1033 - b.getBoundingClientRect(); 1034 - 1035 - return ( 1036 - ar.top-br.top|| 1037 - ar.height-br.height 1038 - ); 1039 - }); 1040 - 1041 - return rows[0]||null; 721 + return candidates[0].el; 1042 1042 } 1043 1043 1044 - function align Generic(cards){724 + function alignFirstSections(cards){ 1045 1045 var rows= 1046 1046 cards.map(function(card){ 1047 - var h=1048 - heading(card);727 + var bar= 728 + visuallyFirstSectionBar(card); 1049 1049 1050 - var c= 1051 - firstContent( 1052 - card, 1053 - h 1054 - ); 730 + if(!bar)return null; 1055 1055 1056 - if(!h||!c)return null; 1057 - 1058 - var cr= 732 + var cardRect= 1059 1059 card.getBoundingClientRect(); 1060 1060 1061 - var r r=1062 - c.getBoundingClientRect();735 + var barRect= 736 + bar.getBoundingClientRect(); 1063 1063 1064 1064 return { 1065 - content:c, 739 + card:card, 740 + bar:bar, 1066 1066 relativeTop: 1067 - rr.top-cr.top 742 + barRect.top-cardRect.top, 743 + label: 744 + normalizeText(bar.textContent) 1068 1068 }; 1069 1069 }); 1070 1070 748 + // Conservative: only align when every card in the horizontal row 749 + // has a reliable first section bar. 1071 1071 if( 1072 1072 rows.some(function(row){ 1073 1073 return !row; 1074 1074 }) 1075 1075 ){ 1076 - return false;755 + return null; 1077 1077 } 1078 1078 1079 - var target= 758 + var targetTop= 1080 1080 Math.max.apply( 1081 1081 Math, 1082 1082 rows.map(function(row){ ... ... @@ -1086,47 +1086,54 @@ 1086 1086 1087 1087 rows.forEach(function(row){ 1088 1088 var delta= 1089 - target- 768 + targetTop- 1090 1090 row.relativeTop; 1091 1091 1092 1092 if(delta<=1)return; 1093 1093 1094 1094 saveOriginal( 1095 - row. content,774 + row.bar, 1096 1096 originalMarginTop, 1097 1097 "margin-top" 1098 1098 ); 1099 1099 1100 - var current= 779 + var currentMargin= 1101 1101 num( 1102 1102 getComputedStyle( 1103 - row. content782 + row.bar 1104 1104 ).marginTop 1105 1105 ); 1106 1106 1107 - row. content.style.setProperty(786 + row.bar.style.setProperty( 1108 1108 "margin-top", 1109 1109 ( 1110 - current+ 789 + currentMargin+ 1111 1111 delta 1112 1112 )+"px" 1113 1113 ); 1114 1114 1115 - row. content.setAttribute(1116 - "data-sn-first-con tent-aligned",1117 - " 7"794 + row.bar.setAttribute( 795 + "data-sn-first-section-aligned", 796 + "4" 1118 1118 ); 1119 1119 1120 - contentManaged.add( 1121 - row.content 1122 - ); 799 + sectionManaged.add(row.bar); 1123 1123 }); 1124 1124 1125 - return true; 802 + return { 803 + labels: 804 + rows.map(function(row){ 805 + return row.label; 806 + }), 807 + targetRelativeTop: 808 + Math.round(targetTop) 809 + }; 1126 1126 } 1127 1127 1128 - function applyRow(group){ 1129 - if(!validRow(group))return false; 812 + function applyGroup(group){ 813 + if(!validHorizontalGroup(group)){ 814 + return null; 815 + } 1130 1130 1131 1131 var cards=[]; 1132 1132 var seen=new Set(); ... ... @@ -1138,30 +1138,25 @@ 1138 1138 } 1139 1139 }); 1140 1140 1141 - if(cards.length<2)return false;827 + if(cards.length<2)return null; 1142 1142 1143 - if( 1144 - !alignKnownBars(cards) 1145 - ){ 1146 - alignGeneric(cards); 1147 - } 829 + var firstSection= 830 + alignFirstSections(cards); 1148 1148 832 + // The section alignment can change natural card heights. 833 + // Measure only afterwards. 1149 1149 var maxHeight=0; 1150 1150 1151 1151 cards.forEach(function(card){ 1152 - maxHeight= 1153 - Math.max( 1154 - maxHeight, 1155 - card.getBoundingClientRect().height 1156 - ); 837 + maxHeight=Math.max( 838 + maxHeight, 839 + card.getBoundingClientRect().height 840 + ); 1157 1157 }); 1158 1158 1159 - maxHeight= 1160 - Math.ceil( 1161 - maxHeight 1162 - ); 843 + maxHeight=Math.ceil(maxHeight); 1163 1163 1164 - if(maxHeight<MIN_H)return false;845 + if(maxHeight<MIN_H)return null; 1165 1165 1166 1166 cards.forEach(function(card){ 1167 1167 saveOriginal( ... ... @@ -1177,228 +1177,125 @@ 1177 1177 1178 1178 card.setAttribute( 1179 1179 "data-sn-equal-height-managed", 1180 - " 7"861 + "4" 1181 1181 ); 1182 1182 1183 1183 heightManaged.add(card); 1184 1184 }); 1185 1185 1186 - return true; 867 + return { 868 + cards:cards.length, 869 + height:maxHeight, 870 + firstSection:firstSection 871 + }; 1187 1187 } 1188 1188 1189 - function calculate(force){1190 - if(applying)return;874 + function run(){ 875 + reset(); 1191 1191 1192 - var width= 1193 - Math.round( 1194 - window.innerWidth 1195 - ); 1196 - 1197 - var meaningful= 1198 - lastWidth<0|| 1199 - Math.abs( 1200 - width- 1201 - lastWidth 1202 - )>= 1203 - MIN_WIDTH_CHANGE; 1204 - 1205 1205 if( 1206 - hasApplied&& 1207 - !force&& 1208 - !meaningful 878 + window.innerWidth<=MOBILE_MAX 1209 1209 ){ 1210 1210 return; 1211 1211 } 1212 1212 1213 - applying=true; 883 + var parents= 884 + Array.from( 885 + root.querySelectorAll("*") 886 + ).filter(function(parent){ 887 + return ( 888 + visible(parent,MIN_W,MIN_H)&& 889 + parent.children&& 890 + parent.children.length>=2&& 891 + parent.children.length<=MAX_PARENT_CHILDREN 892 + ); 893 + }); 1214 1214 1215 - try{ 1216 - harmoniseCustomCallouts(); 895 + var touched=new Set(); 1217 1217 1218 - if(width<=MOBILE_MAX){ 1219 - if(hasApplied){ 1220 - resetRuntime(); 1221 - } 897 + parents.forEach(function(parent){ 898 + var entries=[]; 1222 1222 1223 - hasApplied=false; 1224 - lastWidth=width; 1225 - return; 1226 - } 900 + Array.from(parent.children).forEach(function(child){ 901 + if(!visible(child,MIN_W,MIN_H))return; 1227 1227 1228 - if(hasApplied){ 1229 - resetRuntime(); 1230 - } 903 + var target=pickTarget(child); 904 + if(!target)return; 1231 1231 1232 - var parents= 1233 - Array.from( 1234 - root.querySelectorAll("*") 1235 - ) 1236 - .filter(function(parent){ 1237 - return ( 1238 - visible( 1239 - parent, 1240 - MIN_W, 1241 - MIN_H 1242 - )&& 1243 - parent.children&& 1244 - parent.children.length>=2&& 1245 - parent.children.length<= 1246 - MAX_PARENT_CHILDREN 1247 - ); 906 + entries.push({ 907 + target:target, 908 + rect:target.getBoundingClientRect() 1248 1248 }); 910 + }); 1249 1249 1250 - var touched= 1251 - new Set(); 912 + if(entries.length<2)return; 1252 1252 1253 - pa rents.forEach(function(parent){1254 - var entries=[];914 + groupsByVisualRow(entries).forEach(function(group){ 915 + if(!validHorizontalGroup(group))return; 1255 1255 1256 - Array.from( 1257 - parent.children 1258 - ).forEach(function(child){ 1259 - if( 1260 - !visible( 1261 - child, 1262 - MIN_W, 1263 - MIN_H 1264 - ) 1265 - ){ 1266 - return; 1267 - } 1268 - 1269 - var target= 1270 - pickTarget(child); 1271 - 1272 - if(!target)return; 1273 - 1274 - entries.push({ 1275 - target:target, 1276 - rect: 1277 - target.getBoundingClientRect() 917 + var signature= 918 + group.entries.map(function(entry){ 919 + return entry.target; 1278 1278 }); 1279 - }); 1280 1280 1281 - groupRows( 1282 - entries 1283 - ).forEach(function(group){ 1284 - if(!validRow(group))return; 922 + if( 923 + !signature.some(function(el){ 924 + return !touched.has(el); 925 + }) 926 + ){ 927 + return; 928 + } 1285 1285 1286 - var targets= 1287 - group.entries.map(function(entry){ 1288 - return entry.target; 1289 - }); 930 + var applied= 931 + applyGroup(group); 1290 1290 1291 - if( 1292 - !targets.some(function(el){ 1293 - return !touched.has(el); 1294 - }) 1295 - ){ 1296 - return; 1297 - } 1298 - 1299 - if(applyRow(group)){ 1300 - targets.forEach(function(el){ 1301 - touched.add(el); 1302 - }); 1303 - } 1304 - }); 933 + if(applied){ 934 + signature.forEach(function(el){ 935 + touched.add(el); 936 + }); 937 + } 1305 1305 }); 1306 - 1307 - lastWidth=width; 1308 - hasApplied=true; 1309 - }finally{ 1310 - applying=false; 1311 - } 939 + }); 1312 1312 } 1313 1313 1314 - function resize(){ 1315 - if(resizeTimer){ 1316 - clearTimeout( 1317 - resizeTimer 1318 - ); 1319 - } 942 + function schedule(){ 943 + if(raf)cancelAnimationFrame(raf); 1320 1320 1321 - resizeTimer= 1322 - setTimeout( 1323 - function(){ 1324 - resizeTimer=0; 1325 - calculate(false); 1326 - }, 1327 - RESIZE_DEBOUNCE 1328 - ); 945 + raf=requestAnimationFrame(function(){ 946 + raf=requestAnimationFrame(function(){ 947 + raf=0; 948 + run(); 949 + }); 950 + }); 1329 1329 } 1330 1330 1331 - function waitImages(){ 1332 - var images= 1333 - Array.from( 1334 - root.querySelectorAll("img") 1335 - ); 953 + addEventListener("resize",schedule,{passive:true}); 954 + addEventListener("load",schedule,{once:true}); 1336 1336 1337 - return Promise.all( 1338 - images.map(function(img){ 1339 - if(img.complete){ 1340 - return Promise.resolve(); 1341 - } 956 + root.addEventListener( 957 + "click", 958 + function(){ 959 + setTimeout(schedule,0); 960 + }, 961 + true 962 + ); 1342 1342 1343 - return new Promise(function(resolve){ 1344 - var done=function(){ 1345 - resolve(); 1346 - }; 1347 - 1348 - img.addEventListener( 1349 - "load", 1350 - done, 1351 - {once:true} 1352 - ); 1353 - 1354 - img.addEventListener( 1355 - "error", 1356 - done, 1357 - {once:true} 1358 - ); 1359 - }); 1360 - }) 1361 - ); 1362 - } 1363 - 1364 - async function initial(){ 1365 - if( 1366 - document.readyState!== 1367 - "complete" 1368 - ){ 1369 - await new Promise(function(resolve){ 1370 - addEventListener( 1371 - "load", 1372 - resolve, 1373 - {once:true} 1374 - ); 1375 - }); 964 + Array.from(root.querySelectorAll("img")).forEach(function(img){ 965 + if(!img.complete){ 966 + img.addEventListener("load",schedule,{once:true}); 1376 1376 } 968 + }); 1377 1377 1378 - if( 1379 - document.fonts&& 1380 - document.fonts.ready 1381 - ){ 1382 - try{ 1383 - await document.fonts.ready; 1384 - }catch(error){} 1385 - } 1386 - 1387 - await waitImages(); 1388 - 1389 - // Exactly one initial layout calculation. 1390 - calculate(true); 970 + if(document.fonts&&document.fonts.ready){ 971 + document.fonts.ready.then(schedule); 1391 1391 } 1392 1392 1393 - addEventListener( 1394 - "resize", 1395 - resize, 1396 - {passive:true} 1397 - ); 1398 - 1399 - initial(); 974 + schedule(); 975 + setTimeout(schedule,200); 976 + setTimeout(schedule,650); 977 + setTimeout(schedule,1400); 1400 1400 })(); 1401 1401 </script> 1402 - 1403 1403 <!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 1404 -{{/html}}\n 981 +{{/html}} 982 +