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

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

№2630-09-2023 08:35:20

fuchsfan
Участник
 
Группа: Members
Зарегистрирован: 07-08-2023
Сообщений: 130
UA: Firefox 119.0

Re: userChrome.js

Вит

Вит пишет

Вот где бы взять ......... и Боковую панель, как ранее была от Виталия, что бы эти кнопки там расположить?

Эта боковая панель входит в состав UCF, выдирается ли она оттуда как самостоятельный скрипт, не знаю. Если у вас UCF не установлен, то вот вам боковая панель от Aris-t2 https://github.com/Aris-t2/CustomJSforF … ical.uc.js

Отсутствует

 

№2730-09-2023 09:59:11

fuchsfan
Участник
 
Группа: Members
Зарегистрирован: 07-08-2023
Сообщений: 130
UA: Firefox 119.0

Re: userChrome.js

Вит пишет

Не появляется, видимо из-за конфликта

У вас v115, видимо, вам нужна версия скрипта постарее https://github.com/Aris-t2/CustomJSforF … ical.uc.js. Та, что в посту выше, исправлена автором для v117 и выше. Оба скрипта на своих версиях активируются совершенно легко и без бубна.

Отредактировано fuchsfan (30-09-2023 10:02:24)

Отсутствует

 

№2830-09-2023 13:43:55

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

Re: userChrome.js

6e73epo пишет

Dumby, существует ли возможность на кнопке в onCommand: function(event) {...)  прописать команды, чтобы кнопка открывала определенный раздел реестра через запуск regedit? Просто ответьте да или нет.

Ну, как сказать, вот состряпал тестовую кнопку, и мне открывает.


Если использовать launch(); то открывает
только с нуля, то есть когда regedit не запущен,
иначе просто активируется окно, в котором открыто то, что открыто.


А если прицепить ключ «/m», то открывает всегда, но всегда в новом окне.


Вобщем, ответ, скорее «да».

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

Выделить код

Код:

(async (regedit, file) => CustomizableUI.createWidget({
	id: "regedit-opener",
	label: "RegEdit",
	localized: false,
	onCreated(btn) {
		file = Services.dirsvc.get("WinD", Ci.nsIFile);
		file.append("regedit.exe");
		var img = "moz-icon:file://" + file.path;
		(this.onCreated = btn => btn.image = img)(btn);
	},
	onCommand() {

		var path = "Компьютер\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Mozilla";

		var wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
		wrk.open(wrk.ROOT_KEY_CURRENT_USER, regedit, wrk.ACCESS_WRITE);
		wrk.writeStringValue("LastKey", path);

		//file.launch();

		var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
		process.init(file);
		process.runwAsync(["/m"], 1);
	}
}))("Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit");

Отсутствует

 

№2901-10-2023 00:45:13

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 207
UA: Firefox 117.0

Re: userChrome.js

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

Совсем забыл про "WinD" и делал типа такого: Services.env.get("windir") + "\\regedit.exe". А вместо wrk.open и process.runwAsync прописывал соответственно излишнее wrk.create и process.run

Теперь по вашему коду:
Без проверки на окно пишет, что CustomizableUI не определен. А почему ошибок нет через try {} catch(e) {}? Получается, что при запуске браузера идет как минимум 2 обращения и какое-то из них с ошибкой, но мы ее не видим? Или еще как-то писали про возвращение promise и при try это происходит позже на этапе, когда уже CustomizableUI инициализирован?
Компьютер\\ можно убрать, это автоматом дописывается.
wrk.close() стоит добавить или нет необходимости?
Если runwAsync без аргументов, то правильней ([], 0) или (null, null)?

Отредактировано 6e73epo (01-10-2023 01:03:18)

Отсутствует

 

№3001-10-2023 09:44:41

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

Re: userChrome.js

6e73epo пишет

Без проверки на окно пишет, что CustomizableUI не определен.

Ну, я-то, разумеется, в custom_script.js добавил.
Вызывать CustomizableUI.createWidget() следует только один раз,
поэтому в окнах такой код слегка неуместен.


