Полезная информация

Список ответов на каверзные вопросы можно получить в FAQ-разделе форума.

№130127-02-2024 15:22:40

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 416
UA: Firefox 116.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

.jsm
А вот и баг о временах и сроках.

когда JSM уберут, то UCF практически перестанет работать!
Я не осилю добавить ESM в UCF-user_chrome.js и в CustomStylesScripts.jsm загрузчик скриптов/стилей…

Отсутствует

 

№130227-02-2024 16:19:42

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 249
UA: Google 2.1

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dobrov пишет

Я не осилю добавить ESM

там нечего сложного глаза бояться, руки делают, здесь Dumby писал как...


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№130328-02-2024 12:36:01

Northtech
Участник
 
Группа: Members
Зарегистрирован: 16-04-2011
Сообщений: 261
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dobrov
есть возможность сделать в ucf_contextsearch.js открытие вкладки поиска фоном? Или зависящим от этой настройки: user_pref("browser.search.context.loadInBackground", true);

Отсутствует

 

№130428-02-2024 21:36:49

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 410
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby если удалят jsm то и эти кнопки перестанут работать?

скрытый текст
text-to-link.js

Выделить код

Код:

//Ссылки кликабельны
try {(() => {
    var id = "ucf-text-to-link",
    label = "Текст URL в кликабельные ссылки",
    tooltiptext = "Превратить текст URL в кликабельные ссылки",
    img = (rph => {
        var subst = "ucf-text-to-link-btn-img";
        rph.setSubstitution(subst, Services.io.newURI(
           "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='m5.6 10.4 4.8-4.8m-8.9 4.9c-2.6 2.6 1.4 6.6 4 4l1-1c2.6-2.6-1.4-6.6-4-4zm9-9c2.6-2.6 6.6 1.4 4 4l-1 1c-2.6 2.6-6.6-1.4-4-4z'/></svg>"        ));
        return `resource://${subst}/`;
    })(Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler));
	
    var texttolink = {
        _registerActor() {
            if (this.registerActor) return;
            ChromeUtils.registerWindowActor("UcfTextToLinkActor", {
                child: {
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.jsm",
                },
                allFrames: true,
                messageManagerGroups: ["browsers"],
            });
            this.registerActor = true;
        },
        sendAsyncMessages(win, message, data) {
            this._registerActor();
            this.sendAsyncMessages = this._sendAsyncMessages;
            this.sendAsyncMessages(win, message, data);
        },
        _sendAsyncMessages(win, message, data) {
            var {browsingContext} = win.gBrowser.selectedBrowser;
            ({
                "UcfTextToLinkActor:TextToLink"() {
                    for (let actor of this)
                        actor.sendAsyncMessage(message);
                },
                *[Symbol.iterator]() {
                    var contextsToVisit = [browsingContext];
                    while (contextsToVisit.length) {
                        let currentContext = contextsToVisit.pop();
                        let global = currentContext?.currentWindowGlobal;
                        if (!global) continue;
                        yield global.getActor("UcfTextToLinkActor");
                        contextsToVisit.push(...currentContext.children);
                    }
                },
            })[message]?.();
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
        },
        onCommand(e) {
            texttolink.sendAsyncMessages(e.view, "UcfTextToLinkActor:TextToLink");
        },
    });
})();} catch(e) {}

UcfTextToLinkActorChild.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfTextToLinkActorChild"];
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["NodeFilter", "Node"]);
XPCOMUtils.defineLazyGetter(this, "excludedTags", () => {
    return new Set(["a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img","select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"]);
});

