Neues Script mit dynamischer Navigation
This commit is contained in:
@@ -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);
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user