Если нет выбора, то хотя бы следует проверять, что виджет уже создавался.
Типа флаг какой-нибудь куда-нибудь поставить, или, может проверять, что
CustomizableUI.getWidget(id).provider равно (или не равно) "api"


К тому же, если коде самого виджета есть что-то связанное с promise, какой-то async, то если
закрыть окно браузера в котором виджет создавался, то этот функционал перестанет работать.

А почему ошибок нет через try {} catch(e) {}? Получается, что при запуске браузера идет как минимум 2 обращения и какое-то из них с ошибкой, но мы ее не видим?

Видим мы ошибку, или не видим, зависит от того, что находится в блоке catch.
Если там что-то типа Cu.reportError(e) или console.error(e), то дожно быть видно.
А если блок catch пустой, то тогда неудивительно, на то он и try.


2 обращения, даже не знаю, может второе это, собственно,
от самого консольского окна, в него ведь тоже могут скрипты грузиться.

Или еще как-то писали про возвращение promise и при try это происходит позже на этапе, когда уже CustomizableUI инициализирован?

Нет, try никак не должен влиять. Да это и не важно.
Первый же инлайн скрипт в browser.xhtml подгружает скрипт browser.js
который устанавливает в окно кучу геттеров модулей, среди которых и CustomizableUI.


То есть, это ещё до события "DOMContentLoaded".
Не думаю, что существует какой-либо uc-загрузчик, который подгружает скрипты так рано.
Ноборот, дожидаются этого сбытия, или даже события "load".

Компьютер\\ можно убрать, это автоматом дописывается.

Хорошая находка.
Я-то просто написал по-аналогии с тем, что там увидел.

wrk.close() стоит добавить или нет необходимости?

О, кстати да, лучше добавить.

Если runwAsync без аргументов, то правильней ([], 0) или (null, null)?

Как правильней мне, конечно, не известно, но если метод принимает null
без негативных последствий, то и хорошо, не создаётся ненужный пустой массив.
(null, 0) выглядит логичней.
С другой стороны, если без аргументов, то почему бы не просто file.launch();
возможно, есть приемущество в асинхронном вызове, типа UI браузера не подвиснет,
пока он за файлом сходит, не знаю.

Отсутствует

 

№3101-10-2023 20:49:30

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 207
UA: Firefox 117.0

Re: userChrome.js

Dumby, благодарю за подробные ответы. Добавил проверку на окно, теперь ошибок нет. Думаю, что дополнительных проверок не требуется и виджет повторно не запустится, иначе будет ошибка нормализации. Дополнительных окон никогда не открываю.
Состряпал кнопку для открытия папки Roaming. Не знаю, правильно или нет, но работает и без ошибок. Иконки не нужны, т.к. вывожу кнопки на доп. панель в текстовом виде

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

Выделить код

Код:

// Import from userChrome.js
location.href.endsWith("://browser/content/browser.xhtml") && (async (file) => CustomizableUI.createWidget({
	id: "openfolder-roaming",
	label: "roaming",
	tooltiptext: "Open Users/./Roaming",
	localized: false,
	onCreated() {
		file = Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent.parent;
	},
	onCommand() {
		if (file.exists()) file.launch();
	}
}))();

Отсутствует

 

№3201-10-2023 23:24:51

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

Re: userChrome.js

6e73epo пишет

Состряпал кнопку для открытия папки Roaming. Не знаю, правильно или нет, но работает и без ошибок.

Возможно, стоит добавить, что вроде как есть свойство "AppData"
по которому Services.dirsvc.get() отдаст папку Roaming сразу,
и обращение к parent'ам не потребуется.


И добавить, что это всё, конечно, должно подходить для установленной версии,
но вот, например, у меня портабельные лисицы на libportable,
так вот там, ни "XREUSysExt", ни "AppData", да вообще почти ничего,
никакой папки Roaming в пути не содержат.


Насколько я вижу, остались только "Progs" (четыре раза parent) и "CookD" (три раза parent).
Ещё можно от "Home" прибавить AppData и Roaming.
Ну, это я в том смысле, что если стоит задача получить с такой лисицы именно «Users/./Roaming».

Отсутствует

 