class UcfTextToLinkActorChild extends JSWindowActorChild {
    receiveMessage(msg) {
        return ({
            "UcfTextToLinkActor:TextToLink": () => {
                this.textToLink();
            },
        })[msg.name]?.();
    }
    textToLink() {
        if (this.running || !this.document?.body)
            return;
        this.running = true;
        var url_regexp = /(^|[\s(,;'"`“\[\]=_])((?:(?:https?|ftp):\/\/[-\wа-яё.!~*'();,/?:@&=+$#%_\u2300-\u23FF\u2600-\u27BF]|www\d{0,3}[.][a-zа-яё0-9.-]{2,249}|[a-zа-яё0-9.-]{2,250}[.][a-zа-яё]{2,4}\/)[-\wа-яё.!~*'();,/?:@&=+$#%_\u2300-\u23FF\u2600-\u27BF]*)/gim,
        email_regexp = /(^|mailto:|[\s(,;'"`“\[\]=])([\w!#$%&'*+/=?^`{|}~.-]{2,}@[\[\]a-z0-9.-]+)/gim,
        setEmail = (node, text) => {
            var repl = text.replace(email_regexp, '$1<a href="mailto:$2" class="add__TextToEmail">$2</a>');
            if (text.length == repl.length)
                return;
            var span = node.ownerDocument.createElement("span");
            span["innerHTML"] = repl;
            node.replaceWith(span);
        },
        setLink = (node, text) => {
            if (!(text = node.textContent)) return;
            text = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            var repl = text.replace(url_regexp, '$1<a href="$2" target="_blank" class="add__TextToLink">$2</a>');
            if (text.length == repl.length) {
                setEmail(node, text);
                return;
            }
            var span = node.ownerDocument.createElement("span");
            span["innerHTML"] = repl;
            for (let el of span.querySelectorAll("a.add__TextToLink[href]:not([href^='http']):not([href^='ftp'])"))
                el.setAttribute("href", `http://${el.getAttribute("href")}`);
            node.replaceWith(span);
            var txtnode = Node.TEXT_NODE;
            for (let child of span.childNodes) {
                let txt;
                if (child.nodeType === txtnode && (txt = child.textContent))
                    setEmail(child, txt);
            }
        },
        elList = [],
        getWalker = elem => {
            var doc = elem.ownerDocument, reject = NodeFilter.FILTER_REJECT, skip = NodeFilter.FILTER_SKIP, accept = NodeFilter.FILTER_ACCEPT, txtnode = Node.TEXT_NODE;
            var walker = doc.createTreeWalker(elem, NodeFilter.SHOW_ALL, {
                acceptNode(node) {
                    if (excludedTags.has(node.localName))
                        return reject;
                    if (node.nodeType !== txtnode && !node.shadowRoot)
                        return skip;
                    return accept;
                }
            }, false);
            while (walker.nextNode()) {
                let currnode = walker.currentNode;
                if (!currnode.shadowRoot)
                    elList.push(currnode);
                else
                    getWalker(currnode.shadowRoot);
            }
        };
        getWalker(this.document.body);
        for (let el of elList)
            setLink(el);
        elList = [];
        this.running = false;
    }
}

скрытый текст

Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}

скрытый текст
special_widgets.js

Выделить код

Код:

(this.specialwidgets = {
    _timer: null,
    get Customizable() {
        delete this.Customizable;
        if ("createSpecialWidget" in CustomizableUI)
            return this.Customizable = CustomizableUI;
        var scope = null;
        try {
            scope = Cu.import("resource:///modules/CustomizableUI.jsm", {}).CustomizableUIInternal;
        } catch (e) { }
        return this.Customizable = scope;
    },
    init(that) {
        if (!("CustomizableUI" in window) || !("gCustomizeMode" in window))
            return;
        that.unloadlisteners.push("specialwidgets");
        window.addEventListener("customizationready", this);
    },
    destructor() {
        window.removeEventListener("customizationready", this);
    },
    handleEvent(e) {
        this[e.type](e);
    },
    customizationchange() {
        clearTimeout(this._timer);
        this._timer = setTimeout(() => {
            this.createSpecialWidgets();
        }, 1000);
    },
    customizationready() {
        if (!this.Customizable)
            return;
        this.createSpecialWidgets();
        window.addEventListener("customizationchange", this);
        window.addEventListener("customizationending", this);
    },
    customizationending() {
        window.removeEventListener("customizationchange", this);
        window.removeEventListener("customizationending", this);
    },
    createSpecialWidgets() {
        try {
            let fragment = document.createDocumentFragment();
            if (this.findSpecialWidgets("spring")) {
                let spring = this.Customizable.createSpecialWidget("spring", document);
                spring.setAttribute("label", "Растягивающийся интервал");
                fragment.append(gCustomizeMode.wrapToolbarItem(spring, "palette"));
            }
            if (this.findSpecialWidgets("spacer")) {
                let spacer = this.Customizable.createSpecialWidget("spacer", document);
                spacer.setAttribute("label", "Интервал");
                fragment.append(gCustomizeMode.wrapToolbarItem(spacer, "palette"));
            }
            if (this.findSpecialWidgets("separator")) {
                let separator = this.Customizable.createSpecialWidget("separator", document);
                separator.setAttribute("label", "Разделитель");
                fragment.append(gCustomizeMode.wrapToolbarItem(separator, "palette"));
            }
            gCustomizeMode.visiblePalette.append(fragment);
        } catch (e) {}
    },
    findSpecialWidgets(string) {
        try {
            if (!gCustomizeMode.visiblePalette.querySelector(`toolbar${string}[id^="customizableui-special-${string}"]`))
                return true;
        } catch (e) {}
        return false;
    }
}).init(this);

если это так ,то просьба переделать их пожалуйста.

Добавлено 28-02-2024 22:00:00
и еще одна

скрытый текст

Выделить код

Код:

// http://infocatcher.ucoz.net/js/cb/toggleRestartlessAddons.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57948
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Toggle_Restartless_Add-ons

// Toggle Restartless Add-ons button for Custom Buttons
// (code for "initialization" section)
// Also the code can be used from main window context (as Mouse Gestures code, for example)

// Also you can check for add-ons updates using right-click:
// copy all code from
// https://github.com/Infocatcher/Custom_Buttons/blob/master/Check_for_Addons_Updates/checkForAddonsUpdates.js
// after "//== Check for Addons Updates begin"

// See "var style = " to modify styles for specific add-ons

// (c) Infocatcher 2013-2019
// version 0.1.3pre4 - 2020-01-01

var options = {
	addonTypes: ["extension", "plugin"],
	// Possible values: "extension", "plugin"
	// From extensions: "userstyle" (Stylish), "greasemonkey-user-script" (Greasemonkey), "userscript" (Scriptish)
	// (swap to reorder in the menu)
	showVersions: 0,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

var tb = this.parentNode;
if(tb && tb.getAttribute("orient") == "vertical") {
	// https://addons.mozilla.org/firefox/addon/vertical-toolbar/
	var isRight = tb.parentNode.getAttribute("placement") == "right";
	mp.setAttribute("position", isRight ? "start_before" : "end_before");
}

var cleanupTimer = 0;
mp.updateMenu = function() {
	clearTimeout(cleanupTimer);
	addStyle();
	getRestartlessAddons(options.addonTypes, function(addons) {
		var df = document.createDocumentFragment();
		var prevType;
		function sortPosition(addon) {
			if("STATE_ASK_TO_ACTIVATE" in AddonManager && addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE)
				return options.sort.clickToPlay;
			if(addon.isActive)
				return options.sort.enabled;
			return options.sort.disabled;
		}
		function key(addon) {
			return options.addonTypes.indexOf(addon.type)
				+ "\n" + sortPosition(addon)
				+ "\n" + addon.name.toLowerCase();
		}
		addons.sort(function(a, b) {
			var ka = key(a);
			var kb = key(b);
			return ka == kb ? 0 : ka < kb ? -1 : 1;
		}).forEach(function(addon) {
			var type = addon.type;
			if(prevType && type != prevType)
				df.appendChild(document.createElementNS(xulns, "menuseparator"));
			prevType = type;
			var icon = addon.iconURL || addon.icon64URL;
			var mi = document.createElementNS(xulns, "menuitem");
			mi.className = "menuitem-iconic";
			var label = addon.name;
			if(options.showVersions == 1)
				label += " " + addon.version;
			else if(options.showVersions == 2)
				mi.setAttribute("acceltext", addon.version);
			mi.setAttribute("label", label);
			mi.setAttribute("image", icon || mp.icons[type] || "");
			if(!icon && mp.icons.useSVG)
				mi.style.fill = "#15c";
			var tip = addon.description || "";
			var delay = "delayedStartupAddons" in Services
				&& Services.delayedStartupAddons[addon.id] || null;
			var isDelayed = delay !== null;
			mi.classList.toggle("toggleRestartlessAddons-isDelayed", isDelayed);
			if(isDelayed)
				tip = "[Delayed Startup: " + delay.toLocaleString() + "]" + (tip ? "\n" + tip : "");
			tip && mi.setAttribute("tooltiptext", tip);
			mi.classList.toggle("toggleRestartlessAddons-isHidden", addon.hidden || false);
			setDisabled(mi, addon.userDisabled);
			mi._cbAddon = addon;
			df.appendChild(mi);
		});
		mp.textContent = "";
		mp.appendChild(df);
	});
};
mp.handleEvent = function(e) {
	var mi = e.target;
	if(!("_cbAddon" in mi))
		return;
	var addon = mi._cbAddon;
	if(e.type == "mousedown") {
		var closeMenu = isAskToActivateAddon(addon)
			? options.closeMenuClickToPlay
			: options.closeMenu;
		if(e.shiftKey)
			closeMenu = !closeMenu;
		mi.setAttribute("closemenu", closeMenu ? "auto" : "none");
		return;
	}
	var hasMdf = hasModifier(e);
	if(e.type == "command" && (!hasMdf || e.shiftKey)) {
		let newDis = setNewDisabled(addon);
		setDisabled(mi, newDis);
	}
	else if(e.type == "command" && hasMdf || e.type == "click" && e.button == 1) {
		openAddonPage(addon);
		closeMenus(mi);
	}
	else if(e.type == "click" && e.button == 2) {
		if(openAddonOptions(addon))
			closeMenus(mi);
	}
};
mp.destroyMenu = function() {
	removeStyle();
	clearTimeout(cleanupTimer);
	cleanupTimer = setTimeout(function() {
		mp.textContent = "";
	}, 5000);
};
mp.icons = {
	get platformVersion() {
		delete this.platformVersion;
		return this.platformVersion = parseFloat(Services.appinfo.platformVersion);
	},
	get useSVG() {
		delete this.useSVG;
		return this.useSVG = Services.appinfo.name == "Firefox" && this.platformVersion >= 57;
	},
	get plugin() {
		delete this.plugin;
		return this.plugin = this.useSVG
			? this.platformVersion >= 65
				? "chrome://global/skin/plugins/pluginGeneric.svg"
				: "chrome://mozapps/skin/plugins/pluginGeneric.svg"
			: "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},
	get extension() {
		delete this.extension;
		return this.extension = this.useSVG
			? this.platformVersion >= 76
				? "chrome://mozapps/skin/extensions/extensionGeneric.svg" // Or chrome://mozapps/skin/extensions/extension.svg
				: "chrome://mozapps/skin/extensions/extensionGeneric-16.svg"
			: "chrome://mozapps/skin/extensions/extensionGeneric-16.png";
	}
};
function isAskToActivateAddon(addon) {
	return addon.type == "plugin"
		&& "STATE_ASK_TO_ACTIVATE" in AddonManager
		&& Services.prefs.getBoolPref("plugins.click_to_play", true);
}
function setNewDisabled(addon) {
	var newDis = getNewDisabled(addon);
	var oldDis = addon.userDisabled;
	try {
		addon.userDisabled = newDis;
	}
	catch(e) { // Error: Cannot disable hidden add-on firefox@getpocket.com
		_log("Can't set addon.userDisabled to " + newDis + ", error:\n" + e);
		if(addon.hidden)
			setNewDisabledRaw(addon, newDis);
	}
	var realDis = addon.userDisabled;
	if(realDis != newDis && addon.type == "extension") { // Firefox 62+? Weird things happens
		setNewDisabledRaw(addon, newDis);
		realDis = addon.userDisabled;
	}
	if(realDis != newDis) { // We can't enable vulnerable plugins
		let err = "Can't set addon.userDisabled to " + newDis + ", real value: " + realDis;
		if(newDis) {
			_log(err + "\nSTATE_ASK_TO_ACTIVATE not supported?");
			newDis = false;
		}
		else {
			_log(err + "\nVulnerable plugin?");
			if(oldDis == AddonManager.STATE_ASK_TO_ACTIVATE)
				newDis = true;
			else
				newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		}
		addon.userDisabled = newDis;
	}
	ensureSpecialDisabled(addon, newDis);
	return addon.userDisabled;
}
function getNewDisabled(addon) {
	// disabled -> STATE_ASK_TO_ACTIVATE -> enabled -> ...
	var curDis = addon.userDisabled;
	var newDis;
	if("STATE_ASK_TO_ACTIVATE" in AddonManager && curDis == AddonManager.STATE_ASK_TO_ACTIVATE)
		newDis = false;
	else if(!curDis)
		newDis = true;
	else {
		if(isAskToActivateAddon(addon))
			newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		else
			newDis = false;
	}
	return newDis;
}
function setNewDisabledRaw(addon, newDis) {
	_log("Let's try set addon.userDisabled using raw hack");
	let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	if("lazy" in g) g = g.lazy;
	if("XPIDatabase" in g && "updateAddonDisabledState" in g.XPIDatabase) { // Firefox 61+
		let rawAddon = g.XPIDatabase.getAddons().find(function(rawAddon) {
			return rawAddon.id == addon.id;
		});
		g.XPIDatabase.updateAddonDisabledState(
			rawAddon,
			g.XPIDatabase.updateAddonDisabledState.length == 1 // Firefox 74+
				? { userDisabled: newDis }
				: newDis
		);
	}
	else if("eval" in g) { // See "set userDisabled(val)"
		let addonFor = g.eval("addonFor");
		let rawAddon = addonFor(addon);
		//rawAddon.userDisabled = newDis;
		g.XPIProvider.updateAddonDisabledState(rawAddon, newDis);
	}
	else { // Firefox 57+? See https://forum.mozilla-russia.org/viewtopic.php?pid=745272#p745272
		updateAddonDisabledState(addon, newDis);
	}
}
function updateAddonDisabledState(addon, newDis) {
	var nsvo = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	var key = "_cbToggleRestartlessAddonsData";
	var url = URL.createObjectURL(new Blob([
		"XPIProvider.updateAddonDisabledState(addonFor(this." + key + "[0]), this." + key + "[1]); delete this." + key + ";"
	]));
	addDestructor(function() {
		URL.revokeObjectURL(url);
	});
	(updateAddonDisabledState = function(addon, newDis) {
		nsvo[key] = [addon, newDis];
		Services.scriptloader.loadSubScript(url, nsvo);
	})(addon, newDis);
}
function setDisabled(mi, disabled) {
	var askToActivate = "STATE_ASK_TO_ACTIVATE" in AddonManager && disabled == AddonManager.STATE_ASK_TO_ACTIVATE;
	var cl = mi.classList;
	cl.toggle("toggleRestartlessAddons-askToActivate", askToActivate);
	cl.toggle("toggleRestartlessAddons-disabled", disabled && !askToActivate);
}
function ensureSpecialDisabled(addon, newDis) {
	if(addon.id == "screenshots@mozilla.org")
		Services.prefs.setBoolPref("extensions.screenshots.disabled", newDis);
}

if(
	this instanceof XULElement // Custom Buttons
	&& typeof event == "object"
	&& !("type" in event) && typeof _phase == "string" && _phase == "init" // Initialization
) {
	this.type = "menu";
	this.orient = "horizontal";
	this.appendChild(mp);

	this.onmouseover = function(e) {
		if(e.target != this)
			return;
		Array.prototype.some.call(
			this.parentNode.getElementsByTagName("*"),
			function(node) {
				if(
					node != this
					&& node.namespaceURI == xulns
					// See https://github.com/Infocatcher/Custom_Buttons/issues/28
					//&& node.boxObject
					//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
					&& "open" in node
					&& node.open
					&& node.getElementsByTagName("menupopup").length
				) {
					node.open = false;
					this.open = true;
					return true;
				}
				return false;
			},
			this
		);
	};
	this.onmousedown = function(e) {
		if(e.target == this && e.button == 0 && hasModifier(e))
			e.preventDefault();
	};
	this.oncontextmenu = function(e) {
		if(e.target == this && !hasModifier(e) && hasUpdater())
			e.preventDefault();
	};
	this.onclick = function(e) {
		if(e.target != this)
			return;
		if(e.button == 0 && hasModifier(e) || e.button == 1)
			openAddonsManager();
		else if(e.button == 2 && !hasModifier(e) && hasUpdater())
			checkForAddonsUpdates.call(this);
	};
}
else { // Mouse gestures or something other...
	let e;
	if(typeof event == "object" && event instanceof Event && "screenX" in event) // FireGestures
		e = event;
	else if(
		this instanceof Components.interfaces.nsIDOMChromeWindow
		&& "mgGestureState" in window && "endEvent" in mgGestureState // Mouse Gestures Redox
	)
		e = mgGestureState.endEvent;
	else {
		let anchor = this instanceof XULElement && this
			|| window.gBrowser && gBrowser.selectedBrowser
			|| document.documentElement;
		if("boxObject" in anchor) {
			let bo = anchor.boxObject;
			e = {
				screenX: bo.screenX,
				screenY: bo.screenY
			};
			if(this instanceof XULElement)
				e.screenY += bo.height;
		}
	}
	if(!e || !("screenX" in e))
		throw new Error("[Toggle Restartless Add-ons]: Can't get event object");
	document.documentElement.appendChild(mp);
	mp.addEventListener("popuphidden", function destroy(e) {
		mp.removeEventListener(e.type, destroy, false);
		setTimeout(function() {
			mp.destroyMenu();
			mp.parentNode.removeChild(mp);
		}, 0);
	}, false);
	mp.openPopupAtScreen(e.screenX, e.screenY);
}

function getRestartlessAddons(addonTypes, callback, context) {
	if(!("AddonManager" in window))
		Components.utils.import("resource://gre/modules/AddonManager.jsm");
	var then, promise = AddonManager.getAddonsByTypes(addonTypes, then = function(addons) {
		callback.call(context, addons.filter(function(addon) {
			var ops = addon.operationsRequiringRestart;
			return !addon.appDisabled
				&& !(ops & AddonManager.OP_NEEDS_RESTART_ENABLE || ops & AddonManager.OP_NEEDS_RESTART_DISABLE)
				&& (
					!addon.hidden
					|| options.showHidden > 0
					|| options.showHidden == -1 && !addon.userDisabled
				)
				&& (addon.iconURL || "").substr(0, 29) != "resource://search-extensions/";
		}));
	});
	promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+
}
function openAddonOptions(addon) {
	// Based on code from chrome://mozapps/content/extensions/extensions.js
	// Firefox 21.0a1 (2013-01-27)
	var optionsURL = addon.optionsURL;
	if(!addon.isActive || !optionsURL)
		return false;
	if(addon.type == "plugin") // No options for now!
		return false;
	if(
		addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_INFO || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_BROWSER || NaN)
	)
		openAddonPage(addon, true);
	else if(addon.optionsType == AddonManager.OPTIONS_TYPE_TAB && "switchToTabHavingURI" in window)
		switchToTabHavingURI(optionsURL, true);
	else {
		let windows = Services.wm.getEnumerator(null);
		while(windows.hasMoreElements()) {
			let win = windows.getNext();
			if(win.document.documentURI == optionsURL) {
				win.focus();
				return true;
			}
		}
		// Note: original code checks browser.preferences.instantApply and may open modal windows
		window.openDialog(optionsURL, "", "chrome,titlebar,toolbar,centerscreen,dialog=no");
	}
	return true;
}
function openAddonsManager(view) {
	var openAddonsMgr = window.BrowserOpenAddonsMgr // Firefox
		|| window.openAddonsMgr // Thunderbird
		|| window.toEM; // SeaMonkey
	openAddonsMgr(view);
}
function openAddonPage(addon, scrollToPreferences) {
	var platformVersion = parseFloat(
		Services.appinfo.name == "Pale Moon"
			? Services.appinfo.version
			: Services.appinfo.platformVersion
	);
	scrollToPreferences = scrollToPreferences && platformVersion >= 12
		? "/preferences"
		: "";
	openAddonsManager("addons://detail/" + encodeURIComponent(addon.id) + scrollToPreferences);
}

function hasModifier(e) {
	return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;
}

function addStyle() {
	if(addStyle.hasOwnProperty("_style"))
		return;
	var style = '\
		.toggleRestartlessAddons-isDelayed > .menu-iconic-text {\n\
			opacity: 0.75;\n\
			color: #070;\n\
		}\n\
		.toggleRestartlessAddons-isHidden > .menu-iconic-text {\n\
			color: #609;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-left {\n\
			opacity: 0.4;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-text,\n\
		.toggleRestartlessAddons-disabled > .menu-accel-container {\n\
			opacity: 0.5;\n\
		}\n\
		.toggleRestartlessAddons-askToActivate {\n\
			color: -moz-nativehyperlinktext;\n\
		}';
	addStyle._style = document.insertBefore(
		document.createProcessingInstruction(
			"xml-stylesheet",
			'href="' + "data:text/css,"
				+ encodeURIComponent(style) + '" type="text/css"'
		),
		document.documentElement
	);
}
function removeStyle() {
	if(!addStyle.hasOwnProperty("_style"))
		return;
	var s = addStyle._style;
	s.parentNode.removeChild(s);
	delete addStyle._style;
}
function closeMenus(node) {
	// Based on function closeMenus from chrome://browser/content/utilityOverlay.js
	for(; node && "tagName" in node; node = node.parentNode) {
		if(
			node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
			&& (node.localName == "menupopup" || node.localName == "popup")
		)
			node.hidePopup();
	}
}
function _log(s) {
	if(typeof LOG == "function") // Custom Buttons
		LOG(s);
	else // Or something else
		Services.console.logStringMessage("Toggle Restartless Add-ons: " + s);
}

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
function checkForAddonsUpdates() {
//== Check for Addons Updates begin
// http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57958
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates

// Check for Addons Updates button for Custom Buttons
// (code for "code" section)

// (c) Infocatcher 2012-2021
// version 0.1.6pre4 - 2021-03-28

// Button just open hidden tab with about:addons and trigger built-in "Check for Updates" function.
// And show tab, if found updates.

(function() {
var btn = this instanceof XULElement
	? this
	: { // Launched not from custom button
		image: "", // Base64-encoded icon (if empty, will be used "imgLoading")
		label: "Check for Addons Updates",
		tooltipText: ""
	};
if("_cb_disabled" in btn)
	return;
btn._cb_disabled = true;


var app = Services.appinfo.name;
var pv = parseFloat(Services.appinfo.platformVersion);

var ADDONS_URL = "about:addons";

var progressIcon = new ProgressIcon(btn);
var image = btn.image || progressIcon.imgLoading;
var tip = btn.tooltipText;
btn.tooltipText = "Open " + ADDONS_URL + "…";

var tab, browser, gBrowser;
var tbTabInfo, tbTab;

var trgWindow = Services.wm.getMostRecentWindow("navigator:browser")
	|| app == "Thunderbird" && Services.wm.getMostRecentWindow("mail:3pane")
	|| window;
var trgDocument = trgWindow.document;
var tabmail = trgDocument.getElementById("tabmail");

if(tabmail && app == "Thunderbird") { // Note: SeaMonkey doesn't support content tabs in mail window
	let addonsWin;
	let receivePong = function(subject, topic, data) {
		addonsWin = subject;
	};
	Services.obs.addObserver(receivePong, "EM-pong", false);
	Services.obs.notifyObservers(null, "EM-ping", "");
	Services.obs.removeObserver(receivePong, "EM-pong");
	if(addonsWin) {
		let rootWindow = addonsWin
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIWebNavigation)
			.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
			.rootTreeItem
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIDOMWindow);
		tabmail = rootWindow.document.getElementById("tabmail");
		tbTabInfo = tabmail.getBrowserForDocument(addonsWin);
		tbTab = tab = tbTabInfo.tabNode;
		processAddonsTab(addonsWin);
	}
	else {
		Services.obs.addObserver(function observer(subject, topic, data) {
			Services.obs.removeObserver(observer, topic);
			if(subject.document.readyState == "complete")
				processAddonsTab(subject);
			else {
				subject.addEventListener("load", function onLoad(e) {
					subject.removeEventListener(e.type, onLoad, false);
					processAddonsTab(subject);
				}, false);
			}
		}, "EM-loaded", false);
		// See openAddonsMgr() -> openContentTab()
		tbTabInfo = tabmail.openTab("contentTab", {
			contentPage: ADDONS_URL,
			clickHandler: "specialTabs.siteClickHandler(event, /addons\.mozilla\.org/);",
			background: true
		});
		tbTab = tab = tbTabInfo.tabNode;
		tbTab.collapsed = true;
		// Note: dontSelectHiddenTab() not implemented
	}
}
else if("gBrowser" in trgWindow && trgWindow.gBrowser.tabs) {
	let isPending = false;
	let ws = Services.wm.getEnumerator("navigator:browser");
	windowsLoop:
	while(ws.hasMoreElements()) {
		let w = ws.getNext();
		let tabs = w.gBrowser.tabs;
		for(let i = 0, l = tabs.length; i < l; ++i) {
			let t = tabs[i];
			if(
				!t.closing
				&& t.linkedBrowser
				&& t.linkedBrowser.currentURI.spec == ADDONS_URL
			) {
				tab = t;
				break windowsLoop;
			}
		}
	}

	gBrowser = trgWindow.gBrowser;
	if(!tab) {
		tab = gBrowser.addTab(ADDONS_URL, {
			triggeringPrincipal: "Services" in window // Firefox 63+
				&& Services.scriptSecurityManager
				&& Services.scriptSecurityManager.getSystemPrincipal()
		});
		tab.collapsed = true;
		tab.closing = true; // See "visibleTabs" getter in chrome://browser/content/tabbrowser.xml
		trgWindow.addEventListener("TabSelect", dontSelectHiddenTab, false);
	}
	else if(
		tab.getAttribute("pending") == "true" // Gecko >= 9.0
		|| tab.linkedBrowser.contentDocument.readyState == "uninitialized"
		// || tab.linkedBrowser.__SS_restoreState == 1
	)
		isPending = true;

	browser = tab.linkedBrowser;
	if(
		isPending
		|| browser.webProgress.isLoadingDocument
		|| browser.currentURI.spec == "about:blank" // Firefox 79+
	) {
		browser.addEventListener("load", processAddonsTab, true);
		if(isPending) {
			if(pv >= 41) {
				// Workaround to correctly restore pending tab
				// See https://github.com/Infocatcher/Custom_Buttons/issues/39
				let selTab = gBrowser.selectedTab;
				gBrowser.selectedTab = tab;
				gBrowser.selectedTab = selTab;
			}
			else {
				browser.reload();
			}
		}
	}
	else {
		processAddonsTab();
	}
}
else {
	progressIcon.restore();
	btn.tooltipText = tip;
	delete btn._cb_disabled;
	Services.prompt.alert(window, btn.label, "Error: Can't find supported window!");
	return;
}

function processAddonsTab(e, again) {
	var doc;
	if(e && e instanceof Components.interfaces.nsIDOMWindow) {
		doc = e.document;
	}
	else if(e) {
		doc = e.target;
		if(doc.location != ADDONS_URL)
			return;
		browser.removeEventListener(e.type, processAddonsTab, true);
	}
	else {
		doc = browser.contentDocument;
	}

	btn.tooltipText = "Process " + ADDONS_URL + "…";
	progressIcon.loading();

	var origAttr = "_cb_checkForAddonsUpdates_origImage";
	if(!tab.hasAttribute(origAttr)) {
		var link = doc.querySelector('link[rel="shortcut icon"]'); // Not loaded yet?
		tab.setAttribute(origAttr, link && link.href || tab.image);
	}
	tab.image = image;

	var fu = $("cmd_findAllUpdates");
	if(!fu) { // Firefox 72+
		var win = doc.defaultView;
		var vb = doc.getElementById("html-view-browser");
		if(!vb) {
			if(!HTMLHtmlElement.isInstance(doc.documentElement)) { // Firefox 87+
				win.setTimeout(processAddonsTab, 20, win);
				return;
			}
			vb = browser;
		}
		if(!again) { // Strange errors happens
			// chrome://mozapps/content/extensions/aboutaddons.js
			// getTelemetryViewName() -> el.closest(...) is null
			win.setTimeout(processAddonsTab, 20, win, true);
			return;
		}
		var vbDoc = vb.contentDocument;
		fu = vbDoc.querySelector('[action="check-for-updates"]');
		var um = vbDoc.getElementById("updates-message");
	}

	var notFound = $("updates-noneFound") || {
		get hidden() { return um.getAttribute("state") != "none-found"; }
	};
	var updated = $("updates-installed") || {
		get hidden() { return um.getAttribute("state") != "installed"; }
	};
	// Avoid getting false results from the past update check (may not be required for "noneFound")
	if(um) { // Firefox 72+
		um.hidden = true;
		um.removeAttribute("state");
	}
	else {
		notFound.hidden = updated.hidden = true;
	}

	//fu.doCommand();
	fu.click();

	function localize(node, key, callback) {
		if(um) { // Firefox 72+
			doc.l10n.formatValue(key).then(function(s) {
				callback(s || key);
			}, Components.utils.reportError);
			return;
		}
		callback(node.getAttribute("value") || key);
	}

	var inProgress = $("updates-progress") || {
		get hidden() { return um.getAttribute("state") != "updating"; }
	};
	localize(inProgress, "addon-updates-updating", function(s) {
		btn.tooltipText = s;
	});

	var waitTimer = setInterval(function() {
		if(!doc.defaultView || doc.defaultView.closed) {
			stopWait();
			notify("Tab with add-ons manager was closed!");
			return;
		}
		if(!inProgress.hidden)
			return;
		var autoUpdate = $("utils-autoUpdateDefault")
			|| vbDoc.querySelector('[action="set-update-automatically"]');
		var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"
			|| autoUpdate.checked;

		var found = $("updates-manualUpdatesFound-btn") || {
			get hidden() { return um.getAttribute("state") != "manual-updates-found"; }
		};
		if(
			autoUpdateChecked
				? notFound.hidden && updated.hidden
				: notFound.hidden && found.hidden
		) // Too early?
			return;

		stopWait();
		if(!tbTab)
			tab.closing = false;
		function removeTab() {
			if(!tab.collapsed)
				return;
			if(tbTab) {
				tabmail.closeTab(tbTabInfo, true /*aNoUndo*/);
				return;
			}
			gBrowser.removeTab(tab);
			(function forgetClosedTab(isSecondTry) {
				var ss = "nsISessionStore" in Components.interfaces
					? (
						Components.classes["@mozilla.org/browser/sessionstore;1"]
						|| Components.classes["@mozilla.org/suite/sessionstore;1"]
					).getService(Components.interfaces.nsISessionStore)
					: trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
				if(!("forgetClosedTab" in ss))
					return;
				var closedTabs = (ss.getClosedTabDataForWindow(window));
				for(let i = 0, l = closedTabs.length; i < l; ++i) {
					let closedTab = closedTabs[i];
					let state = closedTab.state;
					if(state.entries[state.index - 1].url == ADDONS_URL) {
						ss.forgetClosedTab(window, i);
						return;
					}
				}
				if(!isSecondTry) // May be needed in SeaMonkey
					setTimeout(forgetClosedTab, 0, true);
			})();
		}

		if(!notFound.hidden) {
			removeTab();
			localize(notFound, "addon-updates-none-found", function(s) {
				notify(s);
			});
			return;
		}
		if(autoUpdateChecked) {
			removeTab();
			localize(updated, "addon-updates-installed", function(s) {
				notify(s);
			});
			return;
		}

		tab.collapsed = false;

		var cats = $("categories");
		var upds = $("category-availableUpdates");
		if(cats && upds) {
			if(vb && cats.selectedItem == upds) // Only for Firefox 72+
				cats.selectedItem = $("category-extension"); // Trick to force update
			cats.selectedItem = upds;
		}
		else { // Firefox 76+ ?
			vbDoc.querySelector('.category[name="available-updates"]').click();
		}

		var tabWin = tab.ownerDocument.defaultView;
		if(tbTab)
			tabmail.switchToTab(tbTabInfo);
		else
			tabWin.gBrowser.selectedTab = tab;
		setTimeout(function() {
			tabWin.focus();
			doc.defaultView.focus();
			var al = $("addon-list") || vb;
			al.focus();
		}, 0);
	}, 50);
	function $(id) {
		return doc.getElementById(id);
	}
	function stopWait() {
		clearInterval(waitTimer);
		progressIcon.restore();
		btn.tooltipText = tip;
		if(tab.image == image)
			tab.image = tab.getAttribute(origAttr);
		tab.removeAttribute(origAttr);
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() {
			delete btn._cb_disabled;
		}, 500);
	}
	function notify(msg) {
		Components.classes["@mozilla.org/alerts-service;1"]
			.getService(Components.interfaces.nsIAlertsService)
			.showAlertNotification(
				app == "Firefox" && pv >= 57
					? "chrome://mozapps/skin/extensions/extensionGeneric.svg"
					: "chrome://mozapps/skin/extensions/extensionGeneric.png",
				btn.label,
				msg, false, "", null
			);
	}
}
function dontSelectHiddenTab(e) {
	// <tab /><tab collapsed="true" />
	// Close first tab: collapsed tab becomes selected
	var trgTab = e.originalTarget || e.target;
	if(trgTab != tab)
		return;

	if(/\n(?:BrowserOpenAddonsMgr|toEM)@chrome:\/\//.test(new Error().stack)) {
		// User open Add-ons Manager, show tab
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() { // Hidden tab can't be selected, so select it manually...
			tab.collapsed = tab.closing = false;
			gBrowser.selectedTab = tab;
		}, 0);
	}

	function done(t) {
		if(!t.hidden && !t.closing) {
			e.preventDefault();
			e.stopPropagation();
			return gBrowser.selectedTab = t;
		}
		return false;
	}
	for(var t = tab.nextSibling; t; t = t.nextSibling)
		if(done(t))
			return;
	for(var t = tab.previousSibling; t; t = t.previousSibling)
		if(done(t))
			return;
}
function ProgressIcon(btn) {
	var app = Services.appinfo.name;
	var pv = parseFloat(Services.appinfo.platformVersion);
	if(app == "SeaMonkey")
		this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif";
	else if(app == "Thunderbird") {
		this.imgConnecting = "chrome://messenger/skin/icons/connecting.png";
		this.imgLoading = "chrome://messenger/skin/icons/loading.png";
	}
	else {
		this.imgConnecting = app == "Firefox" && pv >= 58
			? "chrome://browser/skin/tabbrowser/tab-connecting.png"
			: "chrome://browser/skin/tabbrowser/connecting.png";
		this.imgLoading = app == "Firefox" && pv >= 48
			? "chrome://global/skin/icons/loading.png"
			: "chrome://browser/skin/tabbrowser/loading.png";
	}
	if(!(btn instanceof XULElement)) {
		this.loading = this.restore = function() {};
		return;
	}
	var useAnimation = app == "Firefox" && pv >= 32 && pv < 48;
	var btnIcon = btn.icon
		|| btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon");
	var origIcon = btnIcon.src;
	btnIcon.src = this.imgConnecting;
	if(useAnimation) {
		let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null);
		let s = btnIcon.style;
		s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" ");
		s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" ");
		s.width = cs.width;
		s.height = cs.height;
		s.boxShadow = "none";
		s.borderColor = s.background = "transparent";
		btnIcon.setAttribute("fadein", "true");
		btnIcon.setAttribute("busy", "true");
		btnIcon.classList.add("tab-throbber");
		btnIcon._restore = function() {
			delete btnIcon._restore;
			btnIcon.removeAttribute("busy");
			btnIcon.removeAttribute("progress");
			setTimeout(function() {
				btnIcon.classList.remove("tab-throbber");
				btnIcon.removeAttribute("style");
				btnIcon.removeAttribute("fadein");
			}, 0);
		};
	}
	this.loading = function() {
		btnIcon.src = this.imgLoading;
		if(useAnimation)
			btnIcon.setAttribute("progress", "true");
	};
	this.restore = function() {
		btnIcon.src = origIcon;
		if(useAnimation)
			btnIcon._restore();
	};
}
}).call(this);
//== Check for Addons Updates end
}              

