199 lines
6.6 KiB
JavaScript
199 lines
6.6 KiB
JavaScript
// ==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);
|
|
|
|
})(); |