№3302-10-2023 00:14:06

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 207
UA: Firefox 117.0

Re: userChrome.js

Dumby, в линуксе и маке вроде должны работать "XREUserNativeManifests" и "XRESysNativeManifests"

Отсутствует

 

№3402-10-2023 00:43:51

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

Re: userChrome.js

6e73epo пишет

в линуксе и маке

Об этих ничего не знаю.
Но попробовал сейчас на Debian, и показывает так:


XRESysNativeManifests —> /usr/lib/mozilla
XREUserNativeManifests —> /home/dumby/.mozilla


Мне это ни о чём не говорит :).

Отсутствует

 

№3502-10-2023 07:34:42

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Dumby
А возможно ли приспособить скрипт SingleHTML.jsm в загрузчик метода Endor8 ?
Очень нужный скрипт.

Отсутствует

 

№3602-10-2023 09:37:53

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

Re: userChrome.js

paths — путь до папки со скриптами (.js, .jsm, mjs), в данном случае %Профиль%\chrome\widget\
content — хром-регистрация на эту папку, в данном случае chrome://widget/content/

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

Выделить код

Код:

// UCF In background [System Principal] by Dumby, tested FF 115+
(async topic => {
	let ucf = async doc => {
		try { (async (xpc, cui) => {
			let paths = ["chrome", "widget"], content = "widget";

			let {ChromeUtils, Services} = Cu.getGlobalForObject(Cu),
				imp = ChromeUtils.import, impESM = ChromeUtils.importESModule,
				{XPCOMUtils} = impESM(xpc), {CustomizableUI} = impESM(cui);
			let sb = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(),
				{wantComponents: true, sandboxName: "ucfBox", sandboxPrototype: globalThis,});
			Object.assign(sb, {Services, XPCOMUtils, CustomizableUI, ChromeUtils});
		
			XPCOMUtils.defineLazyGlobalGetters(sb, [
				"atob", "btoa", "crypto", "fetch", "Blob", "CSS", "CSSRule", "Document",
				"DOMException", "DOMParser", "Element", "Event", "File", "FileReader", "FormData",
				"Headers", "InspectorUtils", "Node", "NodeFilter", "PathUtils", "Range", "Selection",
				"TextDecoder", "TextEncoder", "URL", "URLSearchParams", "XMLHttpRequest", "XMLSerializer"
			]);
			let m = {console: "Console"};
			m.AddonManager = m.AppConstants = m.E10SUtils = m.FileUtils = m.PlacesUtils = false;
			for (let [key, val] of Object.entries(m)) m[key] = `resource://gre/modules/${val || key}.sys.mjs`;
			m.setTimeout = m.setTimeoutWithTarget = m.clearTimeout = m.setInterval
				= m.setIntervalWithTarget = m.clearInterval = "resource://gre/modules/Timer.sys.mjs";
			ChromeUtils.defineESModuleGetters(sb, m);

			let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
			paths.forEach(dir.append);
			let ams = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
			sb[Symbol()] = ams.registerChrome(Services.io.newFileURI(dir), [["content", content, paths.pop() + "/"]]);
		
			let re = /\.(m)?js(m)?$/, lss = Services.scriptloader.loadSubScript, prfx = `chrome://${content}/content/`;
			for(let {leafName} of dir.directoryEntries) if (re.test(leafName))
				try {RegExp.$1 ? impESM(prfx + leafName) : RegExp.$2 ? imp(prfx + leafName) : lss(prfx + leafName, sb);} catch(ex) {Cu.reportError(ex);}
		})("resource://gre/modules/XPCOMUtils.sys.mjs", "resource:///modules/CustomizableUI.sys.mjs");
		} catch(ex) {Cu.reportError(ex);}
	}
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;
	let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
	obs.addObserver(ucf, topic, false);
	obs.addObserver(function quitucf(aDoc, qtopic) {
		obs.removeObserver(quitucf, qtopic);
		obs.removeObserver(ucf, topic);
	}, "quit-application-granted");
})("profile-after-change");

Отредактировано Farby (06-10-2023 10:57:21)


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

На форуме

 

№3702-10-2023 10:15:24

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 207
UA: Firefox 117.0