this.tooltipText = "Переключатель джетпаков" 
                   + "\nПКМ – проверить обновления"
                   + "\nСКМ – открыть страницу дополнений"
                   + "\nShift+ПКМ – меню кнопки"
                   + "\n\nВ меню: \nЛКМ – включить/выключить дополнение без закрытия меню"
                   + "\nShift+ЛКМ – включить/выключить дополнение"   
                   + "\nСКМ – открыть страницу дополнения в управлении дополнениями"                    
                   + "\nПКМ – открыть настройки дополнения (если есть)";     
// Autoopen/close feature
var openDelay = 200;
var closeDelay = 350;

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
		if(!isContextOpened())
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& node.namespaceURI == xulns
				// See https://github.com/Infocatcher/Custom_Buttons/issues/28
				//&& node.boxObject
				//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
				&& "open" in node
				&& node.open
				&& node.getElementsByTagName("menupopup").length
			) {
				node.open = false;
				return true;
			}
			return false;
		}
	);
}
function isContextOpened() {
	return inBtn(document.popupNode);
}
function inBtn(node) {
	for(; node; node = node.parentNode)
		if(node == self)
			return true;
	return false;
}
Выделить код

Код:

//Дополнения
try {CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js"
		));
	},
	onCreated(btn) {
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC");
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
});} catch(ex) {Cu.reportError(ex);}

