Changes for page Plan Your Data Management
Last modified by Veronika Kocher on Friday, 04 Sep 2026 14:45
From version 4.9
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 4.10
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
-
... ... @@ -257,15 +257,104 @@ 257 257 </div> 258 258 {{/html}} 259 259 260 -{{html clean="false"}} 260 +\n\n 261 +\n\n{{html clean="false"}} 261 261 <!-- SN_HORIZONTAL_BOX_HARMONIZER_START --> 262 -<style data-sn-horizontal-box-harmonizer="4"> 263 -[data-sn-equal-height-managed], 264 -[data-sn-first-section-aligned]{ 263 + 264 +<style data-sn-horizontal-box-harmonizer="7"> 265 +/* ---------- Typography: never justify headings ---------- */ 266 +#xwikicontent h1, 267 +#xwikicontent h2, 268 +#xwikicontent h3, 269 +#xwikicontent h4, 270 +#xwikicontent h5, 271 +#xwikicontent h6, 272 +#xwikicontent .box-title, 273 +#xwikicontent [role="heading"], 274 +#xwikicontent h1 a, 275 +#xwikicontent h2 a, 276 +#xwikicontent h3 a, 277 +#xwikicontent h4 a, 278 +#xwikicontent h5 a, 279 +#xwikicontent h6 a, 280 +#xwikicontent .box-title a, 281 +#xwikicontent [role="heading"] a{ 282 + text-align:left!important; 283 + text-justify:auto!important; 284 + word-spacing:normal!important; 285 +} 286 + 287 +/* ---------- Native XWiki semantic callouts ---------- */ 288 +#xwikicontent .box.infomessage, 289 +#xwikicontent .infomessage, 290 +#xwikicontent .box.warningmessage, 291 +#xwikicontent .warningmessage, 292 +#xwikicontent .box.successmessage, 293 +#xwikicontent .successmessage, 294 +#xwikicontent .box.errormessage, 295 +#xwikicontent .errormessage, 296 +#xwikicontent [data-sn-callout-harmonized="1"]{ 297 + box-sizing:border-box!important; 298 + border-radius:8px!important; 299 + border-top:1px solid rgba(0,0,0,.08)!important; 300 + border-right:1px solid rgba(0,0,0,.08)!important; 301 + border-bottom:1px solid rgba(0,0,0,.08)!important; 302 + border-left:4px solid var(--sn-callout-accent,#21b6d7)!important; 303 + padding:14px 16px!important; 304 + text-align:left!important; 305 + text-justify:auto!important; 306 + word-spacing:normal!important; 307 + box-shadow:none!important; 308 +} 309 + 310 +#xwikicontent .box.infomessage, 311 +#xwikicontent .infomessage{ 312 + --sn-callout-accent:#21b6d7; 313 + background:#eaf7fb!important; 314 +} 315 + 316 +#xwikicontent .box.warningmessage, 317 +#xwikicontent .warningmessage{ 318 + --sn-callout-accent:#f0aa00; 319 + background:#fff8e7!important; 320 +} 321 + 322 +#xwikicontent .box.successmessage, 323 +#xwikicontent .successmessage{ 324 + --sn-callout-accent:#38b76a; 325 + background:#edf9f1!important; 326 +} 327 + 328 +#xwikicontent .box.errormessage, 329 +#xwikicontent .errormessage{ 330 + --sn-callout-accent:#e94b13; 331 + background:#fff1ec!important; 332 +} 333 + 334 +#xwikicontent [data-sn-callout-harmonized="1"] > :first-child, 335 +#xwikicontent .infomessage > :first-child, 336 +#xwikicontent .warningmessage > :first-child, 337 +#xwikicontent .successmessage > :first-child, 338 +#xwikicontent .errormessage > :first-child{ 339 + margin-top:0!important; 340 +} 341 + 342 +#xwikicontent [data-sn-callout-harmonized="1"] > :last-child, 343 +#xwikicontent .infomessage > :last-child, 344 +#xwikicontent .warningmessage > :last-child, 345 +#xwikicontent .successmessage > :last-child, 346 +#xwikicontent .errormessage > :last-child{ 347 + margin-bottom:0!important; 348 +} 349 + 350 +#xwikicontent [data-sn-equal-height-managed], 351 +#xwikicontent [data-sn-first-section-aligned], 352 +#xwikicontent [data-sn-first-content-aligned]{ 265 265 box-sizing:border-box; 266 266 } 267 267 </style> 268 -<script data-sn-horizontal-box-harmonizer="4"> 356 + 357 +<script data-sn-horizontal-box-harmonizer="7"> 269 269 (function(){ 270 270 "use strict"; 271 271 ... ... @@ -273,8 +273,10 @@ 273 273 var ROW_TOLERANCE=6; 274 274 var MIN_W=150; 275 275 var MIN_H=36; 276 - var MAX_PARENT_CHILDREN=1 6;365 + var MAX_PARENT_CHILDREN=18; 277 277 var SEARCH_DEPTH=5; 367 + var RESIZE_DEBOUNCE=220; 368 + var MIN_WIDTH_CHANGE=2; 278 278 279 279 var root= 280 280 document.getElementById("xwikicontent")|| ... ... @@ -286,10 +286,16 @@ 286 286 287 287 var heightManaged=new Set(); 288 288 var sectionManaged=new Set(); 380 + var contentManaged=new Set(); 381 + 289 289 var originalMinHeight=new WeakMap(); 290 290 var originalMarginTop=new WeakMap(); 291 - var raf=0; 292 292 385 + var resizeTimer=0; 386 + var lastWidth=-1; 387 + var hasApplied=false; 388 + var applying=false; 389 + 293 293 function num(v){ 294 294 var n=parseFloat(v); 295 295 return Number.isFinite(n)?n:0; ... ... @@ -316,6 +316,13 @@ 316 316 ); 317 317 } 318 318 416 + function normalizeText(text){ 417 + return String(text||"") 418 + .replace(/\s+/g," ") 419 + .trim() 420 + .toUpperCase(); 421 + } 422 + 319 319 function excluded(el){ 320 320 return ( 321 321 !el.matches|| ... ... @@ -325,57 +325,84 @@ 325 325 ); 326 326 } 327 327 328 - function namedLikeBox(el){ 432 + /* ---------- Custom coloured callouts ---------- */ 433 + 434 + function nativeCallout(el){ 329 329 var c= 330 330 typeof el.className==="string" 331 - ? el.className 437 + ? el.className.toLowerCase() 332 332 : ""; 333 333 334 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c); 440 + return ( 441 + /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c) 442 + ); 335 335 } 336 336 337 - function boxLike(el){338 - if(!visible(el, MIN_W,MIN_H)||excluded(el))return false;445 + function customCallout(el){ 446 + if(!visible(el,220,42)||excluded(el))return false; 339 339 340 - var s=getComputedStyle(el); 341 341 var r=el.getBoundingClientRect(); 342 342 343 - var border= 344 - num(s.borderTopWidth)+ 345 - num(s.borderRightWidth)+ 346 - num(s.borderBottomWidth)+ 347 - num(s.borderLeftWidth); 450 + if(r.height>520)return false; 348 348 349 - var radius=Math.max( 350 - num(s.borderTopLeftRadius), 351 - num(s.borderTopRightRadius), 352 - num(s.borderBottomLeftRadius), 353 - num(s.borderBottomRightRadius) 354 - ); 452 + var s=getComputedStyle(el); 355 355 356 356 var bg= 357 357 s.backgroundColor&& 358 358 s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 359 - s.backgroundColor!=="transparent"; 457 + s.backgroundColor!=="transparent"&& 458 + s.backgroundColor!=="rgb(255, 255, 255)"; 360 360 361 - var shadow= 362 - s.boxShadow&& 363 - s.boxShadow!=="none"; 460 + var left= 461 + num(s.borderLeftWidth); 364 364 365 365 return ( 366 - namedLikeBox(el)|| 367 - border>=1|| 368 - (bg&&radius>=3)|| 369 - (shadow&&r.width>=180) 464 + bg&& 465 + left>=2 370 370 ); 371 371 } 372 372 469 + function harmoniseCustomCallouts(){ 470 + Array.from( 471 + root.querySelectorAll( 472 + "div,aside,section" 473 + ) 474 + ).forEach(function(el){ 475 + if(nativeCallout(el))return; 476 + if(!customCallout(el))return; 477 + 478 + var s=getComputedStyle(el); 479 + 480 + el.setAttribute( 481 + "data-sn-callout-harmonized", 482 + "1" 483 + ); 484 + 485 + if( 486 + s.borderLeftColor 487 + ){ 488 + el.style.setProperty( 489 + "--sn-callout-accent", 490 + s.borderLeftColor 491 + ); 492 + } 493 + }); 494 + } 495 + 496 + /* ---------- Restore only values V7 itself changed ---------- */ 497 + 373 373 function saveOriginal(el,map,property){ 374 374 if(map.has(el))return; 375 375 376 376 map.set(el,{ 377 - value:el.style.getPropertyValue(property), 378 - priority:el.style.getPropertyPriority(property) 502 + value: 503 + el.style.getPropertyValue( 504 + property 505 + ), 506 + priority: 507 + el.style.getPropertyPriority( 508 + property 509 + ) 379 379 }); 380 380 } 381 381 ... ... @@ -389,11 +389,13 @@ 389 389 original.priority||"" 390 390 ); 391 391 }else{ 392 - el.style.removeProperty(property); 523 + el.style.removeProperty( 524 + property 525 + ); 393 393 } 394 394 } 395 395 396 - function reset(){ 529 + function resetRuntime(){ 397 397 heightManaged.forEach(function(el){ 398 398 restoreOriginal( 399 399 el, ... ... @@ -418,16 +418,79 @@ 418 418 ); 419 419 }); 420 420 554 + contentManaged.forEach(function(el){ 555 + restoreOriginal( 556 + el, 557 + originalMarginTop, 558 + "margin-top" 559 + ); 560 + 561 + el.removeAttribute( 562 + "data-sn-first-content-aligned" 563 + ); 564 + }); 565 + 421 421 heightManaged.clear(); 422 422 sectionManaged.clear(); 568 + contentManaged.clear(); 423 423 } 424 424 571 + /* ---------- Card detection ---------- */ 572 + 573 + function namedLikeBox(el){ 574 + var c= 575 + typeof el.className==="string" 576 + ? el.className 577 + : ""; 578 + 579 + return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c); 580 + } 581 + 582 + function boxLike(el){ 583 + if( 584 + !visible(el,MIN_W,MIN_H)|| 585 + excluded(el) 586 + ){ 587 + return false; 588 + } 589 + 590 + var s=getComputedStyle(el); 591 + var r=el.getBoundingClientRect(); 592 + 593 + var border= 594 + num(s.borderTopWidth)+ 595 + num(s.borderRightWidth)+ 596 + num(s.borderBottomWidth)+ 597 + num(s.borderLeftWidth); 598 + 599 + var radius=Math.max( 600 + num(s.borderTopLeftRadius), 601 + num(s.borderTopRightRadius), 602 + num(s.borderBottomLeftRadius), 603 + num(s.borderBottomRightRadius) 604 + ); 605 + 606 + var bg= 607 + s.backgroundColor&& 608 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 609 + s.backgroundColor!=="transparent"; 610 + 611 + var shadow= 612 + s.boxShadow&& 613 + s.boxShadow!=="none"; 614 + 615 + return ( 616 + namedLikeBox(el)|| 617 + border>=1|| 618 + (bg&&radius>=3)|| 619 + (shadow&&r.width>=180) 620 + ); 621 + } 622 + 425 425 function pickTarget(child){ 426 426 if(!visible(child,MIN_W,MIN_H))return null; 427 427 428 - if(boxLike(child)){ 429 - return child; 430 - } 626 + if(boxLike(child))return child; 431 431 432 432 var cr=child.getBoundingClientRect(); 433 433 var q=[]; ... ... @@ -489,7 +489,7 @@ 489 489 return best; 490 490 } 491 491 492 - function group sByVisualRow(entries){688 + function groupRows(entries){ 493 493 var groups=[]; 494 494 495 495 entries ... ... @@ -529,7 +529,7 @@ 529 529 return groups; 530 530 } 531 531 532 - function valid HorizontalGroup(group){728 + function validRow(group){ 533 533 if(group.entries.length<2)return false; 534 534 535 535 var lefts= ... ... @@ -552,25 +552,26 @@ 552 552 553 553 if(widths.length<2)return false; 554 554 555 - var minW idth=Math.min.apply(Math,widths);556 - varmaxWidth=Math.max.apply(Math,widths);751 + var minW= 752 + Math.min.apply(Math,widths); 557 557 754 + var maxW= 755 + Math.max.apply(Math,widths); 756 + 558 558 return ( 559 - maxW idth/560 - Math.max(1,minW idth)<=758 + maxW/ 759 + Math.max(1,minW)<= 561 561 1.9 562 562 ); 563 563 } 564 564 565 - function normalizeText(text){ 566 - return String(text||"") 567 - .replace(/s+/g," ") 568 - .trim() 569 - .toUpperCase(); 570 - } 764 + /* ---------- Known ARSN first section bars ---------- */ 571 571 572 - function exactKnownFirstSection(el){ 573 - var text=normalizeText(el.textContent); 766 + function knownBar(el){ 767 + var text= 768 + normalizeText( 769 + el.textContent 770 + ); 574 574 575 575 return ( 576 576 text==="HIER STARTEN"|| ... ... @@ -584,150 +584,274 @@ 584 584 ); 585 585 } 586 586 587 - function visuallyFirstSectionBar(card){588 - var cardRect= card.getBoundingClientRect();589 - varcandidates=[];784 + function findKnownBar(card){ 785 + var cardRect= 786 + card.getBoundingClientRect(); 590 590 591 - Array.from( 592 - card.querySelectorAll( 593 - "div,p,span,strong,h3,h4,h5,h6" 788 + var rows= 789 + Array.from( 790 + card.querySelectorAll( 791 + "div,p,span,strong,h3,h4,h5,h6" 792 + ) 594 594 ) 595 - ).forEach(function(el){596 - if(!visible(el,20,6))return; 597 - if( excluded(el))return;794 + .filter(function(el){ 795 + if(!visible(el,20,6))return false; 796 + if(!knownBar(el))return false; 598 598 599 - var r=el.getBoundingClientRect(); 600 - var relativeTop=r.top-cardRect.top; 798 + var r=el.getBoundingClientRect(); 601 601 602 - if( 603 - relativeTop<45|| 604 - relativeTop>cardRect.height*.72 605 - ){ 606 - return; 607 - } 800 + var rel= 801 + r.top- 802 + cardRect.top; 608 608 609 - var widthRatio= 610 - r.width/ 611 - Math.max(1,cardRect.width); 804 + return ( 805 + rel>=35&& 806 + rel<= 807 + cardRect.height*.75 808 + ); 809 + }) 810 + .sort(function(a,b){ 811 + return ( 812 + a.getBoundingClientRect().top- 813 + b.getBoundingClientRect().top 814 + ); 815 + }); 612 612 613 - if( 614 - widthRatio<.55|| 615 - widthRatio>1.05|| 616 - r.height>54 617 - ){ 618 - return; 619 - } 817 + return rows[0]||null; 818 + } 620 620 621 - var text=normalizeText(el.textContent); 820 + function alignKnownBars(cards){ 821 + var rows= 822 + cards.map(function(card){ 823 + var bar= 824 + findKnownBar(card); 622 622 623 - if( 624 - text.length<2|| 625 - text.length>90 626 - ){ 627 - return; 628 - } 826 + if(!bar)return null; 629 629 630 - var style=getComputedStyle(el); 828 + var cr= 829 + card.getBoundingClientRect(); 631 631 632 - var bg= 633 - style.backgroundColor&& 634 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&& 635 - style.backgroundColor!=="transparent"; 831 + var br= 832 + bar.getBoundingClientRect(); 636 636 637 - var border= 638 - num(style.borderTopWidth)+ 639 - num(style.borderRightWidth)+ 640 - num(style.borderBottomWidth)+ 641 - num(style.borderLeftWidth); 834 + return { 835 + bar:bar, 836 + relativeTop: 837 + br.top- 838 + cr.top 839 + }; 840 + }); 642 642 643 - var mostlyUpper= 644 - text.length>=3&& 645 - text===text.toUpperCase(); 842 + if( 843 + rows.some(function(row){ 844 + return !row; 845 + }) 846 + ){ 847 + return false; 848 + } 646 646 647 - var exact=exactKnownFirstSection(el); 850 + var target= 851 + Math.max.apply( 852 + Math, 853 + rows.map(function(row){ 854 + return row.relativeTop; 855 + }) 856 + ); 648 648 649 - var score= 650 - (exact?20:0)+ 651 - (bg?6:0)+ 652 - (border>=1?2:0)+ 653 - (mostlyUpper?4:0)+ 654 - (num(style.fontWeight)>=600?1:0); 858 + rows.forEach(function(row){ 859 + var delta= 860 + target- 861 + row.relativeTop; 655 655 656 - if(score>=7){ 657 - candidates.push({ 658 - el:el, 659 - relativeTop:relativeTop, 660 - width:r.width, 661 - score:score 662 - }); 663 - } 664 - }); 863 + if(delta<=1)return; 665 665 666 - if(!candidates.length)return null; 865 + saveOriginal( 866 + row.bar, 867 + originalMarginTop, 868 + "margin-top" 869 + ); 667 667 668 - candidates.sort(function(a,b){ 669 - if(a.score!==b.score)return b.score-a.score; 670 - if(Math.abs(a.relativeTop-b.relativeTop)>3){ 671 - return a.relativeTop-b.relativeTop; 672 - } 673 - return b.width-a.width; 674 - }); 871 + var current= 872 + num( 873 + getComputedStyle( 874 + row.bar 875 + ).marginTop 876 + ); 675 675 676 - // For exact known labels always prefer the earliest exact match. 677 - var exacts=candidates.filter(function(c){ 678 - return exactKnownFirstSection(c.el); 878 + row.bar.style.setProperty( 879 + "margin-top", 880 + ( 881 + current+ 882 + delta 883 + )+"px" 884 + ); 885 + 886 + row.bar.setAttribute( 887 + "data-sn-first-section-aligned", 888 + "7" 889 + ); 890 + 891 + sectionManaged.add( 892 + row.bar 893 + ); 679 679 }); 680 680 681 - if(exacts.length){ 682 - exacts.sort(function(a,b){ 683 - return a.relativeTop-b.relativeTop||b.width-a.width; 896 + return true; 897 + } 898 + 899 + /* ---------- Generic first content after heading ---------- */ 900 + 901 + function heading(card){ 902 + var cr= 903 + card.getBoundingClientRect(); 904 + 905 + var rows= 906 + Array.from( 907 + card.querySelectorAll( 908 + "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]" 909 + ) 910 + ) 911 + .filter(function(el){ 912 + if(!visible(el,20,8))return false; 913 + 914 + var r=el.getBoundingClientRect(); 915 + 916 + var rel= 917 + r.top- 918 + cr.top; 919 + 920 + return ( 921 + rel>=0&& 922 + rel<= 923 + Math.min( 924 + 160, 925 + cr.height*.45 926 + ) 927 + ); 928 + }) 929 + .sort(function(a,b){ 930 + return ( 931 + a.getBoundingClientRect().top- 932 + b.getBoundingClientRect().top 933 + ); 684 684 }); 685 - return exacts[0].el; 686 - } 687 687 688 - // Generic fallback: earliest high-confidence bar. 689 - candidates.sort(function(a,b){ 690 - return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width; 691 - }); 936 + return rows[0]||null; 937 + } 692 692 693 - return candidates[0].el; 939 + function firstContent(card,h){ 940 + if(!h)return null; 941 + 942 + var cr= 943 + card.getBoundingClientRect(); 944 + 945 + var hr= 946 + h.getBoundingClientRect(); 947 + 948 + var rows= 949 + Array.from( 950 + card.querySelectorAll( 951 + "p,div,ul,ol,dl,blockquote" 952 + ) 953 + ) 954 + .filter(function(el){ 955 + if( 956 + el===h|| 957 + h.contains(el)|| 958 + el.contains(h) 959 + ){ 960 + return false; 961 + } 962 + 963 + if(!visible(el,30,8))return false; 964 + 965 + var text= 966 + String(el.textContent||"") 967 + .replace(/\s+/g," ") 968 + .trim(); 969 + 970 + if(text.length<3)return false; 971 + 972 + var r=el.getBoundingClientRect(); 973 + 974 + if( 975 + r.top< 976 + hr.bottom-1 977 + ){ 978 + return false; 979 + } 980 + 981 + if( 982 + r.top-cr.top> 983 + Math.min( 984 + 280, 985 + cr.height*.65 986 + ) 987 + ){ 988 + return false; 989 + } 990 + 991 + if( 992 + r.height> 993 + cr.height*.58 994 + ){ 995 + return false; 996 + } 997 + 998 + return true; 999 + }) 1000 + .sort(function(a,b){ 1001 + var ar= 1002 + a.getBoundingClientRect(); 1003 + 1004 + var br= 1005 + b.getBoundingClientRect(); 1006 + 1007 + return ( 1008 + ar.top-br.top|| 1009 + ar.height-br.height 1010 + ); 1011 + }); 1012 + 1013 + return rows[0]||null; 694 694 } 695 695 696 - function align FirstSections(cards){1016 + function alignGeneric(cards){ 697 697 var rows= 698 698 cards.map(function(card){ 699 - var bar=700 - visuallyFirstSectionBar(card);1019 + var h= 1020 + heading(card); 701 701 702 - if(!bar)return null; 1022 + var c= 1023 + firstContent( 1024 + card, 1025 + h 1026 + ); 703 703 704 - var cardRect= 1028 + if(!h||!c)return null; 1029 + 1030 + var cr= 705 705 card.getBoundingClientRect(); 706 706 707 - var barRect=708 - bar.getBoundingClientRect();1033 + var rr= 1034 + c.getBoundingClientRect(); 709 709 710 710 return { 711 - card:card, 712 - bar:bar, 1037 + content:c, 713 713 relativeTop: 714 - barRect.top-cardRect.top, 715 - label: 716 - normalizeText(bar.textContent) 1039 + rr.top-cr.top 717 717 }; 718 718 }); 719 719 720 - // Conservative: only align when every card in the horizontal row 721 - // has a reliable first section bar. 722 722 if( 723 723 rows.some(function(row){ 724 724 return !row; 725 725 }) 726 726 ){ 727 - return null;1048 + return false; 728 728 } 729 729 730 - var target Top=1051 + var target= 731 731 Math.max.apply( 732 732 Math, 733 733 rows.map(function(row){ ... ... @@ -737,54 +737,47 @@ 737 737 738 738 rows.forEach(function(row){ 739 739 var delta= 740 - target Top-1061 + target- 741 741 row.relativeTop; 742 742 743 743 if(delta<=1)return; 744 744 745 745 saveOriginal( 746 - row. bar,1067 + row.content, 747 747 originalMarginTop, 748 748 "margin-top" 749 749 ); 750 750 751 - var current Margin=1072 + var current= 752 752 num( 753 753 getComputedStyle( 754 - row. bar1075 + row.content 755 755 ).marginTop 756 756 ); 757 757 758 - row. bar.style.setProperty(1079 + row.content.style.setProperty( 759 759 "margin-top", 760 760 ( 761 - current Margin+1082 + current+ 762 762 delta 763 763 )+"px" 764 764 ); 765 765 766 - row. bar.setAttribute(767 - "data-sn-first- section-aligned",768 - " 4"1087 + row.content.setAttribute( 1088 + "data-sn-first-content-aligned", 1089 + "7" 769 769 ); 770 770 771 - sectionManaged.add(row.bar); 1092 + contentManaged.add( 1093 + row.content 1094 + ); 772 772 }); 773 773 774 - return { 775 - labels: 776 - rows.map(function(row){ 777 - return row.label; 778 - }), 779 - targetRelativeTop: 780 - Math.round(targetTop) 781 - }; 1097 + return true; 782 782 } 783 783 784 - function applyGroup(group){ 785 - if(!validHorizontalGroup(group)){ 786 - return null; 787 - } 1100 + function applyRow(group){ 1101 + if(!validRow(group))return false; 788 788 789 789 var cards=[]; 790 790 var seen=new Set(); ... ... @@ -796,25 +796,30 @@ 796 796 } 797 797 }); 798 798 799 - if(cards.length<2)return null;1113 + if(cards.length<2)return false; 800 800 801 - var firstSection= 802 - alignFirstSections(cards); 1115 + if( 1116 + !alignKnownBars(cards) 1117 + ){ 1118 + alignGeneric(cards); 1119 + } 803 803 804 - // The section alignment can change natural card heights. 805 - // Measure only afterwards. 806 806 var maxHeight=0; 807 807 808 808 cards.forEach(function(card){ 809 - maxHeight=Math.max( 810 - maxHeight, 811 - card.getBoundingClientRect().height 812 - ); 1124 + maxHeight= 1125 + Math.max( 1126 + maxHeight, 1127 + card.getBoundingClientRect().height 1128 + ); 813 813 }); 814 814 815 - maxHeight=Math.ceil(maxHeight); 1131 + maxHeight= 1132 + Math.ceil( 1133 + maxHeight 1134 + ); 816 816 817 - if(maxHeight<MIN_H)return null;1136 + if(maxHeight<MIN_H)return false; 818 818 819 819 cards.forEach(function(card){ 820 820 saveOriginal( ... ... @@ -830,125 +830,228 @@ 830 830 831 831 card.setAttribute( 832 832 "data-sn-equal-height-managed", 833 - " 4"1152 + "7" 834 834 ); 835 835 836 836 heightManaged.add(card); 837 837 }); 838 838 839 - return { 840 - cards:cards.length, 841 - height:maxHeight, 842 - firstSection:firstSection 843 - }; 1158 + return true; 844 844 } 845 845 846 - function run(){847 - re set();1161 + function calculate(force){ 1162 + if(applying)return; 848 848 1164 + var width= 1165 + Math.round( 1166 + window.innerWidth 1167 + ); 1168 + 1169 + var meaningful= 1170 + lastWidth<0|| 1171 + Math.abs( 1172 + width- 1173 + lastWidth 1174 + )>= 1175 + MIN_WIDTH_CHANGE; 1176 + 849 849 if( 850 - window.innerWidth<=MOBILE_MAX 1178 + hasApplied&& 1179 + !force&& 1180 + !meaningful 851 851 ){ 852 852 return; 853 853 } 854 854 855 - var parents= 856 - Array.from( 857 - root.querySelectorAll("*") 858 - ).filter(function(parent){ 859 - return ( 860 - visible(parent,MIN_W,MIN_H)&& 861 - parent.children&& 862 - parent.children.length>=2&& 863 - parent.children.length<=MAX_PARENT_CHILDREN 864 - ); 865 - }); 1185 + applying=true; 866 866 867 - var touched=new Set(); 1187 + try{ 1188 + harmoniseCustomCallouts(); 868 868 869 - parents.forEach(function(parent){ 870 - var entries=[]; 1190 + if(width<=MOBILE_MAX){ 1191 + if(hasApplied){ 1192 + resetRuntime(); 1193 + } 871 871 872 - Array.from(parent.children).forEach(function(child){ 873 - if(!visible(child,MIN_W,MIN_H))return; 1195 + hasApplied=false; 1196 + lastWidth=width; 1197 + return; 1198 + } 874 874 875 - var target=pickTarget(child); 876 - if(!target)return; 1200 + if(hasApplied){ 1201 + resetRuntime(); 1202 + } 877 877 878 - entries.push({ 879 - target:target, 880 - rect:target.getBoundingClientRect() 1204 + var parents= 1205 + Array.from( 1206 + root.querySelectorAll("*") 1207 + ) 1208 + .filter(function(parent){ 1209 + return ( 1210 + visible( 1211 + parent, 1212 + MIN_W, 1213 + MIN_H 1214 + )&& 1215 + parent.children&& 1216 + parent.children.length>=2&& 1217 + parent.children.length<= 1218 + MAX_PARENT_CHILDREN 1219 + ); 881 881 }); 882 - }); 883 883 884 - if(entries.length<2)return; 1222 + var touched= 1223 + new Set(); 885 885 886 - groupsByVisualRow(entries).forEach(function(group){887 - if(!validHorizontalGroup(group))return;1225 + parents.forEach(function(parent){ 1226 + var entries=[]; 888 888 889 - var signature= 890 - group.entries.map(function(entry){ 891 - return entry.target; 892 - }); 1228 + Array.from( 1229 + parent.children 1230 + ).forEach(function(child){ 1231 + if( 1232 + !visible( 1233 + child, 1234 + MIN_W, 1235 + MIN_H 1236 + ) 1237 + ){ 1238 + return; 1239 + } 893 893 894 - if( 895 - !signature.some(function(el){ 896 - return !touched.has(el); 897 - }) 898 - ){ 899 - return; 900 - } 1241 + var target= 1242 + pickTarget(child); 901 901 902 - var applied= 903 - applyGroup(group); 1244 + if(!target)return; 904 904 905 - if(applied){ 906 - signature.forEach(function(el){ 907 - touched.add(el); 1246 + entries.push({ 1247 + target:target, 1248 + rect: 1249 + target.getBoundingClientRect() 908 908 }); 909 - } 1251 + }); 1252 + 1253 + groupRows( 1254 + entries 1255 + ).forEach(function(group){ 1256 + if(!validRow(group))return; 1257 + 1258 + var targets= 1259 + group.entries.map(function(entry){ 1260 + return entry.target; 1261 + }); 1262 + 1263 + if( 1264 + !targets.some(function(el){ 1265 + return !touched.has(el); 1266 + }) 1267 + ){ 1268 + return; 1269 + } 1270 + 1271 + if(applyRow(group)){ 1272 + targets.forEach(function(el){ 1273 + touched.add(el); 1274 + }); 1275 + } 1276 + }); 910 910 }); 911 - }); 1278 + 1279 + lastWidth=width; 1280 + hasApplied=true; 1281 + }finally{ 1282 + applying=false; 1283 + } 912 912 } 913 913 914 - function schedule(){ 915 - if(raf)cancelAnimationFrame(raf); 1286 + function resize(){ 1287 + if(resizeTimer){ 1288 + clearTimeout( 1289 + resizeTimer 1290 + ); 1291 + } 916 916 917 - raf=requestAnimationFrame(function(){ 918 - raf=requestAnimationFrame(function(){ 919 - raf=0; 920 - run(); 921 - }); 922 - }); 1293 + resizeTimer= 1294 + setTimeout( 1295 + function(){ 1296 + resizeTimer=0; 1297 + calculate(false); 1298 + }, 1299 + RESIZE_DEBOUNCE 1300 + ); 923 923 } 924 924 925 - addEventListener("resize",schedule,{passive:true}); 926 - addEventListener("load",schedule,{once:true}); 1303 + function waitImages(){ 1304 + var images= 1305 + Array.from( 1306 + root.querySelectorAll("img") 1307 + ); 927 927 928 - root.addEventListener( 929 - "click", 930 - function(){ 931 - setTimeout(schedule,0); 932 - }, 933 - true 934 - ); 1309 + return Promise.all( 1310 + images.map(function(img){ 1311 + if(img.complete){ 1312 + return Promise.resolve(); 1313 + } 935 935 936 - Array.from(root.querySelectorAll("img")).forEach(function(img){ 937 - if(!img.complete){ 938 - img.addEventListener("load",schedule,{once:true}); 1315 + return new Promise(function(resolve){ 1316 + var done=function(){ 1317 + resolve(); 1318 + }; 1319 + 1320 + img.addEventListener( 1321 + "load", 1322 + done, 1323 + {once:true} 1324 + ); 1325 + 1326 + img.addEventListener( 1327 + "error", 1328 + done, 1329 + {once:true} 1330 + ); 1331 + }); 1332 + }) 1333 + ); 1334 + } 1335 + 1336 + async function initial(){ 1337 + if( 1338 + document.readyState!== 1339 + "complete" 1340 + ){ 1341 + await new Promise(function(resolve){ 1342 + addEventListener( 1343 + "load", 1344 + resolve, 1345 + {once:true} 1346 + ); 1347 + }); 939 939 } 940 - }); 941 941 942 - if(document.fonts&&document.fonts.ready){ 943 - document.fonts.ready.then(schedule); 1350 + if( 1351 + document.fonts&& 1352 + document.fonts.ready 1353 + ){ 1354 + try{ 1355 + await document.fonts.ready; 1356 + }catch(error){} 1357 + } 1358 + 1359 + await waitImages(); 1360 + 1361 + // Exactly one initial layout calculation. 1362 + calculate(true); 944 944 } 945 945 946 - schedule(); 947 - setTimeout(schedule,200); 948 - setTimeout(schedule,650); 949 - setTimeout(schedule,1400); 1365 + addEventListener( 1366 + "resize", 1367 + resize, 1368 + {passive:true} 1369 + ); 1370 + 1371 + initial(); 950 950 })(); 951 951 </script> 952 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 953 -{{/html}} 954 954 1375 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 1376 +{{/html}}\n