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
Change comment: There is no comment for this version
To version 22.13
edited by Veronika Kocher
on Thursday, 03 Sep 2026 9:18
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -287,22 +287,20 @@
287 287  
288 288  {{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]{
293 - box-sizing:border-box;
290 +<style data-sn-horizontal-box-harmonizer="1">
291 +[data-sn-equal-height-managed="1"]{box-sizing:border-box}
292 +@media(max-width:767px){
293 + [data-sn-equal-height-managed="1"]{min-height:0!important}
294 294  }
295 295  </style>
296 -<script data-sn-horizontal-box-harmonizer="4">
296 +<script data-sn-horizontal-box-harmonizer="1">
297 297  (function(){
298 298   "use strict";
299 299  
300 300   var MOBILE_MAX=767;
301 - var ROW_TOLERANCE=6;
301 + var ROW_TOLERANCE=4;
302 302   var MIN_W=150;
303 303   var MIN_H=36;
304 - var MAX_PARENT_CHILDREN=16;
305 - var SEARCH_DEPTH=5;
306 306  
307 307   var root=
308 308   document.getElementById("xwikicontent")||
... ... @@ -312,10 +312,7 @@
312 312  
313 313   if(!root)return;
314 314  
315 - var heightManaged=new Set();
316 - var sectionManaged=new Set();
317 - var originalMinHeight=new WeakMap();
318 - var originalMarginTop=new WeakMap();
313 + var managed=new Set();
319 319   var raf=0;
320 320  
321 321   function num(v){
... ... @@ -323,181 +323,64 @@
323 323   return Number.isFinite(n)?n:0;
324 324   }
325 325  
326 - function visible(el,minW,minH){
321 + function visible(el){
327 327   if(!el||el.nodeType!==1)return false;
328 -
329 329   var s=getComputedStyle(el);
330 -
331 - if(
332 - s.display==="none"||
333 - s.visibility==="hidden"||
334 - Number(s.opacity)===0
335 - ){
336 - return false;
337 - }
338 -
324 + if(s.display==="none"||s.visibility==="hidden"||Number(s.opacity)===0)return false;
339 339   var r=el.getBoundingClientRect();
340 -
341 - return (
342 - r.width>=(minW||1)&&
343 - r.height>=(minH||1)
344 - );
326 + return r.width>=MIN_W&&r.height>=MIN_H;
345 345   }
346 346  
347 347   function excluded(el){
348 - return (
349 - !el.matches||
350 - el.matches(
351 - "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]"
352 - )
330 + return !el.matches||el.matches(
331 + "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]"
353 353   );
354 354   }
355 355  
356 356   function namedLikeBox(el){
357 - var c=
358 - typeof el.className==="string"
359 - ? el.className
360 - : "";
361 -
362 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c);
336 + var c=typeof el.className==="string"?el.className:"";
337 + return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c);
363 363   }
364 364  
365 365   function boxLike(el){
366 - if(!visible(el,MIN_W,MIN_H)||excluded(el))return false;
367 -
341 + if(!visible(el)||excluded(el))return false;
368 368   var s=getComputedStyle(el);
369 369   var r=el.getBoundingClientRect();
370 -
371 371   var border=
372 - num(s.borderTopWidth)+
373 - num(s.borderRightWidth)+
374 - num(s.borderBottomWidth)+
375 - num(s.borderLeftWidth);
376 -
345 + num(s.borderTopWidth)+num(s.borderRightWidth)+
346 + num(s.borderBottomWidth)+num(s.borderLeftWidth);
377 377   var radius=Math.max(
378 - num(s.borderTopLeftRadius),
379 - num(s.borderTopRightRadius),
380 - num(s.borderBottomLeftRadius),
381 - num(s.borderBottomRightRadius)
348 + num(s.borderTopLeftRadius),num(s.borderTopRightRadius),
349 + num(s.borderBottomLeftRadius),num(s.borderBottomRightRadius)
382 382   );
383 -
384 - var bg=
385 - s.backgroundColor&&
386 - s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
387 - s.backgroundColor!=="transparent";
388 -
389 - var shadow=
390 - s.boxShadow&&
391 - s.boxShadow!=="none";
392 -
393 - return (
394 - namedLikeBox(el)||
395 - border>=1||
396 - (bg&&radius>=3)||
397 - (shadow&&r.width>=180)
398 - );
351 + var bg=s.backgroundColor&&s.backgroundColor!=="rgba(0, 0, 0, 0)"&&s.backgroundColor!=="transparent";
352 + var shadow=s.boxShadow&&s.boxShadow!=="none";
353 + return namedLikeBox(el)||border>=1||(bg&&radius>=3)||(shadow&&r.width>=180);
399 399   }
400 400  
401 - function saveOriginal(el,map,property){
402 - if(map.has(el))return;
403 -
404 - map.set(el,{
405 - value:el.style.getPropertyValue(property),
406 - priority:el.style.getPropertyPriority(property)
407 - });
408 - }
409 -
410 - function restoreOriginal(el,map,property){
411 - var original=map.get(el);
412 -
413 - if(original&&original.value){
414 - el.style.setProperty(
415 - property,
416 - original.value,
417 - original.priority||""
418 - );
419 - }else{
420 - el.style.removeProperty(property);
421 - }
422 - }
423 -
424 - function reset(){
425 - heightManaged.forEach(function(el){
426 - restoreOriginal(
427 - el,
428 - originalMinHeight,
429 - "min-height"
430 - );
431 -
432 - el.removeAttribute(
433 - "data-sn-equal-height-managed"
434 - );
435 - });
436 -
437 - sectionManaged.forEach(function(el){
438 - restoreOriginal(
439 - el,
440 - originalMarginTop,
441 - "margin-top"
442 - );
443 -
444 - el.removeAttribute(
445 - "data-sn-first-section-aligned"
446 - );
447 - });
448 -
449 - heightManaged.clear();
450 - sectionManaged.clear();
451 - }
452 -
453 453   function pickTarget(child){
454 - if(!visible(child,MIN_W,MIN_H))return null;
357 + if(!visible(child))return null;
358 + if(boxLike(child))return child;
455 455  
456 - if(boxLike(child)){
457 - return child;
458 - }
459 -
460 460   var cr=child.getBoundingClientRect();
461 461   var q=[];
462 -
463 463   Array.from(child.children||[]).forEach(function(el){
464 464   q.push({el:el,depth:1});
465 465   });
466 466  
467 - var best=null;
468 - var bestScore=-Infinity;
366 + var best=null,bestScore=-1;
469 469  
470 470   while(q.length){
471 471   var item=q.shift();
472 472   var el=item.el;
371 + if(!visible(el))continue;
473 473  
474 - if(!visible(el,MIN_W,MIN_H))continue;
475 -
476 476   var r=el.getBoundingClientRect();
477 477  
478 - if(
479 - r.width>=cr.width*.68&&
480 - boxLike(el)
481 - ){
482 - var widthFit=
483 - 1-Math.min(
484 - 1,
485 - Math.abs(cr.width-r.width)/
486 - Math.max(1,cr.width)
487 - );
488 -
489 - var topFit=
490 - 1-Math.min(
491 - 1,
492 - Math.abs(cr.top-r.top)/100
493 - );
494 -
495 - var score=
496 - widthFit*6+
497 - topFit*3+
498 - Math.min(r.height,900)/450+
499 - Math.min(r.width*r.height,900000)/900000;
500 -
375 + if(r.width>=cr.width*.72&&boxLike(el)){
376 + var widthFit=1-Math.min(1,Math.abs(cr.width-r.width)/Math.max(1,cr.width));
377 + var topFit=1-Math.min(1,Math.abs(cr.top-r.top)/80);
378 + var score=widthFit*5+topFit*2+Math.min(r.height,600)/600;
501 501   if(score>bestScore){
502 502   best=el;
503 503   bestScore=score;
... ... @@ -504,12 +504,9 @@
504 504   }
505 505   }
506 506  
507 - if(item.depth<SEARCH_DEPTH){
385 + if(item.depth<3){
508 508   Array.from(el.children||[]).forEach(function(next){
509 - q.push({
510 - el:next,
511 - depth:item.depth+1
512 - });
387 + q.push({el:next,depth:item.depth+1});
513 513   });
514 514   }
515 515   }
... ... @@ -517,392 +517,61 @@
517 517   return best;
518 518   }
519 519  
520 - function groupsByVisualRow(entries){
521 - var groups=[];
522 -
523 - entries
524 - .slice()
525 - .sort(function(a,b){
526 - return (
527 - a.rect.top-b.rect.top||
528 - a.rect.left-b.rect.left
529 - );
530 - })
531 - .forEach(function(entry){
532 - var group=null;
533 -
534 - for(var i=0;i<groups.length;i++){
535 - if(
536 - Math.abs(
537 - groups[i].top-
538 - entry.rect.top
539 - )<=ROW_TOLERANCE
540 - ){
541 - group=groups[i];
542 - break;
543 - }
544 - }
545 -
546 - if(!group){
547 - group={
548 - top:entry.rect.top,
549 - entries:[]
550 - };
551 - groups.push(group);
552 - }
553 -
554 - group.entries.push(entry);
555 - });
556 -
557 - return groups;
395 + function reset(){
396 + managed.forEach(function(el){
397 + el.style.removeProperty("min-height");
398 + el.removeAttribute("data-sn-equal-height-managed");
399 + });
400 + managed.clear();
558 558   }
559 559  
560 - function validHorizontalGroup(group){
561 - if(group.entries.length<2)return false;
562 -
563 - var lefts=
564 - group.entries
565 - .map(function(e){return e.rect.left;})
566 - .sort(function(a,b){return a-b;});
567 -
568 - if(
569 - lefts[lefts.length-1]-
570 - lefts[0]<
571 - 80
572 - ){
573 - return false;
403 + function horizontalLayout(parent){
404 + if(!visible(parent))return false;
405 + var s=getComputedStyle(parent);
406 + if(s.display==="grid"||s.display==="inline-grid")return true;
407 + if(s.display==="flex"||s.display==="inline-flex"){
408 + return s.flexDirection==="row"||s.flexDirection==="row-reverse";
574 574   }
575 -
576 - var widths=
577 - group.entries
578 - .map(function(e){return e.rect.width;})
579 - .filter(function(w){return w>0;});
580 -
581 - if(widths.length<2)return false;
582 -
583 - var minWidth=Math.min.apply(Math,widths);
584 - var maxWidth=Math.max.apply(Math,widths);
585 -
586 - return (
587 - maxWidth/
588 - Math.max(1,minWidth)<=
589 - 1.9
590 - );
410 + return false;
591 591   }
592 592  
593 - function normalizeText(text){
594 - return String(text||"")
595 - .replace(/s+/g," ")
596 - .trim()
597 - .toUpperCase();
598 - }
599 -
600 - function exactKnownFirstSection(el){
601 - var text=normalizeText(el.textContent);
602 -
603 - return (
604 - text==="HIER STARTEN"||
605 - text==="ORGANISIEREN & DOKUMENTIEREN"||
606 - text==="ENTSCHEIDEN"||
607 - text==="START HERE"||
608 - text==="GET STARTED"||
609 - text==="ORGANISE & DOCUMENT"||
610 - text==="ORGANIZE & DOCUMENT"||
611 - text==="DECIDE"
612 - );
613 - }
614 -
615 - function visuallyFirstSectionBar(card){
616 - var cardRect=card.getBoundingClientRect();
617 - var candidates=[];
618 -
619 - Array.from(
620 - card.querySelectorAll(
621 - "div,p,span,strong,h3,h4,h5,h6"
622 - )
623 - ).forEach(function(el){
624 - if(!visible(el,20,6))return;
625 - if(excluded(el))return;
626 -
627 - var r=el.getBoundingClientRect();
628 - var relativeTop=r.top-cardRect.top;
629 -
630 - if(
631 - relativeTop<45||
632 - relativeTop>cardRect.height*.72
633 - ){
634 - return;
413 + function rowGroups(entries){
414 + var groups=[];
415 + entries.slice().sort(function(a,b){
416 + return a.rect.top-b.rect.top||a.rect.left-b.rect.left;
417 + }).forEach(function(entry){
418 + var group=null;
419 + for(var i=0;i<groups.length;i++){
420 + if(Math.abs(groups[i].top-entry.rect.top)<=ROW_TOLERANCE){
421 + group=groups[i];
422 + break;
423 + }
635 635   }
636 -
637 - var widthRatio=
638 - r.width/
639 - Math.max(1,cardRect.width);
640 -
641 - if(
642 - widthRatio<.55||
643 - widthRatio>1.05||
644 - r.height>54
645 - ){
646 - return;
425 + if(!group){
426 + group={top:entry.rect.top,entries:[]};
427 + groups.push(group);
647 647   }
648 -
649 - var text=normalizeText(el.textContent);
650 -
651 - if(
652 - text.length<2||
653 - text.length>90
654 - ){
655 - return;
656 - }
657 -
658 - var style=getComputedStyle(el);
659 -
660 - var bg=
661 - style.backgroundColor&&
662 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&&
663 - style.backgroundColor!=="transparent";
664 -
665 - var border=
666 - num(style.borderTopWidth)+
667 - num(style.borderRightWidth)+
668 - num(style.borderBottomWidth)+
669 - num(style.borderLeftWidth);
670 -
671 - var mostlyUpper=
672 - text.length>=3&&
673 - text===text.toUpperCase();
674 -
675 - var exact=exactKnownFirstSection(el);
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);
683 -
684 - if(score>=7){
685 - candidates.push({
686 - el:el,
687 - relativeTop:relativeTop,
688 - width:r.width,
689 - score:score
690 - });
691 - }
429 + group.entries.push(entry);
692 692   });
693 -
694 - if(!candidates.length)return null;
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 - });
703 -
704 - // For exact known labels always prefer the earliest exact match.
705 - var exacts=candidates.filter(function(c){
706 - return exactKnownFirstSection(c.el);
707 - });
708 -
709 - if(exacts.length){
710 - exacts.sort(function(a,b){
711 - return a.relativeTop-b.relativeTop||b.width-a.width;
712 - });
713 - return exacts[0].el;
714 - }
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 - });
720 -
721 - return candidates[0].el;
431 + return groups;
722 722   }
723 723  
724 - function alignFirstSections(cards){
725 - var rows=
726 - cards.map(function(card){
727 - var bar=
728 - visuallyFirstSectionBar(card);
729 -
730 - if(!bar)return null;
731 -
732 - var cardRect=
733 - card.getBoundingClientRect();
734 -
735 - var barRect=
736 - bar.getBoundingClientRect();
737 -
738 - return {
739 - card:card,
740 - bar:bar,
741 - relativeTop:
742 - barRect.top-cardRect.top,
743 - label:
744 - normalizeText(bar.textContent)
745 - };
746 - });
747 -
748 - // Conservative: only align when every card in the horizontal row
749 - // has a reliable first section bar.
750 - if(
751 - rows.some(function(row){
752 - return !row;
753 - })
754 - ){
755 - return null;
756 - }
757 -
758 - var targetTop=
759 - Math.max.apply(
760 - Math,
761 - rows.map(function(row){
762 - return row.relativeTop;
763 - })
764 - );
765 -
766 - rows.forEach(function(row){
767 - var delta=
768 - targetTop-
769 - row.relativeTop;
770 -
771 - if(delta<=1)return;
772 -
773 - saveOriginal(
774 - row.bar,
775 - originalMarginTop,
776 - "margin-top"
777 - );
778 -
779 - var currentMargin=
780 - num(
781 - getComputedStyle(
782 - row.bar
783 - ).marginTop
784 - );
785 -
786 - row.bar.style.setProperty(
787 - "margin-top",
788 - (
789 - currentMargin+
790 - delta
791 - )+"px"
792 - );
793 -
794 - row.bar.setAttribute(
795 - "data-sn-first-section-aligned",
796 - "4"
797 - );
798 -
799 - sectionManaged.add(row.bar);
800 - });
801 -
802 - return {
803 - labels:
804 - rows.map(function(row){
805 - return row.label;
806 - }),
807 - targetRelativeTop:
808 - Math.round(targetTop)
809 - };
810 - }
811 -
812 - function applyGroup(group){
813 - if(!validHorizontalGroup(group)){
814 - return null;
815 - }
816 -
817 - var cards=[];
818 - var seen=new Set();
819 -
820 - group.entries.forEach(function(entry){
821 - if(!seen.has(entry.target)){
822 - seen.add(entry.target);
823 - cards.push(entry.target);
824 - }
825 - });
826 -
827 - if(cards.length<2)return null;
828 -
829 - var firstSection=
830 - alignFirstSections(cards);
831 -
832 - // The section alignment can change natural card heights.
833 - // Measure only afterwards.
834 - var maxHeight=0;
835 -
836 - cards.forEach(function(card){
837 - maxHeight=Math.max(
838 - maxHeight,
839 - card.getBoundingClientRect().height
840 - );
841 - });
842 -
843 - maxHeight=Math.ceil(maxHeight);
844 -
845 - if(maxHeight<MIN_H)return null;
846 -
847 - cards.forEach(function(card){
848 - saveOriginal(
849 - card,
850 - originalMinHeight,
851 - "min-height"
852 - );
853 -
854 - card.style.setProperty(
855 - "min-height",
856 - maxHeight+"px"
857 - );
858 -
859 - card.setAttribute(
860 - "data-sn-equal-height-managed",
861 - "4"
862 - );
863 -
864 - heightManaged.add(card);
865 - });
866 -
867 - return {
868 - cards:cards.length,
869 - height:maxHeight,
870 - firstSection:firstSection
871 - };
872 - }
873 -
874 874   function run(){
875 875   reset();
876 876  
877 - if(
878 - window.innerWidth<=MOBILE_MAX
879 - ){
880 - return;
881 - }
437 + if(window.innerWidth<=MOBILE_MAX)return;
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 - });
439 + var parents=Array.from(root.querySelectorAll("*")).filter(function(el){
440 + return el.children&&el.children.length>=2&&horizontalLayout(el);
441 + });
894 894  
895 - var touched=new Set();
896 -
897 897   parents.forEach(function(parent){
898 898   var entries=[];
899 899  
900 900   Array.from(parent.children).forEach(function(child){
901 - if(!visible(child,MIN_W,MIN_H))return;
902 -
447 + if(!visible(child))return;
903 903   var target=pickTarget(child);
904 904   if(!target)return;
905 -
906 906   entries.push({
907 907   target:target,
908 908   rect:target.getBoundingClientRect()
... ... @@ -911,30 +911,37 @@
911 911  
912 912   if(entries.length<2)return;
913 913  
914 - groupsByVisualRow(entries).forEach(function(group){
915 - if(!validHorizontalGroup(group))return;
458 + rowGroups(entries).forEach(function(group){
459 + if(group.entries.length<2)return;
916 916  
917 - var signature=
918 - group.entries.map(function(entry){
919 - return entry.target;
920 - });
461 + var lefts=group.entries.map(function(e){return e.rect.left;}).sort(function(a,b){return a-b;});
462 + if(lefts[lefts.length-1]-lefts[0]<80)return;
921 921  
922 - if(
923 - !signature.some(function(el){
924 - return !touched.has(el);
925 - })
926 - ){
927 - return;
928 - }
464 + var targets=[];
465 + var seen=new Set();
929 929  
930 - var applied=
931 - applyGroup(group);
467 + group.entries.forEach(function(e){
468 + if(!seen.has(e.target)){
469 + seen.add(e.target);
470 + targets.push(e.target);
471 + }
472 + });
932 932  
933 - if(applied){
934 - signature.forEach(function(el){
935 - touched.add(el);
936 - });
937 - }
474 + if(targets.length<2)return;
475 +
476 + var max=0;
477 + targets.forEach(function(el){
478 + max=Math.max(max,el.getBoundingClientRect().height);
479 + });
480 +
481 + max=Math.ceil(max);
482 + if(max<MIN_H)return;
483 +
484 + targets.forEach(function(el){
485 + el.style.setProperty("min-height",max+"px");
486 + el.setAttribute("data-sn-equal-height-managed","1");
487 + managed.add(el);
488 + });
938 938   });
939 939   });
940 940   }
... ... @@ -941,7 +941,6 @@
941 941  
942 942   function schedule(){
943 943   if(raf)cancelAnimationFrame(raf);
944 -
945 945   raf=requestAnimationFrame(function(){
946 946   raf=requestAnimationFrame(function(){
947 947   raf=0;
... ... @@ -953,28 +953,17 @@
953 953   addEventListener("resize",schedule,{passive:true});
954 954   addEventListener("load",schedule,{once:true});
955 955  
956 - root.addEventListener(
957 - "click",
958 - function(){
959 - setTimeout(schedule,0);
960 - },
961 - true
962 - );
506 + root.addEventListener("click",function(){
507 + setTimeout(schedule,0);
508 + },true);
963 963  
964 - Array.from(root.querySelectorAll("img")).forEach(function(img){
965 - if(!img.complete){
966 - img.addEventListener("load",schedule,{once:true});
967 - }
968 - });
969 -
970 970   if(document.fonts&&document.fonts.ready){
971 971   document.fonts.ready.then(schedule);
972 972   }
973 973  
974 974   schedule();
975 - setTimeout(schedule,200);
976 - setTimeout(schedule,650);
977 - setTimeout(schedule,1400);
515 + setTimeout(schedule,250);
516 + setTimeout(schedule,900);
978 978  })();
979 979  </script>
980 980  <!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->