- Firefox-Version
- 154.0.1
- Betriebssystem
- Win 11
Das Script im Original ist hier zu finden. Mit KI übersetzt und weitere Anpassungen vorgenommen. Es erstellt einen Button, bei Klick darauf öffnet sich ein Menü mit alle Erweiterungen, wie sie unter about:addons zu finden sind. Verweilt die Maus über einem Eintrag, dann öffnet sich ein Tooltip-Dialog, dem man Handlungsanweisungen entnehmen kann. Außerdem sind Beispiele für die Pfade des Icons angegeben., und auch im Code selbst gibt es Einstellungsmöglichkeiten für das Aussehen des Tooltip-Dialoges.
JavaScript
// ==UserScript==
// @name Extension Options Menu.uc.js
// @description Fügt eine Menüschaltfläche zur Verwaltung von Erweiterungen hinzu
// @include main
// @charset UTF-8
// @version 4.1.1
// @sandbox false
// ==/UserScript==
'use strict';
let EOM = {
/*
* ============================================================
* EINSTELLUNGEN
* ============================================================
*/
// Version hinter dem Add-on-Namen anzeigen
showVersion: true,
// Auch Add-ons ohne Einstellungsseite anzeigen
showAll: true,
// Deaktivierte Add-ons anzeigen
showDisabled: true,
/* Position des Buttons von rechts:
* 0 = ganz rechts
* z.B. 1 = eine Position vom rechten Rand entfernt
* z.B. 5 = fünf Positionen vom rechten Rand entfernt */
positionFromRight: 5,
/* Das ICON für den Button = iconPath:
* Leerzeichen im Pfad mit %20 ergänzen
* 1 = C:\FoxIcons2\addons.png
* 2 = G:\Firefox Test\Firefox22p\Profilordner\chrome\icons\addons.png (portable)
* 2 = C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\ArbeitsFox\chrome\Icons\addons.png" (installiert)
* Zum Wechseln einfach die Zahl ändern */
iconPath: 1,
iconPaths: {
1: 'file:///C:/FoxIcons2/addons.png',
2: 'file:///G:/Firefox%20Test/Firefox22p/Profilordner/chrome/icons/addons.png'
},
// Gewählten Icon-Pfad zurückgeben
get buttonIconURL() {
return this.iconPaths[this.iconPath] || this.iconPaths[1];
},
// Eigenes Hinweisfenster
hoverHint: null,
/* Sortierung:
* enabled = Position aktiver Add-ons
* disabled = Position deaktivierter Add-ons
* 0, 0 = alle alphabetisch
* 0, 1 = aktive zuerst, deaktivierte danach */
sort: {
enabled: 0,
disabled: 1
},
/*========== * CSS für Button u. Menü * =========*/
injectStyles: function() {
const css = `
#eom-button image.toolbarbutton-icon {
scale: 1.0 !important;
padding: 7px !important;
}
#eom-button-popup menuitem.menuitem-iconic img.menu-icon {
margin-left: -28px !important;
}
#eom-button-popup menuitem.menuitem-iconic label.menu-text {
margin-left: 5px !important;
}
`;
try {
let style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
let head = document.getElementsByTagName('head')[0];
if (head) {
head.appendChild(style);
} else {
document.documentElement.appendChild(style);
}
} catch (e) {
Cu.reportError(
'EOM: CSS konnte nicht injiziert werden: ' + e
);
}
},
/*=========== * INITIALISIERUNG * ===========*/
init: function() {
this.injectStyles();
const XUL_NS =
'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
// Button nicht doppelt erstellen
if (document.getElementById('eom-button')) {
return;
}
// Navigationsleiste suchen
let toolbar =
document.getElementById('nav-bar-customization-target') ||
document.getElementById('nav-bar');
// Falls die Toolbar noch nicht vorhanden ist, später erneut versuchen
if (!toolbar) {
setTimeout(() => EOM.init(), 1000);
return;
}
/*=========== * Toolbarbutton erstellen * ===========*/
let toolbarButton = document.createElementNS(
XUL_NS,
'toolbarbutton'
);
toolbarButton.setAttribute('id', 'eom-button');
toolbarButton.setAttribute(
'class',
'toolbarbutton-1 chromeclass-toolbar-additional'
);
toolbarButton.setAttribute('type', 'menu');
toolbarButton.setAttribute(
'label',
'Menü für Erweiterungsoptionen'
);
toolbarButton.setAttribute(
'tooltiptext',
'Menü für Erweiterungsoptionen'
);
// Icon setzen
toolbarButton.setAttribute(
'image',
this.buttonIconURL
);
try {
toolbarButton.style.setProperty(
'list-style-image',
`url("${this.buttonIconURL}")`,
'important'
);
} catch (e) {
Cu.reportError(
'EOM: Button-Icon konnte nicht gesetzt werden: ' + e
);
}
/*============ * Kontextmenü unterdrücken * =============*/
toolbarButton.addEventListener(
'contextmenu',
event => event.preventDefault(),
false
);
/*============ * Button-Klicks * =============*/
toolbarButton.addEventListener(
'click',
event => EOM.iconClick(event),
false
);
/*=========== * Menü-Popup erstellen * ===========*/
let menuPopup = document.createElementNS(
XUL_NS,
'menupopup'
);
menuPopup.setAttribute(
'id',
'eom-button-popup'
);
menuPopup.addEventListener(
'popupshowing',
event => EOM.populateMenu(event),
false
);
menuPopup.addEventListener(
'popuphiding',
() => EOM.hideHoverHint(),
false
);
menuPopup.addEventListener(
'popuphidden',
() => EOM.hideHoverHint(),
false
);
toolbarButton.appendChild(menuPopup);
/*======= * Button an gewünschter Position einsetzen * =======*/
let toolbarItems = Array.from(toolbar.children)
.filter(item => item.id !== 'eom-button');
let insertIndex = Math.max(
0,
toolbarItems.length - this.positionFromRight
);
let referenceItem = toolbarItems[insertIndex];
if (referenceItem) {
toolbar.insertBefore(
toolbarButton,
referenceItem
);
} else {
toolbar.appendChild(toolbarButton);
}
},
/* ============================================================
* HINWEISFENSTER
* ============================================================ */
createHoverHint: function() {
if (this.hoverHint) {
return this.hoverHint;
}
const XUL_NS =
'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
let panel = document.createElementNS(
XUL_NS,
'panel'
);
panel.setAttribute(
'id',
'eom-custom-hover-hint'
);
panel.setAttribute('noautohide', 'true');
panel.setAttribute('ignorekeys', 'true');
panel.setAttribute('level', 'top');
panel.setAttribute('orient', 'vertical');
panel.setAttribute('hidden', 'true');
panel.style.cssText = `
-moz-appearance: none !important;
appearance: none !important;
box-sizing: border-box !important;
width: 520px !important;
min-width: 520px !important;
max-width: 520px !important;
padding: 10px !important;
background: #202124 !important;
color: #fff !important;
border: 3px solid #4a90e2 !important;
border-radius: 8px !important;
box-shadow: 0 5px 18px rgba(0,0,0,.55) !important;
pointer-events: none !important;
z-index: 2147483647 !important;
`;
let description = document.createElementNS(
XUL_NS,
'description'
);
description.setAttribute(
'id',
'eom-custom-hover-hint-text'
);
description.style.cssText = `
display: block !important;
margin: 0 !important;
padding: 5px 5px 5px 15px !important;
background: grey !important;
color: #fff !important;
font-family: Arial, sans-serif !important;
font-size: 15px !important;
font-weight: normal !important;
line-height: 1.5 !important;
white-space: pre-wrap !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
`;
panel.appendChild(description);
let popupSet =
document.getElementById('mainPopupSet');
if (popupSet) {
popupSet.appendChild(panel);
} else {
document.documentElement.appendChild(panel);
}
this.hoverHint = panel;
return panel;
},
showHoverHint: function(event, message) {
let panel = this.createHoverHint();
let description =
document.getElementById(
'eom-custom-hover-hint-text'
);
if (!description) {
return;
}
description.textContent = message;
try {
panel.hidePopup();
} catch (e) {}
panel.removeAttribute('hidden');
try {
panel.openPopupAtScreen(
event.screenX + 18,
event.screenY + 32,
false
);
} catch (e) {
Cu.reportError(
'EOM: Hinweisfenster konnte nicht geöffnet werden: ' + e
);
}
},
hideHoverHint: function() {
if (!this.hoverHint) {
return;
}
try {
this.hoverHint.hidePopup();
} catch (e) {}
this.hoverHint.setAttribute(
'hidden',
'true'
);
},
/*========= * MENÜ AUFBAUEN * ==========*/
populateMenu: async function(event) {
let popup = event.target;
if (popup !== event.currentTarget) {
return;
}
this.hideHoverHint();
// Vorhandene Einträge entfernen
while (popup.hasChildNodes()) {
popup.removeChild(popup.firstChild);
}
let previousState;
let addons =
await AddonManager.getAddonsByTypes(['extension']);
addons
.sort((a, b) => {
let keyA = this.key(a);
let keyB = this.key(b);
return keyA < keyB ? -1 : 1;
})
.forEach(addon => {
// Integrierte Add-ons und Such-Plugins ausblenden
if (
addon.isBuiltin ||
addon.id.endsWith('@search.mozilla.org')
) {
return;
}
// Prüfen, ob das Add-on angezeigt werden soll
if (
!addon.appDisabled &&
(
(
addon.isActive &&
addon.optionsURL
) ||
(
addon.userDisabled &&
this.showDisabled
) ||
(
!addon.userDisabled &&
this.showAll
)
)
) {
let state = addon.isActive;
// Trennlinie zwischen aktiv und deaktiviert
if (
this.sort.disabled === 1 &&
previousState !== undefined &&
state !== previousState
) {
popup.appendChild(
document.createXULElement(
'menuseparator'
)
);
}
previousState = state;
/* Menüeintrag erstellen */
let menuItem =
document.createXULElement('menuitem');
let label = addon.name;
if (this.showVersion) {
label += ' [' + addon.version + ']';
}
menuItem.setAttribute(
'label',
label
);
menuItem.setAttribute(
'class',
'menuitem-iconic'
);
/* Hinweistext */
menuItem._EOMHint =
'ID: ' + addon.id +
'\n\n' +
'Linksklick = Einstellungsseite des Add-ons öffnen\n' +
'Rechtsklick = Add-on aktivieren/deaktivieren\n' +
'Mittelklick = Startseite des Add-ons öffnen\n' +
'Strg + Linksklick = Ordner des Add-ons öffnen\n' +
'Strg + Rechtsklick = Add-on deinstallieren/Rückgängigmachen\n' +
'※ Bei Add-ons, die keinen Neustart benötigen, kann die Deinstallation nicht rückgängig gemacht werden\n' +
'Alt + Linksklick = ID des Add-ons kopieren';
/* Hinweis beim Überfahren */
menuItem.addEventListener(
'mouseenter',
event => {
this.showHoverHint(
event,
menuItem._EOMHint
);
},
false
);
menuItem.addEventListener(
'mouseleave',
() => this.hideHoverHint(),
false
);
/* Add-on-Symbol */
let icon =
addon.iconURL ||
addon.iconURL64 ||
'';
if (icon) {
menuItem.setAttribute(
'image',
icon
);
}
menuItem._Addon = addon;
menuItem.addEventListener(
'click',
event => this.handleClick(event),
false
);
/* Darstellung */
if (
!addon.optionsURL &&
addon.isActive
) {
menuItem.setAttribute(
'style',
'color: Gray'
);
}
if (addon.optionsURL) {
menuItem.setAttribute(
'style',
'color: #000'
);
}
// Status aktualisieren
this.updateState(
menuItem,
addon.userDisabled
);
this.setUninstall(
menuItem,
this.isPending(addon)
);
popup.appendChild(menuItem);
}
});
},
/* ============================================================
* BUTTON-KLICKS
* ============================================================ */
iconClick: function(event) {
if (event.target !== event.currentTarget) {
return;
}
// Mittelklick = Firefox neu starten
if (event.button === 1) {
this.restart();
}
// Rechtsklick = Add-on-Manager öffnen
else if (event.button === 2) {
event.preventDefault();
event.stopPropagation();
setTimeout(() => {
let contextMenu =
document.getElementById(
'toolbar-context-menu'
);
if (contextMenu) {
contextMenu.hidePopup();
}
}, 0);
BrowserAddonUI.openAddonsMgr(
'addons://list/extension'
);
}
},
/* ============================================================
* MENÜEINTRÄGE BEDIENEN
* ============================================================ */
handleClick: function(event) {
let menuItem = event.target;
if (menuItem !== event.currentTarget) {
return;
}
if (!('_Addon' in menuItem)) {
return;
}
let addon = menuItem._Addon;
let pending = this.isPending(addon);
let hasModifier =
event.ctrlKey ||
event.shiftKey ||
event.altKey ||
event.metaKey;
switch (event.button) {
/* Linksklick */
case 0:
// Einstellungsseite öffnen
if (
addon.optionsURL &&
!hasModifier
) {
this.openAddonOptions(addon);
}
// Strg + Linksklick = Erweiterungsordner öffnen
else if (event.ctrlKey) {
this.browseDir(addon);
}
// Alt + Linksklick = ID kopieren
else if (event.altKey) {
let clipboard =
Cc['@mozilla.org/widget/clipboardhelper;1']
.getService(Ci.nsIClipboardHelper);
clipboard.copyString(addon.id);
}
break;
/* Mittelklick = Startseite öffnen */
case 1:
if (addon.homepageURL) {
openURL(addon.homepageURL);
}
break;
/* Rechtsklick */
case 2:
// Rechtsklick = aktivieren/deaktivieren
if (!hasModifier) {
let newState =
!addon.userDisabled;
if (newState) {
addon.disable();
} else {
addon.enable();
}
this.updateState(
menuItem,
newState
);
}
// Strg + Rechtsklick = deinstallieren/rückgängig
else if (event.ctrlKey) {
if (pending) {
addon.cancelUninstall();
} else {
addon.uninstall();
}
this.setUninstall(
menuItem,
!pending
);
}
break;
}
},
/* ============================================================
* STATUSVERWALTUNG
* ============================================================ */
updateState: function(menuItem, disabled) {
if (disabled) {
menuItem.classList.add('addon-disabled');
} else {
menuItem.classList.remove('addon-disabled');
}
},
setUninstall: function(menuItem, uninstallPending) {
if (uninstallPending) {
menuItem.classList.add('addon-uninstall');
} else {
menuItem.classList.remove('addon-uninstall');
}
},
isPending: function(addon) {
return addon.pendingOperations &
AddonManager.PENDING_UNINSTALL;
},
/* ============================================================
* ADD-ON-EINSTELLUNGEN ÖFFNEN
* ============================================================ */
openAddonOptions: function(addon) {
let optionsURL = addon.optionsURL || '';
if (!addon.isActive || !optionsURL) {
return;
}
// optionsType 3 = eigene Seite in einem Tab
if (addon.optionsType === 3) {
if ('switchToTabHavingURI' in window) {
switchToTabHavingURI(
optionsURL,
true
);
} else {
openTab(
'contentTab',
{
contentPage: optionsURL
}
);
}
} else {
// Add-on-Manager direkt bei den Einstellungen öffnen
BrowserAddonUI.openAddonsMgr(
'addons://detail/' +
encodeURIComponent(addon.id) +
'/preferences'
);
}
},
/* ============================================================
* ADD-ON-ORDNER ÖFFNEN
* ============================================================ */
browseDir: function(addon) {
try {
let directory =
Services.dirsvc.get(
'ProfD',
Ci.nsIFile
);
directory.append('extensions');
directory.append(
addon.id +
(addon.unpack ? '' : '.xpi')
);
if (directory.exists()) {
directory.reveal();
} else {
Services.prompt.alert(
null,
'Fehler',
'Ordner oder Datei nicht gefunden.'
);
}
} catch (e) {
Cu.reportError(
'EOM: browseDir fehlgeschlagen: ' + e
);
}
},
/* ============================================================
* SORTIERUNG
* ============================================================ */
key: function(addon) {
let sortPosition =
addon.isActive
? this.sort.enabled
: this.sort.disabled;
return (
sortPosition +
'\n' +
addon.name.toLowerCase()
);
},
/* ============================================================
* FIREFOX NEU STARTEN
* ============================================================ */
restart: function() {
let cancelQuit =
Cc['@mozilla.org/supports-PRBool;1']
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(
cancelQuit,
'quit-application-requested',
'restart'
);
if (cancelQuit.data) {
return;
}
// Im abgesicherten Modus neu starten
if (Services.appinfo.inSafeMode) {
Services.startup.restartInSafeMode(
Ci.nsIAppStartup.eAttemptQuit
);
return;
}
// Normaler Neustart
Services.appinfo.invalidateCachesOnRestart();
Services.startup.quit(
Ci.nsIAppStartup.eAttemptQuit |
Ci.nsIAppStartup.eRestart
);
}
};
/* ================================================================
* START
* ================================================================ */
EOM.init();
Alles anzeigen