Neues Script mit dynamischer Navigation

This commit is contained in:
2026-06-23 09:23:05 +02:00
parent 9854cfc1cd
commit 8bd0ffb15b
+199
View File
@@ -0,0 +1,199 @@
// ==UserScript==
// @name academyFIVE::ResizeNavbar2
// @namespace dakp/academyfive
// @version 2026-06-19
// @description Ermöglicht es, die Breite des Baummenüs per Drag & Drop dynamisch anzupassen. Die Icons laufen dabei automatisch mit.
// @author Dims Akpan
// @match https://a5.fhdw-hannover.de/*
// @match https://a5.fhdw.de/*
// @icon https://www.academyfive.com/typo3conf/ext/sitepackage/Resources/Public/build/assets/images/favicon-academyfive.ico
// @grant none
// ==/UserScript==
(function() {
'use strict';
const MIN_WIDTH = 300;
const MAX_WIDTH_FACTOR = 0.5; // 50% des Viewports
const ICON_OFFSET = 80; // Abstand der controlLayer-Icons vom rechten Rand der Navbar
const iframe = document.getElementById('nav');
const navbarCell = document.getElementById('navTree');
if (!iframe || !navbarCell) {
return;
}
// --- Unsichtbarer Resize-Griff am rechten Rand der Navbar ---
const navbarCellPosition = window.getComputedStyle(navbarCell).position;
if (navbarCellPosition === 'static') {
navbarCell.style.position = 'relative';
}
const grip = document.createElement('div');
grip.style.position = 'absolute';
grip.style.top = '0';
grip.style.right = '0';
grip.style.bottom = '0';
grip.style.width = '6px';
grip.style.background = 'transparent';
grip.style.cursor = 'ew-resize';
grip.style.zIndex = '9999';
grip.style.transition = 'background 0.15s ease';
navbarCell.appendChild(grip);
let isDragging = false;
let startX = 0;
let startWidth = 0;
let activePointerId = null;
/** Beschränkt einen Wert auf den Bereich [min, max]. */
function clamp(value, min, max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
/** Setzt die Breite von navbarCell und iframe auf den angegebenen Wert. */
function applyWidth(newWidth) {
navbarCell.style.width = newWidth + 'px';
navbarCell.setAttribute('width', newWidth);
iframe.style.width = newWidth + 'px';
iframe.setAttribute('width', newWidth);
}
grip.addEventListener('mouseenter', function() {
grip.style.background = 'rgba(0, 0, 0, 0.25)';
});
grip.addEventListener('mouseleave', function() {
if (!isDragging) {
grip.style.background = 'transparent';
}
});
grip.addEventListener('pointerdown', function(e) {
if (e.button !== 0) {
return;
}
isDragging = true;
startX = e.clientX;
startWidth = navbarCell.offsetWidth;
activePointerId = e.pointerId;
grip.setPointerCapture(e.pointerId);
document.body.style.userSelect = 'none';
document.body.style.cursor = 'ew-resize';
e.preventDefault();
});
grip.addEventListener('pointermove', function(e) {
if (!isDragging) {
return;
}
const delta = e.clientX - startX;
const maxWidth = window.innerWidth * MAX_WIDTH_FACTOR;
applyWidth(clamp(startWidth + delta, MIN_WIDTH, maxWidth));
});
/** Beendet den Drag-Vorgang und setzt Cursor/Selektion zurück. */
function endDrag() {
if (!isDragging) {
return;
}
isDragging = false;
document.body.style.userSelect = '';
document.body.style.cursor = '';
grip.style.background = 'transparent';
if (activePointerId !== null) {
try {
grip.releasePointerCapture(activePointerId);
} catch (err) {
// bereits aufgeloest
}
activePointerId = null;
}
}
grip.addEventListener('pointerup', endDrag);
grip.addEventListener('pointercancel', endDrag);
// --- controlLayer-Icons synchron mit der Navbar-Breite bewegen ---
//
// Wichtig: controlLayer lebt INNERHALB des iframes (nav.php4).
// Wir muessen ueber iframe.contentDocument darauf zugreifen.
// Das iframe wird auch durch Klick auf das "Aktualisieren"-Icon
// neu geladen -> wir reagieren auf jeden load-Event.
/** Verschiebt die controlLayer-Icons im iframe passend zur aktuellen Navbar-Breite. */
function syncControlLayer() {
let iframeDoc;
try {
iframeDoc = iframe.contentDocument;
} catch (err) {
// Same-Origin verletzt
return;
}
if (!iframeDoc) {
return;
}
const controlLayer = iframeDoc.getElementById('controlLayer');
if (!controlLayer) {
return;
}
const iconLeft = navbarCell.offsetWidth - ICON_OFFSET;
controlLayer.style.left = iconLeft + 'px';
}
// Wenn das iframe schon fertig ist, sofort synchronisieren,
// ansonsten auf das load-Event warten.
/** Führt syncControlLayer() aus, sofern das iframe bereits vollständig geladen ist. */
function trySetupControlLayer() {
if (iframe.contentDocument && iframe.contentDocument.readyState === 'complete') {
syncControlLayer();
}
}
iframe.addEventListener('load', function() {
// Kurze Verzoegerung: das DOM des iframes ist zwar 'complete',
// aber einzelne Elemente werden teils erst danach verfuegbar.
setTimeout(syncControlLayer, 50);
});
// Erst-Initialisierung
trySetupControlLayer();
// Reagiert auf Aenderungen der Navbar-Groesse
if (typeof ResizeObserver !== 'undefined') {
new ResizeObserver(syncControlLayer).observe(navbarCell);
}
// Bei Fenster-Resize aendert sich ggf. die Maximalbreite
window.addEventListener('resize', syncControlLayer);
// --- showHideTree() ueberschreiben, damit Einklappen auch bei aktivem
// Resize-Griff funktioniert. Die Originalfunktion setzt nur das HTML-
// Attribut "width", aber unser Inline-CSS (style.width) hat Vorrang.
// Wir rufen das Original danach trotzdem auf, falls es noch mehr macht.
const originalShowHideTree = window.showHideTree;
if (typeof originalShowHideTree !== 'function') {
window.showHideTree = function() {};
}
window.showHideTree = (function(original) {
return function() {
const navExpandCell = jQuery('#navigationExpandCell');
// expandCell sichtbar = Nav ist eingeklappt -> wir wollen ausklappen
const navIsCollapsed = navExpandCell.css('display') !== 'none';
if (navIsCollapsed) {
applyWidth(MIN_WIDTH);
} else {
applyWidth(0);
}
return original.apply(this, arguments);
};
})(window.showHideTree);
})();