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

Хотите узнать больше о расширениях? Посмотрите ролики, рассказывающие о работе с расширениями Firefox.

№1625129-01-2022 07:18:43

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

Re: Custom Buttons

Есть старый код, подобный расширению locationbar - позволяет при клике на часть url перейти к нему. Например, есть некий url с адресом site.domen/forum/loremipsum, и при клике на слово "forum" в нём, браузер переходил на адрес site.domen/forum.
Как переделать этот код для работы с UCF (такое расширение не нашёл)

Выделить код

Код:

(function () {
        if (location != "chrome://browser/content/browser.xul") return;
        var URLBarInput = gURLBar.mInputField;
        var locationBar = URLBarInput.parentNode.appendChild(document.createElement("hbox"));
        locationBar.style.display = "none";
        URLBarInput.parentNode.addEventListener("click", function () {
            if (URLBarInput.style.display === "none") {
                URLBarInput.style.display = "";
                locationBar.style.display = "none";
                gURLBar.select();
            }
        }, false);
        URLBarInput.parentNode.addEventListener("mouseout", function () {
            if (document.activeElement !== gURLBar.inputField) {
                locationBar.style.display = "none";
                URLBarInput.style.display = "";
                gURLBar._urlTooltip && gURLBar._hideURLTooltip();
            }
        }, false);
        URLBarInput.addEventListener("mouseover", function (event) {
            if (event.ctrlKey || document.activeElement === gURLBar.inputField) {
                return;
            }
            locationBar.parentNode.removeChild(locationBar);
            locationBar = URLBarInput.parentNode.appendChild(document.createElement("hbox"));
            locationBar.style.overflow = "hidden";
            locationBar.style.width = URLBarInput.clientWidth + "px";
            gURLBar.value.split("?")[0].split("/").map(function (value, index, arr) {
                return index + 1 === arr.length ? (gURLBar.value.split("?")[1] ? (value + "?" + gURLBar.value.split("?")[1]) : value) : value;
            }).map(function (value, index, arr) {
                var sec = locationBar.appendChild(document.createElement("label"));
                //sec.style.margin = "5px";
                sec.style.margin = "5px 0 0 0";
                sec.value = value;
                if (index < arr.length - 1) sec.value = value.replace(/[^\/]$/, "$&/");
                index === 0 && (sec.style.marginLeft = "1px");
                if (index === 0 && /:\/$/.test(sec.value)) {
                    sec.value += "/";
                } else {
                    sec.onmouseover = function () {
                        this.style.textDecoration = "underline";
                        this.style.color = "blue";
                        this.style.fontWeight = "bold";
                        this.style.cursor = "pointer";
                        //this.setAttribute("style", "font-weight: bold;");
                    }
                    sec.onmouseout = function () {
                        this.style.textDecoration = "";
                    }
                    sec.onclick = function (event) {
                        if (event.button === 0) {
                                var vert=URLBarInput.value.split(this.value)[0] + this.value;
                                                    getBrowser (). loadURI(vert, {
                                                    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
                                                                  });
                            //loadURI(URLBarInput.value.split(this.value)[0] + this.value);
                            while (this.nextSibling) {
                                this.nextSibling.parentNode.removeChild(this.nextSibling);
                            }
                            event.stopPropagation();
                        }
                    }
                    URLBarInput.style.display = "none";
                    locationBar.style.display = "";
                }
            })
        }, false);
    })();

Отсутствует

 

№1625230-01-2022 09:32:26

shadow_user
Участник
 
Группа: Members
Зарегистрирован: 14-02-2007
Сообщений: 244
UA: Firefox 91.0

Re: Custom Buttons

Dobrov пишет

(такое расширение не нашёл)

Scroll Up Folder https://addons.mozilla.org/ru/firefox/a … -up-folder

Отсутствует

 

№1625303-02-2022 09:19:05

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

Re: Custom Buttons

unter_officer

скрытый текст
Пытался размышлять над однооконным режимом, и понял,
что ничего хорошего у меня придумать не получится.


Есть вариант просто тупо закрывать окна, что уже нехорошо.
Вот так, например, перекрывает основной попс — окна,
создаваемые с участием parent (docShell).


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


Увы, для WebExtensions, браузер создаёт окна именно так.
Вписал некое вмешательство в создание, но закомментировал.
Можно раскомментировать, посмотреть что ещё может сломаться.


Вобщем, частично и грубо.

Выделить код

Код:

(br => ({
	async init() {
		lockPref("browser.link.open_newwindow", 3);
		lockPref("browser.link.open_newwindow.restriction", 0);
		lockPref("browser.link.open_newwindow.override.external", 3);
		lockPref("browser.link.open_newwindow.disabled_in_fullscreen", true);

		var self = this;
		var topic = "webNavigation-createdNavigationTarget-from-js";
		var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);

		obs.addObserver(this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");

		delete this.init;
		this.imp = Cu.getGlobalForObject(Cu).ChromeUtils.import;
/*
		var url = "resource://gre/modules/ExtensionParent.jsm";
		var mgr = this.imp(url).ExtensionParent.apiManager;
		await new Promise(resolve => mgr.once("ready", resolve));

		var g = mgr.global;
		g.Object.defineProperty(g, "windows", {
			configurable: true, enumerable: true, set(val) {
				var proto = val.prototype;
				g.Object.assign(proto, g.eval(`({${proto.getAPI}})`.replace(
					/\n\n[^\n]+?Services.ww/,
					"\n\n          var res = redirectBWCArgs(args, features, windowManager);" +
					"\n          if (res) return res;$&"
				)));
				delete this.windows;
				this.windows = val;
			}
		});
		g.redirectBWCArgs = (arr, features, wm) => {
			var pr = features.at(-1);
			var win = this.getWin(null, pr.startsWith("p"));
			if (!win) {
				if (features.length > 3)
					features.join = () => "dialog=no,all," + pr;
				return;
			}
			var args = [];
			for(var ind = 0, len = arr.length; ind < len; ind++) {
				var item = arr.GetElementAt(ind);
				if (item) {
					if (ind == 5) item = item.QueryInterface(Ci.nsISupportsPRUint32).data;
					else if (ind > 5 && ind < 9) item = item.QueryInterface(Ci.nsIPrincipal);
					else if (ind == 9) item = false;
				}
				args.push(item);
			}
			this.onArgs(win, args);
			return g.Promise.resolve(wm.getWrapper(win).convert({populate: true}));
		}
*/
	},
	getWin(win, p) {
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		var bwt = this.imp(url).BrowserWindowTracker;
		return (this.getWin = (win, p) => bwt.getTopWindow({
			private: win ? win.browsingContext.usePrivateBrowsing : p
		}))(win, p);
	},
	observe(subj) {
		var bag = subj.QueryInterface(Ci.nsIPropertyBag2);
		if (bag.get("url") != br) return;

		var w = bag.get("createdTabDocShell").domWindow;
		if (w) {
			var win = this.getWin(w);
			if (win)
				w.close(),
				w.arguments ? this.onArgs(win, w.arguments): this.tab(win);
		}
	},
	onArgs(win, args) {
		var [arg] = args;
		if (arg) {
			if (win.XULElement.isInstance(arg))
				return win.gBrowser.duplicateTab(arg, true, {inBackground: false});

			if (arg instanceof Ci.nsIArray)
				arg = Array.from(arg.enumerate(Ci.nsISupportsString), ss => ss.data);
			else if (arg instanceof Ci.nsISupportsString) arg = arg.data;
		}
		else arg = args[0] = "about:blank";

		win.arguments = args;
		win.gBrowserInit.uriToLoadPromise = arg;
		this.tab(win, args[5]);
		win.gBrowserInit._handleURIToLoad();
	},
	tab(win, id) {
		var index = win.gBrowser.selectedTab._tPos + 1;
		win.gBrowser.selectedTab = win.gBrowser
			.addTrustedTab("about:blank", {index, userContextId: id});
		win.focus();
	}
}).init())("chrome://browser/content/browser.xhtml");

ВВП пишет

Помните скрипт "сохранить сессию" А как бы туда втулить " Удалить все сессии" ?

Да, что-то припоминаю. Хорошо, попробую добавить.

Да и восстановить сессию тоже желательно в новой вкладке...

Это что значит? Если как при клике по пункту «Восстановить»
с зажатым Ctrl или средней кнопкой мыши, когда (тогда)
вкладки первого окна сессии добавляются в окно к уже имеющимся,
то можно инвертировать: найти
//return this[cmd](arg, !e.button && !e.ctrlKey && e.view);
и расскомментировать, а строку перед ней закомментировать или удалить.

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

Выделить код

Код:

(async (pid, mp) => CustomizableUI.createWidget(({
	id: "797321",
	label: "Simple Session Manager",
	tooltiptext: "Simple Session Manager",
	localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = () => this.saveSession() || this.save();
		Services.obs.addObserver(this, "quit-application");
		var {openMenu} = this;
		this.render = function() {
			this.openMenu = openMenu;
			this.constructor.prototype.render.call(this);
		}
		delete this.init;
		return this;
	},
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn._handleClick = this.click;
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC");

		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;
		popup.setAttribute("onpopupshowing", "event.defaultPrevented || filler.fill(event)");
		btn.prepend(popup);

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
		btn.ownerGlobal.addEventListener("unload", () => {
			btn.removeEventListener("mousedown", this);
			popup.removeEventListener("command", this);
			if (popup.filler != this)
				popup.removeEventListener("dragstart", this),
				popup.removeEventListener("popuphidden", this);
		}, {once: true});
	},
	openMenu(arg) {
		var pos;
		if (this.matches(".widget-overflow-list > :scope"))
			pos = "after_start";
		else var win = this.ownerGlobal, {width, height, top, bottom, left, right} =
			this.closest("toolbar").getBoundingClientRect(), pos = width > height
				? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
				: `${win.innerWidth - right > left ? "end" : "start"}_before`;
		this.firstChild.setAttribute("position", pos);
		delete this.openMenu;
		this.openMenu(arg);
	},

	mousedown(e) {
		if (e.button) return;
		var trg = e.target;
		if (trg.nodeName.startsWith("t")) {
			trg.mdTimestamp = Cu.now();
			trg.tid = e.view.setTimeout(this.onTimeout, 500);
			return e.preventDefault();
		}
		e.detail == 2 && trg.nodeName == "menu" && this.boot(trg);
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.dblMD = true;
	},
	click() {
		var win = this.ownerGlobal;
		if (win.event.target != this) return;
		win.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r")) {
			arg = trg.parentNode.parentNode.label;
			if (cmd.startsWith("res"))
				return this[cmd](arg, (e.button == 1 || e.ctrlKey) && e.view);
				//return this[cmd](arg, !e.button && !e.ctrlKey && e.view);
		}
		this[cmd](arg) || this.save();
	},

	dragstart(e) {
		var trg = e.target;
		if (trg.nodeName != "menu") return;

		var popup = trg.parentNode;
		this.dragData = {trg, mouse: true};
		trg.menupopup.hidePopup();

		var win = trg.ownerGlobal;
		win.setCursor("grabbing");
		var {width} = trg.getBoundingClientRect();
		trg.setAttribute("maxwidth", width);

		win.addEventListener("mouseup", this);
		popup.addEventListener("mousemove", this);
	},
	mousemove(e) {
		var trg = e.target, dtrg = this.dragData.trg;
		if (trg == dtrg || trg.nodeName != "menu") return;

		e.movementY > 0
			? trg.nextSibling != dtrg && trg.after(dtrg)
			: trg.previousSibling != dtrg && trg.before(dtrg);
	},
	mouseup(arg) {
		if (arg.constructor.isInstance?.(arg)) {
			arg.preventDefault();
			var {trg} = this.dragData;
			this.dragData.mouse = false;
		}
		else var trg = arg;

		trg.removeAttribute("maxwidth");
		trg.ownerGlobal.setCursor("auto");
		trg.ownerGlobal.removeEventListener("mouseup", this);
		trg.parentNode.removeEventListener("mousemove", this);
	},

	popuphidden(e) {
		if (!e.target.id) return;
		e.view.removeEventListener("keydown", this, true);
		var popup = e.target;
		popup.parentNode.phTimestamp = Cu.now();
		if (!this.dragData && !popup.dblMD) return;

		var save;
		if (this.dragData) {
			var {trg, mouse} = this.dragData;
			mouse && this.mouseup(trg);
	
			delete this.dragData;
			var order = Array.from(popup.getElementsByTagName("menu"), m => m.label);
			if (order.toString() != this.meta.order.toString()) {
				var hasBoot = this.meta.boot != null;
				if (hasBoot) var bootName = this.meta.order[this.meta.boot];
				this.meta.order = order;
				if (hasBoot) this.meta.boot = this.meta.order.indexOf(bootName);
				save = true;
			}
		}
		if (popup.dblMD) {
			delete popup.dblMD;
			var {boot} = this.meta;
			var bootNode = e.target.querySelector("[boot]");
			var ind = bootNode && this.meta.order.indexOf(bootNode.label);
			if (ind != boot)
				this.meta.boot = ind,
				save = true;
		}
		save && this.save(e.view);
	},

	sku: `#${pid} > menu[maxwidth]`,
	skd: `#${pid} > menu:is([maxwidth],[_moz-menuactive]):not([open])`,
	keydown(e) {
		if (e.repeat && e.key == "Shift" || !e.shiftKey) return;
		var func = this.keyHandlers[e.key];
		if (!func) return;

		var menu = e.view.windowRoot
			.ownerGlobal.document.querySelector(this.skd);
		if (menu)
			e.stopImmediatePropagation(),
			func.call(this, menu);
	},
	keyup(e) {
		if (e.key != "Shift") return;
		var win = e.view.windowRoot.ownerGlobal;
		win.removeEventListener("keyup", this, true);
		win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
	},
	keyHandlers: {
		Enter(menu) {
			this.boot(menu);
		},
		ArrowDown(menu) {
			var ns = menu.nextSibling;
			if (ns) ns.after(menu), this.arrow(menu);
		},
		ArrowUp(menu) {
			var ps = menu.previousSibling;
			if (ps.nodeName == "menu") ps.before(menu), this.arrow(menu);
		}
	},
	arrow(menu) {
		if (menu.hasAttribute("maxwidth")) return;
		menu.setAttribute("maxwidth", menu.getBoundingClientRect().width);
		menu.ownerGlobal.addEventListener("keyup", this, true);
		this.dragData = {trg: menu};
	},

	fill(e) {
		var mxe = e.view.MozXULElement;

		var initFrag = mxe.parseXULToFragment(`
			<menuitem value="saveSession" class="menuitem-iconic" label="Сохранить сессию"/>
			<menuitem value="deleteAllSessions" class="menuitem-iconic" label="Удалить все сессии"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<menuitem label="Восстановить"
				class="menuitem-iconic" value="restoreSession"/>
			<menuitem label="Переименовать"
				class="menuitem-iconic" value="renameSession"/>
			<menuitem label="Удалить"
				class="menuitem-iconic" value="removeSession"/>
		</menupopup></menu>`);

		this.regStyle();

		var filler = {fill: e => e.target.id
			? e.view.addEventListener("keydown", this, true)
				|| e.target.fillFlag || this.fillSessions(e.target)
			: this.dragData?.mouse && e.preventDefault()
		};

		(this.fill = e => {
			var trg = e.target;
			trg.setAttribute("context", "");
			trg.append(trg.ownerDocument.importNode(initFrag, true));
			(trg.filler = filler).fill(e);
			trg.addEventListener("dragstart", this);
			trg.addEventListener("popuphidden", this);
		})(e);
	},
	fillSessions(popup) {
		while(popup.lastChild.nodeName == "menu") popup.lastChild.remove();
		var ind = 0, {boot} = this.meta;
		for(var name of this.meta.order) {
			var df = popup.ownerDocument.importNode(this.menuFrag, true);
			df.firstChild.setAttribute("label", name);
			if (ind++ == boot) df.firstChild.toggleAttribute("boot");
			popup.append(df);
		}
		popup.fillFlag = true;
	},
	regStyle() {
		delete this.regStyle;
		var subst = "ucf-ssm-style-resurl";
		Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(
			subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(`
			@-moz-document url-prefix(chrome://browser/content/browser.xhtml) {
				#${pid} > menu {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC");
				}
				#${pid} > [value=saveSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKVSURBVHjajFNNTBNREJ6+7na73bLbQn8I0BKNCYWKGr0oYoiJPxeNh6oHozcOxgSVi4le1JNHQRPigRsewURNPKiJMQicSISUACelRVJ2tz/bdktpy9adBzT8xTjJ5L35ezNv5hsLHEAv7noEntguChx7EuV8oTydKRhfno8kCnt9LTuFZ7f87mAz/8rnl86GOoOBBq+LQX1S1ioLs0txWdEmsulS38Phlcy+BwZ6W0L+JsfolcjpsFDnBKNcgQ193bRUwSrwQFgr6Dkd3o9NzilqMdI/FF+sPYCZ245I4zdunw9DtQr6LxlK6bVdpdrcPAiHfGBYLDD69ltUXsmfw0oIGoPNjkHMjMHa3J9asDyRpoyEOrQR0+dq5MxR0c29Rj3Bhvn8YjeWjZmN9cpWcApSkzpkptYg8T1FdWjTfysg1Ang9YtdT+80OogTbJc7OlsD+OftzOqsBrMflqHe6QKXIEL00zLV0UpSBbM/G9DeEQy6OXKJsfPsCbdXYjb0Yu2/nmMS9LSGYGlYpXLPkxCwkq1mR98Gn8Q4XexxYhhgYKf3TPTfRDaPqklkvViaSSpaxSpw/x1v5e0UG8W10k+iW8qfF2ZiccIydFQ7ScsXKO8ep4NiYj4ai6k5+Eoev1F1Wcn8QJDgnAnHbGYRWTh800cZ77Ryzmr6eCGXzYGiZCcQ2vQ32XT5PiIMTJBI4Waw1ZtZzLu7XaSMd1s9b9paKJA+vpuKqols3y4ov7wXaPN67GPXIl1hnDOOansyVicHhGHMzHkzeDKqqPnr/UOJxX3LNNDb5BJd3KC3Uexu7wgEGxq3limRqcxH47FVWRtPreYePBpJagdu4zYhwjx1cMFus59CuVgqTmPDDlrnvwIMAGS8IFCLagHpAAAAAElFTkSuQmCC");
				}
				#${pid} [value=restoreSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACzElEQVQ4jX2QPWwbZRjHf+/5vfPdObZjO3Ga2pVpGtxCP4QIDKgqDCDUNQwgJCS2qFI3hBCRCvXSCcTAAu0GY0Fi4kuq+IjUCpWAgCTq4IpS3Di2G8c+x/bZ57t7mSJFMfBf/3p+z+95BPvy3pufxqKmcVY3ZFFKmQtFGIReuNnrDze6lv5TqfSyx4EIgA/e+fzkZML6MDmVnD80mz1kxW3DMAxQCs/z6Hb6g3q1Xt1ttdfa7dbFN668vrkHkADJhPntufNnc0LTDi7AjFkkUknzcGF2LvCDuZVvfiwCj+/1GoAmhL3x6x2cZhsVqjFIGIZs15ts/LJOJCLt/Z0EiE3Y9eLpR1ONaoMH9zbHAGiC9FSKkwunWF1Z3RoDABhRg/zRPPmj+XHA/0QDaLectFLj6gcT+AHdTm9mzMC27e766lrWtCwy2Qz2hIUeNVDAaDCku9ujWW/ij3wsO9oaMzCiunf66TMU5gt4wyGbf1Upr5W5u15mq1JDhYqZQo6HIuqtueLw4vLXzwC89NZXeQFw/ePv7zx5buHEf6nf3qgMVipOGE0ko6EKhoQi3NnZ+c51BwsaQLOxM9N1uv86vNtz+aHSDn0pH9S2tl6r/Pn3s/VG/WYunzs/NZ2OSID0dKpRrzZS98v3kYbEsi1004BQcbtc9Q07bdYb229/dvmFL5aWVvVts9mq1Wo/e54vJIAQqGOPzQHgj3zcvos39BAIBshRqJQYjYZ/AFy79tQIeHVx+ctiIDRTA+g43Xud9q4PIHVJPBknk82QzqY5VZg2UUrEYhOX9s5aXL5RnMykb8Rj9kcC4OrSVZ1Hpt+PJ5PPz+SzR5KpyYRpGgRKMei7fHLr7lBmZ1XHcX4LgsCxLPt4JBLRm82Hr4j9DyuVSprdO/ZELGafkXpkXtPA94OK03Vv/R4/csEw5XOGbuieN6y6bv/S9XdfvPkPTgcoDlpQJpwAAAAASUVORK5CYII=");
				}
				#${pid} [value=renameSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPFJREFUeNrEU0sOgkAMbc1IIHoQT+KeY7hkBW5ccwLdsOICxI2X8peIGhggI21gggpCwsImM01f+tpOO0WlFIwR0efgeR5nsG0boihizPd91A5UwZDjuq5qwycwUkQYhttSr4Y4l76fDdtBEAQqz3OVZdnPQ09o2sQhrpBSQgnAen/qzLxZzljHcawxy7KAuCJNUyiKAm53SS0FBGQNlUW3lFO2iVCLYRhAXJEkCQPn65NGAswpL/4fWAebt1ZGXB3geHlw9maXsKqjS94CHNxF7xRM0/wKgI7jDBojvbnZAz3Gv/9E7NvGvmXCsev8EmAAWocA9ofpaRIAAAAASUVORK5CYII=");
				}
				#${pid} :is([value=removeSession], [value=deleteAllSessions]) {
					list-style-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABt0lEQVQ4ja2RT2sTURTFz8tk0nntFAQrVLBQxIUg/tkkMoLyIEVECHZhNrrxI+hOP5Mb6eBCcEipYIjZ2LpvhFasUtBSkwzOve+6mE6YSScuxLt67753fudyLvA/a7T+qCHmiTfrfWCMd3j9diPfq2SHX+uPnzJx71gP33w3bX9avBcE2j+SkK30Di7ffJ71VebMxD1hC2GGEEfH41prpftynIm92A0t2aYQQYgBThrndz/2KwCgf9Z2LNtImFOAtc356ijcCwJdJrZEUWzPfJpMAAAStPWR/h2Ktc10CoIQRUKEabFNvNbKfndcAKSjtrVfHW5YorWTzxDKYAxLtOWM+P7yt51hIYPpsDTNTyB/EwNAtWxdMuV8GCfbgxjxIviUYSV/SQOrbVjitbx4N8alBeXcdZR+3Tl3xS8FlIkt0dbnWH3xlbPgKgUo3HEq+tX7C4EuAAbGeOmqCuLIJt69s3PeQ1epKGfapCQJO6vGmwAWf/C1Wau6td8dV123BaAAUfHw6gSwtP3ugxA/y6X9INszAOQgbwFAIC/MQb9/Kv2vF2/UByejlVVn1Xiby/X6rPd/qj/1ak71UYKuwQAAAABJRU5ErkJggg==");
				}
				#${pid} > menuseparator:last-child,
				#${pid} > menu[maxwidth] > .menu-right,
				#${pid} > [value=deleteAllSessions]:nth-last-child(2) {
					display: none;
				}
				#${pid} > menu[boot] {
					color: red;
					font-weight: bold;
				}
				#${pid} > menu[maxwidth] {
					color: blue;
					font-weight: bold;
					outline-offset: -2px;
					outline: 2px solid orangered;
				}
			}
		`.replace(/;$/gm, " !important;"))));
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		sss.loadAndRegisterSheet(Services.io.newURI("resource://" + subst), sss.USER_SHEET);
	},

	get gs() {
		delete this.gs;
		return this.gs = Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {});
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	splice(name, newName) {
		var ind = this.meta.order.indexOf(name);
		if (ind == -1) return;
		var args = [ind, 1];

		if (1 in arguments) args.push(newName);
		else {
			if (ind == this.meta.boot) this.meta.boot = null;
			else if (ind < this.meta.boot) this.meta.boot--;
		}
		this.meta.order.splice(...args);
	},

	get meta() {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("simple_session_manager.json");
		this.path = file.path;
		try {
			this.data = JSON.parse(Cu.readUTF8File(file));
		} catch {
			this.pp = file.parent.path;
			this.data = Object.create(null);
		}
		var meta = this.data[mp];
		if (!meta) {
			var order = Object.keys(this.data);
			meta = this.data[mp] = {order, boot: null};
		}
		delete this.meta;
		return this.meta = meta;
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils;
		if (this.pp)
			await io.makeDirectory(this.pp), delete this.pp;
		(this.save = excWin => {
			this.meta.order.length
				? io.writeJSON(this.path, this.data)
				: io.remove(this.path, {ignoreAbsent: true});
			for(var win of CustomizableUI.windows) {
				if (win == excWin) continue;
				var popup = win.document.getElementById(pid);
				if (popup) popup.fillFlag = false;
			}
		})(excWin);
	},

	get prompter() {
		var {prompt} = Services;
		var p = {}, args = [null, null, "UCF Simple Session Manager"];
		p.alert = prompt.alert.bind(...args);
		p.confirm = prompt.confirm.bind(...args);
		var pr = prompt.prompt.bind(...args);
		p.prompt = (msg, value) => {
			var res = {value};
			return pr(msg, res, null, {}) ? res.value : null;
		}
		delete this.prompter;
		return this.prompter = p;
	},
	observe(s, t, data) {
		Services.obs.removeObserver(this, "quit-application");
		if (data.includes("restart")) return;

		var {boot} = this.meta;
		if (boot == null) return;
		
		var state = this.data[this.meta.order[boot]];
		//this.gs.SessionStoreInternal.getCurrentState = () => state;
		var ssi = this.gs.SessionStoreInternal;
		ssi.getCurrentState = () => state;
		Services.obs.removeObserver(ssi, "browser:purge-session-history");

		Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
	},

	get bwt() {
		delete this.bwt;
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		return this.bwt = ChromeUtils.import(url).BrowserWindowTracker;
	},
	getName(state) {
		var wl = state.windows.length, tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		} ${wl}/${tl} [${
			new Date().toLocaleString("mn").replace(" ", "-")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	saveSession(state = this.getState(), name = this.getName(state)) {
		var name = this.prompter.prompt("Сохранить:", name);
		if (name == null) return true;

		if (this.exists(name)) return this.saveSession(state, name);

		this.data[name] = state;

		this.meta.order.push(name);
		//this.meta.order.unshift(name);
		//if (this.meta.boot != null) this.meta.boot++;
	},
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
	renameSession(name, newName = name) {
		var newName = this.prompter.prompt(`Переименовать "${name}" в:`, newName);
		if (newName == null || newName == name) return true;

		if (this.exists(newName)) return this.renameSession(name, newName);

		var {data} = this;
		this.splice(name, newName);
		data[newName] = data[name];
		delete data[name];
	},
	removeSession(name) {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить ${name} ?`))
			return true;
		delete this.data[name];
		this.splice(name);
	},
	deleteAllSessions() {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить все сессии?`))
			return true;
		delete this.dragData;
		delete this.bwt.getTopWindow().document.getElementById(pid).dblMD;
		this.meta = (this.data = Object.create(null))[mp] = {order: [], boot: null};
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");

Dobrov пишет

Как переделать этот код

В смысле как? Всякий древний стафф зачистить, типа
mInputField - inputField, createElement - createXULElement, getBrowser () - gBrowser, ...
Никогда что ли не делал такое.

для работы с UCF

В custom_script_win.js проверка location не требуется.
Подключать из «по событию "load"».

такое расширение не нашёл

Вроде заявлен какой-то AdvancedLocationbar².
Ещё есть скрипт dav_LinkifiesLocationBar.uc.js,
(там проверка адреса идёт интервалом, долбёжка 20 раз в секунду).

Отсутствует

 

№1625403-02-2022 14:18:12

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 93.0

Re: Custom Buttons

Dumby
Теперь класс ! resource://usercontext-content/cart.svg - это обратно подставил. return this[cmd](arg, !e.button && !e.ctrlKey && e.view); -это тоже.
Хорошая фишка получилась. Жаль сессия в фоне открывается , как бы последняя вкладка активной была ?

Отредактировано ВВП (03-02-2022 18:03:41)

Отсутствует

 

№1625503-02-2022 14:41:35

ALEX_45_ORP
Участник
 
Группа: Members
Зарегистрирован: 18-01-2018
Сообщений: 162
UA: Firefox 93.0

Re: Custom Buttons

ВВП пишет

Хорошая фишка получилась

в 93 или новее затачиваешь?


Win 10х64

Отсутствует

 

№1625604-02-2022 02:06:07

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

Re: Custom Buttons

Dumby пишет

unter_officer

скрытый текст
Пытался размышлять над однооконным режимом, и понял,
что ничего хорошего у меня придумать не получится.


Есть вариант просто тупо закрывать окна, что уже нехорошо.
Вот так, например, перекрывает основной попс — окна,
создаваемые с участием parent (docShell).


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


Увы, для WebExtensions, браузер создаёт окна именно так.
Вписал некое вмешательство в создание, но закомментировал.
Можно раскомментировать, посмотреть что ещё может сломаться.


Вобщем, частично и грубо.

Выделить код

Код:

(br => ({
	async init() {
		lockPref("browser.link.open_newwindow", 3);
		lockPref("browser.link.open_newwindow.restriction", 0);
		lockPref("browser.link.open_newwindow.override.external", 3);
		lockPref("browser.link.open_newwindow.disabled_in_fullscreen", true);

		var self = this;
		var topic = "webNavigation-createdNavigationTarget-from-js";
		var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);

		obs.addObserver(this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");

		delete this.init;
		this.imp = Cu.getGlobalForObject(Cu).ChromeUtils.import;
/*
		var url = "resource://gre/modules/ExtensionParent.jsm";
		var mgr = this.imp(url).ExtensionParent.apiManager;
		await new Promise(resolve => mgr.once("ready", resolve));

		var g = mgr.global;
		g.Object.defineProperty(g, "windows", {
			configurable: true, enumerable: true, set(val) {
				var proto = val.prototype;
				g.Object.assign(proto, g.eval(`({${proto.getAPI}})`.replace(
					/\n\n[^\n]+?Services.ww/,
					"\n\n          var res = redirectBWCArgs(args, features, windowManager);" +
					"\n          if (res) return res;$&"
				)));
				delete this.windows;
				this.windows = val;
			}
		});
		g.redirectBWCArgs = (arr, features, wm) => {
			var pr = features.at(-1);
			var win = this.getWin(null, pr.startsWith("p"));
			if (!win) {
				if (features.length > 3)
					features.join = () => "dialog=no,all," + pr;
				return;
			}
			var args = [];
			for(var ind = 0, len = arr.length; ind < len; ind++) {
				var item = arr.GetElementAt(ind);
				if (item) {
					if (ind == 5) item = item.QueryInterface(Ci.nsISupportsPRUint32).data;
					else if (ind > 5 && ind < 9) item = item.QueryInterface(Ci.nsIPrincipal);
					else if (ind == 9) item = false;
				}
				args.push(item);
			}
			this.onArgs(win, args);
			return g.Promise.resolve(wm.getWrapper(win).convert({populate: true}));
		}
*/
	},
	getWin(win, p) {
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		var bwt = this.imp(url).BrowserWindowTracker;
		return (this.getWin = (win, p) => bwt.getTopWindow({
			private: win ? win.browsingContext.usePrivateBrowsing : p
		}))(win, p);
	},
	observe(subj) {
		var bag = subj.QueryInterface(Ci.nsIPropertyBag2);
		if (bag.get("url") != br) return;

		var w = bag.get("createdTabDocShell").domWindow;
		if (w) {
			var win = this.getWin(w);
			if (win)
				w.close(),
				w.arguments ? this.onArgs(win, w.arguments): this.tab(win);
		}
	},
	onArgs(win, args) {
		var [arg] = args;
		if (arg) {
			if (win.XULElement.isInstance(arg))
				return win.gBrowser.duplicateTab(arg, true, {inBackground: false});

			if (arg instanceof Ci.nsIArray)
				arg = Array.from(arg.enumerate(Ci.nsISupportsString), ss => ss.data);
			else if (arg instanceof Ci.nsISupportsString) arg = arg.data;
		}
		else arg = args[0] = "about:blank";

		win.arguments = args;
		win.gBrowserInit.uriToLoadPromise = arg;
		this.tab(win, args[5]);
		win.gBrowserInit._handleURIToLoad();
	},
	tab(win, id) {
		var index = win.gBrowser.selectedTab._tPos + 1;
		win.gBrowser.selectedTab = win.gBrowser
			.addTrustedTab("about:blank", {index, userContextId: id});
		win.focus();
	}
}).init())("chrome://browser/content/browser.xhtml");

Попробовал оба варианта - с закомментированным участком кода и с раскомментированным.
На первый взгляд оба варианта работают нормально.
Понаблюдаю в работе и если что-то пойдёт не так, то обязательно отпишусь.


Ну, а пока всё нормально. Большое спасибо за код.


«The Truth Is Out There»

Отсутствует

 

№1625704-02-2022 11:12:35

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

Re: Custom Buttons

ВВП пишет

как бы последняя вкладка активной была ?

В смысле которая была активной в сессии?
Если да, то можно такую правку попробовать

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

Выделить код

Код:

/*
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
*/
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = this.data[name];
		if (!win) return ss.setBrowserState(JSON.stringify(state));

		var [w] = state.windows;
		var tab = w.tabs[w.selected - 1];
		var ext = tab.extData;
		(ext || (tab.extData = {}))[mp] = "1";

		win.addEventListener("SSWindowRestored", this.winRetored, {once: true});
		ss.setWindowState(win, JSON.stringify(state));
		ext ? delete tab.extData[mp] : delete tab.extData;
	},
	winRetored(e) {
		var {gBrowser, SessionStore} = e.target;
		for(var tab of gBrowser.visibleTabs)
			if (SessionStore.getCustomTabValue(tab, mp) == "1") {
				gBrowser.selectedTab = tab;
				SessionStore.deleteCustomTabValue(tab, mp);
				break;
			}
	},