Отредактировано egorsemenov06 (28-02-2024 22:00:00)

Отсутствует

 

№130529-02-2024 01:50:06

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 416
UA: Firefox 116.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Northtech пишет

есть возможность сделать в ucf_contextsearch.js открытие вкладки поиска фоном? Или зависящим от этой настройки: user_pref("browser.search.context.loadInBackground", true);

Поиск и так открывается в фоновой вкладке по клику правой кнопкой или колёсиком на любых строках меню и подменю.

Отредактировано Dobrov (29-02-2024 01:51:11)

Отсутствует

 

№130629-02-2024 05:12:23

xrun1
Участник
 
Группа: Members
Зарегистрирован: 12-12-2013
Сообщений: 1165
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

egorsemenov06
Я так понял. Когда накроется jsm, отвалится весь UCF. Куда Вы будете эти кнопки писать, если CustomStylesScripts.jsm не будет работать? Или пересаживаться на старую версию версию UCF?

Отредактировано xrun1 (29-02-2024 05:31:45)

Отсутствует

 

№130729-02-2024 06:06:58

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 416
UA: Firefox 121.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby возникла хотелка для менюшки кнопки, которую я немного доработал.


это меню создаётся однократно и перед кликом на кнопке его не существует, т.е. однократно выполняется menu.render = this.render;
Нужно, чтоб менялись строки меню перед его открытием в зависимости от опций about:config и прочих проверок, например "Графика сайтов включена" вместо "Графика сайтов вкл/выкл"