Re: userChrome.js

rubel, загрузчик Endor8 не рекомендую, а именно когда папка userChromeJS в core находится.

Отсутствует

 

№3802-10-2023 10:40:06

fuchsfan
Участник
 
Группа: Members
Зарегистрирован: 07-08-2023
Сообщений: 130
UA: Firefox 119.0

Re: userChrome.js

Farby пишет

Проще говоря закинул в папку и работает, но не забываем после изменения чистить startupCache

У меня из установленных 30 активировались 10, допускаю, что мог допустить ошибку.

6e73epo пишет

загрузчик Endor8 не рекомендую, а именно когда папка userChromeJS в core находится.

Чтобы "тупо" бекапить не только папку профиля, как все делают всю жизнь, но и папку core?

Отсутствует

 

№3902-10-2023 11:37:20

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

Re: userChrome.js

fuchsfan пишет

У меня из установленных 30 активировались 10, допускаю, что мог допустить ошибку.

Ну конечно у многих скриптов есть вызовы типа

Выделить код

Код:

chrome://user_chrome_files/content/custom_scripts/...

и в этом есть подвох! Надо менять на

Выделить код

Код:

chrome://widget/content/...

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

На форуме

 

№4002-10-2023 11:52:25

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby
Ваш код я применил для файла System Principal.uc.js. Поместил его в папку со скриптами
D:\\Firefox 115.2.0esr\\Profiles\\chrome\\Scripts"
В папке chrome создал папку widget в неё поместил файл SingleHTML.jsm
Что еще нужно, растолкуйте, пожалуйста.
И что делать с кодом от Dumby ?

Отсутствует

 

№4102-10-2023 12:00:44

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 207
UA: Firefox 117.0

Re: userChrome.js

fuchsfan пишет

Чтобы "тупо" бекапить не только папку профиля, как все делают всю жизнь, но и папку core?

С загрузчиком Endor8 , хоть скрипты и работают, идут ошибки в главной консоли, не помогает даже проверка на окно. Их не видно, потому что  catch(e) пустой.

Отсутствует

 

№4202-10-2023 12:04:28

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

Re: userChrome.js

rubel
Не в папку со скриптами, а добавить в config.js который расположен рядом с firefox.exe


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

На форуме

 

№4302-10-2023 12:30:43

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby

В папке chrome создал папку widget в неё поместил файл SingleHTML.jsm
Вот такой сейчас config.js

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

Выделить код

Код:

// config.js

try {
  Cu.importGlobalProperties(['PathUtils']);

  if (!Services.appinfo.inSafeMode) {
    let path = PathUtils.parent(PathUtils.xulLibraryPath);
    if (Services.appinfo.OS == 'Darwin') { // macOS
      path = PathUtils.join(PathUtils.parent(path), 'Resources');
    }
    var ucjsDirPath = PathUtils.join(path, 'userChromeJS');
    path = PathUtils.join(ucjsDirPath, 'main.js');
    const mainFileURI = PathUtils.toFileURI(path);
    Services.scriptloader.loadSubScript(mainFileURI, this, 'UTF-8');
  }
}
catch(e) {
  Cu.reportError(e);
}

