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

Общайтесь со знакомыми и друзьями в нашем сообществе в Facebook.

№1876Вчера 15:25:47

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

Re: UCF - ваши кнопки, скрипты…

unter_officer
Во навалился!
Проще browser.xhtml заоверрайдить без csp-меты (шучу, конечно :)).

Восстановление удалённых закладок или папок

Вроде этот код был признан непригодным к использованию,
из-за загадочного глюка, возникающего после вызова PlacesTransactions.undo()


Но, если абстрагироваться от этого, то что тут сложного?
Там, где устанавливаются атрибуты, изымаем oncommand


А вместо этого, далее, пишем строку, добавляющую листенер
menuitem.addEventListener("command", () => PlacesTransactions.undo().catch(Cu.reportError));
И все дела.


Остальное, — вот,
заменил четырёх-пробелья на табуляцию,
плюс попытка удаления «on…» атрибутов.
Ну, import() ещё, в этом (кем-то испорченном) SSM.
Только это, на большее рассчитывать не следует.

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

Выделить код

Код:

//
(async (pid, mp, self) => CustomizableUI.createWidget((self = {
	id: "ucf_SimpleSessionManager",
	label: "Simple Session Manager",
	tooltiptext: "Менеджер сессий",
	localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = async () => await 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;
	},
	image: "data:image/png;base64,.....",
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn.onclick = this.click;
		btn.setAttribute("image", this.image);
		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;

		btn.prepend(popup);
		popup.shadowRoot;
		popup.ensureInitialized = this.handlePopupshowing;

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
	},
	handlePopupshowing() {
		var e = this.ownerGlobal.event;
		e.defaultPrevented || this.filler.fill(e);
	},
	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),
			e.preventDefault();
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.bootChanged = true;
	},
	muTimestamp: 0,
	click(e) {
		var trg = e.target;
		if (trg.nodeName == "menu") {
			if (e.button > 1) self.boot(trg);
			else if (Cu.now() - self.muTimestamp > 50)
				e.view.closeMenus(trg.menupopup),
				self.restoreSession(trg.label, (e.button || e.ctrlKey) && e.view);
		}
		else if (trg != this || e.button) return;
		e.view.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	async command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r"))
			arg = trg.parentNode.parentNode.label;
		await 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;
			this.muTimestamp = Cu.now();
		}
		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();

		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.bootChanged) {
			delete popup.bootChanged;
			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 && e.key != " ") 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, e);
	},
	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);
		}
	},
	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="Сохранить сессию"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<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("${this.image}");
						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} [value=removeSession] {
						list-style-image: url("data:image/png;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 {
						display: none;
					}
					#${pid} > menu[boot] {
						color: #AA0000;
						font-weight: bold;
					}
				}`.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() {
		var gs = Object.create(null);
		Object.assign(gs, ChromeUtils.importESModule("resource:///modules/sessionstore/SessionStore.sys.mjs"));
		gs.SessionStoreInternal = UcfPrefs.dbg.ref("SessionStoreInternal", gs.SessionStore.undoCloseTab);
		delete this.gs;
		return this.gs = gs;
	},
	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;
	},
	io: {
		get OS() {
			delete this.OS;
			Cu.import("resource://gre/modules/osfile.jsm", this);
			return this.OS;
		},
		makeDirectory(path) {
			return (this.makeDirectory = this.OS.File.makeDir)(path);
		},
		writeJSON(path, obj) {
			var wa = this.OS.File.writeAtomic;
			return (this.writeJSON = (path, obj) => wa(path, JSON.stringify(obj)))(path, obj);
		}
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils || this.io;
		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]];
		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.sys.mjs";
		return this.bwt = ChromeUtils.importESModule(url).BrowserWindowTracker;
	},
	getTabLabel() {
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		}`;
	},
	getName(state) {
		var tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${tl}(B) [${
			new Date().toLocaleString("mn").replace(" ", " - ")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	get spref() {
		var pref = "browser.sessionstore.interval";
		var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
		var wait = cb => timer.initWithCallback(cb, 1e3, timer.TYPE_ONE_SHOT);
		delete this.spref;
		return this.spref = async cb => {
			var val = Services.prefs.getIntPref(pref);
			Services.prefs.setIntPref(pref, 100);
			await new Promise(wait);
			Services.prefs.setIntPref(pref, val);
		}
	},
	async saveSession(name = this.getName(this.getState()), getTabLabel = this.getTabLabel()) {
		var saveName = this.prompter.prompt("Сохранить", getTabLabel );
		var name = (saveName + ", " + name );
		if (name == null || saveName == null) return true;

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

		await this.spref();
		this.data[name] = this.getState();

		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);
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");


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

Выделить код

Код:

//
try {CustomizableUI.createWidget({
	label: "Переключить текущий поисковик",
	id: "ucf_ToggleCurrentSearchEngine",
	localized: false,
	image: "data:image/png;base64,.....",
	excludeHiddenOneOffs: true,
	gn: () => Services.search.defaultEngine,
	gp: () => Services.search.defaultPrivateEngine,
	sn: val => Services.search.defaultEngine = val,
	sp: val => Services.search.defaultPrivateEngine = val,
	onCreated(btn) {
		btn.type = "menu";
		btn.setAttribute("image", this.image);

		var win = btn.ownerGlobal;
		var popup = btn.appendChild(win.document.createXULElement("menupopup"));
		var pr = win.PrivateBrowsingUtils.isWindowPrivate(win);

		popup.getDefaultEngine = pr ? this.gp : this.gn;
		popup.setDefaultEngine = pr ? this.sp : this.sn;

		popup.linkedObject = this;
		popup.addEventListener("command", this.command);
		popup.addEventListener("popupshowing", this.popupshowing);

		this.autoOpenCloseFeature(win, btn);
		this.updButton(btn, win);
	},
	command(e) {
		this.setDefaultEngine(e.target.engine);
	},
	popupshowing() {
		this.shouldRebuild && this.linkedObject.rebuild(this, this.ownerDocument);
	},
	getEngines() {
		var ve = Services.search.getVisibleEngines;
		if (!this.excludeHiddenOneOffs) return (this.getEngines = ve)();

		var arr = [];
		var args = this.fx116
			? [e => !e.hideOneOffButton]
			: Object.defineProperty(
				[function(e) {return !this.includes(e.name);}], "1", {get: () => {
					var str = Services.prefs.getStringPref(this.pref);
					return str ? str.split(",") : arr;
				}}
			);
			return (this.getEngines = async () => (await ve()).filter(...args))();
	},
	async rebuild(popup, doc) {
		popup.textContent = "";
		var df = doc.createDocumentFragment();
		var de = popup.getDefaultEngine().wrappedJSObject, jsde = this.json(de);
		var check = true;
		for(var engine of await this.getEngines()) {
			if (check && engine.name == de.name && this.json(engine) == jsde) {
				check = false; continue;
			}
			var menuitem = df.appendChild(doc.createXULElement("menuitem"));
			menuitem.engine = engine;
			menuitem.label = engine.name;
			menuitem.className = "menuitem-iconic";
			menuitem.image = await this.img(engine);
		}
		popup.append(df);
		delete popup.shouldRebuild;
	},
	async updButton(btn, win) {
		this.updButton = () => {};
		Services.search.isInitialized || await Services.search.init();
		this.fx116 = "hideOneOffButton" in Services.search.defaultEngine;

		var topics = ["browser-search-engine-modified", "quit-application-granted"];
		for(var topic of topics) Services.obs.addObserver(this, topic, false);
		this.observe = (s, topic) => this[topic[0]]();

		var remove = () => topics.forEach(
			topic => Services.obs.removeObserver(this, topic)
		);
		var {id} = this;
		var wins = callback => {
			for(var win of CustomizableUI.windows) {
				var btn = win.document.getElementById(id);
				btn && callback(btn, win);
			}
		}
		if (this.excludeHiddenOneOffs && !this.fx116) {
			var setRebuild = btn => btn.firstChild.shouldRebuild = true;
			var {pref} = this, obs = () => wins(setRebuild);
			Services.prefs.addObserver(pref, obs);
			this.q = () => remove(Services.prefs.removeObserver(pref, obs));
		}
		else this.q = remove;

		var updButton = (btn, win) => {
			var popup = btn.firstChild;
			var engine = popup.getDefaultEngine();
			/*btn.label =*/ btn.tooltipText = engine.name;
			popup.shouldRebuild = true;
			win.requestAnimationFrame(async () => btn.icon.src = await this.img(engine));
		}
		(this.b = () => wins(updButton))();
		this.updButton = updButton;
		btn.tooltipText || updButton(btn, win);
	},
	pref: "browser.search.hiddenOneOffs",
	json: e => JSON.stringify(e.toJSON()),
	img: async e => await e.getIconURL?.() || e.iconURI?.spec || "chrome://browser/skin/search-engine-placeholder.png",

	// https://github.com/Infocatcher/Custom_Buttons/blob/master/code_snippets/autoOpenCloseMenu.js
	// Automatically open menu on mouse over (and hide it on mouse out)
	autoOpenCloseFeature(win, btn, openDelay = 20000, closeDelay = 350) {
		var _openTimer = 0;
		var _closeTimer = 0;
		btn.onmouseover = function(e) {
			win.clearTimeout(_closeTimer);
			if(e.target == btn && closeOtherMenus()) {
				btn.open = true;
				return;
			}
			_openTimer = win.setTimeout(function() {
				btn.open = true;
			}, openDelay);
		};
		btn.onmouseout = function(e) {
			win.clearTimeout(_openTimer);
			_closeTimer = win.setTimeout(function() {
				btn.open = false;
			}, closeDelay);
		};
		function closeOtherMenus() {
			return win.Array.prototype.some.call(
				btn.parentNode.getElementsByTagName("*"),
				function(node) {
					if(
						node != btn
						&& win.XULElement.isInstance(node)
						// 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;
				}
			);
		}
	}
});} catch(ex) {Cu.reportError(ex);}


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

Выделить код

Код:

//
(async func => CustomizableUI.createWidget({
	id: "ucf_SaveAsPNG",
	label: "Сохранить как PNG",
	tooltiptext: "Сохранить как PNG",
	localized: false,
	// defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated(btn) {
		var win = btn.ownerGlobal;
		new win.Function("_id, xhtmlns, addDestructor", func.toString().slice(7, -1)).call(
			btn, this.id, "http://www.w3.org/1999/xhtml",
			destructor => win.addEventListener("unload", destructor, {once: true})
		);
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGwSURBVHjarJPPaxNBGIafmd1km23saqtRaLB6kFYl2ELpRayCVL20RvCmJwk9ePNWvPsXtCA9eNCDeO0hLemPo96LYAuKkRhtsBViEtI0rrt+GyFQ1oMkfjAf883M+8w7w4zyfZ9uwsxms10RzCBNTd3sSLy2lvsDCCKzWMCKGHgotOTAlpamJPm+jCmFL00bmmbD5WlmsKXTdBltB3bcxowaLWJwsTpi4bvNVu1JMmVn5cmcqTB6/DAg3hcjGpUFskjZjlgFKdmvVIiISKlfUhlyQMVPywgDjvX1YCjpOBZ2fZ+9+TuUpxcZuz7Ebl7ArkfM0dgDcOAFCvfwHTi9URJJi2RCqEuPmRxPUXn9imodLo1D6orGjMHW6govZqfDDkYviDXBlZ4vsFsrk8vXqZRWeDY8hz55lvvpGU7sFEn0RhhTtTBg/tETCvlNdj68papsUoND3Lh6iyPOOYrvv/JufYNrqcssbW8zkjwdBnz7/JKL50/x4F4at9GgsFckX/2IefQHZyb6mbl9l2LpE8ff1PnufWkD/t87yC1v/pvi4V8AwZvuNFS33/m3AAMAhEeAiqLmty4AAAAASUVORK5CYII=");
	}
}))(() => {


((main, parts) => this._handleClick = () => {
	var df = MozXULElement.parseXULToFragment(`
		<menupopup>
			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
				label="Сохранить всю страницу как PNG"
				value="all"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
				label="Сохранить видимую часть страницы как PNG"
				value="page"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKaSURBVHjaYpx94sPD44//CDG9+/SDp8bsBw9AADEmrX7zmQEIGF+//fD/w8dPDAABxNix6/nbcA0GIQYoYGJiZGSQFBdmOHnqLAMrKwsDQAAxJq958/n/f4jsm2//GBifP3v+/8cfiMDDj/8ZmHi5ORl4gPjPn18M///9ZWDh5uVm4GJkYfj56zcD9z9GBoAAYmwH2aL8UwimDRk8+fyfgYWRgZFBgI+b4TsWBR+BtoNcxcDDwwW2R1iQn2HvgcMMX799Y/j6/TvDzz+/GVj+/fvPvO0BC8N/oGpGhv8ML3m0GS59EwWbcP/9XwaAAGSRwQ3CMAxFfxq3TgrqiSMswARMwSJMxZEdGAXBAEiQquICJMFuekDqwTlY38/Wi9kf78OqNTO8kndretBh+1puuvl+R8Dpwm9iruG8xPP/tIGzhULc1OB6voJJXxGjQpumGpsKCc8wEqISwGJfA1P1IYCEmCegkTmqLIELT/8KC+/x+cbxBpEPOl8zbn0q5voWXdcipQQrgZiC/QlQORmzNBAEUfjt7N4ichBUDIKCWNsJhiBiZ2mZf5XC0v/gT7C10lqwNMVBkBC9kKAmt7e+uQtJxBS6cNwy7L2b981bTrz/fnmIxnHTYN081i1F8PQacddDzm0M+UfAYPw/gfyTlqMNVfOJVxIGpfxNgMcZRwoYpkVpe1ezLn/jplc7B2wWb+1gwwfuvxg31lwi7MAimOU4E+IevuXoXt+gILTE1aQ1VBJnwNYR9ltXFKCweM9RWFpYCnjWiukU7dYJLs7PUMyKuglNAZt6GVncZ7SgIU2ZJ8M/bLqVQEFYN9hJPQ5201pgvhztjnk2ZhOYh+esd/vY39ZPZYWBXlURqZ4Qwg8uZXU3gM7p3vAbsXveewxHZWUAAAAASUVORK5CYII="
				label="Сохранить выбранный элемент страницы как PNG"
				value="click"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL/SURBVHjaYijccvd25ZHvv5n4RSUVQs2vswAEECOIxwACy+8v/3/30Zv/AAHE0Hb6y+//UPDv4///LD/+MjJ8+PqF4cvnnww/vgkxAAQQY8Xhb78YgQCsj5mFgQGm/P+v///fvvj/n+n/fwaGe//vMexg2Mrw7BsDA8vXHwwMPN/5GdQ/mzG8BbIBAoiRfdqvX8xf/rBCDAEaAWL8ByOgmZwMLBXGvxnrzLgYVj9cyfCL+TfDj9+/Gd7//sAQw5vK8PEXAwMTRBsD0Li3DNKikgwC4twM3NIMDF9/fGf4CVTA8u0fM+ODjwwM7qJZDH8/MTBI/GFgUAba/QYo+fEnAwNAADKoXQVhIAjOJYfgsxIR/AwRi/yBlY3gz9j7FUI+wt7CwkbEzs7CNIo2Yl53t3duElDBhYXZZXaWGbHYKW0M4b+cex7WG9FaxmxGfNcfYNH3T+pr/LcSzu1h3Wx11EU2eOkYYRSi7jVgrQeb1TDtzFHkLgu13GQIBgGkJ6GFxva8h6XqX0kgRlF+AfkEw4S7ujEGbKnAzDa6GNEYLvOh2N6wOUGaVXHKayqQsHeiHgyfqKJ51sbxjvAWoG1qWWkYiKJnZhKTpgapK1tEslRw50+4camIGz/ObfcuBP0HBREEn12I+MBUadPM5HomqbWgi8kJydzHOfdctXmU29Vhbhqq8leNfxQyLP6lWgjjdxds9Yxs95ZRVAo+h8yFqEau2fHEKjpKqgojRDg+H0iw1gb212NY2oC/MCxz5DxGaYgPsKrGsnRYUEusnvKb4MNqnBlN5aZD8q54Hb2gP+gj62bohOyKdVxgobTBk3vE7cMzDhYPkVA0NW0xaBrlk6BZtRN20I16SMIWPt0CJlLQowqJStE2YwqnQRbE2ZwazrYURDquL5zcnCINU4xdiYo7QXXo4TesSMaIGGRTJ6jnqBxvkJNwfhFa2Ovu1jlrwXjD2gZ9wITeKVhImEFXgXcIgrvri/udqySztpDZemJurTzK70h/3h1xY3xpvwGLI42vrCwxmAAAAABJRU5ErkJggg=="
				label="Сохранить выбранную область страницы как PNG"
				value="clipping"/>
		</menupopup>
	`);
	var popup = df.firstChild;
	popup.setAttribute("context", "");

	popup.addEventListener("command", e => popup.handleCommand(e));

	popup.handleCommand = e => {
		var name = _id + ":DataURLReady";
		main = main.replace("%MESSAGE_NAME%", name);

		var urls = {}, configurable = true, enumerable = true;
		Object.entries(parts).forEach(([key, part]) => Object.defineProperty(urls, key, {
			configurable, enumerable, get() {
				var value = `data:;charset=utf-8,({${
					encodeURIComponent(main + part)
				}%0A}).init("${key}")`;
				Object.defineProperty(urls, key, {configurable, enumerable, value});
				return value;
		}}));

		// Получить название вкладки без не сохраняемых символов и лишних пробелов .....
		var getTabLabel = () => {
			var label = gBrowser.selectedTab.label;
			var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
			return label.substring(0, 50);
		}

		var listener = msg => {
			var fp = makeFilePicker();
			// fp.init(window, "Сохранить как…", fp.modeSave); // Farby: https://forum.mozilla-russia.org/viewtopic.php?pid=809686#p809686 .....
			fp.init(
				!("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
				  ? window.browsingContext
				  : window
				, "Сохранить как…", fp.modeSave);
			fp.appendFilter("", "*.png");

			var fileName = getTabLabel();
			fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '').replace(/\s+/g, '_').slice(0, 100).replace(/^\s+|\s+$/g, '');
			var fileDate = (function () {
			  var d = new Date(), z = function(n){return (n < 10 ? '0' : '') + n};
			  return '[' + z(d.getFullYear()) + '_' + z(d.getMonth()+1) + '_' + z(d.getDate()) + '\u2014' + z(d.getHours()) + '_' + z(d.getMinutes()) + '_' + z(d.getSeconds()) + ']';
			})();

			fp.defaultString = fileName + "_" + fileDate + ".png";
			fp.open(res => res == fp.returnCancel || !fp.file || makeWebBrowserPersist().saveURI(
				Services.io.newURI(msg.data), document.nodePrincipal,
				null, null, null, null, null, fp.file, null, null
			));
		}
		messageManager.addMessageListener(name, listener);
		addDestructor(() => messageManager.removeMessageListener(name, listener));

		(popup.handleCommand = e => gBrowser.selectedBrowser.messageManager
			.loadFrameScript(urls[e.target.value], false)
		)(e);
	}
	this.append(df);
	(this._handleClick = () => popup.openPopup(this, "after_start"))();
})(`
	init(cmd) {
		cmd.startsWith("c")
			? this[cmd].init(this[cmd].parent = this)
			: this[cmd]();
	},
	capture(win, x, y, width, height) {
		var canvas = win.document.createElementNS("${xhtmlns}", "canvas");
		canvas.width = width;
		canvas.height = height;
		var ctx = canvas.getContext("2d");
		var tryDraw = ind => {
			try {ctx.drawWindow(win, x, y, canvas.width, canvas.height, "white")}
			catch(ex) {canvas.height = ind * canvas.width; tryDraw(--ind);}
		}
		tryDraw(17);
		sendAsyncMessage("%MESSAGE_NAME%", canvas.toDataURL("image/png"));
	},
	`, {

	all: `all() {
		var win = content;
		this.capture(win, 0, 0, win.innerWidth + win.scrollMaxX, win.innerHeight + win.scrollMaxY);
	}`,
	page: `page() {
		var win = content, doc = win.document, body = doc.body, html = doc.documentElement;
		var scrX = (body.scrollLeft || html.scrollLeft) - html.clientLeft;
		var scrY = (body.scrollTop || html.scrollTop) - html.clientTop;
		this.capture(win, scrX, scrY, win.innerWidth, win.innerHeight);
	}`,
	clipping: `clipping: {
		handleEvent(e) {
			if (e.button) return false;
			e.preventDefault();
			e.stopPropagation();
			switch(e.type) {
				case "mousedown":
					this.downX = e.pageX;
					this.downY = e.pageY;
					this.bs.left = this.downX + "px";
					this.bs.top = this.downY + "px";
					this.body.appendChild(this.box);
					this.flag = true;
					break;
				case "mousemove":
					if (!this.flag) return;
					this.moveX = e.pageX;
					this.moveY = e.pageY;
					if (this.downX > this.moveX) this.bs.left = this.moveX + "px";
					if (this.downY > this.moveY) this.bs.top  = this.moveY + "px";
					this.bs.width = Math.abs(this.moveX - this.downX) + "px";
					this.bs.height = Math.abs(this.moveY - this.downY) + "px";
					break;
				case "mouseup":
					this.uninit();
					break;
			}
		},
		init() {
			var win = {};
			Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
				.getFocusedElementForWindow(content, true, win);
			this.win = win.value;

			this.doc = this.win.document;
			this.body = this.doc.body;
			if (!HTMLBodyElement.isInstance(this.body)) {
				Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
					.showAlertNotification("${self.image}", ${JSON.stringify(self.label)}, "Не удается захватить!");
				return false;
			}
			this.flag = null;
			this.box = this.doc.createElement("div");
			this.bs = this.box.style;
			this.bs.border = "red dashed 1px";
			this.bs.position = "absolute";
			this.bs.zIndex = "2147483647";
			this.defaultCursor = this.win.getComputedStyle(this.body, "").cursor;
			this.body.style.cursor = "crosshair";
			["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.addEventListener(type, this, true));
		},
		uninit() {
			var pos = [this.win, parseInt(this.bs.left), parseInt(this.bs.top), parseInt(this.bs.width), parseInt(this.bs.height)];
			this.body.style.cursor = this.defaultCursor;
			this.body.removeChild(this.box);
			this.parent.capture.apply(this, pos);
			["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.removeEventListener(type, this, true));
		}
	}`,
	click: `click: {
		getPosition() {
			var html = this.doc.documentElement;
			var body = this.doc.body;
			var rect = this.target.getBoundingClientRect();
			return [
				this.win,
				Math.round(rect.left) + (body.scrollLeft || html.scrollLeft) - html.clientLeft,
				Math.round(rect.top) + (body.scrollTop || html.scrollTop) - html.clientTop,
				parseInt(rect.width),
				parseInt(rect.height)
			];
		},
		highlight() {
			this.orgStyle = this.target.hasAttribute("style") ? this.target.style.cssText : false;
			this.target.style.cssText += "outline: red 1px solid; outline-offset: 1px; -moz-outline-radius: 2px;";
		},
		lowlight() {
			if (this.orgStyle) this.target.style.cssText = this.orgStyle;
			else this.target.removeAttribute("style");
		},
		handleEvent(e) {
			switch(e.type){
				case "click":
					if (e.button) return;
					e.preventDefault();
					e.stopPropagation();
					this.lowlight();
					this.parent.capture.apply(this, this.getPosition());
					this.uninit();
					break;
				case "mouseover":
					if (this.target) this.lowlight();
					this.target = e.target;
					this.highlight();
					break;
			}
		},
		init() {
			this.win = content;
			this.doc = content.document;
			["click", "mouseover"].forEach(type=> this.doc.addEventListener(type, this, true));
		},
		uninit() {
			this.target = false;
			["click", "mouseover"].forEach(type=> this.doc.removeEventListener(type, this, true));
		}
	}`
});
});


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

Выделить код

Код:

//
(async (id, popup, self) => (self = {

	clickInterval: 5*60,
	intervals: [
		10, 15, 30, 60, 3*60, 5*60, 10*60, 15*60, 30*60, 45*60, 60*60,
	],
	async init() {
		this.addStyle();
		var dsp = e => this[e.type](e);
		var tc = document.getElementById("tabbrowser-tabs");
		ucf_custom_scripts_win[id] = this;

		Object.entries({
			popupshowing: popup,
			TabClose: tc,
			SSTabRestored: tc,
			EndSwapDocShells: document.getElementById("tabbrowser-tabpanels")
		})
			.forEach(([type, trg], ind) => trg.addEventListener(type, dsp, ind == 3));

		await SessionStore.promiseAllWindowsRestored;
		for(var tab of gBrowser.tabs)
			tab.linkedPanel || this.maybeInitTab(tab);
	},
	maybeInitTab(tab) {
		var sec = this.sec(tab);
		sec && this.initTab(tab, sec, true);
	},
	mousedown(e) {
		if (e.button) return;
		e.stopImmediatePropagation();
		self.destroyTab(this.closest("tab"));
	},
	initTab(tab, sec, skipSet) {
		skipSet || SessionStore.setCustomTabValue(tab, id, sec);
		var img = document.createXULElement("hbox");
		img.className = id;
		// img.onmousedown = this.mousedown; // клик по иконке отключает перезагрузку вкладки .....

		tab.throbber.before(img);
		tab.setAttribute(id, setInterval(this.reload, sec * 1e3, tab));
	},
	destroyTab(tab) {
		clearInterval(tab.getAttribute(id));
		SessionStore.deleteCustomTabValue(tab, id);
		tab.removeAttribute(id);
		tab.querySelector("." + id).remove();
	},
	addStyle() {
		var css = `
			tab.tabbrowser-tab[${id}] .${id} {
				width: 16px;
				height: 16px;
				position: relative;
				margin-top: -1px;
				margin-inline-start: -2px;
				margin-inline-end: -14px;
				background-position: top right;
				background-repeat: no-repeat;
				background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEHSURBVHjaYmQAArWgflEglQrEgUCsAsR3gHg9EM++ta7wNSNIASMjQx/rr7fhEd6WrAbaygy3Hr1l2Hjw+u8Xbz+v/P+foYgFZIKEMG94foARKxfTDwZ3G3UGb6Cgj406a2rrhvDnbz5fZwJZ4W+vyRroacdw4MB+hjt37jA8e/aMYd/2NQzOxrKsIHmQIhU1OWGQ0xiSk5MZVFVVGdasWcPg4eHBYKglDxJWAVl3B+gGE5AV8vLyDLdv3wbTrKysDOuOHgMpugMyaT3IkbeBjgVJqKiogOnbUMeD5JmFNT1ufP3+S2b/mfua7z//YH778RvD1iO3GLoXHwH7DqionZGYcAIIMADkw2lofXkQ/wAAAABJRU5ErkJggg==");
				z-index: 1000;
			}
			tab.tabbrowser-tab[${id}]:-moz-locale-dir(rtl) .${id} {
				background-position: top right;
			}
			tab.tabbrowser-tab[${id}] .tab-icon-image {
				display: -moz-box;
			}
			tab.tabbrowser-tab[${id}][pendingicon] .tab-icon-image {
				visibility: hidden;
			}
			#context_autoreloadTab[checked] > menupopup > :nth-child(2),
			#context_autoreloadTab:not([checked]) > menupopup > :first-child {
				display: none;
			}
			#context_autoreloadTab[checked] > .menu-iconic-left > image {
				fill: currentColor;
				-moz-context-properties: fill;
				list-style-image: url("chrome://global/skin/icons/check.svg");
			}
			/*
			tab.tabbrowser-tab[${id}] .tab-throbber,
			tab.tabbrowser-tab[${id}] .tab-icon-pending,
			tab.tabbrowser-tab[${id}]:not([pendingicon]) .tab-icon-image:not([src],[busy],[pinned],[crashed],[sharing]) {
				display: none;
			}
			*/
		`.replace(/;\s*\n/g, " !important;\n");
		windowUtils.loadSheetUsingURIString(
			"data:text/css," + encodeURIComponent(css), windowUtils.USER_SHEET
		);
	},
	get tab() {
		return TabContextMenu.contextTab;
	},
	sec(tab) {
		return SessionStore.getCustomTabValue(tab, id);
	},
	click(menu) {
		var {tab} = this;
		var has = menu.toggleAttribute("checked");
		has
			? this.initTab(tab, this.clickInterval)
			: this.destroyTab(tab);

		var w = menu.clientWidth;
		this.setLabel(has && self.clickInterval);

		if (this.menupopup.state == "open")
			this.updMenupopup(),
			menu.clientWidth != w && setTimeout(this.move, 50);
	},
	changeInterval(tab, sec) {
		clearInterval(tab.getAttribute(id)),
		SessionStore.setCustomTabValue(tab, id, sec),
		tab.setAttribute(id, setInterval(this.reload, sec * 1e3, tab));
	},
	cmd(e) {
		var {value} = e.target;
		if (value == this.currSec) return;

		var {tab} = this;
		this.setLabel(value);

		if (this.menu.hasAttribute("checked"))
			this.changeInterval(tab, value);
		else
			this.menu.toggleAttribute("checked"),
			this.initTab(tab, value);
	},
	reload(tab) {
		gBrowser.reloadTab(tab);
	},
	get shouldHide() {
		return !this.tab.linkedBrowser.currentURI.scheme.startsWith("http");
	},
	format(sec) {
		var map = new Map();
		// resource://gre/modules/PluralForm.jsm
		var f = n => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
		var hh = ["", "а", "ов"], ms = ["а", "ы", ""];
		return (this.format = sec => {
			var res = map.get(sec = +sec);
			if (!res) {
				var num, arr = [];
				if ((num = Math.floor(sec / 3600)) > 0)
					sec -= num * 3600,
					arr.push(`${num} час${hh[f(num)]}`);
				if ((num = Math.floor(sec / 60)) > 0)
					sec -= num * 60,
					arr.push(`${num} минут${ms[f(num)]}`);
				sec > 0 && arr.push(`${sec} секунд${ms[f(sec)]}`);
				map.set(sec, res = arr.join(" "));
			}
			return res;
		})(sec);
	},
	async prompt(val) {
		var {tab} = this, sec = this.sec(tab);
		var res = await Services.prompt.asyncPrompt(
			null, Services.prompt.MODAL_TYPE_WINDOW,
			val ? "ЕЩЁ РАЗ:" : "Задать интервал обновления",
			"Введите число секунд авто-обновления",
			val || sec || this.clickInterval, null, null
		);
		if (!res.get("ok")) return;

		var val = res.get("value");
		if (!val) return;
		if (!isFinite(val)) return this.prompt(val);

		var val = String(Math.round(val) || 1);
		sec ? this.changeInterval(tab, val) : this.initTab(tab, val);
	},
	initShadowDOM() {
		delete this.initShadowDOM;
		this.initShadowDOM();

		var df = MozXULElement.parseXULToFragment(
			`<menuitem closemenu="single" label="Не перезагружать"/>
			<menuitem closemenu="single" value="${self.clickInterval}"
				label="${self.format(self.clickInterval)}" type="radio"/>
			<menuitem label="Другой…"/>
			<menuseparator/>`
		);
		var {children} = df;
		children[0].addEventListener("command", e => self.menu.click(e.stopPropagation()));
		children[2].addEventListener("command", e => self.prompt(e.stopPropagation()));

		var menuitem = children[1];

		for(var sec of self.intervals) {
			if (sec == self.clickInterval) continue;
			menuitem = menuitem.cloneNode(false);
			menuitem.setAttribute("value", sec);
			menuitem.setAttribute("label", self.format(sec));
			df.append(menuitem);
		}
		this.append(df);
	},
	setLabel(sec) {
		this.menu.setAttribute("label", (this.currSec = sec)
			? `Интервал перезагрузки:   ${this.format(sec)}`
			: "Задать интервал перезагрузки"
		);
	},
	popupshowing(e) {
		if (this.shouldHide) return;

		var df = MozXULElement.parseXULToFragment(
			`<menu id="context_autoreloadTab" class="menu-iconic">
				<menupopup/>
			</menu>`
		);
		var menu = this.menu = df.firstChild;
		menu.onclick = e => e.target == menu && this.click(menu);

		var menupopup = this.menupopup = menu.firstChild;
		menupopup.addEventListener("command", e => this.cmd(e));

		menupopup.initShadowDOM = this.initShadowDOM;
		popup.querySelector("#context_duplicateTab").after(menu);

		this.clickInterval = String(this.clickInterval);
		this.move = () => menupopup.moveToAnchor(menu, "end_before");

		this.updMenupopup = () => {
			var old = menupopup.querySelector("[checked=true]");
			var cur = this.currSec && menupopup.querySelector(`[value="${this.currSec}"]`);
			if (old != cur)
				old?.removeAttribute("checked"),
				cur && cur.setAttribute("checked", true);
		}
		(this.popupshowing = e => {
			if (e.target == popup) {
				if (menu.hidden = this.shouldHide) return;

				var sec = this.sec(this.tab);
				var has = menu.hasAttribute("checked");
				if (Boolean(sec) ^ has)
					has = !has,
					menu.toggleAttribute("checked");

				var curr = has && sec;
				curr !== this.currSec && this.setLabel(curr);
			}
			else if (e.target == menupopup) this.updMenupopup();
		})(e);
	},
	TabClose(e) {
		var intervalId = e.target.getAttribute(id);
		if (!intervalId) return;
		clearInterval(intervalId);

		var tab = e.detail.adoptedBy;
		tab?.ownerGlobal.ucf_custom_scripts_win[id].initTab(tab, this.sec(e.target));
	},
	SSTabRestored(e) {
		var tab = e.target;
		tab.hasAttribute(id) || this.maybeInitTab(tab);
	},
	async EndSwapDocShells(e) {
		var br = e.detail, trg = e.target;
		await new Promise(requestAnimationFrame);

		var win = br.ownerGlobal;
		if (!win.closed) return;
		var tab = win.gBrowser.getTabForBrowser(br);
		if (!tab) return;

		var sec = this.sec(tab);
		if (sec)
			tab = gBrowser.getTabForBrowser(trg),
			tab.hasAttribute(id) || this.initTab(tab, sec);
	}
}).init())("ucf-tab-auto-reload", document.getElementById("tabContextMenu"));

Отредактировано Dumby (Вчера 15:28:18)

Отсутствует

 

№1877Вчера 17:15:34

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

Re: UCF - ваши кнопки, скрипты…

Dumby пишет

unter_officer
Во навалился!

Dumby, всё отлично работает!
Огромное спасибо! drinks.gif


«The Truth Is Out There»

Отсутствует

 

Board footer

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