Прошу доработать код, чтоб ключи «lab» могли быть с backticks-кавычками и текст мог обновляться перед каждым открытием меню:
lab: `${проверка условия ? "√ Да, всё OK" : "Х Нет"}`

Автономное меню для тех, кому не нужен ucf_hookClicks.js

Выделить код

Код:

(async id => { // by Dumby forum.mozilla-russia.org/viewtopic.php?pid=807581#p807581

MyMenu = { //массив команд пользователя, alt() клик правой кнопкой
	Pics: {
		lab: `Графика сайтов вкл/выкл | Right Click`,
		inf: 'текст обновляется перед открытием меню',
		cmd(){
			var n = uc.pref(E.v) == 2; uc.pref(E.v, n ? 1 : 2);
			BrowserReload();
		},
		alt(){ /* для этих действий нужен ucf_hookClicks.js */},
	},
	"Прочие команды": { sep: 1, //сперва разделитель
		cmd(){console.log("OK");}
	},
}

CustomizableUI.createWidget({
		id: id, label: id, tooltiptext: id, localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onCreated(btn) {
			btn.style.setProperty("list-style-image", "url(chrome://branding/content/icon32.png)");
			btn.type = "menu";
			var doc = btn.ownerDocument, m = nn => doc.createXULElement(nn);
			var popup = m("menupopup"), menu = m("menuitem");
			menu.m = m;
			menu.fill = this.fill;
			menu.render = this.render;
			popup.append(menu);
			btn.prepend(popup);
		},
		render(){
			var popup = this.parentNode;
			this.remove();
			this.fill(MyMenu, popup);
		},
		fill(o, popup){
			if (typeof o == "object") for (key in o){
			var {lab, sep, sub, cmd, alt, inf, img} = o[key];
			var name = sub ? "menu" : "menuitem";
			sep && popup.append(this.m("menuseparator"));
			var item = this.m(name);
			item.setAttribute("label", lab || key); //update при изменении настроек
			if (img)
				item.className = name +"-iconic", item.setAttribute("image",img);
			if (inf) item.tooltipText = inf;
			item.alt = alt; //cmd2
			sub || cmd && item.setAttribute("oncommand", cmd.toString().replace(/cmd\(.*?\){/,'{var trg = event.target || event;'));
			/^(sub|sep|inf|lab|img)$/.test(key) || popup.append(item);
			sub && this.fill(o[key], item.appendChild(this.m("menupopup")));
			}
		},
});

E = {
	v: "permissions.default.image"
}
window.uc = { //all ChromeOnly-scripts
	pref(key,set){ //или key = [key,default]
		if (!Array.isArray(key)) key = [key];
		var t = prefs.getPrefType(key[0]), m = {b:"Bool",n:"Int",s:"String"};
		t = m[t == 128 ? "b" : t == 64 ? "n" : t == 32 ? "s" : ""];
		if (set == "get") return t; //тип опции
		if (!t) t = m[set != undefined ? (typeof set)[0] : (typeof key[1])[0]];
		if (t) if (set != undefined)
			prefs[`set${t}Pref`](key[0],set)
		else
			set = prefs[`get${t}Pref`](...key);
		return set;
	}
}

})("ucf_test_menu");


xrun1 я просто останусь на старой версии браузера, как это делает Dumby (у него вроде FF78)

Отсутствует

 

№130829-02-2024 15:21:27

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2156
UA: Firefox 78.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

egorsemenov06 пишет

Ссылки кликабельны

Здесь можно переделать. Переименовать UcfTextToLinkActorChild.{ jsm —> mjs } и правки

скрытый текст

В text-to-link.js

Выделить код

Код:

/*
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.jsm",
*/
                    esModuleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.mjs"

В UcfTextToLinkActorChild.mjs

Выделить код

Код:

/*
var EXPORTED_SYMBOLS = ["UcfTextToLinkActorChild"];
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["NodeFilter", "Node"]);
XPCOMUtils.defineLazyGetter(this, "excludedTags", () => {
    return new Set(["a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img","select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"]);
});

class UcfTextToLinkActorChild extends JSWindowActorChild {
*/
var excludedTags = Object.defineProperty({}, "has", {
    value: tag => (excludedTags = new Set([
        "a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img",
        "select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"
    ])).has(tag)
});

export class UcfTextToLinkActorChild extends JSWindowActorChild {

переключение раскладки клавиатуры по F8

Для этого кода нужен дебаггер.
В UCF 3 вообще необходимо встраивать дебаггер,
не городить же в каждом скрипте, где он нужен, свой собственный.


Но, наверно, можно пробросить implementation из кода само́й кнопки,
однако, этот код не предоставлен.

special_widgets.js

Этот скрипт — часть UCF.
На первый взгляд, в данном контексте ему ничего не угрожает.
Наоборот, его скорее покрамсать хочется.

и еще одна

Здесь можно попробовать влепить что-нибудь такое,
тогда, надеюсь, не будет импорта никаких jsm'ок.

скрытый текст

Выделить код

Код:

/*
	try {
*/
	if(Components.interfaces.nsIWebTransportHash) { // random, Fx 123+
		var func = function() {
			func = false;
		}
		var thread = Services.tm.currentThread;
		var meth = newDis ? "disable" : "enable";
		addon[meth]({allowSystemAddons: true}).finally(func);
		while(func) thread.processNextEvent(true);
	}
	else try {


Dobrov пишет

немного доработал

Ничего себе «немного». Как там задумана обработка
структуры MyMenu я сходу понять не могу, субменю вообще в примере нет.


Ну хорошо хоть вопрос не об этом.
Добавил туда var {prefs} = Services; и заработало.


Можно ввести ещё один ключ — upd
который будет представлять собой функцию,
которая перекроет метод render() для item'а.


Ключевое слово this в этой функции будет ссылаться на сам item.
Например, вместо lab и inf пишем:


upd() {
        //var {uc, E} = this.ownerGlobal;
        var val = uc.pref(E.v);
        this.label = `${E.v} is ${val == 1 || val == 2 ? val : "not 1 and not 2"}`;
        this.tooltipText = val;
},


Далее, учитываем новый ключ в var {lab, sep, sub, cmd, alt, inf, img, upd} = o[key];
И дописываем подмену после добавления item'а в popup:
/^(sub|sep|inf|lab|img)$/.test(key) || popup.append(item) || upd && (item.render = upd).call(item);


Однако, хоть lab и убран, код тогда всё равно проставляет вместо него label — key,
а upd() уже затем ставит свой. Это слегка нехорошо, но тут уж думай сам, или забей.

Отсутствует

 

№130929-02-2024 15:59:36

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 416
UA: Firefox 116.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Ничего себе «немного». Как там задумана обработка структуры MyMenu я сходу понять не могу, субменю вообще в примере нет.

Спасибо за помошь! :beer:
P.S. Массив вместе с субменю есть в ucf_hookClicks.js, вложенность любая.

Отсутствует

 

№131029-02-2024 17:59:20

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 410
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет
egorsemenov06 пишет

Но, наверно, можно пробросить implementation из кода само́й кнопки,
однако, этот код не предоставлен

Если можно сделайте пожалуйста из этих 2-х кнопок одну что бы переключение раскладки клавиатуры было по F8

скрытый текст

Выделить код

Код:

// Switch Keyboard Layout
try {(keybUtils => CustomizableUI.createWidget({
    type: "custom",
    id: "SwitchKeyboardLayout",
    onBuild(doc) {
        var btn = doc.createXULElement("toolbarbutton");
        btn.id = this.id;
        btn.label = btn.tooltipText = "Switch Keyboard Layout";
        btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";

        btn.setAttribute("oncommand", "linkedObj.switch(document);");
        btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
        btn.linkedObj = this;
        return btn;
    },
    switch(doc) {
        var br = doc.activeElement;
        br && br.localName == "browser" && br.isRemoteBrowser
            ? br.messageManager.loadFrameScript(this.url, false)
            : this.keybUtils.switchSelKeybLayout();
    },
    get url() {
        delete this.url;
        return this.url = `data:;charset=utf-8,(${
            encodeURIComponent(keybUtils)
        }).switchSelKeybLayout()`;
    },
    get keybUtils() {
        delete this.keybUtils;
        var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
        var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
        Services.scriptloader.loadSubScript(url, this);
        var {id} = this;
        this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
            var window = Services.focus.activeWindow, {document} = window;
            var button = document.getElementById(id);
            if(
                !_focusFixed
                && "closeMenus" in window
                && document.commandDispatcher.focusedElement == button
            ) {
                window.closeMenus(button);
                window.setTimeout(function(_this) {
                    _this.switchSelKeybLayout(_subCall, true);
                }, 0, this);
                return;
            }
            return document.commandDispatcher.focusedElement;
        }
        return this.keybUtils;
    }
}))(`{
    //== Options
    noSelBehavior:
        "BeginLine",
        //"EndLine",
        //"All",
        //"Top",
        //"Bottom",
        //"PageUp",
        //"PageDown",
        // etc

    // falsy - do nothing
    // Or use string like following to call cmd_select{string}

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            var primary = c in this.convTableForward;
            if(primary ^ c in this.convTableBackward)
                return primary;
        }
        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(Element.isInstance(node)) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
//        var e = this.noSelBehavior;
//        if(!e || typeof e != "object")
//            return;
//        var evt = node.ownerDocument.createEvent("KeyboardEvent");
//        evt.initKeyEvent(
//            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//            e.keyCode, e.charCode
//        );
//        node.dispatchEvent(evt);

       var beh = this.noSelBehavior;
       beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
    },
    beh2cmd: { // Ctrl_Shift_VK
        false_true_36: "cmd_selectLinePrevious", // Shift+Home
    },
    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`)} catch(ex) {Cu.reportError(ex);}
Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}


Большое спасибо Вам!!!

Отредактировано egorsemenov06 (29-02-2024 22:21:09)

Отсутствует

 

№131101-03-2024 16:45:40

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2156
UA: Firefox 78.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dobrov пишет

Массив вместе с субменю есть в ucf_hookClicks.js, вложенность любая.

Да, теперь чуть понятнее, наверно.
А то я думал, к чему это /^(sub|sep|inf|lab|img)$/.test(key)


Оказывается, что если вписан sub, код прогоняет через for in
объект в который вписан sub.
То есть, и для ключей, упомянутых в этом регулярном выражении
тоже создаётся (ненужный) элемент (как минимум <menuitem>)
и ему присваивается одноимённый атрибут "label".


Если значение ключа окажется строкой, то тогда создаётся не <menuitem>,
а <menu>, и создаётся <menupopup>, который добавляется к этому menu.
Всё потому, что у строки есть sub.
Вот проверь с консоли: var {sub} = "bla-bla"; alert(sub);
Это будет — String.prototype.sub().


Эти ненужные элементы можно посмотреть в консоли,
если добавить в конец метода fill() строку
if (!item.parentNode) console.log(item.outerHTML);


Вот чтобы всё это низачем-созданное добро
не попало в итоговую DOM-структуру и нужен этот regexp.
Может этого как-то избежать, типа

скрытый текст

Выделить код

Код:

//
	fill(o, popup) {
		for (key in o) {
			var val = o[key];
			if (typeof val != "object") continue;

			var {lab, sep, sub, cmd, alt, inf, img, upd} = val;

			sep && popup.append(this.m("menuseparator"));
			var name = sub ? "menu" : "menuitem";
			var item = this.m(name);

			item.setAttribute("label", lab || key);
			if (img)
				item.className = name + "-iconic",
				item.setAttribute("image", img);

			item.alt = alt; //cmd2
			if (inf) item.tooltipText = inf;
			sub || cmd && item.setAttribute("oncommand", cmd.toString().replace(
				/cmd\(.*?\){/, "{var trg = event.target || event;"
			));
			popup.append(item);
			upd && (item.render = upd).call(item);
			sub && this.fill(val, item.appendChild(this.m("menupopup")));
		}
	},

egorsemenov06 пишет

сделайте пожалуйста из этих 2-х

скрытый текст

Выделить код

Код:

// Switch Keyboard Layout
(async keybUtils => CustomizableUI.createWidget(({
	localized: false,
	id: "SwitchKeyboardLayout",
	init() {
		this.label = this.tooltiptext = "Switch Keyboard Layout";
		var args = ["keydown", e => {
			if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat) return;
			//e.preventDefault();
			//e.stopPropagation();
			this.switch(e.view.document);
		}, true];
		var unload = e => e.target.ownerGlobal.removeEventListener(...args);
		var obs = win => {
			win.addEventListener(...args);
			win.addEventListener("unload", unload, {once: true});
		}
		var topic = "browser-delayed-startup-finished";
		Services.obs.addObserver(obs, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(obs, topic);
		}, "quit-application-granted");
		return this;
	},
	onCreated(btn) {
		btn.linkedObj = this;
		btn._handleClick = this.click;
		btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";
	},
	click() {
		this.linkedObj.switch(this.ownerDocument);
	},
	switch(doc) {
		var br = doc.activeElement;
		br && br.localName == "browser" && br.isRemoteBrowser
			? br.messageManager.loadFrameScript(this.url, false)
			: this.keybUtils.switchSelKeybLayout();
	},
	get url() {
		delete this.url;
		return this.url = `data:;charset=utf-8,(${
			encodeURIComponent(keybUtils)
		}).switchSelKeybLayout()`;
	},
	get keybUtils() {
		delete this.keybUtils;
		var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
		var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
		Services.scriptloader.loadSubScript(url, this);
		var {id} = this;
		this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
			var window = Services.focus.activeWindow, {document} = window;
			var button = document.getElementById(id);
			if(
				!_focusFixed
				&& "closeMenus" in window
				&& document.commandDispatcher.focusedElement == button
			) {
				window.closeMenus(button);
				window.setTimeout(function(_this) {
					_this.switchSelKeybLayout(_subCall, true);
				}, 0, this);
				return;
			}
			return document.commandDispatcher.focusedElement;
		}
		return this.keybUtils;
	}
}).init()))(`{
	//== Options
	noSelBehavior:
		"BeginLine",
		//"EndLine",
		//"All",
		//"Top",
		//"Bottom",
		//"PageUp",
		//"PageDown",
		// etc

	// falsy - do nothing
	// Or use string like following to call cmd_select{string}

	convTableForward: { // ru -> en
		"\\"": "@",
		":": "^",
		";": "$",
		"?": "&",
		",": "?",
		"/": "|",
		".": "/",
		"э": "'",
		"б": ",",
		"ю": ".",
		"Ж": ":",
		"ж": ";",
		"Б": "<",
		"Ю": ">",
		"Э": "\\"",
		"х": "[",
		"ъ": "]",
		"ё": "\`",
		"Х": "{",
		"Ъ": "}",
		"Ё": "~",
		"№": "#",
		"Ф": "A",
		"ф": "a",
		"И": "B",
		"и": "b",
		"С": "C",
		"с": "c",
		"В": "D",
		"в": "d",
		"У": "E",
		"у": "e",
		"А": "F",
		"а": "f",
		"П": "G",
		"п": "g",
		"Р": "H",
		"р": "h",
		"Ш": "I",
		"ш": "i",
		"О": "J",
		"о": "j",
		"Л": "K",
		"л": "k",
		"Д": "L",
		"д": "l",
		"Ь": "M",
		"ь": "m",
		"Т": "N",
		"т": "n",
		"Щ": "O",
		"щ": "o",
		"З": "P",
		"з": "p",
		"Й": "Q",
		"й": "q",
		"К": "R",
		"к": "r",
		"Ы": "S",
		"ы": "s",
		"Е": "T",
		"е": "t",
		"Г": "U",
		"г": "u",
		"М": "V",
		"м": "v",
		"Ц": "W",
		"ц": "w",
		"Ч": "X",
		"ч": "x",
		"Н": "Y",
		"н": "y",
		"Я": "Z",
		"я": "z",
		__proto__: null
	},
	//== End of options

	get convTableBackward() {
		var ctb = { __proto__: null };
		var ctf = this.convTableForward;
		for(var c in ctf)
			ctb[ctf[c]] = c;
		delete this.convTableBackward;
		return this.convTableBackward = ctb;
	},
	inPrimaryLayout: function(s) {
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			var primary = c in this.convTableForward;
			if(primary ^ c in this.convTableBackward)
				return primary;
		}
		return false;
	},
	switchKeybLayout: function(s, convTable) {
		var res = "";
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			res += c in convTable ? convTable[c] : c;
		}
		return res;
	},
	getFocusedElement: function() {
		return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
			.getFocusedElementForWindow(content, true, {});
	},
	switchSelKeybLayout: function(_subCall, _focusFixed) {
		var fe = this.getFocusedElement(_subCall, _focusFixed);
		if(!fe)
			return;
		if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
			var ta = fe;
			try {
				var val = ta.value;
				var sel = val.substring(ta.selectionStart, ta.selectionEnd);
			}
			catch(e) { // Non-text HTMLInputElement
				return;
			}
			if(!sel && val && this.noSelBehavior && !_subCall) {
				if(this.noSelBehavior == 1) {
					ta.selectionStart = 0;
					ta.selectionEnd = val.length;
					sel = val;
				}
				else {
					this.handleNoSel(ta);
					return;
				}
			}
			if(!sel)
				return;
			var res = this.switchKeybLayout(
				sel,
				this.inPrimaryLayout(sel)
					? this.convTableForward
					: this.convTableBackward
			);
			if(res != sel)
				this.insertText(ta, res);
		}
		else if(fe.contentEditable == "true") {
			var doc = fe.ownerDocument;

			var docURI = doc.documentURI;
			if(
				docURI.substr(0, 5) == "data:"
				&& docURI.indexOf("chrome://browser/skin/devtools/") != -1
			) {
				//~ todo: seems like we only can use paste from clipboard here...
				return;
			}

			var sel = doc.defaultView.getSelection();
			var rng = sel.rangeCount && sel.getRangeAt(0);
			var tmpNode;
			if(!rng || rng.collapsed) {
				if(!this.noSelBehavior || _subCall)
					return;
				if(this.noSelBehavior == 1) {
					var r = doc.createRange();
					r.selectNodeContents(fe);
					sel.removeAllRanges();
					sel.addRange(r);
					tmpNode = fe.cloneNode(true);
				}
				else {
					this.handleNoSel(fe);
					return;
				}
			}
			else {
				tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
				tmpNode.appendChild(rng.cloneContents());
			}

			var orig = tmpNode.innerHTML;
			var convTable = this.inPrimaryLayout(tmpNode.textContent)
				? this.convTableForward
				: this.convTableBackward;

			var _this = this;
			var parseChildNodes = function(node) {
				if(Element.isInstance(node)) {
					var childNodes = node.childNodes;
					for(var i = childNodes.length - 1; i >= 0; --i)
						parseChildNodes(childNodes[i]);
				}
				else if(node.nodeType == node.TEXT_NODE) {
					var text = node.nodeValue;
					var newText = _this.switchKeybLayout(node.nodeValue, convTable);
					if(newText != text)
						node.parentNode.replaceChild(doc.createTextNode(newText), node);
				}
			}
			parseChildNodes(tmpNode);

			var res = tmpNode.innerHTML;
			if(res != orig)
				doc.execCommand("insertHTML", false, res);
		}
	},
	handleNoSel: function(node) {
		this.select(node);
		this.switchSelKeybLayout(true);
	},
	select: function(node) {
//		var e = this.noSelBehavior;
//		if(!e || typeof e != "object")
//			return;
//		var evt = node.ownerDocument.createEvent("KeyboardEvent");
//		evt.initKeyEvent(
//			"keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//			e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//			e.keyCode, e.charCode
//		);
//		node.dispatchEvent(evt);

	   var beh = this.noSelBehavior;
	   beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
	},
	beh2cmd: { // Ctrl_Shift_VK
		false_true_36: "cmd_selectLinePrevious", // Shift+Home
	},
	insertText: function(ta, text) {
		//var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
		var editor = ta.editor
			.QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
		if(editor.flags & editor.eEditorReadonlyMask)
			return;

		var sTop = ta.scrollTop;
		var sHeight = ta.scrollHeight;
		var sLeft = ta.scrollLeft;
		// var sWidth = ta.scrollWidth;

		if(text)
			editor.insertText(text);
		else
			editor.deleteSelection(0, 0);

		ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
		ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
	}
}`);