Отсутствует

 

№1625804-02-2022 12:21:49

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 96.0

Re: Custom Buttons

Dumby

Dumby пишет

Если да, то можно такую правку попробовать

От теперь класс !  . Как бы еще автосубтиры на ютубе загасит ? Приходиться аж две кнопки лепить. т. е. стилем гасить...
А, как на "Отмена" можно код всунуть ? Не важно какой , ну, чтоб окно закрылось , и чтоб код нужный сработал ?
if (Services.prompt.confirm(null, "ВНИМАНИЕ !", "Открытые вкладки не восстановятся !" + "\nПерезапустить с зачисткой профиля ?", "Да", "Отмена"))

ТИПА,  else Services.prefs.setIntPref а дальше не знаю...

Отредактировано ВВП (04-02-2022 23:20:01)

Отсутствует

 

№1625905-02-2022 04:20:52

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

Re: Custom Buttons

Dumby
Вы когда-то давно написали вот этот код:

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

Выделить код

Код:

/*Initialization Code*/

// Закрытие вкладки = переход на предыдущую посещенную ..........
// https://forum.mozilla-russia.org/viewtopic.php?pid=759770#p759770 .....
((blurTab, dummy, s, lastSelect) => {
    (s = () => lastSelect = Date.now())();
    addEventListener("TabSelect", s, false, gBrowser.tabContainer || 1);
    gBrowser._blurTab = tab => {
        if (!tab.selected) return;
        var tabToSelect = dummy;
        for(var t of gBrowser.tabs) if (
            !t.hidden && !t.closed
            && t.lastAccessed > tabToSelect.lastAccessed
            && t.lastAccessed < lastSelect
        )
            tabToSelect = t;
        if (tabToSelect == dummy) blurTab.call(gBrowser, tab);
        else gBrowser.selectedTab = tabToSelect;
    }
    addDestructor(() => gBrowser._blurTab = blurTab);
})(gBrowser._blurTab, {lastAccessed: 0});

