Changes for page Copyright, Rights & Licences
Last modified by Veronika Kocher on Friday, 18 Sep 2026 16:47
From version 5.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 5.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
-
... ... @@ -68,15 +68,104 @@ 68 68 <!-- ARSN_SEARCH_TERMS_END --> 69 69 {{/html}} 70 70 71 -{{html clean="false"}} 71 +\n\n 72 +\n\n{{html clean="false"}} 72 72 <!-- SN_HORIZONTAL_BOX_HARMONIZER_START --> 73 -<style data-sn-horizontal-box-harmonizer="4"> 74 -[data-sn-equal-height-managed], 75 -[data-sn-first-section-aligned]{ 74 + 75 +<style data-sn-horizontal-box-harmonizer="7"> 76 +/* ---------- Typography: never justify headings ---------- */ 77 +#xwikicontent h1, 78 +#xwikicontent h2, 79 +#xwikicontent h3, 80 +#xwikicontent h4, 81 +#xwikicontent h5, 82 +#xwikicontent h6, 83 +#xwikicontent .box-title, 84 +#xwikicontent [role="heading"], 85 +#xwikicontent h1 a, 86 +#xwikicontent h2 a, 87 +#xwikicontent h3 a, 88 +#xwikicontent h4 a, 89 +#xwikicontent h5 a, 90 +#xwikicontent h6 a, 91 +#xwikicontent .box-title a, 92 +#xwikicontent [role="heading"] a{ 93 + text-align:left!important; 94 + text-justify:auto!important; 95 + word-spacing:normal!important; 96 +} 97 + 98 +/* ---------- Native XWiki semantic callouts ---------- */ 99 +#xwikicontent .box.infomessage, 100 +#xwikicontent .infomessage, 101 +#xwikicontent .box.warningmessage, 102 +#xwikicontent .warningmessage, 103 +#xwikicontent .box.successmessage, 104 +#xwikicontent .successmessage, 105 +#xwikicontent .box.errormessage, 106 +#xwikicontent .errormessage, 107 +#xwikicontent [data-sn-callout-harmonized="1"]{ 108 + box-sizing:border-box!important; 109 + border-radius:8px!important; 110 + border-top:1px solid rgba(0,0,0,.08)!important; 111 + border-right:1px solid rgba(0,0,0,.08)!important; 112 + border-bottom:1px solid rgba(0,0,0,.08)!important; 113 + border-left:4px solid var(--sn-callout-accent,#21b6d7)!important; 114 + padding:14px 16px!important; 115 + text-align:left!important; 116 + text-justify:auto!important; 117 + word-spacing:normal!important; 118 + box-shadow:none!important; 119 +} 120 + 121 +#xwikicontent .box.infomessage, 122 +#xwikicontent .infomessage{ 123 + --sn-callout-accent:#21b6d7; 124 + background:#eaf7fb!important; 125 +} 126 + 127 +#xwikicontent .box.warningmessage, 128 +#xwikicontent .warningmessage{ 129 + --sn-callout-accent:#f0aa00; 130 + background:#fff8e7!important; 131 +} 132 + 133 +#xwikicontent .box.successmessage, 134 +#xwikicontent .successmessage{ 135 + --sn-callout-accent:#38b76a; 136 + background:#edf9f1!important; 137 +} 138 + 139 +#xwikicontent .box.errormessage, 140 +#xwikicontent .errormessage{ 141 + --sn-callout-accent:#e94b13; 142 + background:#fff1ec!important; 143 +} 144 + 145 +#xwikicontent [data-sn-callout-harmonized="1"] > :first-child, 146 +#xwikicontent .infomessage > :first-child, 147 +#xwikicontent .warningmessage > :first-child, 148 +#xwikicontent .successmessage > :first-child, 149 +#xwikicontent .errormessage > :first-child{ 150 + margin-top:0!important; 151 +} 152 + 153 +#xwikicontent [data-sn-callout-harmonized="1"] > :last-child, 154 +#xwikicontent .infomessage > :last-child, 155 +#xwikicontent .warningmessage > :last-child, 156 +#xwikicontent .successmessage > :last-child, 157 +#xwikicontent .errormessage > :last-child{ 158 + margin-bottom:0!important; 159 +} 160 + 161 +#xwikicontent [data-sn-equal-height-managed], 162 +#xwikicontent [data-sn-first-section-aligned], 163 +#xwikicontent [data-sn-first-content-aligned]{ 76 76 box-sizing:border-box; 77 77 } 78 78 </style> 79 -<script data-sn-horizontal-box-harmonizer="4"> 167 + 168 +<script data-sn-horizontal-box-harmonizer="7"> 80 80 (function(){ 81 81 "use strict"; 82 82 ... ... @@ -84,8 +84,10 @@ 84 84 var ROW_TOLERANCE=6; 85 85 var MIN_W=150; 86 86 var MIN_H=36; 87 - var MAX_PARENT_CHILDREN=1 6;176 + var MAX_PARENT_CHILDREN=18; 88 88 var SEARCH_DEPTH=5; 178 + var RESIZE_DEBOUNCE=220; 179 + var MIN_WIDTH_CHANGE=2; 89 89 90 90 var root= 91 91 document.getElementById("xwikicontent")|| ... ... @@ -97,10 +97,16 @@ 97 97 98 98 var heightManaged=new Set(); 99 99 var sectionManaged=new Set(); 191 + var contentManaged=new Set(); 192 + 100 100 var originalMinHeight=new WeakMap(); 101 101 var originalMarginTop=new WeakMap(); 102 - var raf=0; 103 103 196 + var resizeTimer=0; 197 + var lastWidth=-1; 198 + var hasApplied=false; 199 + var applying=false; 200 + 104 104 function num(v){ 105 105 var n=parseFloat(v); 106 106 return Number.isFinite(n)?n:0; ... ... @@ -127,6 +127,13 @@ 127 127 ); 128 128 } 129 129 227 + function normalizeText(text){ 228 + return String(text||"") 229 + .replace(/\s+/g," ") 230 + .trim() 231 + .toUpperCase(); 232 + } 233 + 130 130 function excluded(el){ 131 131 return ( 132 132 !el.matches|| ... ... @@ -136,57 +136,84 @@ 136 136 ); 137 137 } 138 138 139 - function namedLikeBox(el){ 243 + /* ---------- Custom coloured callouts ---------- */ 244 + 245 + function nativeCallout(el){ 140 140 var c= 141 141 typeof el.className==="string" 142 - ? el.className 248 + ? el.className.toLowerCase() 143 143 : ""; 144 144 145 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c); 251 + return ( 252 + /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c) 253 + ); 146 146 } 147 147 148 - function boxLike(el){149 - if(!visible(el, MIN_W,MIN_H)||excluded(el))return false;256 + function customCallout(el){ 257 + if(!visible(el,220,42)||excluded(el))return false; 150 150 151 - var s=getComputedStyle(el); 152 152 var r=el.getBoundingClientRect(); 153 153 154 - var border= 155 - num(s.borderTopWidth)+ 156 - num(s.borderRightWidth)+ 157 - num(s.borderBottomWidth)+ 158 - num(s.borderLeftWidth); 261 + if(r.height>520)return false; 159 159 160 - var radius=Math.max( 161 - num(s.borderTopLeftRadius), 162 - num(s.borderTopRightRadius), 163 - num(s.borderBottomLeftRadius), 164 - num(s.borderBottomRightRadius) 165 - ); 263 + var s=getComputedStyle(el); 166 166 167 167 var bg= 168 168 s.backgroundColor&& 169 169 s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 170 - s.backgroundColor!=="transparent"; 268 + s.backgroundColor!=="transparent"&& 269 + s.backgroundColor!=="rgb(255, 255, 255)"; 171 171 172 - var shadow= 173 - s.boxShadow&& 174 - s.boxShadow!=="none"; 271 + var left= 272 + num(s.borderLeftWidth); 175 175 176 176 return ( 177 - namedLikeBox(el)|| 178 - border>=1|| 179 - (bg&&radius>=3)|| 180 - (shadow&&r.width>=180) 275 + bg&& 276 + left>=2 181 181 ); 182 182 } 183 183 280 + function harmoniseCustomCallouts(){ 281 + Array.from( 282 + root.querySelectorAll( 283 + "div,aside,section" 284 + ) 285 + ).forEach(function(el){ 286 + if(nativeCallout(el))return; 287 + if(!customCallout(el))return; 288 + 289 + var s=getComputedStyle(el); 290 + 291 + el.setAttribute( 292 + "data-sn-callout-harmonized", 293 + "1" 294 + ); 295 + 296 + if( 297 + s.borderLeftColor 298 + ){ 299 + el.style.setProperty( 300 + "--sn-callout-accent", 301 + s.borderLeftColor 302 + ); 303 + } 304 + }); 305 + } 306 + 307 + /* ---------- Restore only values V7 itself changed ---------- */ 308 + 184 184 function saveOriginal(el,map,property){ 185 185 if(map.has(el))return; 186 186 187 187 map.set(el,{ 188 - value:el.style.getPropertyValue(property), 189 - priority:el.style.getPropertyPriority(property) 313 + value: 314 + el.style.getPropertyValue( 315 + property 316 + ), 317 + priority: 318 + el.style.getPropertyPriority( 319 + property 320 + ) 190 190 }); 191 191 } 192 192 ... ... @@ -200,11 +200,13 @@ 200 200 original.priority||"" 201 201 ); 202 202 }else{ 203 - el.style.removeProperty(property); 334 + el.style.removeProperty( 335 + property 336 + ); 204 204 } 205 205 } 206 206 207 - function reset(){ 340 + function resetRuntime(){ 208 208 heightManaged.forEach(function(el){ 209 209 restoreOriginal( 210 210 el, ... ... @@ -229,16 +229,79 @@ 229 229 ); 230 230 }); 231 231 365 + contentManaged.forEach(function(el){ 366 + restoreOriginal( 367 + el, 368 + originalMarginTop, 369 + "margin-top" 370 + ); 371 + 372 + el.removeAttribute( 373 + "data-sn-first-content-aligned" 374 + ); 375 + }); 376 + 232 232 heightManaged.clear(); 233 233 sectionManaged.clear(); 379 + contentManaged.clear(); 234 234 } 235 235 382 + /* ---------- Card detection ---------- */ 383 + 384 + function namedLikeBox(el){ 385 + var c= 386 + typeof el.className==="string" 387 + ? el.className 388 + : ""; 389 + 390 + return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c); 391 + } 392 + 393 + function boxLike(el){ 394 + if( 395 + !visible(el,MIN_W,MIN_H)|| 396 + excluded(el) 397 + ){ 398 + return false; 399 + } 400 + 401 + var s=getComputedStyle(el); 402 + var r=el.getBoundingClientRect(); 403 + 404 + var border= 405 + num(s.borderTopWidth)+ 406 + num(s.borderRightWidth)+ 407 + num(s.borderBottomWidth)+ 408 + num(s.borderLeftWidth); 409 + 410 + var radius=Math.max( 411 + num(s.borderTopLeftRadius), 412 + num(s.borderTopRightRadius), 413 + num(s.borderBottomLeftRadius), 414 + num(s.borderBottomRightRadius) 415 + ); 416 + 417 + var bg= 418 + s.backgroundColor&& 419 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 420 + s.backgroundColor!=="transparent"; 421 + 422 + var shadow= 423 + s.boxShadow&& 424 + s.boxShadow!=="none"; 425 + 426 + return ( 427 + namedLikeBox(el)|| 428 + border>=1|| 429 + (bg&&radius>=3)|| 430 + (shadow&&r.width>=180) 431 + ); 432 + } 433 + 236 236 function pickTarget(child){ 237 237 if(!visible(child,MIN_W,MIN_H))return null; 238 238 239 - if(boxLike(child)){ 240 - return child; 241 - } 437 + if(boxLike(child))return child; 242 242 243 243 var cr=child.getBoundingClientRect(); 244 244 var q=[]; ... ... @@ -300,7 +300,7 @@ 300 300 return best; 301 301 } 302 302 303 - function group sByVisualRow(entries){499 + function groupRows(entries){ 304 304 var groups=[]; 305 305 306 306 entries ... ... @@ -340,7 +340,7 @@ 340 340 return groups; 341 341 } 342 342 343 - function valid HorizontalGroup(group){539 + function validRow(group){ 344 344 if(group.entries.length<2)return false; 345 345 346 346 var lefts= ... ... @@ -363,25 +363,26 @@ 363 363 364 364 if(widths.length<2)return false; 365 365 366 - var minW idth=Math.min.apply(Math,widths);367 - varmaxWidth=Math.max.apply(Math,widths);562 + var minW= 563 + Math.min.apply(Math,widths); 368 368 565 + var maxW= 566 + Math.max.apply(Math,widths); 567 + 369 369 return ( 370 - maxW idth/371 - Math.max(1,minW idth)<=569 + maxW/ 570 + Math.max(1,minW)<= 372 372 1.9 373 373 ); 374 374 } 375 375 376 - function normalizeText(text){ 377 - return String(text||"") 378 - .replace(/s+/g," ") 379 - .trim() 380 - .toUpperCase(); 381 - } 575 + /* ---------- Known ARSN first section bars ---------- */ 382 382 383 - function exactKnownFirstSection(el){ 384 - var text=normalizeText(el.textContent); 577 + function knownBar(el){ 578 + var text= 579 + normalizeText( 580 + el.textContent 581 + ); 385 385 386 386 return ( 387 387 text==="HIER STARTEN"|| ... ... @@ -395,150 +395,274 @@ 395 395 ); 396 396 } 397 397 398 - function visuallyFirstSectionBar(card){399 - var cardRect= card.getBoundingClientRect();400 - varcandidates=[];595 + function findKnownBar(card){ 596 + var cardRect= 597 + card.getBoundingClientRect(); 401 401 402 - Array.from( 403 - card.querySelectorAll( 404 - "div,p,span,strong,h3,h4,h5,h6" 599 + var rows= 600 + Array.from( 601 + card.querySelectorAll( 602 + "div,p,span,strong,h3,h4,h5,h6" 603 + ) 405 405 ) 406 - ).forEach(function(el){407 - if(!visible(el,20,6))return; 408 - if( excluded(el))return;605 + .filter(function(el){ 606 + if(!visible(el,20,6))return false; 607 + if(!knownBar(el))return false; 409 409 410 - var r=el.getBoundingClientRect(); 411 - var relativeTop=r.top-cardRect.top; 609 + var r=el.getBoundingClientRect(); 412 412 413 - if( 414 - relativeTop<45|| 415 - relativeTop>cardRect.height*.72 416 - ){ 417 - return; 418 - } 611 + var rel= 612 + r.top- 613 + cardRect.top; 419 419 420 - var widthRatio= 421 - r.width/ 422 - Math.max(1,cardRect.width); 615 + return ( 616 + rel>=35&& 617 + rel<= 618 + cardRect.height*.75 619 + ); 620 + }) 621 + .sort(function(a,b){ 622 + return ( 623 + a.getBoundingClientRect().top- 624 + b.getBoundingClientRect().top 625 + ); 626 + }); 423 423 424 - if( 425 - widthRatio<.55|| 426 - widthRatio>1.05|| 427 - r.height>54 428 - ){ 429 - return; 430 - } 628 + return rows[0]||null; 629 + } 431 431 432 - var text=normalizeText(el.textContent); 631 + function alignKnownBars(cards){ 632 + var rows= 633 + cards.map(function(card){ 634 + var bar= 635 + findKnownBar(card); 433 433 434 - if( 435 - text.length<2|| 436 - text.length>90 437 - ){ 438 - return; 439 - } 637 + if(!bar)return null; 440 440 441 - var style=getComputedStyle(el); 639 + var cr= 640 + card.getBoundingClientRect(); 442 442 443 - var bg= 444 - style.backgroundColor&& 445 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&& 446 - style.backgroundColor!=="transparent"; 642 + var br= 643 + bar.getBoundingClientRect(); 447 447 448 - var border= 449 - num(style.borderTopWidth)+ 450 - num(style.borderRightWidth)+ 451 - num(style.borderBottomWidth)+ 452 - num(style.borderLeftWidth); 645 + return { 646 + bar:bar, 647 + relativeTop: 648 + br.top- 649 + cr.top 650 + }; 651 + }); 453 453 454 - var mostlyUpper= 455 - text.length>=3&& 456 - text===text.toUpperCase(); 653 + if( 654 + rows.some(function(row){ 655 + return !row; 656 + }) 657 + ){ 658 + return false; 659 + } 457 457 458 - var exact=exactKnownFirstSection(el); 661 + var target= 662 + Math.max.apply( 663 + Math, 664 + rows.map(function(row){ 665 + return row.relativeTop; 666 + }) 667 + ); 459 459 460 - var score= 461 - (exact?20:0)+ 462 - (bg?6:0)+ 463 - (border>=1?2:0)+ 464 - (mostlyUpper?4:0)+ 465 - (num(style.fontWeight)>=600?1:0); 669 + rows.forEach(function(row){ 670 + var delta= 671 + target- 672 + row.relativeTop; 466 466 467 - if(score>=7){ 468 - candidates.push({ 469 - el:el, 470 - relativeTop:relativeTop, 471 - width:r.width, 472 - score:score 473 - }); 474 - } 475 - }); 674 + if(delta<=1)return; 476 476 477 - if(!candidates.length)return null; 676 + saveOriginal( 677 + row.bar, 678 + originalMarginTop, 679 + "margin-top" 680 + ); 478 478 479 - candidates.sort(function(a,b){ 480 - if(a.score!==b.score)return b.score-a.score; 481 - if(Math.abs(a.relativeTop-b.relativeTop)>3){ 482 - return a.relativeTop-b.relativeTop; 483 - } 484 - return b.width-a.width; 485 - }); 682 + var current= 683 + num( 684 + getComputedStyle( 685 + row.bar 686 + ).marginTop 687 + ); 486 486 487 - // For exact known labels always prefer the earliest exact match. 488 - var exacts=candidates.filter(function(c){ 489 - return exactKnownFirstSection(c.el); 689 + row.bar.style.setProperty( 690 + "margin-top", 691 + ( 692 + current+ 693 + delta 694 + )+"px" 695 + ); 696 + 697 + row.bar.setAttribute( 698 + "data-sn-first-section-aligned", 699 + "7" 700 + ); 701 + 702 + sectionManaged.add( 703 + row.bar 704 + ); 490 490 }); 491 491 492 - if(exacts.length){ 493 - exacts.sort(function(a,b){ 494 - return a.relativeTop-b.relativeTop||b.width-a.width; 707 + return true; 708 + } 709 + 710 + /* ---------- Generic first content after heading ---------- */ 711 + 712 + function heading(card){ 713 + var cr= 714 + card.getBoundingClientRect(); 715 + 716 + var rows= 717 + Array.from( 718 + card.querySelectorAll( 719 + "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]" 720 + ) 721 + ) 722 + .filter(function(el){ 723 + if(!visible(el,20,8))return false; 724 + 725 + var r=el.getBoundingClientRect(); 726 + 727 + var rel= 728 + r.top- 729 + cr.top; 730 + 731 + return ( 732 + rel>=0&& 733 + rel<= 734 + Math.min( 735 + 160, 736 + cr.height*.45 737 + ) 738 + ); 739 + }) 740 + .sort(function(a,b){ 741 + return ( 742 + a.getBoundingClientRect().top- 743 + b.getBoundingClientRect().top 744 + ); 495 495 }); 496 - return exacts[0].el; 497 - } 498 498 499 - // Generic fallback: earliest high-confidence bar. 500 - candidates.sort(function(a,b){ 501 - return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width; 502 - }); 747 + return rows[0]||null; 748 + } 503 503 504 - return candidates[0].el; 750 + function firstContent(card,h){ 751 + if(!h)return null; 752 + 753 + var cr= 754 + card.getBoundingClientRect(); 755 + 756 + var hr= 757 + h.getBoundingClientRect(); 758 + 759 + var rows= 760 + Array.from( 761 + card.querySelectorAll( 762 + "p,div,ul,ol,dl,blockquote" 763 + ) 764 + ) 765 + .filter(function(el){ 766 + if( 767 + el===h|| 768 + h.contains(el)|| 769 + el.contains(h) 770 + ){ 771 + return false; 772 + } 773 + 774 + if(!visible(el,30,8))return false; 775 + 776 + var text= 777 + String(el.textContent||"") 778 + .replace(/\s+/g," ") 779 + .trim(); 780 + 781 + if(text.length<3)return false; 782 + 783 + var r=el.getBoundingClientRect(); 784 + 785 + if( 786 + r.top< 787 + hr.bottom-1 788 + ){ 789 + return false; 790 + } 791 + 792 + if( 793 + r.top-cr.top> 794 + Math.min( 795 + 280, 796 + cr.height*.65 797 + ) 798 + ){ 799 + return false; 800 + } 801 + 802 + if( 803 + r.height> 804 + cr.height*.58 805 + ){ 806 + return false; 807 + } 808 + 809 + return true; 810 + }) 811 + .sort(function(a,b){ 812 + var ar= 813 + a.getBoundingClientRect(); 814 + 815 + var br= 816 + b.getBoundingClientRect(); 817 + 818 + return ( 819 + ar.top-br.top|| 820 + ar.height-br.height 821 + ); 822 + }); 823 + 824 + return rows[0]||null; 505 505 } 506 506 507 - function align FirstSections(cards){827 + function alignGeneric(cards){ 508 508 var rows= 509 509 cards.map(function(card){ 510 - var bar=511 - visuallyFirstSectionBar(card);830 + var h= 831 + heading(card); 512 512 513 - if(!bar)return null; 833 + var c= 834 + firstContent( 835 + card, 836 + h 837 + ); 514 514 515 - var cardRect= 839 + if(!h||!c)return null; 840 + 841 + var cr= 516 516 card.getBoundingClientRect(); 517 517 518 - var barRect=519 - bar.getBoundingClientRect();844 + var rr= 845 + c.getBoundingClientRect(); 520 520 521 521 return { 522 - card:card, 523 - bar:bar, 848 + content:c, 524 524 relativeTop: 525 - barRect.top-cardRect.top, 526 - label: 527 - normalizeText(bar.textContent) 850 + rr.top-cr.top 528 528 }; 529 529 }); 530 530 531 - // Conservative: only align when every card in the horizontal row 532 - // has a reliable first section bar. 533 533 if( 534 534 rows.some(function(row){ 535 535 return !row; 536 536 }) 537 537 ){ 538 - return null;859 + return false; 539 539 } 540 540 541 - var target Top=862 + var target= 542 542 Math.max.apply( 543 543 Math, 544 544 rows.map(function(row){ ... ... @@ -548,54 +548,47 @@ 548 548 549 549 rows.forEach(function(row){ 550 550 var delta= 551 - target Top-872 + target- 552 552 row.relativeTop; 553 553 554 554 if(delta<=1)return; 555 555 556 556 saveOriginal( 557 - row. bar,878 + row.content, 558 558 originalMarginTop, 559 559 "margin-top" 560 560 ); 561 561 562 - var current Margin=883 + var current= 563 563 num( 564 564 getComputedStyle( 565 - row. bar886 + row.content 566 566 ).marginTop 567 567 ); 568 568 569 - row. bar.style.setProperty(890 + row.content.style.setProperty( 570 570 "margin-top", 571 571 ( 572 - current Margin+893 + current+ 573 573 delta 574 574 )+"px" 575 575 ); 576 576 577 - row. bar.setAttribute(578 - "data-sn-first- section-aligned",579 - " 4"898 + row.content.setAttribute( 899 + "data-sn-first-content-aligned", 900 + "7" 580 580 ); 581 581 582 - sectionManaged.add(row.bar); 903 + contentManaged.add( 904 + row.content 905 + ); 583 583 }); 584 584 585 - return { 586 - labels: 587 - rows.map(function(row){ 588 - return row.label; 589 - }), 590 - targetRelativeTop: 591 - Math.round(targetTop) 592 - }; 908 + return true; 593 593 } 594 594 595 - function applyGroup(group){ 596 - if(!validHorizontalGroup(group)){ 597 - return null; 598 - } 911 + function applyRow(group){ 912 + if(!validRow(group))return false; 599 599 600 600 var cards=[]; 601 601 var seen=new Set(); ... ... @@ -607,25 +607,30 @@ 607 607 } 608 608 }); 609 609 610 - if(cards.length<2)return null;924 + if(cards.length<2)return false; 611 611 612 - var firstSection= 613 - alignFirstSections(cards); 926 + if( 927 + !alignKnownBars(cards) 928 + ){ 929 + alignGeneric(cards); 930 + } 614 614 615 - // The section alignment can change natural card heights. 616 - // Measure only afterwards. 617 617 var maxHeight=0; 618 618 619 619 cards.forEach(function(card){ 620 - maxHeight=Math.max( 621 - maxHeight, 622 - card.getBoundingClientRect().height 623 - ); 935 + maxHeight= 936 + Math.max( 937 + maxHeight, 938 + card.getBoundingClientRect().height 939 + ); 624 624 }); 625 625 626 - maxHeight=Math.ceil(maxHeight); 942 + maxHeight= 943 + Math.ceil( 944 + maxHeight 945 + ); 627 627 628 - if(maxHeight<MIN_H)return null;947 + if(maxHeight<MIN_H)return false; 629 629 630 630 cards.forEach(function(card){ 631 631 saveOriginal( ... ... @@ -641,125 +641,228 @@ 641 641 642 642 card.setAttribute( 643 643 "data-sn-equal-height-managed", 644 - " 4"963 + "7" 645 645 ); 646 646 647 647 heightManaged.add(card); 648 648 }); 649 649 650 - return { 651 - cards:cards.length, 652 - height:maxHeight, 653 - firstSection:firstSection 654 - }; 969 + return true; 655 655 } 656 656 657 - function run(){658 - re set();972 + function calculate(force){ 973 + if(applying)return; 659 659 975 + var width= 976 + Math.round( 977 + window.innerWidth 978 + ); 979 + 980 + var meaningful= 981 + lastWidth<0|| 982 + Math.abs( 983 + width- 984 + lastWidth 985 + )>= 986 + MIN_WIDTH_CHANGE; 987 + 660 660 if( 661 - window.innerWidth<=MOBILE_MAX 989 + hasApplied&& 990 + !force&& 991 + !meaningful 662 662 ){ 663 663 return; 664 664 } 665 665 666 - var parents= 667 - Array.from( 668 - root.querySelectorAll("*") 669 - ).filter(function(parent){ 670 - return ( 671 - visible(parent,MIN_W,MIN_H)&& 672 - parent.children&& 673 - parent.children.length>=2&& 674 - parent.children.length<=MAX_PARENT_CHILDREN 675 - ); 676 - }); 996 + applying=true; 677 677 678 - var touched=new Set(); 998 + try{ 999 + harmoniseCustomCallouts(); 679 679 680 - parents.forEach(function(parent){ 681 - var entries=[]; 1001 + if(width<=MOBILE_MAX){ 1002 + if(hasApplied){ 1003 + resetRuntime(); 1004 + } 682 682 683 - Array.from(parent.children).forEach(function(child){ 684 - if(!visible(child,MIN_W,MIN_H))return; 1006 + hasApplied=false; 1007 + lastWidth=width; 1008 + return; 1009 + } 685 685 686 - var target=pickTarget(child); 687 - if(!target)return; 1011 + if(hasApplied){ 1012 + resetRuntime(); 1013 + } 688 688 689 - entries.push({ 690 - target:target, 691 - rect:target.getBoundingClientRect() 1015 + var parents= 1016 + Array.from( 1017 + root.querySelectorAll("*") 1018 + ) 1019 + .filter(function(parent){ 1020 + return ( 1021 + visible( 1022 + parent, 1023 + MIN_W, 1024 + MIN_H 1025 + )&& 1026 + parent.children&& 1027 + parent.children.length>=2&& 1028 + parent.children.length<= 1029 + MAX_PARENT_CHILDREN 1030 + ); 692 692 }); 693 - }); 694 694 695 - if(entries.length<2)return; 1033 + var touched= 1034 + new Set(); 696 696 697 - groupsByVisualRow(entries).forEach(function(group){698 - if(!validHorizontalGroup(group))return;1036 + parents.forEach(function(parent){ 1037 + var entries=[]; 699 699 700 - var signature= 701 - group.entries.map(function(entry){ 702 - return entry.target; 703 - }); 1039 + Array.from( 1040 + parent.children 1041 + ).forEach(function(child){ 1042 + if( 1043 + !visible( 1044 + child, 1045 + MIN_W, 1046 + MIN_H 1047 + ) 1048 + ){ 1049 + return; 1050 + } 704 704 705 - if( 706 - !signature.some(function(el){ 707 - return !touched.has(el); 708 - }) 709 - ){ 710 - return; 711 - } 1052 + var target= 1053 + pickTarget(child); 712 712 713 - var applied= 714 - applyGroup(group); 1055 + if(!target)return; 715 715 716 - if(applied){ 717 - signature.forEach(function(el){ 718 - touched.add(el); 1057 + entries.push({ 1058 + target:target, 1059 + rect: 1060 + target.getBoundingClientRect() 719 719 }); 720 - } 1062 + }); 1063 + 1064 + groupRows( 1065 + entries 1066 + ).forEach(function(group){ 1067 + if(!validRow(group))return; 1068 + 1069 + var targets= 1070 + group.entries.map(function(entry){ 1071 + return entry.target; 1072 + }); 1073 + 1074 + if( 1075 + !targets.some(function(el){ 1076 + return !touched.has(el); 1077 + }) 1078 + ){ 1079 + return; 1080 + } 1081 + 1082 + if(applyRow(group)){ 1083 + targets.forEach(function(el){ 1084 + touched.add(el); 1085 + }); 1086 + } 1087 + }); 721 721 }); 722 - }); 1089 + 1090 + lastWidth=width; 1091 + hasApplied=true; 1092 + }finally{ 1093 + applying=false; 1094 + } 723 723 } 724 724 725 - function schedule(){ 726 - if(raf)cancelAnimationFrame(raf); 1097 + function resize(){ 1098 + if(resizeTimer){ 1099 + clearTimeout( 1100 + resizeTimer 1101 + ); 1102 + } 727 727 728 - raf=requestAnimationFrame(function(){ 729 - raf=requestAnimationFrame(function(){ 730 - raf=0; 731 - run(); 732 - }); 733 - }); 1104 + resizeTimer= 1105 + setTimeout( 1106 + function(){ 1107 + resizeTimer=0; 1108 + calculate(false); 1109 + }, 1110 + RESIZE_DEBOUNCE 1111 + ); 734 734 } 735 735 736 - addEventListener("resize",schedule,{passive:true}); 737 - addEventListener("load",schedule,{once:true}); 1114 + function waitImages(){ 1115 + var images= 1116 + Array.from( 1117 + root.querySelectorAll("img") 1118 + ); 738 738 739 - root.addEventListener( 740 - "click", 741 - function(){ 742 - setTimeout(schedule,0); 743 - }, 744 - true 745 - ); 1120 + return Promise.all( 1121 + images.map(function(img){ 1122 + if(img.complete){ 1123 + return Promise.resolve(); 1124 + } 746 746 747 - Array.from(root.querySelectorAll("img")).forEach(function(img){ 748 - if(!img.complete){ 749 - img.addEventListener("load",schedule,{once:true}); 1126 + return new Promise(function(resolve){ 1127 + var done=function(){ 1128 + resolve(); 1129 + }; 1130 + 1131 + img.addEventListener( 1132 + "load", 1133 + done, 1134 + {once:true} 1135 + ); 1136 + 1137 + img.addEventListener( 1138 + "error", 1139 + done, 1140 + {once:true} 1141 + ); 1142 + }); 1143 + }) 1144 + ); 1145 + } 1146 + 1147 + async function initial(){ 1148 + if( 1149 + document.readyState!== 1150 + "complete" 1151 + ){ 1152 + await new Promise(function(resolve){ 1153 + addEventListener( 1154 + "load", 1155 + resolve, 1156 + {once:true} 1157 + ); 1158 + }); 750 750 } 751 - }); 752 752 753 - if(document.fonts&&document.fonts.ready){ 754 - document.fonts.ready.then(schedule); 1161 + if( 1162 + document.fonts&& 1163 + document.fonts.ready 1164 + ){ 1165 + try{ 1166 + await document.fonts.ready; 1167 + }catch(error){} 1168 + } 1169 + 1170 + await waitImages(); 1171 + 1172 + // Exactly one initial layout calculation. 1173 + calculate(true); 755 755 } 756 756 757 - schedule(); 758 - setTimeout(schedule,200); 759 - setTimeout(schedule,650); 760 - setTimeout(schedule,1400); 1176 + addEventListener( 1177 + "resize", 1178 + resize, 1179 + {passive:true} 1180 + ); 1181 + 1182 + initial(); 761 761 })(); 762 762 </script> 763 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 764 -{{/html}} 765 765 1186 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 1187 +{{/html}}\n