Отсутствует

 

№131201-03-2024 17:35:07

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 410
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет
egorsemenov06 пишет

сделайте пожалуйста из этих 2-х

скрытый текст

Выделить код

Код:

// Switch Keyboard Layout
(async keybUtils => CustomizableUI.createWidget(({
	localized: false,
	id: "SwitchKeyboardLayout",
	init() {
		this.label = this.tooltiptext = "Switch Keyboard Layout";
		var args = ["keydown", e => {
			if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat) return;
			//e.preventDefault();
			//e.stopPropagation();
			this.switch(e.view.document);
		}, true];
		var unload = e => e.target.ownerGlobal.removeEventListener(...args);
		var obs = win => {
			win.addEventListener(...args);
			win.addEventListener("unload", unload, {once: true});
		}
		var topic = "browser-delayed-startup-finished";
		Services.obs.addObserver(obs, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(obs, topic);
		}, "quit-application-granted");
		return this;
	},
	onCreated(btn) {
		btn.linkedObj = this;
		btn._handleClick = this.click;
		btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";
	},
	click() {
		this.linkedObj.switch(this.ownerDocument);
	},
	switch(doc) {
		var br = doc.activeElement;
		br && br.localName == "browser" && br.isRemoteBrowser
			? br.messageManager.loadFrameScript(this.url, false)
			: this.keybUtils.switchSelKeybLayout();
	},
	get url() {
		delete this.url;
		return this.url = `data:;charset=utf-8,(${
			encodeURIComponent(keybUtils)
		}).switchSelKeybLayout()`;
	},
	get keybUtils() {
		delete this.keybUtils;
		var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
		var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
		Services.scriptloader.loadSubScript(url, this);
		var {id} = this;
		this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
			var window = Services.focus.activeWindow, {document} = window;
			var button = document.getElementById(id);
			if(
				!_focusFixed
				&& "closeMenus" in window
				&& document.commandDispatcher.focusedElement == button
			) {
				window.closeMenus(button);
				window.setTimeout(function(_this) {
					_this.switchSelKeybLayout(_subCall, true);
				}, 0, this);
				return;
			}
			return document.commandDispatcher.focusedElement;
		}
		return this.keybUtils;
	}
}).init()))(`{
	//== Options
	noSelBehavior:
		"BeginLine",
		//"EndLine",
		//"All",
		//"Top",
		//"Bottom",
		//"PageUp",
		//"PageDown",
		// etc

	// falsy - do nothing
	// Or use string like following to call cmd_select{string}

	convTableForward: { // ru -> en
		"\\"": "@",
		":": "^",
		";": "$",
		"?": "&",
		",": "?",
		"/": "|",
		".": "/",
		"э": "'",
		"б": ",",
		"ю": ".",
		"Ж": ":",
		"ж": ";",
		"Б": "<",
		"Ю": ">",
		"Э": "\\"",
		"х": "[",
		"ъ": "]",
		"ё": "\`",
		"Х": "{",
		"Ъ": "}",
		"Ё": "~",
		"№": "#",
		"Ф": "A",
		"ф": "a",
		"И": "B",
		"и": "b",
		"С": "C",
		"с": "c",
		"В": "D",
		"в": "d",
		"У": "E",
		"у": "e",
		"А": "F",
		"а": "f",
		"П": "G",
		"п": "g",
		"Р": "H",
		"р": "h",
		"Ш": "I",
		"ш": "i",
		"О": "J",
		"о": "j",
		"Л": "K",
		"л": "k",
		"Д": "L",
		"д": "l",
		"Ь": "M",
		"ь": "m",
		"Т": "N",
		"т": "n",
		"Щ": "O",
		"щ": "o",
		"З": "P",
		"з": "p",
		"Й": "Q",
		"й": "q",
		"К": "R",
		"к": "r",
		"Ы": "S",
		"ы": "s",
		"Е": "T",
		"е": "t",
		"Г": "U",
		"г": "u",
		"М": "V",
		"м": "v",
		"Ц": "W",
		"ц": "w",
		"Ч": "X",
		"ч": "x",
		"Н": "Y",
		"н": "y",
		"Я": "Z",
		"я": "z",
		__proto__: null
	},
	//== End of options

	get convTableBackward() {
		var ctb = { __proto__: null };
		var ctf = this.convTableForward;
		for(var c in ctf)
			ctb[ctf[c]] = c;
		delete this.convTableBackward;
		return this.convTableBackward = ctb;
	},
	inPrimaryLayout: function(s) {
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			var primary = c in this.convTableForward;
			if(primary ^ c in this.convTableBackward)
				return primary;
		}
		return false;
	},
	switchKeybLayout: function(s, convTable) {
		var res = "";
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			res += c in convTable ? convTable[c] : c;
		}
		return res;
	},
	getFocusedElement: function() {
		return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
			.getFocusedElementForWindow(content, true, {});
	},
	switchSelKeybLayout: function(_subCall, _focusFixed) {
		var fe = this.getFocusedElement(_subCall, _focusFixed);
		if(!fe)
			return;
		if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
			var ta = fe;
			try {
				var val = ta.value;
				var sel = val.substring(ta.selectionStart, ta.selectionEnd);
			}
			catch(e) { // Non-text HTMLInputElement
				return;
			}
			if(!sel && val && this.noSelBehavior && !_subCall) {
				if(this.noSelBehavior == 1) {
					ta.selectionStart = 0;
					ta.selectionEnd = val.length;
					sel = val;
				}
				else {
					this.handleNoSel(ta);
					return;
				}
			}
			if(!sel)
				return;
			var res = this.switchKeybLayout(
				sel,
				this.inPrimaryLayout(sel)
					? this.convTableForward
					: this.convTableBackward
			);
			if(res != sel)
				this.insertText(ta, res);
		}
		else if(fe.contentEditable == "true") {
			var doc = fe.ownerDocument;

			var docURI = doc.documentURI;
			if(
				docURI.substr(0, 5) == "data:"
				&& docURI.indexOf("chrome://browser/skin/devtools/") != -1
			) {
				//~ todo: seems like we only can use paste from clipboard here...
				return;
			}

			var sel = doc.defaultView.getSelection();
			var rng = sel.rangeCount && sel.getRangeAt(0);
			var tmpNode;
			if(!rng || rng.collapsed) {
				if(!this.noSelBehavior || _subCall)
					return;
				if(this.noSelBehavior == 1) {
					var r = doc.createRange();
					r.selectNodeContents(fe);
					sel.removeAllRanges();
					sel.addRange(r);
					tmpNode = fe.cloneNode(true);
				}
				else {
					this.handleNoSel(fe);
					return;
				}
			}
			else {
				tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
				tmpNode.appendChild(rng.cloneContents());
			}

			var orig = tmpNode.innerHTML;
			var convTable = this.inPrimaryLayout(tmpNode.textContent)
				? this.convTableForward
				: this.convTableBackward;

			var _this = this;
			var parseChildNodes = function(node) {
				if(Element.isInstance(node)) {
					var childNodes = node.childNodes;
					for(var i = childNodes.length - 1; i >= 0; --i)
						parseChildNodes(childNodes[i]);
				}
				else if(node.nodeType == node.TEXT_NODE) {
					var text = node.nodeValue;
					var newText = _this.switchKeybLayout(node.nodeValue, convTable);
					if(newText != text)
						node.parentNode.replaceChild(doc.createTextNode(newText), node);
				}
			}
			parseChildNodes(tmpNode);

			var res = tmpNode.innerHTML;
			if(res != orig)
				doc.execCommand("insertHTML", false, res);
		}
	},
	handleNoSel: function(node) {
		this.select(node);
		this.switchSelKeybLayout(true);
	},
	select: function(node) {
//		var e = this.noSelBehavior;
//		if(!e || typeof e != "object")
//			return;
//		var evt = node.ownerDocument.createEvent("KeyboardEvent");
//		evt.initKeyEvent(
//			"keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//			e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//			e.keyCode, e.charCode
//		);
//		node.dispatchEvent(evt);

	   var beh = this.noSelBehavior;
	   beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
	},
	beh2cmd: { // Ctrl_Shift_VK
		false_true_36: "cmd_selectLinePrevious", // Shift+Home
	},
	insertText: function(ta, text) {
		//var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
		var editor = ta.editor
			.QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
		if(editor.flags & editor.eEditorReadonlyMask)
			return;

		var sTop = ta.scrollTop;
		var sHeight = ta.scrollHeight;
		var sLeft = ta.scrollLeft;
		// var sWidth = ta.scrollWidth;

		if(text)
			editor.insertText(text);
		else
			editor.deleteSelection(0, 0);

		ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
		ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
	}
}`);