Либо я не совсем понимаю алгоритм работы этого кода, либо он просто устарел.
В общем, он не всегда срабатывает как надо - то переходит на предыдущую посещенную вкладку, то вдруг переходит на какую ему вздумается вкладку.
Какой-то закономерности я не смог заметить.


У меня две просьбы.
1. Подправить, чтобы код срабатывал нормально.
2. Переделать этот код для UCF.


«The Truth Is Out There»

Отсутствует

 

№1626006-02-2022 09:02:33

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

Re: Custom Buttons

ВВП пишет

А, как на "Отмена" можно код всунуть ? Не важно какой , ну, чтоб окно закрылось , и чтоб код нужный сработал ?
ТИПА,  else

Как-то так, наверно. Если код синхронный.

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

Выделить код

Код:

else if (WindowIsClosing()) {

	// Здесь код

	window.close();
}

unter_officer пишет

то переходит на предыдущую посещенную вкладку, то вдруг переходит на какую ему вздумается вкладку

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


Допустим, под «предыдущей посещенной вкладкой»,
подразумевается предыдущая (по времени)
активировавшаяся вкладка, не скрытая и не закрытая.
Следует понимать, что такой может не быть,
тогда переход пойдёт туда, куда перейдёт сам браузер.

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

Выделить код

Код:

(async ucf => {
	await delayedStartupPromise;
	var set = new Set([gBrowser.selectedTab]);
	var bt = gBrowser._blurTab;
	gBrowser._blurTab = tab => {
		if (!tab.selected) return;
		set.delete(tab);
		var res;
		for(var t of set) t.hidden || (res = t);
		res ? gBrowser.selectedTab = res : bt.call(gBrowser, tab);
	}
	var arr = [
		["TabClose", e => set.delete(e.target)],
		["TabSelect", e => set.add(e.target, set.delete(e.target))]
	];
	var id, tc = gBrowser.tabContainer;
	for(var args of arr) tc.addEventListener(...args);
	ucf.unloadlisteners.push(id = Symbol());
	ucf[id] = {destructor() {
		set.clear();
		for(var args of arr) tc.removeEventListener(...args);
	}};
})(ucf_custom_script_win);

Отсутствует

 

№1626106-02-2022 11:01:03

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 96.0

Re: Custom Buttons

Dumby пишет

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

Как я понял, это для custom_script_win.js и куда его там надо разместить? А то у меня не срабатывает.
Спасибо. Отлично работает.

Отредактировано voqabuhe (06-02-2022 11:37:14)

Отсутствует

 

№1626206-02-2022 12:18:46

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 96.0

Re: Custom Buttons

Dumby

Dumby пишет

Как-то так, наверно. Если код синхронный.

Не догоняю, как использовать с этим ?

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

Выделить код

Код:

if(event.button == 1 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){

 if (custombuttons.confirmBox(null, "Сброс Кукситов !", "Да", "Отмена") ) {
  SiteDataManager.removeSiteData();


alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
                      alertsService.showAlertNotification("chrome://global/skin/icons/cpd_OK.png", "Кукситы", "Куки Сброшены" );
                       setTimeout(()=> alertsService.closeAlert(), 1600); 
}
}


Или этим ?
скрытый текст

Выделить код

Код:

/*CODE*/

 if (Services.prompt.confirm(null, "ВНИМАНИЕ !", "Открытые вкладки не восстановятся !" + "\nПерезапустить с зачисткой профиля ?", "Да", "Отмена")){		 

var file = Services.dirsvc.get('ProfD', Ci.nsIFile);
         file.initWithPath(file.path + "\\memory\\del.vbs");
         
file.launch();  


}

Отредактировано ВВП (06-02-2022 12:23:27)

Отсутствует

 

№1626306-02-2022 13:33:41

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

Re: Custom Buttons

ВВП пишет

Не догоняю, как использовать с этим ?

Добавляешь в код после закрывающей скобки «}» от if с confirm'ом.
В приведённых кодах эта скобка:
в первом — предпоследняя, во втором — последняя.

Отсутствует

 

№1626406-02-2022 14:11:56

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 96.0

Re: Custom Buttons

Dumby
Куда ? Ни фига не врубаюсь .

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

Выделить код

Код:

else if (WindowIsClosing()) {
CustomizableUI.setToolbarVisibility("PersonalToolbar", document.querySelector("#PersonalToolbar").closed);
window.close();
}

Разобрался window.close(); - убрать

Отредактировано ВВП (06-02-2022 15:06:19)

Отсутствует

 

№1626506-02-2022 19:47:41

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

Re: Custom Buttons

Dumby пишет

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


Допустим, под «предыдущей посещенной вкладкой»,
подразумевается предыдущая (по времени)
активировавшаяся вкладка, не скрытая и не закрытая.
Следует понимать, что такой может не быть,
тогда переход пойдёт туда, куда перейдёт сам браузер.

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

Выделить код

Код:

(async ucf => {
	await delayedStartupPromise;
	var set = new Set([gBrowser.selectedTab]);
	var bt = gBrowser._blurTab;
	gBrowser._blurTab = tab => {
		if (!tab.selected) return;
		set.delete(tab);
		var res;
		for(var t of set) t.hidden || (res = t);
		res ? gBrowser.selectedTab = res : bt.call(gBrowser, tab);
	}
	var arr = [
		["TabClose", e => set.delete(e.target)],
		["TabSelect", e => set.add(e.target, set.delete(e.target))]
	];
	var id, tc = gBrowser.tabContainer;
	for(var args of arr) tc.addEventListener(...args);
	ucf.unloadlisteners.push(id = Symbol());
	ucf[id] = {destructor() {
		set.clear();
		for(var args of arr) tc.removeEventListener(...args);
	}};
})(ucf_custom_script_win);

Dumby, спасибо большое.


«The Truth Is Out There»

Отсутствует

 

№1626607-02-2022 10:40:50

Азат55555
Участник
 
Группа: Members
Зарегистрирован: 01-11-2018
Сообщений: 28
UA: Yandex 22

Re: Custom Buttons

unter_officer благодарю за ответ по поводу создания rar файла.

Отсутствует

 

№1626708-02-2022 13:11:25

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 93.0

Re: Custom Buttons

Dumby
Как , кодом, стилем , настройкой можно вкл/выкл превью вкладок ?
pnc7u7ob.jpg

Отсутствует

 

№1626808-02-2022 22:21:16

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

Re: Custom Buttons