// UCF In background [System Principal] by Dumby
(async topic => {
	let ucf = async doc => {
		try { (async (xpc, cui) => {
			let paths = ["chrome", "widget"], content = "widget";

			let {ChromeUtils, Services} = Cu.getGlobalForObject(Cu),
				imp = ChromeUtils.import, impESM = ChromeUtils.importESModule,
				{XPCOMUtils} = impESM(xpc), {CustomizableUI} = impESM(cui);
			let sb = Cu.Sandbox(Cu.getObjectPrincipal(this), {wantComponents: true, sandboxName: "ucfBox"});
			Object.assign(sb, {Services, XPCOMUtils, CustomizableUI, ChromeUtils});
		
			XPCOMUtils.defineLazyGlobalGetters(sb, [
				"atob", "btoa", "crypto", "fetch", "Blob", "CSS", "CSSRule", "Document",
				"DOMException", "DOMParser", "Element", "Event", "File", "FileReader",
				"FormData", "Headers", "InspectorUtils", "Node", "NodeFilter", "Range", "Selection",
				"TextDecoder", "TextEncoder", "URL", "URLSearchParams", "XMLHttpRequest", "XMLSerializer"
			]);
			let m = {console: "Console"};
			m.AddonManager = m.AppConstants = m.E10SUtils = m.FileUtils = m.PlacesUtils = false;
			for (let [key, val] of Object.entries(m)) m[key] = `resource://gre/modules/${val || key}.sys.jsm`;
			m.setTimeout = m.setTimeoutWithTarget = m.clearTimeout = m.setInterval
				= m.setIntervalWithTarget = m.clearInterval = "resource://gre/modules/Timer.sys.mjs";
			ChromeUtils.defineESModuleGetters(sb, m);

			let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
			paths.forEach(dir.append);
			let ams = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
			sb[Symbol()] = ams.registerChrome(Services.io.newFileURI(dir), [["content", content, paths.pop() + "/"]]);
		
			let re = /\.(m)?js(m)?$/, lss = Services.scriptloader.loadSubScript, prfx = `chrome://${content}/content/`;
			for(let {leafName} of dir.directoryEntries) if (re.test(leafName))
				try {RegExp.$1 ? impESM(prfx + leafName) : RegExp.$2 ? imp(prfx + leafName) : lss(prfx + leafName, sb);} catch(ex) {Cu.reportError(ex);}
		})("resource://gre/modules/XPCOMUtils.sys.mjs", "resource:///modules/CustomizableUI.sys.mjs");
		} catch(ex) {Cu.reportError(ex);}
	}
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;
	let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
	obs.addObserver(ucf, topic, false);
	obs.addObserver(function quit(s, t) {
		obs.removeObserver(quit, t);
		obs.removeObserver(ucf, topic);
	}, "quit-application-granted");
})("profile-after-change");


Почистил startupCache, меню не появляется. Где ещё посмотреть ?

Отсутствует

 

№4402-10-2023 12:50:58

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

Re: userChrome.js

rubel пишет

Где ещё посмотреть ?

файл точно называется SingleHTML.jsm, а не как нибудь так SingleHTML.jsm.txt?
url: chrome://widget/content/SingleHTML.jsm открывается?


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

На форуме

 

№4502-10-2023 13:12:26

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby

url: chrome://widget/content/SingleHTML.jsm открывается?

Сейчас открывается, но все равно меню не появляется.

Отредактировано rubel (02-10-2023 13:21:41)

Отсутствует

 

№4602-10-2023 13:53:58

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

Re: userChrome.js

rubel
В меню гамбургера точно нет?

Отредактировано Farby (03-10-2023 00:12:16)


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

На форуме

 

№4702-10-2023 13:59:58

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby
Точно нет.

Отсутствует

 

№4802-10-2023 14:35:06

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

Re: userChrome.js

rubel
Тогда последняя догадка, код скрипта должен быть в кодировке utf-8
chrome://widget/content/SingleHTML.jsm должны читаться русские буквы.


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

На форуме

 

№4902-10-2023 14:57:24

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby
Русские буквы читаются. Я же скачал этот файл у Доброва.

Добавлено 02-10-2023 15:17:31
Farby
Все заработало! Оказывается сейчас с  https://github.com/VicDobrov/UserChromeFiles/blob/main/profile_ucf_dobrov/chrome/user_chrome_files/custom_scripts/SingleHTML.jsm скачивается файл размером 159 кб. А раньше я качал этот файл размером 13 кб.
Вот он и заработал при замене. Спасибо Вам за помощь. Будут вопросы тогда задам.

Отредактировано rubel (02-10-2023 15:17:31)

Отсутствует

 

№5002-10-2023 18:04:45

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 570
UA: Firefox 115.0

Re: userChrome.js

Farby
Нет, этот скрипт нормально не работает.
Открываю  Firefox, открываю вкладку, на ней скрипт работает. Можно сохранить. Открываю вторую вкладку, на ней скрипт не работает.
Это поправимо ?

Отсутствует

 

Board footer

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