Огромнейшее СПАСИБО!!!!!!!

Отсутствует

 

№131302-03-2024 02:34:07

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 416
UA: Firefox 116.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Оказывается, что если вписан sub, код прогоняет через [for in] объект в который вписан sub.
То есть, и для ключей, упомянутых в этом регулярном выражении тоже создаётся (ненужный) элемент (как минимум <menuitem>)

Спасибо! Я делал так же: (обработать только объект), но результат разный:
for (key in o) { if (typeof o[key] != "object") continue; // Dumby
if (typeof o == "object") for (key in o){ // Dobrov


Dumby пишет

Всё потому, что у строки есть sub. Вот проверь с консоли: var {sub} = "bla-bla"; alert(sub);
Это будет — String.prototype.sub().

Исправил свой код менюшки и убрал имя переменной sub.
Но тогда js-код для var {sub} работает нелогично, ведь var {lab} = "bla-bla"; alert(lab); (и другие имена) возвращают «undefined» !

Отредактировано Dobrov (02-03-2024 02:48:48)

Отсутствует

 

№131403-03-2024 01:03:06

Vitaliy V.
Участник
 
Группа: Members
Зарегистрирован: 19-09-2014
Сообщений: 2107
UA: Firefox 124.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

UserChromeFiles обнова https://github.com/VitaliyVstyle/Vitali … hromeFiles
На старых версиях [firefox] работать не будет т. к.
ESM, CSS3, Fluent (для локализации, пока добавленно три языка en-US, uk, ru)

Отсутствует

 

№131503-03-2024 01:54:42

xrun1
Участник
 
Группа: Members
Зарегистрирован: 12-12-2013
Сообщений: 1165
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Vitaliy V.
Спасибо! А одним архивом можно сделать? Или я не нашёл...:(

Отсутствует

 

№131603-03-2024 06:00:33

Viatcheslav
Участник
 
Группа: Members
Откуда: г. Бобруйск, Беларусь
Зарегистрирован: 23-11-2016
Сообщений: 312
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

xrun1 пишет

А одним архивом можно сделать? Или я не нашёл...:(

А он есть ;)
https://github.com/VitaliyVstyle/VitaliyVstyle.github.io/blob/main/UserChromeFiles/UserChromeFiles.zip

Отсутствует

 

№131703-03-2024 13:45:21

b0ttle
Участник
 
Группа: Members
Зарегистрирован: 22-10-2020
Сообщений: 182
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Vitaliy V.. ;) Рады вас видеть. Спасибо за новую сборку.

Отсутствует

 

№131803-03-2024 15:18:10

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 541
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Vitaliy V. пишет

На старых версиях  работать не будет

Vitaliy V.
Очень рады вас видеть! Огромное спасибо за новую версию.
Вопрос. Какая минимальная версия [firefox] будет работать с новой сборкой?

Отредактировано unter_officer (03-03-2024 15:25:21)


«The Truth Is Out There»

Отсутствует

 

№131903-03-2024 15:58:34

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 410
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby вот эта кнопка отвалиться когда выпилят jsm?если да то поправте ее пожалуйста

скрытый текст

Выделить код

Код:

(async url => {

	var path = "D:\\Portateble Program\\Mozilla Firefox\\Profiles\\opera-proxy.windows-386.lnk";

	var icons = [
		'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/klEQVR42u2b0Q2DMAxE03bG7tN5ukRHcwUqvwjR2D7jd1IkvsD3EinEscdACKXI7DY+w9axPF9Wm8mzo6XpsjC8jMuDiDIuByLLuAQIFfMpENTMR0F4vu0ha/431hjbzXzISqhi3gVCNfNTIVQ1PwVCdfN/Q2gNIDMQCQgKSzAtBrWtKDwexZ+RsJiU/8lDYlM/kEgDeNndHcDyDTcAri8XWQW7k1QpK+MSKwAAUCs7CwAAAAAAAGAbBAAApsXa/jDU/jhMQoSUGElR0uJcjHA1xuUoACiQoESGIqnSELxEoaT6SiiTkKBinL4BOkfoHaKD7Mqmj6hN4yRC0voCm25elE1UjV4AAAAASUVORK5CYII=',
		'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsSAAALEgHS3X78AAABAElEQVR42u2bQQ4CMQhFq57RW/QQnsdLol3M1hgt8BkeSZNZzfBfSaZQGAPDsBQzu8w5ba31fFqdh8hfV0vRZWF4CZcHESVcDkSWcAkQKuJTIKiJD4Nwf9pNVfyxlo/tdj4kEqqId4FQTfxWCFXFb4FQXfzfEFoDyHREAoJCCKb5oPYrCvdH8TAS5pPymTzEN/WERBrAeNjVPSN7f8MPgOvLNaLg4yZVqsq4+AoAANSqzgIAAAAAAAD4DQIAAPt87Z4MkQ5TEKEkRlGUsjgXI1yNcTkKABokaJGhSaoyBLdzePtGSfVIKFOQoGOcuQEmR5gdYoLszKK/si6DkximbS8IilLcn+DA8wAAAABJRU5ErkJggg=='
	];
	var labels = [
		"Active",
		"Not Active"
	];
	var tooltips = [
		"Active",
		"Not Active"
	];

	var type = "network.proxy.type";
	try {var exp = ChromeUtils.importESModule(url + "sys.mjs");}
	catch {exp = ChromeUtils.import(url + "jsm");}

	exp.PageActions.addAction(new exp.PageActions.Action({
		title: labels[1],
		iconURL: icons[1],
		pinnedToUrlbar: true,
		id: "ucf-opera-proxy",
		onPlacedInUrlbar(node) {
			var pref = Services.prefs.getIntPref(type) == 1, proc, active;
			var upd = () => {
				var state = pref && Boolean(proc);
				if (state == active) return;

				var ind = +!(active = state);
				this.setIconURL(icons[ind]);
				this.setTitle(labels[ind]);
				this.setTooltip(tooltips[ind]);
			}
			this["nsPref:changed"] = () => upd(
				pref = Services.prefs.getIntPref(type) == 1
			);
			this["process-finished"] = this["process-failed"] = () => upd(proc = null);

			this["quit-application-granted"] = t => {
				Services.obs.removeObserver(this, t);
				Services.prefs.removeObserver(type, this);
				active && Services.prefs.setIntPref(type, 0);
				proc?.kill();
			}
			this.observe = (s, topic) => this[topic](topic);
			Services.prefs.addObserver(type, this);
			Services.obs.addObserver(this, "quit-application-granted");

			var {id} = node;
			var style = `#${id} {display: flex !important;}\n` +
				`@media (max-width: 680px) {#${id} {visibility: collapse !important;}}`;

			(this.onPlacedInUrlbar = this._onPlacedInUrlbar = node => {
				var sheet = new node.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				node.ownerDocument.adoptedStyleSheets.push(sheet);
			})(node);

			var run = () => {
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				(run = () => {
					proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
					//proc.startHidden = true;
					try {proc.init(file); proc.runwAsync([], 0, this);}
					catch {proc = null;}
					upd();
				})();
			}

			var timeout = 3000;

			var tid, oldState, reload = (gb, tab, state) => {
				tid = null;
				active != state && gb.reloadTab(tab);
			}
			this._onCommand = e => {
				tid ? clearTimeout(tid) : oldState = active;
				var gb = e.view.gBrowser;
				tid = setTimeout(reload, timeout, gb, gb.selectedTab, oldState);
				if (active) Services.prefs.setIntPref(type, 0),  proc.kill();
				else pref || Services.prefs.setIntPref(type, 1), proc || run();
			}
		}
	}));
})("resource:///modules/PageActions.");

Отсутствует

 

№132003-03-2024 17:12:24

Vitaliy V.
Участник
 
Группа: Members
Зарегистрирован: 19-09-2014
Сообщений: 2107
UA: Firefox 124.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

unter_officer пишет

Вопрос. Какая минимальная версия [firefox] будет работать с новой сборкой?

Полноценно с [firefox] 117, но можно в [firefox] 115 запустить, не будут работать некоторые стили (в том числе настройках)
нет поддержки вложения селекторов https://developer.mozilla.org/en-US/doc … SS_nesting
В общем не сложно добавить поддержку со 115 версии только зачем

Отсутствует

 

№132103-03-2024 18:46:27

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 541
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Vitaliy V. пишет

Полноценно с [firefox] 117, но можно в [firefox] 115 запустить, не будут работать некоторые стили (в том числе настройках)

Спасибо за ответ. Я уже успел интереса ради немного потестировать на 115 ESR.
В целом работает нормально, но проблемки со стилями, как вы и сказали, присутствуют.
Глубже копать не стал. Откатился на старую версию UCF.


«The Truth Is Out There»

Отсутствует

 

№132203-03-2024 20:28:20

Vitaliy V.
Участник
 
Группа: Members
Зарегистрирован: 19-09-2014
Сообщений: 2107
UA: Firefox 124.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

unter_officer пишет

115 ESR

Хорошо т. к. 128 ESR выйдет в июле судя по графику, сделал патч для 115 ESR,
заменить файлы css из этого архива https://github.com/VitaliyVstyle/Vitali … 15_ESR.zip

Отредактировано Vitaliy V. (03-03-2024 20:45:20)

Отсутствует

 

№132303-03-2024 21:47:20

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 541
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Vitaliy V. пишет

Хорошо т. к. 128 ESR выйдет в июле судя по графику, сделал патч для 115 ESR,
заменить файлы css из этого архива https://github.com/VitaliyVstyle/Vitali … 15_ESR.zip

Vitaliy V., большое спасибо за патч. Теперь на 115 ESR всё нормально.
Что касается 128 ESR, то для этого надо переходить на Win10+, а я пока этого не планирую.


«The Truth Is Out There»

Отсутствует

 

№132404-03-2024 08:12:11

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2156
UA: Firefox 78.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

egorsemenov06 пишет

вот эта кнопка отвалиться когда выпилят jsm?

В этом смысле — вроде не должна.


Vitaliy V.
Насчёт UcfPrefs.mjs. Сперва по Fluent'у.
Лис 124 ru. Поставил langpack'и uk и de.


STR: Включаю de, рестарт. Открываю «Настройки UserChromeFiles»,
окно локализировано как ru, это нормально, сборка ведь ru, а de отсутствует.
Теперь переключаюсь на uk.


ER: Локализация в окне меняется на uk, ведь uk есть.
AR: Ничего не происходит, локализация остаётся ru.


Да, я понимаю, что STR довольно экзотичен.
Но, может быть, заменить appLocalesAsBCP47 на availableLocales


И по коду.
Первая строка var global = Cu.getGlobalForObject({}); Разве это не globalThis?
И, UcfPrefs.global да, полезно пробросить, но почему геттер, а не просто значение?


UcfPrefs.defineGlobalGetters есть, но в само́м UCF нигде не используется.


UcfPrefs.defineLazyGlobalGetters есть, но нужно ли это?
Вот, добавил в config.js, в самое начало, ещё до антиподписячего (мне можно),
чтобы не было никаких сомнений, что там что-то появится только когда-то позже.

скрытый текст

Выделить код

Код:

//
(globalProperties => {
	var result = [];
	var global = Cu.getGlobalForObject(Cu);

	var max = Math.max(...globalProperties.map(prop => prop.length)) + 2;
	
	for(var name of globalProperties) {
		var desc = global.Object.getOwnPropertyDescriptor(global, name);
		if (desc) {
			for(var key in desc) {
				var type = typeof desc[key];
				if (type != "boolean") desc[key] = type;
			}
		}
		result.push(name.padEnd(max) + JSON.stringify(desc));
	}

	Services.obs.addObserver(async function test(win, topic) {
		Services.obs.removeObserver(test, topic);
		await win.gBrowserInit.idleTasksFinishedPromise;

		var text = encodeURIComponent(result.join("\n"));
		var url = "data:text/plain;charset=utf-8," + text;

		var gb = win.gBrowser;
		gb.selectedTab = gb.addTrustedTab(url);

	}, "browser-delayed-startup-finished");
})([
	"atob",
	"btoa",
	"Blob",
	"CSS",
	"CSSRule",
	"DOMParser",
	"Event",
	"File",
	"FileReader",
	"InspectorUtils",
	"URL",
	"XMLHttpRequest",
	"fetch",
	"L10nFileSource",
	"L10nRegistry",
	"Localization",

	// test non-existens
	"absent",
	"alert",

	// candidates
	"TextEncoder",
	"TextDecoder",
]);


Код собирает дескрипторы свойств и визуализирует во вкладке
первого окна браузера (не сразу, нужно чуть подождать).


Насколько я вижу, все свойства уже есть, и у всех сразу value (не get).
Таким образом, lazy-фикация представляется излишней,
можно просто сразу пробросить ссылки, и все дела.


Кстати, почему только эти.
Мне вот за Text{En, De}coder особенно обидно было ещё в прошлом UCF.

Отредактировано Dumby (04-03-2024 08:13:09)

Отсутствует

 

№132504-03-2024 09:00:56

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 410
UA: Firefox 123.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет
egorsemenov06 пишет

вот эта кнопка отвалиться когда выпилят jsm?

В этом смысле — вроде не должна.

Спасибо за ответ!!!

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]