Changes for page Project Planning & Organisation
Last modified by Veronika Kocher on Friday, 04 Sep 2026 14:45
From version 9.6
edited by Veronika Kocher
on Thursday, 03 Sep 2026 13:04
on Thursday, 03 Sep 2026 13:04
Change comment:
There is no comment for this version
To version 9.4
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
-
... ... @@ -426,100 +426,23 @@ 426 426 {{/html}} 427 427 428 428 {{html clean="false"}} 429 -<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_START --> 430 - 431 -<style data-sn-service-navigator-master="1"> 432 -/* ========================================================= 433 - MASTER TYPOGRAPHY 434 - ========================================================= */ 435 -#xwikicontent h1, 436 -#xwikicontent h2, 437 -#xwikicontent h3, 438 -#xwikicontent h4, 439 -#xwikicontent h5, 440 -#xwikicontent h6, 441 -#xwikicontent .box-title, 442 -#xwikicontent [role="heading"], 443 -#xwikicontent h1 a, 444 -#xwikicontent h2 a, 445 -#xwikicontent h3 a, 446 -#xwikicontent h4 a, 447 -#xwikicontent h5 a, 448 -#xwikicontent h6 a, 449 -#xwikicontent .box-title a, 450 -#xwikicontent [role="heading"] a{ 451 - text-align:left!important; 452 - text-justify:auto!important; 453 - word-spacing:normal!important; 454 -} 455 - 456 -/* ========================================================= 457 - COMING SOON / IN VORBEREITUNG 458 - ========================================================= */ 459 -.sn-coming-soon-badge{ 460 - display:inline-block; 461 - vertical-align:middle; 462 - margin-left:10px; 463 - padding:4px 8px; 464 - border-radius:14px; 465 - border:1px solid #efd9cc; 466 - background:#fff4ee; 467 - color:#8a4d36; 468 - font-size:11px; 469 - font-weight:700; 470 - letter-spacing:.055em; 471 - line-height:1.2; 472 - white-space:nowrap; 473 -} 474 - 475 -/* ========================================================= 476 - NATIVE / DETAIL CALLOUT GEOMETRY 477 - Preserve existing backgrounds. 478 - ========================================================= */ 479 -#xwikicontent .arsn-info, 480 -#xwikicontent .dmp-info, 481 -#xwikicontent .ido-info, 482 -#xwikicontent .pp-info, 483 -#xwikicontent .infomessage{ 484 - box-sizing:border-box!important; 485 - border-left:4px solid #21b6d7!important; 486 - border-radius:8px; 487 - text-align:left!important; 488 - word-spacing:normal!important; 489 -} 490 - 491 -#xwikicontent .arsn-warn, 492 -#xwikicontent .dmp-warn, 493 -#xwikicontent .ido-warn, 494 -#xwikicontent .pp-warn, 495 -#xwikicontent .warn, 496 -#xwikicontent .warning, 497 -#xwikicontent .warningmessage, 498 -#xwikicontent .errormessage{ 499 - box-sizing:border-box!important; 500 - border-left:4px solid #e94b13!important; 501 - border-radius:8px; 502 - text-align:left!important; 503 - word-spacing:normal!important; 504 -} 505 - 506 -[data-sn-master-equal-height], 507 -[data-sn-master-first-row]{ 429 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_START --> 430 +<style data-sn-horizontal-box-harmonizer="4"> 431 +[data-sn-equal-height-managed], 432 +[data-sn-first-section-aligned]{ 508 508 box-sizing:border-box; 509 509 } 510 510 </style> 511 - 512 -<script data-sn-service-navigator-master="1"> 436 +<script data-sn-horizontal-box-harmonizer="4"> 513 513 (function(){ 514 514 "use strict"; 515 515 516 - var PAGE_KIND="detail"; 517 - var PLACEHOLDER=false; 518 - var PLACEHOLDER_LABEL="COMING SOON"; 519 - 520 520 var MOBILE_MAX=767; 521 521 var ROW_TOLERANCE=6; 522 - var RESIZE_DEBOUNCE=220; 442 + var MIN_W=150; 443 + var MIN_H=36; 444 + var MAX_PARENT_CHILDREN=16; 445 + var SEARCH_DEPTH=5; 523 523 524 524 var root= 525 525 document.getElementById("xwikicontent")|| ... ... @@ -529,105 +529,93 @@ 529 529 530 530 if(!root)return; 531 531 532 - var managedHeight=new Set(); 533 - var managedMargin=new Set(); 534 - var originalHeight=new WeakMap(); 535 - var originalMargin=new WeakMap(); 455 + var heightManaged=new Set(); 456 + var sectionManaged=new Set(); 457 + var originalMinHeight=new WeakMap(); 458 + var originalMarginTop=new WeakMap(); 459 + var raf=0; 536 536 537 - var resizeTimer=0; 538 - var running=false; 461 + function num(v){ 462 + var n=parseFloat(v); 463 + return Number.isFinite(n)?n:0; 464 + } 539 539 540 - var THEMES=[ 541 - { 542 - name:"BLUE", 543 - accent:"#21b6d7", 544 - bg:"#eaf7fb", 545 - text:"#315d68", 546 - labels:[ 547 - "HIER STARTEN", 548 - "ENTSCHEIDEN", 549 - "ÜBERPRÜFEN & ANPASSEN", 550 - "START HERE", 551 - "DECIDE", 552 - "REVIEW & ADAPT" 553 - ] 554 - }, 555 - { 556 - name:"RED", 557 - accent:"#e94b13", 558 - bg:"#fff1ec", 559 - text:"#8a4d36", 560 - labels:[ 561 - "PLANEN & ORGANISIEREN", 562 - "ORGANISIEREN & DOKUMENTIEREN", 563 - "PUBLIZIEREN & PRÄSENTIEREN", 564 - "PLAN & ORGANISE", 565 - "PLAN & ORGANIZE", 566 - "ORGANISE & DOCUMENT", 567 - "ORGANIZE & DOCUMENT", 568 - "PUBLISH & PRESENT" 569 - ] 570 - }, 571 - { 572 - name:"YELLOW", 573 - accent:"#f0aa00", 574 - bg:"#fff8e7", 575 - text:"#785b17", 576 - labels:[ 577 - "SCHÜTZEN & KLÄREN", 578 - "STANDARDISIEREN & IDENTIFIZIEREN", 579 - "ÖFFNEN, NACHNUTZEN & AUFFINDBAR MACHEN", 580 - "PROTECT & CLARIFY", 581 - "STANDARDISE & IDENTIFY", 582 - "STANDARDIZE & IDENTIFY", 583 - "OPEN, REUSE & MAKE FINDABLE" 584 - ] 585 - }, 586 - { 587 - name:"GREEN", 588 - accent:"#38b76a", 589 - bg:"#edf9f1", 590 - text:"#3f7451", 591 - labels:[ 592 - "INFRASTRUKTUR EINRICHTEN", 593 - "INFRASTRUKTUR & ZUSAMMENARBEIT EINRICHTEN", 594 - "SPEICHERN & ZUGRIFF STEUERN", 595 - "REPOSITORIUM, ERHALTEN & ÜBERGEBEN", 596 - "SET UP INFRASTRUCTURE", 597 - "SET UP INFRASTRUCTURE & COLLABORATION", 598 - "STORE & CONTROL ACCESS", 599 - "REPOSITORY, PRESERVE & HAND OVER" 600 - ] 466 + function visible(el,minW,minH){ 467 + if(!el||el.nodeType!==1)return false; 468 + 469 + var s=getComputedStyle(el); 470 + 471 + if( 472 + s.display==="none"|| 473 + s.visibility==="hidden"|| 474 + Number(s.opacity)===0 475 + ){ 476 + return false; 601 601 } 602 - ]; 603 603 604 - function num(value){ 605 - var n=parseFloat(value); 606 - return Number.isFinite(n)?n:0; 479 + var r=el.getBoundingClientRect(); 480 + 481 + return ( 482 + r.width>=(minW||1)&& 483 + r.height>=(minH||1) 484 + ); 607 607 } 608 608 609 - function norm(value){ 610 - return String(value||"") 611 - .replace(/\s+/g," ") 612 - .trim() 613 - .toUpperCase(); 487 + function excluded(el){ 488 + return ( 489 + !el.matches|| 490 + el.matches( 491 + "button,input,select,textarea,option,label,img,svg,canvas,table,thead,tbody,tfoot,tr,td,th,summary,script,style,noscript,template,.btn,.chip,[role=button]" 492 + ) 493 + ); 614 614 } 615 615 616 - function visible(el){ 617 - if(!el||el.nodeType!==1)return false; 496 + function namedLikeBox(el){ 497 + var c= 498 + typeof el.className==="string" 499 + ? el.className 500 + : ""; 618 618 619 - var style=getComputedStyle(el);620 - var rect=el.getBoundingClientRect();502 + return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c); 503 + } 621 621 505 + function boxLike(el){ 506 + if(!visible(el,MIN_W,MIN_H)||excluded(el))return false; 507 + 508 + var s=getComputedStyle(el); 509 + var r=el.getBoundingClientRect(); 510 + 511 + var border= 512 + num(s.borderTopWidth)+ 513 + num(s.borderRightWidth)+ 514 + num(s.borderBottomWidth)+ 515 + num(s.borderLeftWidth); 516 + 517 + var radius=Math.max( 518 + num(s.borderTopLeftRadius), 519 + num(s.borderTopRightRadius), 520 + num(s.borderBottomLeftRadius), 521 + num(s.borderBottomRightRadius) 522 + ); 523 + 524 + var bg= 525 + s.backgroundColor&& 526 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&& 527 + s.backgroundColor!=="transparent"; 528 + 529 + var shadow= 530 + s.boxShadow&& 531 + s.boxShadow!=="none"; 532 + 622 622 return ( 623 - style.display!=="none"&&624 - style.visibility!=="hidden"&&625 - r ect.width>20&&626 - r ect.height>4534 + namedLikeBox(el)|| 535 + border>=1|| 536 + (bg&&radius>=3)|| 537 + (shadow&&r.width>=180) 627 627 ); 628 628 } 629 629 630 - function save Style(el,map,property){541 + function saveOriginal(el,map,property){ 631 631 if(map.has(el))return; 632 632 633 633 map.set(el,{ ... ... @@ -636,7 +636,7 @@ 636 636 }); 637 637 } 638 638 639 - function restore Style(el,map,property){550 + function restoreOriginal(el,map,property){ 640 640 var original=map.get(el); 641 641 642 642 if(original&&original.value){ ... ... @@ -650,249 +650,247 @@ 650 650 } 651 651 } 652 652 653 - function reset Runtime(){654 - managedHeight.forEach(function(el){655 - restore Style(564 + function reset(){ 565 + heightManaged.forEach(function(el){ 566 + restoreOriginal( 656 656 el, 657 - originalHeight, 568 + originalMinHeight, 658 658 "min-height" 659 659 ); 571 + 660 660 el.removeAttribute( 661 - "data-sn- master-equal-height"573 + "data-sn-equal-height-managed" 662 662 ); 663 663 }); 664 664 665 - managedMargin.forEach(function(el){666 - restore Style(577 + sectionManaged.forEach(function(el){ 578 + restoreOriginal( 667 667 el, 668 - originalMargin, 580 + originalMarginTop, 669 669 "margin-top" 670 670 ); 583 + 671 671 el.removeAttribute( 672 - "data-sn- master-first-row"585 + "data-sn-first-section-aligned" 673 673 ); 674 674 }); 675 675 676 - managedHeight.clear();677 - managedMargin.clear();589 + heightManaged.clear(); 590 + sectionManaged.clear(); 678 678 } 679 679 680 - function themeFor(text){681 - v ar value=norm(text);593 + function pickTarget(child){ 594 + if(!visible(child,MIN_W,MIN_H))return null; 682 682 683 - for(var i=0;i<THEMES.length;i++){ 684 - for(var j=0;j<THEMES[i].labels.length;j++){ 685 - var label=THEMES[i].labels[j]; 686 - 687 - if( 688 - value===label|| 689 - value.indexOf(label+" ")===0|| 690 - value.indexOf(label+" -")===0|| 691 - value.indexOf(label+" ·")===0 692 - ){ 693 - return THEMES[i]; 694 - } 695 - } 596 + if(boxLike(child)){ 597 + return child; 696 696 } 697 697 698 - retu rnnull;699 - }600 + var cr=child.getBoundingClientRect(); 601 + var q=[]; 700 700 701 - /*=======================================================702 - PLACEHOLDERBADGE703 - ======================================================= */603 + Array.from(child.children||[]).forEach(function(el){ 604 + q.push({el:el,depth:1}); 605 + }); 704 704 705 - functionapplyPlaceholderBadge(){706 - if(!PLACEHOLDER)return;607 + var best=null; 608 + var bestScore=-Infinity; 707 707 708 - if( 709 - document.querySelector( 710 - ".sn-coming-soon-badge" 711 - ) 712 - ){ 713 - return; 714 - } 610 + while(q.length){ 611 + var item=q.shift(); 612 + var el=item.el; 715 715 716 - var title= 717 - document.querySelector( 718 - "#document-title" 719 - )|| 720 - document.querySelector( 721 - "h1" 722 - ); 614 + if(!visible(el,MIN_W,MIN_H))continue; 723 723 724 - if(!title)return;616 + var r=el.getBoundingClientRect(); 725 725 726 - var badge= 727 - document.createElement( 728 - "span" 729 - ); 618 + if( 619 + r.width>=cr.width*.68&& 620 + boxLike(el) 621 + ){ 622 + var widthFit= 623 + 1-Math.min( 624 + 1, 625 + Math.abs(cr.width-r.width)/ 626 + Math.max(1,cr.width) 627 + ); 730 730 731 - badge.className= 732 - "sn-coming-soon-badge"; 629 + var topFit= 630 + 1-Math.min( 631 + 1, 632 + Math.abs(cr.top-r.top)/100 633 + ); 733 733 734 - badge.textContent= 735 - PLACEHOLDER_LABEL; 635 + var score= 636 + widthFit*6+ 637 + topFit*3+ 638 + Math.min(r.height,900)/450+ 639 + Math.min(r.width*r.height,900000)/900000; 736 736 737 - badge.setAttribute( 738 - "aria-label", 739 - PLACEHOLDER_LABEL 740 - ); 641 + if(score>bestScore){ 642 + best=el; 643 + bestScore=score; 644 + } 645 + } 741 741 742 - title.appendChild( 743 - badge 744 - ); 647 + if(item.depth<SEARCH_DEPTH){ 648 + Array.from(el.children||[]).forEach(function(next){ 649 + q.push({ 650 + el:next, 651 + depth:item.depth+1 652 + }); 653 + }); 654 + } 655 + } 656 + 657 + return best; 745 745 } 746 746 747 - /* ======================================================= 748 - DETAIL PAGE OWN-ACCENT PROPAGATION 749 - ======================================================= */ 660 + function groupsByVisualRow(entries){ 661 + var groups=[]; 750 750 751 - function parseRGB(value){ 752 - var match=String(value||"").match( 753 - /rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i 754 - ); 663 + entries 664 + .slice() 665 + .sort(function(a,b){ 666 + return ( 667 + a.rect.top-b.rect.top|| 668 + a.rect.left-b.rect.left 669 + ); 670 + }) 671 + .forEach(function(entry){ 672 + var group=null; 755 755 756 - if(!match)return null; 674 + for(var i=0;i<groups.length;i++){ 675 + if( 676 + Math.abs( 677 + groups[i].top- 678 + entry.rect.top 679 + )<=ROW_TOLERANCE 680 + ){ 681 + group=groups[i]; 682 + break; 683 + } 684 + } 757 757 758 - return [ 759 - Number(match[1]), 760 - Number(match[2]), 761 - Number(match[3]) 762 - ]; 763 - } 686 + if(!group){ 687 + group={ 688 + top:entry.rect.top, 689 + entries:[] 690 + }; 691 + groups.push(group); 692 + } 764 764 765 - function distance(a,b){ 766 - return Math.sqrt( 767 - Math.pow(a[0]-b[0],2)+ 768 - Math.pow(a[1]-b[1],2)+ 769 - Math.pow(a[2]-b[2],2) 770 - ); 694 + group.entries.push(entry); 695 + }); 696 + 697 + return groups; 771 771 } 772 772 773 - function paletteMatch(cssColor){774 - var rgb=parseRGB(cssColor);700 + function validHorizontalGroup(group){ 701 + if(group.entries.length<2)return false; 775 775 776 - if(!rgb)return null; 703 + var lefts= 704 + group.entries 705 + .map(function(e){return e.rect.left;}) 706 + .sort(function(a,b){return a-b;}); 777 777 778 - var palette=[ 779 - {hex:"#21b6d7",rgb:[33,182,215]}, 780 - {hex:"#e94b13",rgb:[233,75,19]}, 781 - {hex:"#f0aa00",rgb:[240,170,0]}, 782 - {hex:"#38b76a",rgb:[56,183,106]} 783 - ]; 708 + if( 709 + lefts[lefts.length-1]- 710 + lefts[0]< 711 + 80 712 + ){ 713 + return false; 714 + } 784 784 785 - var best=null; 786 - var bestDistance=Infinity; 716 + var widths= 717 + group.entries 718 + .map(function(e){return e.rect.width;}) 719 + .filter(function(w){return w>0;}); 787 787 788 - palette.forEach(function(item){ 789 - var d=distance(rgb,item.rgb); 721 + if(widths.length<2)return false; 790 790 791 - if(d<bestDistance){ 792 - best=item; 793 - bestDistance=d; 794 - } 795 - }); 723 + var minWidth=Math.min.apply(Math,widths); 724 + var maxWidth=Math.max.apply(Math,widths); 796 796 797 - return bestDistance<=30 798 - ?best 799 - :null; 726 + return ( 727 + maxWidth/ 728 + Math.max(1,minWidth)<= 729 + 1.9 730 + ); 800 800 } 801 801 802 - function propagateDetailAccents(){ 803 - if(PAGE_KIND!=="detail")return; 733 + function normalizeText(text){ 734 + return String(text||"") 735 + .replace(/s+/g," ") 736 + .trim() 737 + .toUpperCase(); 738 + } 804 804 740 + function exactKnownFirstSection(el){ 741 + var text=normalizeText(el.textContent); 742 + 743 + return ( 744 + text==="HIER STARTEN"|| 745 + text==="ORGANISIEREN & DOKUMENTIEREN"|| 746 + text==="ENTSCHEIDEN"|| 747 + text==="START HERE"|| 748 + text==="GET STARTED"|| 749 + text==="ORGANISE & DOCUMENT"|| 750 + text==="ORGANIZE & DOCUMENT"|| 751 + text==="DECIDE" 752 + ); 753 + } 754 + 755 + function visuallyFirstSectionBar(card){ 756 + var cardRect=card.getBoundingClientRect(); 757 + var candidates=[]; 758 + 805 805 Array.from( 806 - root.querySelectorAll( 807 - ".arsn-call,.dmp-call,.ido-call,.pp-call,"+ 808 - ".arsn-source,.dmp-source,.ido-source,.pp-source" 760 + card.querySelectorAll( 761 + "div,p,span,strong,h3,h4,h5,h6" 809 809 ) 810 810 ).forEach(function(el){ 811 - if(!visible(el))return; 764 + if(!visible(el,20,6))return; 765 + if(excluded(el))return; 812 812 813 - var style=getComputedStyle(el); 767 + var r=el.getBoundingClientRect(); 768 + var relativeTop=r.top-cardRect.top; 814 814 815 815 if( 816 - num(style.borderTopWidth)>=2 771 + relativeTop<45|| 772 + relativeTop>cardRect.height*.72 817 817 ){ 818 - var match= 819 - paletteMatch( 820 - style.borderTopColor 821 - ); 822 - 823 - if(match){ 824 - el.style.setProperty( 825 - "border-left", 826 - "4px solid "+match.hex, 827 - "important" 828 - ); 829 - } 774 + return; 830 830 } 831 - }); 832 832 833 - // Warning families: explicit safety override. 834 - Array.from( 835 - root.querySelectorAll( 836 - ".arsn-warn,.dmp-warn,.ido-warn,.pp-warn,"+ 837 - ".warn,.warning,.warningmessage,.errormessage" 838 - ) 839 - ).forEach(function(el){ 840 - if(!visible(el))return; 777 + var widthRatio= 778 + r.width/ 779 + Math.max(1,cardRect.width); 841 841 842 - el.style.setProperty( 843 - "border-left", 844 - "4px solid #e94b13", 845 - "important" 846 - ); 847 - }); 781 + if( 782 + widthRatio<.55|| 783 + widthRatio>1.05|| 784 + r.height>54 785 + ){ 786 + return; 787 + } 848 848 849 - // Info families: explicit orientation override. 850 - Array.from( 851 - root.querySelectorAll( 852 - ".arsn-info,.dmp-info,.ido-info,.pp-info,.infomessage" 853 - ) 854 - ).forEach(function(el){ 855 - if(!visible(el))return; 789 + var text=normalizeText(el.textContent); 856 856 857 - el.style.setProperty( 858 - "border-left", 859 - "4px solid #21b6d7", 860 - "important" 861 - ); 862 - }); 863 - } 791 + if( 792 + text.length<2|| 793 + text.length>90 794 + ){ 795 + return; 796 + } 864 864 865 - /* ======================================================= 866 - OVERVIEW SEMANTIC THEME PALETTE 867 - ======================================================= */ 798 + var style=getComputedStyle(el); 868 868 869 - function smallestThemeHeaders(){ 870 - return Array.from( 871 - root.querySelectorAll( 872 - "div,span,p,strong,h3,h4,h5,h6" 873 - ) 874 - ).filter(function(el){ 875 - if(!visible(el))return false; 876 - if(!themeFor(el.textContent))return false; 800 + var bg= 801 + style.backgroundColor&& 802 + style.backgroundColor!=="rgba(0, 0, 0, 0)"&& 803 + style.backgroundColor!=="transparent"; 877 877 878 - return !Array.from( 879 - el.children||[] 880 - ).some(function(child){ 881 - return !!themeFor( 882 - child.textContent 883 - ); 884 - }); 885 - }); 886 - } 887 - 888 - function findOverviewCard(header){ 889 - var node=header.parentElement; 890 - var candidates=[]; 891 - 892 - while(node&&node!==root){ 893 - var rect=node.getBoundingClientRect(); 894 - var style=getComputedStyle(node); 895 - 896 896 var border= 897 897 num(style.borderTopWidth)+ 898 898 num(style.borderRightWidth)+ ... ... @@ -899,674 +899,315 @@ 899 899 num(style.borderBottomWidth)+ 900 900 num(style.borderLeftWidth); 901 901 902 - if( 903 - rect.width>=220&& 904 - rect.width<=800&& 905 - rect.height>=120&& 906 - border>=1 907 - ){ 908 - candidates.push(node); 909 - } 811 + var mostlyUpper= 812 + text.length>=3&& 813 + text===text.toUpperCase(); 910 910 911 - node=node.parentElement; 912 - } 815 + var exact=exactKnownFirstSection(el); 913 913 914 - if(!candidates.length)return null; 817 + var score= 818 + (exact?20:0)+ 819 + (bg?6:0)+ 820 + (border>=1?2:0)+ 821 + (mostlyUpper?4:0)+ 822 + (num(style.fontWeight)>=600?1:0); 915 915 916 - candidates.sort(function(a,b){ 917 - return ( 918 - b.getBoundingClientRect().height- 919 - a.getBoundingClientRect().height 920 - ); 824 + if(score>=7){ 825 + candidates.push({ 826 + el:el, 827 + relativeTop:relativeTop, 828 + width:r.width, 829 + score:score 830 + }); 831 + } 921 921 }); 922 922 923 - return candidates[0]; 924 - } 834 + if(!candidates.length)return null; 925 925 926 - function rowCandidates(link,card){ 927 - var result=[]; 928 - var cardRect= 929 - card.getBoundingClientRect(); 930 - 931 - var node=link; 932 - 933 - while(node&&node!==card){ 934 - if(visible(node)){ 935 - var rect= 936 - node.getBoundingClientRect(); 937 - 938 - if( 939 - rect.height<=105&& 940 - rect.width>= 941 - cardRect.width*.50&& 942 - rect.width<= 943 - cardRect.width*1.03 944 - ){ 945 - result.push(node); 946 - } 836 + candidates.sort(function(a,b){ 837 + if(a.score!==b.score)return b.score-a.score; 838 + if(Math.abs(a.relativeTop-b.relativeTop)>3){ 839 + return a.relativeTop-b.relativeTop; 947 947 } 841 + return b.width-a.width; 842 + }); 948 948 949 - node=node.parentElement; 950 - } 844 + // For exact known labels always prefer the earliest exact match. 845 + var exacts=candidates.filter(function(c){ 846 + return exactKnownFirstSection(c.el); 847 + }); 951 951 952 - return result; 953 - } 954 - 955 - function chooseOuterRow( 956 - candidates, 957 - link 958 - ){ 959 - if(!candidates.length){ 960 - return link; 849 + if(exacts.length){ 850 + exacts.sort(function(a,b){ 851 + return a.relativeTop-b.relativeTop||b.width-a.width; 852 + }); 853 + return exacts[0].el; 961 961 } 962 962 963 - var bordered= 964 - candidates.filter(function(el){ 965 - return ( 966 - num( 967 - getComputedStyle(el) 968 - .borderLeftWidth 969 - )>=1 970 - ); 971 - }); 856 + // Generic fallback: earliest high-confidence bar. 857 + candidates.sort(function(a,b){ 858 + return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width; 859 + }); 972 972 973 - var pool= 974 - bordered.length 975 - ?bordered 976 - :candidates; 861 + return candidates[0].el; 862 + } 977 977 978 - pool.sort(function(a,b){ 979 - var ar= 980 - a.getBoundingClientRect(); 864 + function alignFirstSections(cards){ 865 + var rows= 866 + cards.map(function(card){ 867 + var bar= 868 + visuallyFirstSectionBar(card); 981 981 982 - var br= 983 - b.getBoundingClientRect(); 870 + if(!bar)return null; 984 984 985 - return ( 986 - ar.left-br.left|| 987 - br.width-ar.width 988 - ); 989 - }); 872 + var cardRect= 873 + card.getBoundingClientRect(); 990 990 991 - ret urn pool[0]||link;992 - }875 + var barRect= 876 + bar.getBoundingClientRect(); 993 993 994 - function applyOverviewPalette(){ 995 - if(PAGE_KIND!=="overview"){ 996 - return []; 878 + return { 879 + card:card, 880 + bar:bar, 881 + relativeTop: 882 + barRect.top-cardRect.top, 883 + label: 884 + normalizeText(bar.textContent) 885 + }; 886 + }); 887 + 888 + // Conservative: only align when every card in the horizontal row 889 + // has a reliable first section bar. 890 + if( 891 + rows.some(function(row){ 892 + return !row; 893 + }) 894 + ){ 895 + return null; 997 997 } 998 998 999 - var headers= 1000 - smallestThemeHeaders() 1001 - .map(function(el){ 1002 - return { 1003 - el:el, 1004 - theme: 1005 - themeFor( 1006 - el.textContent 1007 - ), 1008 - card: 1009 - findOverviewCard( 1010 - el 1011 - ) 1012 - }; 898 + var targetTop= 899 + Math.max.apply( 900 + Math, 901 + rows.map(function(row){ 902 + return row.relativeTop; 1013 1013 }) 1014 - .filter(function(row){ 1015 - return !!row.card; 1016 - }); 904 + ); 1017 1017 1018 - var unique=[]; 1019 - var seenHeaders=new Set(); 906 + rows.forEach(function(row){ 907 + var delta= 908 + targetTop- 909 + row.relativeTop; 1020 1020 1021 - headers.forEach(function(row){ 1022 - var key= 1023 - row.theme.name+ 1024 - "|"+ 1025 - Math.round( 1026 - row.card 1027 - .getBoundingClientRect() 1028 - .left 1029 - )+ 1030 - "|"+ 1031 - Math.round( 1032 - row.el 1033 - .getBoundingClientRect() 1034 - .top 1035 - ); 911 + if(delta<=1)return; 1036 1036 1037 - if(!seenHeaders.has(key)){ 1038 - seenHeaders.add(key); 1039 - unique.push(row); 1040 - } 1041 - }); 1042 - 1043 - unique.forEach(function(row){ 1044 - var headerRect= 1045 - row.el 1046 - .getBoundingClientRect(); 1047 - 1048 - row.el.style.setProperty( 1049 - "background", 1050 - row.theme.bg, 1051 - "important" 913 + saveOriginal( 914 + row.bar, 915 + originalMarginTop, 916 + "margin-top" 1052 1052 ); 1053 1053 1054 - row.el.style.setProperty( 1055 - "color", 1056 - row.theme.text, 1057 - "important" 1058 - ); 919 + var currentMargin= 920 + num( 921 + getComputedStyle( 922 + row.bar 923 + ).marginTop 924 + ); 1059 1059 1060 - row.el.style.setProperty( 1061 - "text-align", 1062 - "left", 1063 - "important" 926 + row.bar.style.setProperty( 927 + "margin-top", 928 + ( 929 + currentMargin+ 930 + delta 931 + )+"px" 1064 1064 ); 1065 1065 1066 - row.el.style.setProperty( 1067 - "word-spacing", 1068 - "normal", 1069 - "important" 934 + row.bar.setAttribute( 935 + "data-sn-first-section-aligned", 936 + "4" 1070 1070 ); 1071 1071 1072 - var later= 1073 - unique 1074 - .filter(function(other){ 1075 - return ( 1076 - other.card===row.card&& 1077 - other.el 1078 - .getBoundingClientRect() 1079 - .top> 1080 - headerRect.top+2 1081 - ); 1082 - }) 1083 - .sort(function(a,b){ 1084 - return ( 1085 - a.el 1086 - .getBoundingClientRect() 1087 - .top- 1088 - b.el 1089 - .getBoundingClientRect() 1090 - .top 1091 - ); 1092 - }); 1093 - 1094 - var stopTop= 1095 - later.length 1096 - ?later[0].el 1097 - .getBoundingClientRect() 1098 - .top 1099 - :Infinity; 1100 - 1101 - Array.from( 1102 - row.card.querySelectorAll("a") 1103 - ).forEach(function(link){ 1104 - if(!visible(link))return; 1105 - 1106 - var rect= 1107 - link.getBoundingClientRect(); 1108 - 1109 - if( 1110 - rect.top<= 1111 - headerRect.bottom-2|| 1112 - rect.top>= 1113 - stopTop-2 1114 - ){ 1115 - return; 1116 - } 1117 - 1118 - var candidates= 1119 - rowCandidates( 1120 - link, 1121 - row.card 1122 - ); 1123 - 1124 - var target= 1125 - chooseOuterRow( 1126 - candidates, 1127 - link 1128 - ); 1129 - 1130 - candidates.forEach(function(el){ 1131 - el.style.setProperty( 1132 - "border-left", 1133 - "0 solid transparent", 1134 - "important" 1135 - ); 1136 - }); 1137 - 1138 - target.style.setProperty( 1139 - "border-left", 1140 - "3px solid "+ 1141 - row.theme.accent, 1142 - "important" 1143 - ); 1144 - 1145 - target.style.setProperty( 1146 - "box-sizing", 1147 - "border-box", 1148 - "important" 1149 - ); 1150 - }); 939 + sectionManaged.add(row.bar); 1151 1151 }); 1152 1152 1153 - return unique; 942 + return { 943 + labels: 944 + rows.map(function(row){ 945 + return row.label; 946 + }), 947 + targetRelativeTop: 948 + Math.round(targetTop) 949 + }; 1154 1154 } 1155 1155 1156 - /* ======================================================= 1157 - OVERVIEW CARD HEIGHT + FIRST THEMATIC ROW 1158 - ======================================================= */ 1159 - 1160 - function alignOverviewCards(headers){ 1161 - if( 1162 - PAGE_KIND!=="overview"|| 1163 - window.innerWidth<=MOBILE_MAX 1164 - ){ 1165 - return; 952 + function applyGroup(group){ 953 + if(!validHorizontalGroup(group)){ 954 + return null; 1166 1166 } 1167 1167 1168 1168 var cards=[]; 1169 1169 var seen=new Set(); 1170 1170 1171 - headers.forEach(function(row){1172 - if(!seen.has(r ow.card)){1173 - seen.add(r ow.card);1174 - cards.push(r ow.card);960 + group.entries.forEach(function(entry){ 961 + if(!seen.has(entry.target)){ 962 + seen.add(entry.target); 963 + cards.push(entry.target); 1175 1175 } 1176 1176 }); 1177 1177 1178 - if(cards.length<2){ 1179 - return; 1180 - } 967 + if(cards.length<2)return null; 1181 1181 1182 - // Groupcardsby actual top position.1183 - vargroups=[];969 + var firstSection= 970 + alignFirstSections(cards); 1184 1184 1185 - cards.forEach(function(card){1186 - vartop=1187 - card.getBoundingClientRect().top;972 + // The section alignment can change natural card heights. 973 + // Measure only afterwards. 974 + var maxHeight=0; 1188 1188 1189 - var group=null; 1190 - 1191 - for(var i=0;i<groups.length;i++){ 1192 - if( 1193 - Math.abs( 1194 - groups[i].top-top 1195 - )<=ROW_TOLERANCE 1196 - ){ 1197 - group=groups[i]; 1198 - break; 1199 - } 1200 - } 1201 - 1202 - if(!group){ 1203 - group={ 1204 - top:top, 1205 - cards:[] 1206 - }; 1207 - groups.push(group); 1208 - } 1209 - 1210 - group.cards.push(card); 976 + cards.forEach(function(card){ 977 + maxHeight=Math.max( 978 + maxHeight, 979 + card.getBoundingClientRect().height 980 + ); 1211 1211 }); 1212 1212 1213 - groups.forEach(function(group){ 1214 - if(group.cards.length<2){ 1215 - return; 1216 - } 983 + maxHeight=Math.ceil(maxHeight); 1217 1217 1218 - var firstHeaders= 1219 - group.cards.map(function(card){ 1220 - var candidates= 1221 - headers 1222 - .filter(function(row){ 1223 - return row.card===card; 1224 - }) 1225 - .sort(function(a,b){ 1226 - return ( 1227 - a.el 1228 - .getBoundingClientRect() 1229 - .top- 1230 - b.el 1231 - .getBoundingClientRect() 1232 - .top 1233 - ); 1234 - }); 985 + if(maxHeight<MIN_H)return null; 1235 1235 1236 - return candidates.length 1237 - ?candidates[0] 1238 - :null; 1239 - }); 987 + cards.forEach(function(card){ 988 + saveOriginal( 989 + card, 990 + originalMinHeight, 991 + "min-height" 992 + ); 1240 1240 1241 - if( 1242 - !firstHeaders.some(function(row){ 1243 - return !row; 1244 - }) 1245 - ){ 1246 - var targetTop= 1247 - Math.max.apply( 1248 - Math, 1249 - firstHeaders.map(function(row){ 1250 - return ( 1251 - row.el 1252 - .getBoundingClientRect() 1253 - .top- 1254 - row.card 1255 - .getBoundingClientRect() 1256 - .top 1257 - ); 1258 - }) 1259 - ); 994 + card.style.setProperty( 995 + "min-height", 996 + maxHeight+"px" 997 + ); 1260 1260 1261 - firstHeaders.forEach(function(row){ 1262 - var relativeTop= 1263 - row.el 1264 - .getBoundingClientRect() 1265 - .top- 1266 - row.card 1267 - .getBoundingClientRect() 1268 - .top; 999 + card.setAttribute( 1000 + "data-sn-equal-height-managed", 1001 + "4" 1002 + ); 1269 1269 1270 - var delta= 1271 - targetTop-relativeTop; 1272 - 1273 - if(delta>1){ 1274 - saveStyle( 1275 - row.el, 1276 - originalMargin, 1277 - "margin-top" 1278 - ); 1279 - 1280 - var current= 1281 - num( 1282 - getComputedStyle( 1283 - row.el 1284 - ).marginTop 1285 - ); 1286 - 1287 - row.el.style.setProperty( 1288 - "margin-top", 1289 - ( 1290 - current+delta 1291 - )+"px" 1292 - ); 1293 - 1294 - row.el.setAttribute( 1295 - "data-sn-master-first-row", 1296 - "1" 1297 - ); 1298 - 1299 - managedMargin.add( 1300 - row.el 1301 - ); 1302 - } 1303 - }); 1304 - } 1305 - 1306 - var maxHeight=0; 1307 - 1308 - group.cards.forEach(function(card){ 1309 - maxHeight= 1310 - Math.max( 1311 - maxHeight, 1312 - card 1313 - .getBoundingClientRect() 1314 - .height 1315 - ); 1316 - }); 1317 - 1318 - maxHeight= 1319 - Math.ceil(maxHeight); 1320 - 1321 - group.cards.forEach(function(card){ 1322 - saveStyle( 1323 - card, 1324 - originalHeight, 1325 - "min-height" 1326 - ); 1327 - 1328 - card.style.setProperty( 1329 - "min-height", 1330 - maxHeight+"px" 1331 - ); 1332 - 1333 - card.setAttribute( 1334 - "data-sn-master-equal-height", 1335 - "1" 1336 - ); 1337 - 1338 - managedHeight.add( 1339 - card 1340 - ); 1341 - }); 1004 + heightManaged.add(card); 1342 1342 }); 1006 + 1007 + return { 1008 + cards:cards.length, 1009 + height:maxHeight, 1010 + firstSection:firstSection 1011 + }; 1343 1343 } 1344 1344 1345 - /* ======================================================= 1346 - DETAIL GRID EQUAL HEIGHT, DESKTOP ONLY 1347 - ======================================================= */ 1014 + function run(){ 1015 + reset(); 1348 1348 1349 - function equaliseDetailGrids(){ 1350 1350 if( 1351 - PAGE_KIND!=="detail"|| 1352 1352 window.innerWidth<=MOBILE_MAX 1353 1353 ){ 1354 1354 return; 1355 1355 } 1356 1356 1357 - var selectors=[ 1358 - ".arsn-grid", 1359 - ".arsn-grid-2", 1360 - ".arsn-grid-3", 1361 - ".dmp-grid", 1362 - ".ido-grid", 1363 - ".pp-grid" 1364 - ]; 1365 - 1366 - selectors.forEach(function(selector){ 1023 + var parents= 1367 1367 Array.from( 1368 - root.querySelectorAll(selector) 1369 - ).forEach(function(grid){ 1370 - var children= 1371 - Array.from( 1372 - grid.children||[] 1373 - ).filter(visible); 1025 + root.querySelectorAll("*") 1026 + ).filter(function(parent){ 1027 + return ( 1028 + visible(parent,MIN_W,MIN_H)&& 1029 + parent.children&& 1030 + parent.children.length>=2&& 1031 + parent.children.length<=MAX_PARENT_CHILDREN 1032 + ); 1033 + }); 1374 1374 1375 - if(children.length<2){ 1376 - return; 1377 - } 1035 + var touched=new Set(); 1378 1378 1379 - var groups=[]; 1037 + parents.forEach(function(parent){ 1038 + var entries=[]; 1380 1380 1381 - children.forEach(function(child){ 1382 - var top= 1383 - child 1384 - .getBoundingClientRect() 1385 - .top; 1040 + Array.from(parent.children).forEach(function(child){ 1041 + if(!visible(child,MIN_W,MIN_H))return; 1386 1386 1387 - var group=null; 1043 + var target=pickTarget(child); 1044 + if(!target)return; 1388 1388 1389 - for(var i=0;i<groups.length;i++){ 1390 - if( 1391 - Math.abs( 1392 - groups[i].top-top 1393 - )<=ROW_TOLERANCE 1394 - ){ 1395 - group=groups[i]; 1396 - break; 1397 - } 1398 - } 1399 - 1400 - if(!group){ 1401 - group={ 1402 - top:top, 1403 - items:[] 1404 - }; 1405 - groups.push(group); 1406 - } 1407 - 1408 - group.items.push(child); 1046 + entries.push({ 1047 + target:target, 1048 + rect:target.getBoundingClientRect() 1409 1409 }); 1050 + }); 1410 1410 1411 - groups.forEach(function(group){ 1412 - if(group.items.length<2){ 1413 - return; 1414 - } 1052 + if(entries.length<2)return; 1415 1415 1416 - var maxHeight=0; 1054 + groupsByVisualRow(entries).forEach(function(group){ 1055 + if(!validHorizontalGroup(group))return; 1417 1417 1418 - group.items.forEach(function(item){ 1419 - maxHeight= 1420 - Math.max( 1421 - maxHeight, 1422 - item 1423 - .getBoundingClientRect() 1424 - .height 1425 - ); 1057 + var signature= 1058 + group.entries.map(function(entry){ 1059 + return entry.target; 1426 1426 }); 1427 1427 1428 - maxHeight= 1429 - Math.ceil(maxHeight); 1062 + if( 1063 + !signature.some(function(el){ 1064 + return !touched.has(el); 1065 + }) 1066 + ){ 1067 + return; 1068 + } 1430 1430 1431 - group.items.forEach(function(item){ 1432 - saveStyle( 1433 - item, 1434 - originalHeight, 1435 - "min-height" 1436 - ); 1070 + var applied= 1071 + applyGroup(group); 1437 1437 1438 - item.style.setProperty( 1439 - "min-height", 1440 - maxHeight+"px" 1441 - ); 1442 - 1443 - item.setAttribute( 1444 - "data-sn-master-equal-height", 1445 - "1" 1446 - ); 1447 - 1448 - managedHeight.add(item); 1073 + if(applied){ 1074 + signature.forEach(function(el){ 1075 + touched.add(el); 1449 1449 }); 1450 - } );1077 + } 1451 1451 }); 1452 1452 }); 1453 1453 } 1454 1454 1455 - /* ======================================================= 1456 - RUN / RESPONSIVE SCHEDULING 1457 - ======================================================= */ 1082 + function schedule(){ 1083 + if(raf)cancelAnimationFrame(raf); 1458 1458 1459 - function run(){ 1460 - if(running)return; 1461 - 1462 - running=true; 1463 - 1464 - try{ 1465 - resetRuntime(); 1466 - applyPlaceholderBadge(); 1467 - propagateDetailAccents(); 1468 - 1469 - var headers= 1470 - applyOverviewPalette(); 1471 - 1472 - if( 1473 - window.innerWidth> 1474 - MOBILE_MAX 1475 - ){ 1476 - alignOverviewCards( 1477 - headers 1478 - ); 1479 - 1480 - equaliseDetailGrids(); 1481 - } 1482 - }finally{ 1483 - running=false; 1484 - } 1085 + raf=requestAnimationFrame(function(){ 1086 + raf=requestAnimationFrame(function(){ 1087 + raf=0; 1088 + run(); 1089 + }); 1090 + }); 1485 1485 } 1486 1486 1487 - function onResize(){ 1488 - if(resizeTimer){ 1489 - clearTimeout( 1490 - resizeTimer 1491 - ); 1492 - } 1093 + addEventListener("resize",schedule,{passive:true}); 1094 + addEventListener("load",schedule,{once:true}); 1493 1493 1494 - resizeTimer= 1495 - setTimeout( 1496 - function(){ 1497 - resizeTimer=0; 1498 - run(); 1499 - }, 1500 - RESIZE_DEBOUNCE 1501 - ); 1502 - } 1096 + root.addEventListener( 1097 + "click", 1098 + function(){ 1099 + setTimeout(schedule,0); 1100 + }, 1101 + true 1102 + ); 1503 1503 1504 - async function waitForReady(){ 1505 - if( 1506 - document.readyState!=="complete" 1507 - ){ 1508 - await new Promise(function(resolve){ 1509 - window.addEventListener( 1510 - "load", 1511 - resolve, 1512 - {once:true} 1513 - ); 1514 - }); 1104 + Array.from(root.querySelectorAll("img")).forEach(function(img){ 1105 + if(!img.complete){ 1106 + img.addEventListener("load",schedule,{once:true}); 1515 1515 } 1108 + }); 1516 1516 1517 - if( 1518 - document.fonts&& 1519 - document.fonts.ready 1520 - ){ 1521 - try{ 1522 - await document.fonts.ready; 1523 - }catch(error){} 1524 - } 1525 - 1526 - var images= 1527 - Array.from( 1528 - root.querySelectorAll("img") 1529 - ); 1530 - 1531 - await Promise.all( 1532 - images.map(function(img){ 1533 - if(img.complete){ 1534 - return Promise.resolve(); 1535 - } 1536 - 1537 - return new Promise(function(resolve){ 1538 - var done=function(){ 1539 - resolve(); 1540 - }; 1541 - 1542 - img.addEventListener( 1543 - "load", 1544 - done, 1545 - {once:true} 1546 - ); 1547 - 1548 - img.addEventListener( 1549 - "error", 1550 - done, 1551 - {once:true} 1552 - ); 1553 - }); 1554 - }) 1555 - ); 1556 - 1557 - run(); 1110 + if(document.fonts&&document.fonts.ready){ 1111 + document.fonts.ready.then(schedule); 1558 1558 } 1559 1559 1560 - window.addEventListener( 1561 - "resize", 1562 - onResize, 1563 - {passive:true} 1564 - ); 1565 - 1566 - waitForReady(); 1114 + schedule(); 1115 + setTimeout(schedule,200); 1116 + setTimeout(schedule,650); 1117 + setTimeout(schedule,1400); 1567 1567 })(); 1568 1568 </script> 1569 - 1570 -<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_END --> 1120 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END --> 1571 1571 {{/html}} 1572 1572