Changes for page Service Navigator
Last modified by Veronika Kocher on Thursday, 03 Sep 2026 14:15
From 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
To 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
Summary
-
Page properties (1 modified, 0 added, 0 removed)
Details
- Page properties
-
- Content
-
... ... @@ -285,15 +285,104 @@ 285 285 </div> 286 286 {{/html}} 287 287 288 -{{html clean="false"}} 288 +\n\n 289 +\n\n{{html clean="false"}} 289 289 <!-- SN_HORIZONTAL_BOX_HARMONIZER_START --> 290 -<style data-sn-horizontal-box-harmonizer="4"> 291 -[data-sn-equal-height-managed], 292 -[data-sn-first-section-aligned]{ 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]{ 293 293 box-sizing:border-box; 294 294 } 295 295 </style> 296 -<script data-sn-horizontal-box-harmonizer="4"> 384 + 385 +<script data-sn-horizontal-box-harmonizer="7"> 297 297 (function(){ 298 298 "use strict"; 299 299 ... ... @@ -301,8 +301,10 @@ 301 301 var ROW_TOLERANCE=6; 302 302 var MIN_W=150; 303 303 var MIN_H=36; 304 - var MAX_PARENT_CHILDREN=1 6;393 + var MAX_PARENT_CHILDREN=18; 305 305 var SEARCH_DEPTH=5; 395 + var RESIZE_DEBOUNCE=220; 396 + var MIN_WIDTH_CHANGE=2; 306 306 307 307 var root= 308 308 document.getElementById("xwikicontent")|| ... ... @@ -314,10 +314,16 @@ 314 314 315 315 var heightManaged=new Set(); 316 316 var sectionManaged=new Set(); 408 + var contentManaged=new Set(); 409 + 317 317 var originalMinHeight=new WeakMap(); 318 318 var originalMarginTop=new WeakMap(); 319 - var raf=0; 320 320 413 + var resizeTimer=0; 414 + var lastWidth=-1; 415 + var hasApplied=false; 416 + var applying=false; 417 + 321 321 function num(v){ 322 322 var n=parseFloat(v); 323 323 return Number.isFinite(n)?n:0; ... ... @@ -344,6 +344,13 @@ 344 344 ); 345 345 } 346 346 444 + function normalizeText(text){ 445 + return String(text||"") 446 + .replace(/\s+/g," ") 447 + .trim() 448 + .toUpperCase(); 449 + } 450 + 347 347 function excluded(el){ 348 348 return ( 349 349 !el.matches|| ... ... @@ -353,57 +353,84 @@ 353 353 ); 354 354 } 355 355 356 - function namedLikeBox(el){ 460 + /* ---------- Custom coloured callouts ---------- */ 461 + 462 + function nativeCallout(el){ 357 357 var c= 358 358 typeof el.className==="string" 359 - ? el.className 465 + ? el.className.toLowerCase() 360 360 : ""; 361 361 362 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c); 468 + return ( 469 + /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c) 470 + ); 363 363 } 364 364 365 - function boxLike(el){366 - if(!visible(el, MIN_W,MIN_H)||excluded(el))return false;473 + function customCallout(el){ 474 + if(!visible(el,220,42)||excluded(el))return false; 367 367 368 - var s=getComputedStyle(el); 369 369 var r=el.getBoundingClientRect(); 370 370 371 - var border= 372 - num(s.borderTopWidth)+ 373 - num(s.borderRightWidth)+ 374 - num(s.borderBottomWidth)+ 375 - num(s.borderLeftWidth); 478 + if(r.height>520)return false; 376 376 377 - var radius=Math.max( 378 - num(s.borderTopLeftRadius), 379 - num(s.borderTopRightRadius), 380 - num(s.borderBottomLeftRadius), 381 - num(s.borderBottomRightRadius) 382 - ); 480 + var s=getComputedStyle(el); 383 383 384 384 var bg= 385 385 s.backgroundColor&& 386 386 s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 387 - s.backgroundColor!=="transparent"; 485 + s.backgroundColor!=="transparent"&& 486 + s.backgroundColor!=="rgb(255, 255, 255)"; 388 388 389 - var shadow= 390 - s.boxShadow&& 391 - s.boxShadow!=="none"; 488 + var left= 489 + num(s.borderLeftWidth); 392 392 393 393 return ( 394 - namedLikeBox(el)|| 395 - border>=1|| 396 - (bg&&radius>=3)|| 397 - (shadow&&r.width>=180) 492 + bg&& 493 + left>=2 398 398 ); 399 399 } 400 400 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 + 401 401 function saveOriginal(el,map,property){ 402 402 if(map.has(el))return; 403 403 404 404 map.set(el,{ 405 - value:el.style.getPropertyValue(property), 406 - priority:el.style.getPropertyPriority(property) 530 + value: 531 + el.style.getPropertyValue( 532 + property 533 + ), 534 + priority: 535 + el.style.getPropertyPriority( 536 + property 537 + ) 407 407 }); 408 408 } 409 409 ... ... @@ -417,11 +417,13 @@ 417 417 original.priority||"" 418 418 ); 419 419 }else{ 420 - el.style.removeProperty(property); 551 + el.style.removeProperty( 552 + property 553 + ); 421 421 } 422 422 } 423 423 424 - function reset(){ 557 + function resetRuntime(){ 425 425 heightManaged.forEach(function(el){ 426 426 restoreOriginal( 427 427 el, ... ... @@ -446,16 +446,79 @@ 446 446 ); 447 447 }); 448 448 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 + 449 449 heightManaged.clear(); 450 450 sectionManaged.clear(); 596 + contentManaged.clear(); 451 451 } 452 452 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 + 453 453 function pickTarget(child){ 454 454 if(!visible(child,MIN_W,MIN_H))return null; 455 455 456 - if(boxLike(child)){ 457 - return child; 458 - } 654 + if(boxLike(child))return child; 459 459 460 460 var cr=child.getBoundingClientRect(); 461 461 var q=[]; ... ... @@ -517,7 +517,7 @@ 517 517 return best; 518 518 } 519 519 520 - function group sByVisualRow(entries){716 + function groupRows(entries){ 521 521 var groups=[]; 522 522 523 523 entries ... ... @@ -557,7 +557,7 @@ 557 557 return groups; 558 558 } 559 559 560 - function valid HorizontalGroup(group){756 + function validRow(group){ 561 561 if(group.entries.length<2)return false; 562 562 563 563 var lefts= ... ... @@ -580,25 +580,26 @@ 580 580 581 581 if(widths.length<2)return false; 582 582 583 - var minW idth=Math.min.apply(Math,widths);584 - varmaxWidth=Math.max.apply(Math,widths);779 + var minW= 780 + Math.min.apply(Math,widths); 585 585 782 + var maxW= 783 + Math.max.apply(Math,widths); 784 + 586 586 return ( 587 - maxW idth/588 - Math.max(1,minW idth)<=786 + maxW/ 787 + Math.max(1,minW)<= 589 589 1.9 590 590 ); 591 591 } 592 592 593 - function normalizeText(text){ 594 - return String(text||"") 595 - .replace(/s+/g," ") 596 - .trim() 597 - .toUpperCase(); 598 - } 792 + /* ---------- Known ARSN first section bars ---------- */ 599 599 600 - function exactKnownFirstSection(el){ 601 - var text=normalizeText(el.textContent); 794 + function knownBar(el){ 795 + var text= 796 + normalizeText( 797 + el.textContent 798 + ); 602 602 603 603 return ( 604 604 text==="HIER STARTEN"|| ... ... @@ -612,150 +612,274 @@ 612 612 ); 613 613 } 614 614 615 - function visuallyFirstSectionBar(card){616 - var cardRect= card.getBoundingClientRect();617 - varcandidates=[];812 + function findKnownBar(card){ 813 + var cardRect= 814 + card.getBoundingClientRect(); 618 618 619 - Array.from( 620 - card.querySelectorAll( 621 - "div,p,span,strong,h3,h4,h5,h6" 816 + var rows= 817 + Array.from( 818 + card.querySelectorAll( 819 + "div,p,span,strong,h3,h4,h5,h6" 820 + ) 622 622 ) 623 - ).forEach(function(el){624 - if(!visible(el,20,6))return; 625 - if( excluded(el))return;822 + .filter(function(el){ 823 + if(!visible(el,20,6))return false; 824 + if(!knownBar(el))return false; 626 626 627 - var r=el.getBoundingClientRect(); 628 - var relativeTop=r.top-cardRect.top; 826 + var r=el.getBoundingClientRect(); 629 629 630 - if( 631 - relativeTop<45|| 632 - relativeTop>cardRect.height*.72 633 - ){ 634 - return; 635 - } 828 + var rel= 829 + r.top- 830 + cardRect.top; 636 636 637 - var widthRatio= 638 - r.width/ 639 - Math.max(1,cardRect.width); 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 + }); 640 640 641 - if( 642 - widthRatio<.55|| 643 - widthRatio>1.05|| 644 - r.height>54 645 - ){ 646 - return; 647 - } 845 + return rows[0]||null; 846 + } 648 648 649 - var text=normalizeText(el.textContent); 848 + function alignKnownBars(cards){ 849 + var rows= 850 + cards.map(function(card){ 851 + var bar= 852 + findKnownBar(card); 650 650 651 - if( 652 - text.length<2|| 653 - text.length>90 654 - ){ 655 - return; 656 - } 854 + if(!bar)return null; 657 657 658 - var style=getComputedStyle(el); 856 + var cr= 857 + card.getBoundingClientRect(); 659 659 660 - var bg= 661 - style.backgroundColor&& 662 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&& 663 - style.backgroundColor!=="transparent"; 859 + var br= 860 + bar.getBoundingClientRect(); 664 664 665 - var border= 666 - num(style.borderTopWidth)+ 667 - num(style.borderRightWidth)+ 668 - num(style.borderBottomWidth)+ 669 - num(style.borderLeftWidth); 862 + return { 863 + bar:bar, 864 + relativeTop: 865 + br.top- 866 + cr.top 867 + }; 868 + }); 670 670 671 - var mostlyUpper= 672 - text.length>=3&& 673 - text===text.toUpperCase(); 870 + if( 871 + rows.some(function(row){ 872 + return !row; 873 + }) 874 + ){ 875 + return false; 876 + } 674 674 675 - var exact=exactKnownFirstSection(el); 878 + var target= 879 + Math.max.apply( 880 + Math, 881 + rows.map(function(row){ 882 + return row.relativeTop; 883 + }) 884 + ); 676 676 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); 886 + rows.forEach(function(row){ 887 + var delta= 888 + target- 889 + row.relativeTop; 683 683 684 - if(score>=7){ 685 - candidates.push({ 686 - el:el, 687 - relativeTop:relativeTop, 688 - width:r.width, 689 - score:score 690 - }); 691 - } 692 - }); 891 + if(delta<=1)return; 693 693 694 - if(!candidates.length)return null; 893 + saveOriginal( 894 + row.bar, 895 + originalMarginTop, 896 + "margin-top" 897 + ); 695 695 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 - }); 899 + var current= 900 + num( 901 + getComputedStyle( 902 + row.bar 903 + ).marginTop 904 + ); 703 703 704 - // For exact known labels always prefer the earliest exact match. 705 - var exacts=candidates.filter(function(c){ 706 - return exactKnownFirstSection(c.el); 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 + ); 707 707 }); 708 708 709 - if(exacts.length){ 710 - exacts.sort(function(a,b){ 711 - return a.relativeTop-b.relativeTop||b.width-a.width; 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 + ); 712 712 }); 713 - return exacts[0].el; 714 - } 715 715 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 - }); 964 + return rows[0]||null; 965 + } 720 720 721 - return candidates[0].el; 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; 722 722 } 723 723 724 - function align FirstSections(cards){1044 + function alignGeneric(cards){ 725 725 var rows= 726 726 cards.map(function(card){ 727 - var bar=728 - visuallyFirstSectionBar(card);1047 + var h= 1048 + heading(card); 729 729 730 - if(!bar)return null; 1050 + var c= 1051 + firstContent( 1052 + card, 1053 + h 1054 + ); 731 731 732 - var cardRect= 1056 + if(!h||!c)return null; 1057 + 1058 + var cr= 733 733 card.getBoundingClientRect(); 734 734 735 - var barRect=736 - bar.getBoundingClientRect();1061 + var rr= 1062 + c.getBoundingClientRect(); 737 737 738 738 return { 739 - card:card, 740 - bar:bar, 1065 + content:c, 741 741 relativeTop: 742 - barRect.top-cardRect.top, 743 - label: 744 - normalizeText(bar.textContent) 1067 + rr.top-cr.top 745 745 }; 746 746 }); 747 747 748 - // Conservative: only align when every card in the horizontal row 749 - // has a reliable first section bar. 750 750 if( 751 751 rows.some(function(row){ 752 752 return !row; 753 753 }) 754 754 ){ 755 - return null;1076 + return false; 756 756 } 757 757 758 - var target Top=1079 + var target= 759 759 Math.max.apply( 760 760 Math, 761 761 rows.map(function(row){ ... ... @@ -765,54 +765,47 @@ 765 765 766 766 rows.forEach(function(row){ 767 767 var delta= 768 - target Top-1089 + target- 769 769 row.relativeTop; 770 770 771 771 if(delta<=1)return; 772 772 773 773 saveOriginal( 774 - row. bar,1095 + row.content, 775 775 originalMarginTop, 776 776 "margin-top" 777 777 ); 778 778 779 - var current Margin=1100 + var current= 780 780 num( 781 781 getComputedStyle( 782 - row. bar1103 + row.content 783 783 ).marginTop 784 784 ); 785 785 786 - row. bar.style.setProperty(1107 + row.content.style.setProperty( 787 787 "margin-top", 788 788 ( 789 - current Margin+1110 + current+ 790 790 delta 791 791 )+"px" 792 792 ); 793 793 794 - row. bar.setAttribute(795 - "data-sn-first- section-aligned",796 - " 4"1115 + row.content.setAttribute( 1116 + "data-sn-first-content-aligned", 1117 + "7" 797 797 ); 798 798 799 - sectionManaged.add(row.bar); 1120 + contentManaged.add( 1121 + row.content 1122 + ); 800 800 }); 801 801 802 - return { 803 - labels: 804 - rows.map(function(row){ 805 - return row.label; 806 - }), 807 - targetRelativeTop: 808 - Math.round(targetTop) 809 - }; 1125 + return true; 810 810 } 811 811 812 - function applyGroup(group){ 813 - if(!validHorizontalGroup(group)){ 814 - return null; 815 - } 1128 + function applyRow(group){ 1129 + if(!validRow(group))return false; 816 816 817 817 var cards=[]; 818 818 var seen=new Set(); ... ... @@ -824,25 +824,30 @@ 824 824 } 825 825 }); 826 826 827 - if(cards.length<2)return null;1141 + if(cards.length<2)return false; 828 828 829 - var firstSection= 830 - alignFirstSections(cards); 1143 + if( 1144 + !alignKnownBars(cards) 1145 + ){ 1146 + alignGeneric(cards); 1147 + } 831 831 832 - // The section alignment can change natural card heights. 833 - // Measure only afterwards. 834 834 var maxHeight=0; 835 835 836 836 cards.forEach(function(card){ 837 - maxHeight=Math.max( 838 - maxHeight, 839 - card.getBoundingClientRect().height 840 - ); 1152 + maxHeight= 1153 + Math.max( 1154 + maxHeight, 1155 + card.getBoundingClientRect().height 1156 + ); 841 841 }); 842 842 843 - maxHeight=Math.ceil(maxHeight); 1159 + maxHeight= 1160 + Math.ceil( 1161 + maxHeight 1162 + ); 844 844 845 - if(maxHeight<MIN_H)return null;1164 + if(maxHeight<MIN_H)return false; 846 846 847 847 cards.forEach(function(card){ 848 848 saveOriginal( ... ... @@ -858,125 +858,228 @@ 858 858 859 859 card.setAttribute( 860 860 "data-sn-equal-height-managed", 861 - " 4"1180 + "7" 862 862 ); 863 863 864 864 heightManaged.add(card); 865 865 }); 866 866 867 - return { 868 - cards:cards.length, 869 - height:maxHeight, 870 - firstSection:firstSection 871 - }; 1186 + return true; 872 872 } 873 873 874 - function run(){875 - re set();1189 + function calculate(force){ 1190 + if(applying)return; 876 876 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 + 877 877 if( 878 - window.innerWidth<=MOBILE_MAX 1206 + hasApplied&& 1207 + !force&& 1208 + !meaningful 879 879 ){ 880 880 return; 881 881 } 882 882 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 - }); 1213 + applying=true; 894 894 895 - var touched=new Set(); 1215 + try{ 1216 + harmoniseCustomCallouts(); 896 896 897 - parents.forEach(function(parent){ 898 - var entries=[]; 1218 + if(width<=MOBILE_MAX){ 1219 + if(hasApplied){ 1220 + resetRuntime(); 1221 + } 899 899 900 - Array.from(parent.children).forEach(function(child){ 901 - if(!visible(child,MIN_W,MIN_H))return; 1223 + hasApplied=false; 1224 + lastWidth=width; 1225 + return; 1226 + } 902 902 903 - var target=pickTarget(child); 904 - if(!target)return; 1228 + if(hasApplied){ 1229 + resetRuntime(); 1230 + } 905 905 906 - entries.push({ 907 - target:target, 908 - rect:target.getBoundingClientRect() 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 + ); 909 909 }); 910 - }); 911 911 912 - if(entries.length<2)return; 1250 + var touched= 1251 + new Set(); 913 913 914 - groupsByVisualRow(entries).forEach(function(group){915 - if(!validHorizontalGroup(group))return;1253 + parents.forEach(function(parent){ 1254 + var entries=[]; 916 916 917 - var signature= 918 - group.entries.map(function(entry){ 919 - return entry.target; 920 - }); 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 + } 921 921 922 - if( 923 - !signature.some(function(el){ 924 - return !touched.has(el); 925 - }) 926 - ){ 927 - return; 928 - } 1269 + var target= 1270 + pickTarget(child); 929 929 930 - var applied= 931 - applyGroup(group); 1272 + if(!target)return; 932 932 933 - if(applied){ 934 - signature.forEach(function(el){ 935 - touched.add(el); 1274 + entries.push({ 1275 + target:target, 1276 + rect: 1277 + target.getBoundingClientRect() 936 936 }); 937 - } 1279 + }); 1280 + 1281 + groupRows( 1282 + entries 1283 + ).forEach(function(group){ 1284 + if(!validRow(group))return; 1285 + 1286 + var targets= 1287 + group.entries.map(function(entry){ 1288 + return entry.target; 1289 + }); 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 + }); 938 938 }); 939 - }); 1306 + 1307 + lastWidth=width; 1308 + hasApplied=true; 1309 + }finally{ 1310 + applying=false; 1311 + } 940 940 } 941 941 942 - function schedule(){ 943 - if(raf)cancelAnimationFrame(raf); 1314 + function resize(){ 1315 + if(resizeTimer){ 1316 + clearTimeout( 1317 + resizeTimer 1318 + ); 1319 + } 944 944 945 - raf=requestAnimationFrame(function(){ 946 - raf=requestAnimationFrame(function(){ 947 - raf=0; 948 - run(); 949 - }); 950 - }); 1321 + resizeTimer= 1322 + setTimeout( 1323 + function(){ 1324 + resizeTimer=0; 1325 + calculate(false); 1326 + }, 1327 + RESIZE_DEBOUNCE 1328 + ); 951 951 } 952 952 953 - addEventListener("resize",schedule,{passive:true}); 954 - addEventListener("load",schedule,{once:true}); 1331 + function waitImages(){ 1332 + var images= 1333 + Array.from( 1334 + root.querySelectorAll("img") 1335 + ); 955 955 956 - root.addEventListener( 957 - "click", 958 - function(){ 959 - setTimeout(schedule,0); 960 - }, 961 - true 962 - ); 1337 + return Promise.all( 1338 + images.map(function(img){ 1339 + if(img.complete){ 1340 + return Promise.resolve(); 1341 + } 963 963 964 - Array.from(root.querySelectorAll("img")).forEach(function(img){ 965 - if(!img.complete){ 966 - img.addEventListener("load",schedule,{once:true}); 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 + }); 967 967 } 968 - }); 969 969 970 - if(document.fonts&&document.fonts.ready){ 971 - document.fonts.ready.then(schedule); 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); 972 972 } 973 973 974 - schedule(); 975 - setTimeout(schedule,200); 976 - setTimeout(schedule,650); 977 - setTimeout(schedule,1400); 1393 + addEventListener( 1394 + "resize", 1395 + resize, 1396 + {passive:true} 1397 + ); 1398 + 1399 + initial(); 978 978 })(); 979 979 </script> 980 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 981 -{{/html}} 982 982 1403 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 1404 +{{/html}}\n