Dumby
Я для себя переделывал одну кнопочку из СВ в UCF.
В принципе кнопка получилась рабочей, но что-то мне подсказывает, что код получился "кривой".


Посмотрите пожалуйста своим профессиональным взглядом. Хотелось бы услышать ваше мнение.
И ещё. В моём коде мне не нравится использование eval(), но как избавится от eval что-то не соображу. Может подскажете?


Код кнопки

Выделить код

Код:

try {
CustomizableUI.createWidget({
	id: "ucf_exportsBookmarksToHTMLFile",
	label: "Экспорт закладок в HTML-файл",
	image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACY0lEQVQ4y42SXWiPYRjGf0/eAxFFK0lZ8xVrPmcfDQtNESfURBwIWZqIyIEdLZSaTGy1xQGFAyuZKG0MS2yxWigfB3KAkRPb/33e53OPg3f7b0NxHd7P/Vx310fEECKAEEKIspMQQkCIkacxu6Px5yDO/Ax9Xz7z7Xsfr3p7iKSMkVJStnKtAIiUlCglAQiQ3lVJHOLMADLO0D/wExnHZDIx79+9SY8kSUwiJTKRJEpRVFouAJoa6lKGREqUSpBxBiWH6EcLuXO7hT8gBAJExD8QbdvVFprPF1K59wkNJ+ey6cBTLtXksfpIJ+5lrYjcoGfS5Ckk3jB7Xj6JfcCK8gqcaUsZjHX8+PYVZTWve1/QbyRCCMH8wyFd8J6cadOJjaRg8XKkuUeAIN6eS53TTvPp4wekcXR3daJHuShARLEz5ObNZcDeorhkFZibY1V0t+4UzY194dT2cTQ3nqXpUC5UgwCRzXPH7vZgfcB5hw8eax1m0GO8QzuD8gZpHNoopFXE3pJ014wQWD/IlcYyxk+YCEBFZQuXzywid9Y8AOZUXORabQElZavTEJbW0NRQF6qqj6ZJOO/ZuqcD4z0mGLR1bDn4jMQ9QhlHYh3rjj1Hmw6M1aANMGWkkto5rl4oYmrONACWbLzC9TMLyS9YBsDk0tMMdJ0Qw84BVFWP6rT2hs37HqOcJfEKbRwbqp+SGWxHW01sVdbVAAF+M1E7w436EmbMzANgxpp6Ws8WU7i8LNW84PjYhoVAECJkCWKjWb/vIdIkSGeRXlO6/z5O3wGnwJq/VjlL0HN3l+A/MfxxGL8A8lFUSnL6gNAAAAAASUVORK5CYII=",
	// defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn.setAttribute("type", "menu");
		btn.setAttribute("image", this.image);
		btn.onmouseover =()=> {
			var path = getPathToBookmarksFolder();
			btn.setAttribute("tooltiptext", "ЛКМ: Открыть меню кнопки\n\nПапка для экспорта закладок:\n" + path);
		}
		var doc = btn.ownerDocument;
		var win = doc.defaultView;
		var popup = doc.createXULElement("menupopup");
		var alertsImage = this.image;

		var array = [
			["ucf_ExportsBookmarksToHTMLFile", "Экспорт без запроса на сохранение", "exportsBookmarksToHTMLFile();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACeElEQVQ4y5WSXWjOURzHP0/+FyJqUqOVNS8rmrd5GRNNJtNWoibiYqGN5qVEbpYLoZQVYWVxMYVi0Z55aTGvCQslXOBCLjByw57/eT/PcfGfzUOS783p/M63z+93vudE9CsCCCGEaKASQgg5RzneX/VnIc58C70fP/D5Sy8vnj8jEiJGCIGSInEoIVBKIJUcZCgZhzjTh4gzfO/7hohjMpmYN69fJQYpY6QQCCmQSiH7KQOEpCARcQYlBEoqtPylRWdHO39TxD8Uram7EVqPzqJ20z1O7J9EzdYHnGoqomLnfdzTfanIZT0jRuYhvWFC8RSk7WbBokqcuZEQjHV8/fwJZTUvnz/huxF0d12BnzkY7xmdP5bYCEqmz0aYayxZVgOuMzFop3n/7i3COHoe30crwdWOC6BVf/jOUFg0iT57mbllC8FcpHrFamhIrh71pNenWlt6w4G1Q2htaebkjsJk3VtGQ+PFJId1G24G6wPOO3zwWOswWY/xDu0MyhuEcWijEFYRe4vsaUoNBGl9lraWcoYOGw5AZW07pw9No3B8MQATK49zdl8JZeUVAKRmNnHyxOHQ0LgrFQE471m98TbGe0wwaOtYtf0h0t1BGYe0jqW7H6HNbYzVoA2QN/iU2jnOHJvDqNH5AMyobuPcoalMKSkFYOS8g6QPlFKxpCqZYOLm3L+gvWFl/V2Us0iv0MaxvPEBmexNtNXEVrF4Wzf4LnAalP0N4Aznj5RRMK4IgILFR0g3z2XW7PKk4+Q9XD88n6rqVcl+zNpcQGw0VfW3EEYinEV4zbwtXTjdCU6BNSzffA2yafASvMsFPLtal+I/1dB4CYAfXqtvWdjPXR4AAAAASUVORK5CYII="],
			["separator"],
			["ucf_SetPathAndExportsBookmarksToHTMLFile", "Экспорт закладок в HTML-файл", "setPathAndExportsBookmarksToHTMLFile();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACY0lEQVQ4y42SXWiPYRjGf0/eAxFFK0lZ8xVrPmcfDQtNESfURBwIWZqIyIEdLZSaTGy1xQGFAyuZKG0MS2yxWigfB3KAkRPb/33e53OPg3f7b0NxHd7P/Vx310fEECKAEEKIspMQQkCIkacxu6Px5yDO/Ax9Xz7z7Xsfr3p7iKSMkVJStnKtAIiUlCglAQiQ3lVJHOLMADLO0D/wExnHZDIx79+9SY8kSUwiJTKRJEpRVFouAJoa6lKGREqUSpBxBiWH6EcLuXO7hT8gBAJExD8QbdvVFprPF1K59wkNJ+ey6cBTLtXksfpIJ+5lrYjcoGfS5Ckk3jB7Xj6JfcCK8gqcaUsZjHX8+PYVZTWve1/QbyRCCMH8wyFd8J6cadOJjaRg8XKkuUeAIN6eS53TTvPp4wekcXR3daJHuShARLEz5ObNZcDeorhkFZibY1V0t+4UzY194dT2cTQ3nqXpUC5UgwCRzXPH7vZgfcB5hw8eax1m0GO8QzuD8gZpHNoopFXE3pJ014wQWD/IlcYyxk+YCEBFZQuXzywid9Y8AOZUXORabQElZavTEJbW0NRQF6qqj6ZJOO/ZuqcD4z0mGLR1bDn4jMQ9QhlHYh3rjj1Hmw6M1aANMGWkkto5rl4oYmrONACWbLzC9TMLyS9YBsDk0tMMdJ0Qw84BVFWP6rT2hs37HqOcJfEKbRwbqp+SGWxHW01sVdbVAAF+M1E7w436EmbMzANgxpp6Ws8WU7i8LNW84PjYhoVAECJkCWKjWb/vIdIkSGeRXlO6/z5O3wGnwJq/VjlL0HN3l+A/MfxxGL8A8lFUSnL6gNAAAAAASUVORK5CYII="],
			["separator"],
			["ucf_AllBookmarks", "Показать все закладки", "win.PlacesCommandHook.showPlacesOrganizer('AllBookmarks');", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACsElEQVQ4y42RW2gUdxSHv40TDSlSNYqFhorxEqFiiIovtkghXqCPIhQUbw+aB6FF0JcIRhC8PBmISKEPXmiK4oMVg9ZKJGrUKCQmRDSxcc3GNeuSnd2ZnZmdmf/NB6uLqLTn9Xzn9+PwWeev3eVs14CxAM4easZqaWkxl15WYy1etxX/5O9Y56/dZbBQacayeXp6erAAbvYO4LgFjDFYV65eNx0P07zI2EzkXKye7i5mLfoR18rxarIPa3wsSdvB77jYPcjQoz6s0ZGnNO1tJ/A9gH9bbnea5LSFOF5I3gtw/JAgjN8CAE4hz5yVC6nyQ2YKQSbn8M/IkzLw/NkwU+ZOcHrfJgB++LmNd2MBjI89x3yTZnXzcaQUKCXKwMDjEXPjr05eZyYYnbqIvFei4JUoBiGHt65NWBc6ziCE+Gj524FdZFJJLK0URmvm1y9lhh/iFEs4fqlc4RYdtNIQRrT+tJrdbZcplYIyULBttFK0b1kDwLn9G9l2tKMM2JNZjIEjf9ykvrYG2/VJpl+Xgbxto7Xh1qNhrt8PEXGMlLIM+EUXY0B7HlIIlJQoJdnWeopjOze8dfGF8cy9O92s27yHZfO/4v/M8u2tHN2xPmEBPLh3h4qKBFfPnODvFZuIpSISijCWlGJJFEsiIYgiwdOhfk4fbMYYU1blex6JBEipqIwksRSEsWJJ7Sx6h9OEUUwcC0Ih3x9+4Nop5AFQSlLlB0RKsr5xAU2NdSz5eia/dvbiBwEiFh+9YgHkclkSVGCModIt8mV1FU2NdQCsqq/lz9uDpF5l0Up+OiBv2yQArTWVBZeGebP5pf0Sw6kMNdOr+b6hjlv9AVrrTwc4dg4AY6Ci4HCh6yFKarRWuK7DaCqN1gptPhPw7dIG+vt6wYA/mf1PhZlUMnFs5wYA3gDhGJMMzu9r5wAAAABJRU5ErkJggg=="],
			["separator"],
			["ucf_TotalNumberOfBookmarks", "Общее количество закладок", "totalNumberOfBookmarks();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAbFBMVEX///+coqK4vLzc5PG4vLxKktduu5N9brKcoqK4vLzR2d7b5PHc5PHc5vLd5vLe5/Te6PTg6vbg6/Xg6/bh6vXh6vbi7fjk7/rk8Prl7/rl8Prl8Pvn8vzn8/zn8/3o9P7p9P7p9f7q9v////8h14CvAAAABXRSTlMAf39/mY/u188AAABxSURBVBgZBcCBhgJAEADQN2uiO+dIovT/X5YUgkCy7UziBwB4Z5/BNYGILQDwzjgBcHsxuFwAIBV9hzhAWsde+9AUpHrYDT3agjSpJ8QfpPnfcztK9ILUT78+ohSkYn0gEtLaqAEWDACAnBsAJgAAfAEU/Sr37SwFHgAAAABJRU5ErkJggg=="],
		];
		array.forEach(m => {
			if (m[0] == "separator") { popup.append(doc.createXULElement("menuseparator")); return; };
			var menuitem = popup.appendChild(doc.createXULElement("menuitem"));
			menuitem.setAttribute("id", m[0]);
			menuitem.setAttribute("label", m[1]);
			menuitem.className = "menuitem-iconic";
			menuitem.setAttribute("image", m[3]);
			menuitem.addEventListener("command", ()=> eval(m[2]));
		});
		btn.prepend(popup);

		function exportsBookmarksToHTMLFile() {
			var filePath = getPathToBookmarksFolder();

			YMD = new Date().toLocaleString("af").slice(0, 10).replace(/-/g, "_");
			HMS = new Date().toLocaleTimeString('de', '%H_%M_%S').replace(/:/g, "_");
			myDate = (YMD + '\u00F7' + HMS);

			var path = filePath + "\\bookmarks_(" + myDate + ").html";

			Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
			BookmarkHTMLUtils.exportToFile(path).then(null, Cu.reportError);

			var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
			alertsService.showAlertNotification(alertsImage, "Экспорт закладок в HTML-файл", "Файл сохранен в " + filePath);

			// стиль всплывающей подсказки
			var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
			var uri = win.makeURI('data:text/css,'+ encodeURIComponent('\
				#alertBox { border: 1px solid threedshadow !important; border-radius: 3px !important; background-color: -moz-Dialog !important; width: 360px !important; height: 80px !important; }\
				#alertImage { height: 20px !important; width: 20px !important; object-fit: contain !important; margin: 3px 0 20px 20px !important; }\
				#alertNotification:hover { cursor: pointer !important; }\
				.alertTitle { color: #003366 !important; text-align: center !important; font-family: Segoe UI !important; font-size: 120% !important; }\
				#alertTextLabel { color: #2B552B !important; text-align: center !important; text-decoration: underline !important; font-weight: bold !important; }\
			'));
			sss.loadAndRegisterSheet(uri, 0);

			// удалить стиль и подсказку через указанное время
			setTimeout(()=> { sss.unregisterSheet(uri, 0); alertsService.closeAlert(); }, 3500);
		}

		function openBookmarksFolder() {
			var folder = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			folder.initWithPath(getPathToBookmarksFolder());
			folder.launch();
		}

		function setPathAndExportsBookmarksToHTMLFile() {
			var fp = win.makeFilePicker();
			fp.init(win, "Укажите папку для экспорта закладок!", fp.modeGetFolder);
			fp.open(re=> {
				if ( re != fp.returnOK ) return;
				Services.prefs.setCharPref("CB.exportsBookmarksToHTMLFile.path", convertFromUnicode("UTF-8", fp.file.path));
				exportsBookmarksToHTMLFile();
			})
		}

		function getPathToBookmarksFolder() {
			try { return Services.prefs.getComplexValue("CB.exportsBookmarksToHTMLFile.path", Ci.nsIPrefLocalizedString).data }
			catch(e) { return "C:" };
		}

		function convertFromUnicode(charset, str) {
			var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
			converter.charset = charset;
			str = converter.ConvertFromUnicode(str);
			return str + converter.Finish();
		}

		function totalNumberOfBookmarks() {
			var statement = PlacesUtils.history.DBConnection
				.createStatement("SELECT count(fk) FROM moz_bookmarks");
			statement.executeStep();
			var bkmcnt = statement.getInt32(0);
			statement.finalize();
			var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
			prompts.alert(null, "Общее количество закладок", "Общее количество закладок = " + bkmcnt);
		}

	},
});
} catch(ex) { Cu.reportError(ex); }


«The Truth Is Out There»

Отсутствует

 

№1626909-02-2022 11:21:21

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

Re: Custom Buttons

ВВП пишет

Как , кодом, стилем , настройкой можно вкл/выкл превью вкладок ?

Есть код, добавляет в контекстное меню CB-кнопок
чекбокс-пункт «[✔] Включить инициализацию».
Если «превью вкладок» оформлено отдельной кнопкой, то можно использовать.


unter_officer пишет

не нравится использование eval()

А что, вроде бочку катят только на оконный eval(),
а на от сандбокса и глобального объекта JSM'ок нет.
Но можно и без eval. И ["separator"]'ы можно убрать,
если таков замысел, что они всегда присутствуют между menuitem'ами.

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

Выделить код

Код:

/*
		var array = [
			["ucf_ExportsBookmarksToHTMLFile", "Экспорт без запроса на сохранение", "exportsBookmarksToHTMLFile();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACeElEQVQ4y5WSXWjOURzHP0/+FyJqUqOVNS8rmrd5GRNNJtNWoibiYqGN5qVEbpYLoZQVYWVxMYVi0Z55aTGvCQslXOBCLjByw57/eT/PcfGfzUOS783p/M63z+93vudE9CsCCCGEaKASQgg5RzneX/VnIc58C70fP/D5Sy8vnj8jEiJGCIGSInEoIVBKIJUcZCgZhzjTh4gzfO/7hohjMpmYN69fJQYpY6QQCCmQSiH7KQOEpCARcQYlBEoqtPylRWdHO39TxD8Uram7EVqPzqJ20z1O7J9EzdYHnGoqomLnfdzTfanIZT0jRuYhvWFC8RSk7WbBokqcuZEQjHV8/fwJZTUvnz/huxF0d12BnzkY7xmdP5bYCEqmz0aYayxZVgOuMzFop3n/7i3COHoe30crwdWOC6BVf/jOUFg0iT57mbllC8FcpHrFamhIrh71pNenWlt6w4G1Q2htaebkjsJk3VtGQ+PFJId1G24G6wPOO3zwWOswWY/xDu0MyhuEcWijEFYRe4vsaUoNBGl9lraWcoYOGw5AZW07pw9No3B8MQATK49zdl8JZeUVAKRmNnHyxOHQ0LgrFQE471m98TbGe0wwaOtYtf0h0t1BGYe0jqW7H6HNbYzVoA2QN/iU2jnOHJvDqNH5AMyobuPcoalMKSkFYOS8g6QPlFKxpCqZYOLm3L+gvWFl/V2Us0iv0MaxvPEBmexNtNXEVrF4Wzf4LnAalP0N4Aznj5RRMK4IgILFR0g3z2XW7PKk4+Q9XD88n6rqVcl+zNpcQGw0VfW3EEYinEV4zbwtXTjdCU6BNSzffA2yafASvMsFPLtal+I/1dB4CYAfXqtvWdjPXR4AAAAASUVORK5CYII="],
			["separator"],
			["ucf_SetPathAndExportsBookmarksToHTMLFile", "Экспорт закладок в HTML-файл", "setPathAndExportsBookmarksToHTMLFile();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACY0lEQVQ4y42SXWiPYRjGf0/eAxFFK0lZ8xVrPmcfDQtNESfURBwIWZqIyIEdLZSaTGy1xQGFAyuZKG0MS2yxWigfB3KAkRPb/33e53OPg3f7b0NxHd7P/Vx310fEECKAEEKIspMQQkCIkacxu6Px5yDO/Ax9Xz7z7Xsfr3p7iKSMkVJStnKtAIiUlCglAQiQ3lVJHOLMADLO0D/wExnHZDIx79+9SY8kSUwiJTKRJEpRVFouAJoa6lKGREqUSpBxBiWH6EcLuXO7hT8gBAJExD8QbdvVFprPF1K59wkNJ+ey6cBTLtXksfpIJ+5lrYjcoGfS5Ckk3jB7Xj6JfcCK8gqcaUsZjHX8+PYVZTWve1/QbyRCCMH8wyFd8J6cadOJjaRg8XKkuUeAIN6eS53TTvPp4wekcXR3daJHuShARLEz5ObNZcDeorhkFZibY1V0t+4UzY194dT2cTQ3nqXpUC5UgwCRzXPH7vZgfcB5hw8eax1m0GO8QzuD8gZpHNoopFXE3pJ014wQWD/IlcYyxk+YCEBFZQuXzywid9Y8AOZUXORabQElZavTEJbW0NRQF6qqj6ZJOO/ZuqcD4z0mGLR1bDn4jMQ9QhlHYh3rjj1Hmw6M1aANMGWkkto5rl4oYmrONACWbLzC9TMLyS9YBsDk0tMMdJ0Qw84BVFWP6rT2hs37HqOcJfEKbRwbqp+SGWxHW01sVdbVAAF+M1E7w436EmbMzANgxpp6Ws8WU7i8LNW84PjYhoVAECJkCWKjWb/vIdIkSGeRXlO6/z5O3wGnwJq/VjlL0HN3l+A/MfxxGL8A8lFUSnL6gNAAAAAASUVORK5CYII="],
			["separator"],
			["ucf_AllBookmarks", "Показать все закладки", "win.PlacesCommandHook.showPlacesOrganizer('AllBookmarks');", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACsElEQVQ4y42RW2gUdxSHv40TDSlSNYqFhorxEqFiiIovtkghXqCPIhQUbw+aB6FF0JcIRhC8PBmISKEPXmiK4oMVg9ZKJGrUKCQmRDSxcc3GNeuSnd2ZnZmdmf/NB6uLqLTn9Xzn9+PwWeev3eVs14CxAM4easZqaWkxl15WYy1etxX/5O9Y56/dZbBQacayeXp6erAAbvYO4LgFjDFYV65eNx0P07zI2EzkXKye7i5mLfoR18rxarIPa3wsSdvB77jYPcjQoz6s0ZGnNO1tJ/A9gH9bbnea5LSFOF5I3gtw/JAgjN8CAE4hz5yVC6nyQ2YKQSbn8M/IkzLw/NkwU+ZOcHrfJgB++LmNd2MBjI89x3yTZnXzcaQUKCXKwMDjEXPjr05eZyYYnbqIvFei4JUoBiGHt65NWBc6ziCE+Gj524FdZFJJLK0URmvm1y9lhh/iFEs4fqlc4RYdtNIQRrT+tJrdbZcplYIyULBttFK0b1kDwLn9G9l2tKMM2JNZjIEjf9ykvrYG2/VJpl+Xgbxto7Xh1qNhrt8PEXGMlLIM+EUXY0B7HlIIlJQoJdnWeopjOze8dfGF8cy9O92s27yHZfO/4v/M8u2tHN2xPmEBPLh3h4qKBFfPnODvFZuIpSISijCWlGJJFEsiIYgiwdOhfk4fbMYYU1blex6JBEipqIwksRSEsWJJ7Sx6h9OEUUwcC0Ih3x9+4Nop5AFQSlLlB0RKsr5xAU2NdSz5eia/dvbiBwEiFh+9YgHkclkSVGCModIt8mV1FU2NdQCsqq/lz9uDpF5l0Up+OiBv2yQArTWVBZeGebP5pf0Sw6kMNdOr+b6hjlv9AVrrTwc4dg4AY6Ci4HCh6yFKarRWuK7DaCqN1gptPhPw7dIG+vt6wYA/mf1PhZlUMnFs5wYA3gDhGJMMzu9r5wAAAABJRU5ErkJggg=="],
			["separator"],
			["ucf_TotalNumberOfBookmarks", "Общее количество закладок", "totalNumberOfBookmarks();", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAbFBMVEX///+coqK4vLzc5PG4vLxKktduu5N9brKcoqK4vLzR2d7b5PHc5PHc5vLd5vLe5/Te6PTg6vbg6/Xg6/bh6vXh6vbi7fjk7/rk8Prl7/rl8Prl8Pvn8vzn8/zn8/3o9P7p9P7p9f7q9v////8h14CvAAAABXRSTlMAf39/mY/u188AAABxSURBVBgZBcCBhgJAEADQN2uiO+dIovT/X5YUgkCy7UziBwB4Z5/BNYGILQDwzjgBcHsxuFwAIBV9hzhAWsde+9AUpHrYDT3agjSpJ8QfpPnfcztK9ILUT78+ohSkYn0gEtLaqAEWDACAnBsAJgAAfAEU/Sr37SwFHgAAAABJRU5ErkJggg=="],
		];
		array.forEach(m => {
			if (m[0] == "separator") { popup.append(doc.createXULElement("menuseparator")); return; };
			var menuitem = popup.appendChild(doc.createXULElement("menuitem"));
			menuitem.setAttribute("id", m[0]);
			menuitem.setAttribute("label", m[1]);
			menuitem.className = "menuitem-iconic";
			menuitem.setAttribute("image", m[3]);
			menuitem.addEventListener("command", ()=> eval(m[2]));
		});
*/
		[
			["ucf_ExportsBookmarksToHTMLFile", "Экспорт без запроса на сохранение", exportsBookmarksToHTMLFile, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACeElEQVQ4y5WSXWjOURzHP0/+FyJqUqOVNS8rmrd5GRNNJtNWoibiYqGN5qVEbpYLoZQVYWVxMYVi0Z55aTGvCQslXOBCLjByw57/eT/PcfGfzUOS783p/M63z+93vudE9CsCCCGEaKASQgg5RzneX/VnIc58C70fP/D5Sy8vnj8jEiJGCIGSInEoIVBKIJUcZCgZhzjTh4gzfO/7hohjMpmYN69fJQYpY6QQCCmQSiH7KQOEpCARcQYlBEoqtPylRWdHO39TxD8Uram7EVqPzqJ20z1O7J9EzdYHnGoqomLnfdzTfanIZT0jRuYhvWFC8RSk7WbBokqcuZEQjHV8/fwJZTUvnz/huxF0d12BnzkY7xmdP5bYCEqmz0aYayxZVgOuMzFop3n/7i3COHoe30crwdWOC6BVf/jOUFg0iT57mbllC8FcpHrFamhIrh71pNenWlt6w4G1Q2htaebkjsJk3VtGQ+PFJId1G24G6wPOO3zwWOswWY/xDu0MyhuEcWijEFYRe4vsaUoNBGl9lraWcoYOGw5AZW07pw9No3B8MQATK49zdl8JZeUVAKRmNnHyxOHQ0LgrFQE471m98TbGe0wwaOtYtf0h0t1BGYe0jqW7H6HNbYzVoA2QN/iU2jnOHJvDqNH5AMyobuPcoalMKSkFYOS8g6QPlFKxpCqZYOLm3L+gvWFl/V2Us0iv0MaxvPEBmexNtNXEVrF4Wzf4LnAalP0N4Aznj5RRMK4IgILFR0g3z2XW7PKk4+Q9XD88n6rqVcl+zNpcQGw0VfW3EEYinEV4zbwtXTjdCU6BNSzffA2yafASvMsFPLtal+I/1dB4CYAfXqtvWdjPXR4AAAAASUVORK5CYII="],
			["ucf_SetPathAndExportsBookmarksToHTMLFile", "Экспорт закладок в HTML-файл", setPathAndExportsBookmarksToHTMLFile, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACY0lEQVQ4y42SXWiPYRjGf0/eAxFFK0lZ8xVrPmcfDQtNESfURBwIWZqIyIEdLZSaTGy1xQGFAyuZKG0MS2yxWigfB3KAkRPb/33e53OPg3f7b0NxHd7P/Vx310fEECKAEEKIspMQQkCIkacxu6Px5yDO/Ax9Xz7z7Xsfr3p7iKSMkVJStnKtAIiUlCglAQiQ3lVJHOLMADLO0D/wExnHZDIx79+9SY8kSUwiJTKRJEpRVFouAJoa6lKGREqUSpBxBiWH6EcLuXO7hT8gBAJExD8QbdvVFprPF1K59wkNJ+ey6cBTLtXksfpIJ+5lrYjcoGfS5Ckk3jB7Xj6JfcCK8gqcaUsZjHX8+PYVZTWve1/QbyRCCMH8wyFd8J6cadOJjaRg8XKkuUeAIN6eS53TTvPp4wekcXR3daJHuShARLEz5ObNZcDeorhkFZibY1V0t+4UzY194dT2cTQ3nqXpUC5UgwCRzXPH7vZgfcB5hw8eax1m0GO8QzuD8gZpHNoopFXE3pJ014wQWD/IlcYyxk+YCEBFZQuXzywid9Y8AOZUXORabQElZavTEJbW0NRQF6qqj6ZJOO/ZuqcD4z0mGLR1bDn4jMQ9QhlHYh3rjj1Hmw6M1aANMGWkkto5rl4oYmrONACWbLzC9TMLyS9YBsDk0tMMdJ0Qw84BVFWP6rT2hs37HqOcJfEKbRwbqp+SGWxHW01sVdbVAAF+M1E7w436EmbMzANgxpp6Ws8WU7i8LNW84PjYhoVAECJkCWKjWb/vIdIkSGeRXlO6/z5O3wGnwJq/VjlL0HN3l+A/MfxxGL8A8lFUSnL6gNAAAAAASUVORK5CYII="],
			["ucf_AllBookmarks", "Показать все закладки", win.PlacesCommandHook.showPlacesOrganizer.bind(null, "AllBookmarks"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACsElEQVQ4y42RW2gUdxSHv40TDSlSNYqFhorxEqFiiIovtkghXqCPIhQUbw+aB6FF0JcIRhC8PBmISKEPXmiK4oMVg9ZKJGrUKCQmRDSxcc3GNeuSnd2ZnZmdmf/NB6uLqLTn9Xzn9+PwWeev3eVs14CxAM4easZqaWkxl15WYy1etxX/5O9Y56/dZbBQacayeXp6erAAbvYO4LgFjDFYV65eNx0P07zI2EzkXKye7i5mLfoR18rxarIPa3wsSdvB77jYPcjQoz6s0ZGnNO1tJ/A9gH9bbnea5LSFOF5I3gtw/JAgjN8CAE4hz5yVC6nyQ2YKQSbn8M/IkzLw/NkwU+ZOcHrfJgB++LmNd2MBjI89x3yTZnXzcaQUKCXKwMDjEXPjr05eZyYYnbqIvFei4JUoBiGHt65NWBc6ziCE+Gj524FdZFJJLK0URmvm1y9lhh/iFEs4fqlc4RYdtNIQRrT+tJrdbZcplYIyULBttFK0b1kDwLn9G9l2tKMM2JNZjIEjf9ykvrYG2/VJpl+Xgbxto7Xh1qNhrt8PEXGMlLIM+EUXY0B7HlIIlJQoJdnWeopjOze8dfGF8cy9O92s27yHZfO/4v/M8u2tHN2xPmEBPLh3h4qKBFfPnODvFZuIpSISijCWlGJJFEsiIYgiwdOhfk4fbMYYU1blex6JBEipqIwksRSEsWJJ7Sx6h9OEUUwcC0Ih3x9+4Nop5AFQSlLlB0RKsr5xAU2NdSz5eia/dvbiBwEiFh+9YgHkclkSVGCModIt8mV1FU2NdQCsqq/lz9uDpF5l0Up+OiBv2yQArTWVBZeGebP5pf0Sw6kMNdOr+b6hjlv9AVrrTwc4dg4AY6Ci4HCh6yFKarRWuK7DaCqN1gptPhPw7dIG+vt6wYA/mf1PhZlUMnFs5wYA3gDhGJMMzu9r5wAAAABJRU5ErkJggg=="],
			["ucf_TotalNumberOfBookmarks", "Общее количество закладок", totalNumberOfBookmarks, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAbFBMVEX///+coqK4vLzc5PG4vLxKktduu5N9brKcoqK4vLzR2d7b5PHc5PHc5vLd5vLe5/Te6PTg6vbg6/Xg6/bh6vXh6vbi7fjk7/rk8Prl7/rl8Prl8Pvn8vzn8/zn8/3o9P7p9P7p9f7q9v////8h14CvAAAABXRSTlMAf39/mY/u188AAABxSURBVBgZBcCBhgJAEADQN2uiO+dIovT/X5YUgkCy7UziBwB4Z5/BNYGILQDwzjgBcHsxuFwAIBV9hzhAWsde+9AUpHrYDT3agjSpJ8QfpPnfcztK9ILUT78+ohSkYn0gEtLaqAEWDACAnBsAJgAAfAEU/Sr37SwFHgAAAABJRU5ErkJggg=="],
		]
			.forEach((m, ind) => {
				ind && popup.append(doc.createXULElement("menuseparator"));

				var menuitem = popup.appendChild(doc.createXULElement("menuitem"));
				menuitem.id = m[0];
				menuitem.setAttribute("label", m[1]);
				menuitem.className = "menuitem-iconic";
				menuitem.setAttribute("image", m[3]);
				menuitem.addEventListener("command", m[2]);
			});


И, вот здесь, без var (let, const)
YMD, HMS, и myDate, при вызове функции,
уйдут как глобальные переменные, как свойства самого сандбокса,
что не столько даже плохо, сколько не нужно.
Можно так
скрытый текст

Выделить код

Код:

/*
			YMD = new Date().toLocaleString("af").slice(0, 10).replace(/-/g, "_");
			HMS = new Date().toLocaleTimeString('de', '%H_%M_%S').replace(/:/g, "_");
			myDate = (YMD + '\u00F7' + HMS);
*/
			var myDate = new Date().toLocaleString("mn")
				.replace(/(?:(\.|:))/g, "_").replace(" ", "\u00F7");


И, чуть далее, симилярно этому.
Cu.import() — стоит оставить лишь для особых случаев,
ChromeUtils в сандбокс проброшен, вроде нет причин не использовать его.


Но это не строго, теоретически, может случиться ошибка,
тогда алерт будет вроде как некстати, то есть, лучше бы сделать
exportsBookmarksToHTMLFile как async function,
а await ….exportToFile() завернуть в try {...} catch(ex) {Cu.reportError(ex); return;}
или алерт и дальнейшее записать как функцию вместо null, где ….then(null, …
но с чего бы, мы же, наверно, разумные люди, и назначать папку
для экспорта такую, куда браузер не сможет записать, не будем.

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

Выделить код

Код:

/*
			Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
			BookmarkHTMLUtils.exportToFile(path).then(null, Cu.reportError);
*/
			ChromeUtils.import("resource://gre/modules/BookmarkHTMLUtils.jsm")
				.BookmarkHTMLUtils.exportToFile(path).catch(Cu.reportError);


Ещё, setCharPref, getComplexValue, convertFromUnicode,
как-то это всё слишком старомодно.
Полагаю, Services.prefs.{g, s}etStringPref() должно работать.
скрытый текст

Выделить код

Код:

/*
		function setPathAndExportsBookmarksToHTMLFile() {
			var fp = win.makeFilePicker();
			fp.init(win, "Укажите папку для экспорта закладок!", fp.modeGetFolder);
			fp.open(re=> {
				if ( re != fp.returnOK ) return;
				Services.prefs.setCharPref("CB.exportsBookmarksToHTMLFile.path", convertFromUnicode("UTF-8", fp.file.path));
				exportsBookmarksToHTMLFile();
			})
		}

		function getPathToBookmarksFolder() {
			try { return Services.prefs.getComplexValue("CB.exportsBookmarksToHTMLFile.path", Ci.nsIPrefLocalizedString).data }
			catch(e) { return "C:" };
		}

		function convertFromUnicode(charset, str) {
			var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
			converter.charset = charset;
			str = converter.ConvertFromUnicode(str);
			return str + converter.Finish();
		}
*/
		var pref = "CB.exportsBookmarksToHTMLFile.path"; // CB -> UCF ?

		function setPathAndExportsBookmarksToHTMLFile() {
			var fp = win.makeFilePicker();
			fp.init(win, "Укажите папку для экспорта закладок!", fp.modeGetFolder);
			fp.open(re => {
				if (re != fp.returnOK) return;
				Services.prefs.setStringPref(pref, fp.file.path);
				exportsBookmarksToHTMLFile();
			});
		}

		function getPathToBookmarksFolder() {
			return Services.prefs.getStringPref(pref, "C:");
		}


И, function openBookmarksFolder()
я не вижу что (пока?) где-то используется.
Да там до бесконечности можно копаться,
так, по верхушкам прошёлся, надеюсь, ничего не испортил.

Отсутствует

 

№1627009-02-2022 13:21:49

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 97.0

Re: Custom Buttons

Dumby

Dumby пишет

Если «превью вкладок» оформлено отдельной кнопкой

В том то и дело, что скриптом... Стилем можно, но не то ...Tab:hover {display: initial !important; }
Ладно итак сойдет.

Отредактировано ВВП (10-02-2022 11:42:15)

Отсутствует

 

№1627109-02-2022 15:38:33

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

Re: Custom Buttons

Dumby пишет

так, по верхушкам прошёлся, надеюсь, ничего не испортил.

Dumby, большое вам спасибо за такой подробный разбор кода.
Переделал свой код по вашим советам, всё прекрасно работает.



Dumby, если не трудно, переделайте пожалуйста под UCF кнопку Reload user{Chrome, Content}.css

Отредактировано unter_officer (09-02-2022 15:47:24)


«The Truth Is Out There»

Отсутствует

 

№1627210-02-2022 00:26:43

Ki_rrrilll
Участник
 
Группа: Members
Зарегистрирован: 22-11-2013
Сообщений: 127
UA: Firefox 85.0

Re: Custom Buttons

Подскажите, есть ли код Гуглоперевод в алерте? Просто перевод выделенного текста в алерте и больше ничего.


Раньше что то подобное было, но я его найти не могу. Или может можно сделать?

Отсутствует

 

№1627310-02-2022 04:37:49

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

Re: Custom Buttons

Dumby - Приветствую! Вопрос по скрипту Жесты мыши - UCF drag and go (я его немного дополнил)


При перетаскивании ссылок не появляется текст в панели статуса, а должно быть так (как для текста):
Жест мыши: U Поиск текста поисковиком по умолчанию в новой активной странице
1) Для ссылок это не работает, поэтому не понятно, что будет делать данный жест для ссылки.
2) я дополнил код, но при показе текст в строке статуса быстро исчезает - пример в строке 6 скрипта.

Выделить код

Код:

(async win => ({ // UCF drag and go жесты мыши https://forum.mozilla-russia.org/viewtopic.php?pid=797234#p797234
	link: {
		R: {
			name: "Копировать ссылку в буфер обмена", cmd() {
				this.gClipboard.write(this.val);
				this.showInStatus('в буфере: ' + this.val); // сообщение быстро исчезает
			}
		},
		U: {
			name: "Открыть ссылку в новой активной странице", cmd() {
				win.openUILinkIn(this.val, "tab", this.opts);
			}
		},
		D: {
			name: "Открыть ссылку в новой фоновой странице", cmd() {
				win.openUILinkIn(this.val, "tabshifted", this.opts);
			}
		}
	},
	text: {
		U: {
			name: "Поиск текста поисковиком по умолчанию в новой активной странице", cmd() {
				this.search("tab");
			}
		},
		D: {
			name: "Поиск текста поисковиком по умолчанию в новой фоновой странице", cmd() {
				this.search("tabshifted");
			}
		}
	},
	showInStatus(txt) {
		win.StatusPanel._labelElement.value = txt;
		win.StatusPanel.panel.removeAttribute("inactive");
	},
	gClipboard: {
		write(str, ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)) {
			(this.write = str => ch.copyStringToClipboard(str, Services.clipboard.kGlobalClipboard))(str);
		}
	},
	search(where) {
		var engine = Services.search[`default${this.opts.private ? "Private" : ""}Engine`];
		var submission = engine.getSubmission(this.val, null, "");
		win.openUILinkIn(submission.uri.spec, where, {postData: submission.postData, ...this.opts});
	},
	opts: {
		//relatedToCurrent: true,
		triggeringPrincipal: Cu.getObjectPrincipal(this),
		get userContextId() {
			return parseInt(win.gBrowser.selectedBrowser.getAttribute("usercontextid"));
		},
		get private() {
			return win.PrivateBrowsingUtils.isWindowPrivate(win);
		}
	},
	dragstart(e) {
		win = e.view.windowRoot.ownerGlobal;
		//if (!win.gBrowser.currentURI.spec.startsWith("http")) return;
		if (!e.dataTransfer.mozItemCount || !win.gBrowser.selectedBrowser.matches(":hover"))
			return;

		var dt = e.dataTransfer;
		this.type = this.link;
		this.dir = this.val = "";

		var url = dt.getData("text/x-moz-url-data");
		if (url) this.val = url;
		else {
			var txt = dt.getData("text/plain");
			if (txt) {
				this.val = txt;
				if (!this.textLinkRe.test(txt)) this.type = this.text;
			}
			else return;
		}
		this.x = e.screenX; this.y = e.screenY;
		this.drag(true);
	},
	drag(init) {
		var meth = `${init ? "add" : "remove"}EventListener`;
		for(var type of this.events) win[meth](type, this, true);
		init || win.StatusPanel.panel.setAttribute("inactive", true);
	},
	events: ["dragover", "drop", "dragend"],
	dragover(e) {
		var {x, y} = this, cx = e.screenX, cy = e.screenY;
		var dx = cx - x, ax = Math.abs(dx), dy = cy - y, ay = Math.abs(dy);
		if (ax < 10 && ay < 10) return;

		this.x = cx; this.y = cy;
		var dir = ax > ay ? dx > 0 ? "R" : "L" : dy > 0 ? "D" : "U";
		if (this.dir.endsWith(dir)) return;

		dir = this.dir += dir;
		var obj = this.type[dir];
		var txt = `${obj ? "Ж" : "Неизвестный ж"}ест мыши: ${dir + (obj ? "  " + obj.name : "")}`;

		this.showInStatus(txt);
	},
	dragend(e) {
		var dt = e.dataTransfer;
		this.drag();

		var obj = this.type[this.dir];
		if (!obj || dt.mozUserCancelled) return;

		var x = e.screenX, y = e.screenY;
		var wx = win.mozInnerScreenX, wy = win.mozInnerScreenY;
		x > wx && y > wy && x < wx + win.innerWidth && y < wy + win.innerHeight
			&& obj.cmd.call(this);
	},
	textLinkRe: /^([a-z]+:\/\/)?([a-z]([a-z0-9\-]*\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel)|(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-z][a-z0-9_]*)?$|^custombutton:\/\/\S+$/,

	observe(w) {
		this.drop = () => this.drag();
		this.handleEvent = e => this[e.type](e);
		var unload = e => {
			var w = e.target.ownerGlobal;
			w.gBrowser.tabpanels.removeEventListener("dragstart", this, true);
			if (w == win) win = null;
		}
		(this.observe = w => {
			//if (!w.toolbar.visible) return;
			w.gBrowser.tabpanels.addEventListener("dragstart", this, true);
			w.addEventListener("unload", unload, {once: true});
		})(w);
	},
	init(topic, self) {
		delete this.init;
		Services.obs.addObserver(self = this, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(self, topic);
			Services.obs.removeObserver(quit, t);
		}, "quit-application-granted");
	}
}).init("browser-delayed-startup-finished"))();

Отсутствует

 

№1627412-02-2022 21:15:21

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 97.0

Re: Custom Buttons

Dumby
В 97 проблема с undoclose кнопкой .Не реагирует на закрытие одной вкладки. Все закроешь так реагирует. Не проверите?
ссылка то появляется, кнопка не реагирует...Уточняю, если перед вкладкой пустая,тогда и не реагирует на закрытие нормальной...
Ну,пипец, fission.autostart.session = false //Заблокировали...

Отредактировано ВВП (13-02-2022 16:18:18)

Отсутствует

 

№1627513-02-2022 20:47:19

momo2000
Участник
 
Группа: Members
Зарегистрирован: 03-09-2015
Сообщений: 228
UA: Firefox 97.0

Re: Custom Buttons

ВВП
Только так
https://github.com/sdavidg/firefoxChromeScripts/blob/main/scripts/dav_button_undoCloseTabs.uc.js

Отсутствует

 

Board footer

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