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

Mozilla Россия — свежие версии программ Mozilla, а также масса полезной информации по каждому продукту.

№1327623-05-2019 21:21:11

bunda1
Moderator
 
Группа: Moderators
Откуда: Латвия
Зарегистрирован: 09-02-2010
Сообщений: 4811
UA: Firefox 60.0

Re: Custom Buttons

Отсутствует

 

№1327724-05-2019 10:32:46

intersk
Участник
 
Группа: Members
Зарегистрирован: 20-06-2013
Сообщений: 104
UA: unknown 0.0

Re: Custom Buttons

Garalf пишет

interskБольшинство ваших кнопок на последних версиях работать не будут, но местные умельцы их регулярно правят.Если хотите оценить, попробуйте портабельную сборку FF66 https://www.upload.ee/files/9813755/FF66.rar.htmlВсе кнопки рабочие.Автор сборки - Drage
                    Отредактировано Garalf (14-04-2019 11:41:20)

а на 67 можно сделать сборку, чтобы и кнопки CB работали и телеметрию со слежением вычистить? была бы отличная сборка

Отсутствует

 

№1327824-05-2019 12:53:50

anywho
Забанен
 
Группа: Members
Зарегистрирован: 21-05-2019
Сообщений: 54
UA: Firefox 67.0

Re: Custom Buttons

intersk

Отредактировано anywho (24-05-2019 12:55:00)

Отсутствует

 

№1327924-05-2019 14:48:28

Quartz1t
Участник
 
Группа: Members
Зарегистрирован: 25-11-2013
Сообщений: 111
UA: Firefox 60.0

Re: Custom Buttons

Подскажите, где можно найти инструкцию по установке Custom Buttons на FF.

Отсутствует

 

№1328024-05-2019 15:22:41

bunda1
Moderator
 
Группа: Moderators
Откуда: Латвия
Зарегистрирован: 09-02-2010
Сообщений: 4811
UA: Firefox 60.0

Re: Custom Buttons

Quartz1t пишет

Подскажите, где можно найти инструкцию по установке Custom Buttons на FF.

Вот FF64 с уже установленным Custom Buttons: https://yadi.sk/d/Q07HVCUme7wOJg

Отсутствует

 

№1328124-05-2019 16:08:58

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 42.0

Re: Custom Buttons

Вот есть кнопка отрывающая указанный адрес в новой вкладке

Выделить код

Код:

gBrowser.selectedTab = gBrowser.addTab('https://www.inoreader.com/login');

А можете пожалуйста добавить чтобы по нажатию ПКМ на ней, этот адрес отрывался в активной вкладке?

Отсутствует

 

№1328224-05-2019 18:04:58

kokoss
Участник
 
Группа: Members
Зарегистрирован: 15-02-2018
Сообщений: 1728
UA: Firefox 52.0

Re: Custom Buttons

Quartz1t
Для установки Custom Buttons в [firefox] 60 нужно отключить проверку цифровых подписей.


Win7

Отсутствует

 

№1328324-05-2019 18:40:46

anywho
Забанен
 
Группа: Members
Зарегистрирован: 21-05-2019
Сообщений: 54
UA: Firefox 67.0

Re: Custom Buttons

pref("general.config.sandbox_enabled", false);
Забыл это в config.js , короче bootstrap заработал в 67  , вопрос снят . Кстати, скрипт в каталоге, тоже назван   config.js , плюс, в нем же  блокировки от mozilla.cfg и все работает...
Господину Dumby велико респект ! и хвала!
Кстати, после этих "костылей" память жрать на 50% меньше стал....Странно...

Отредактировано anywho (24-05-2019 19:18:35)

Отсутствует

 

№1328424-05-2019 19:41:18

func4ptch4
Участник
 
Группа: Members
Зарегистрирован: 03-05-2018
Сообщений: 220
UA: Firefox 66.0

Re: Custom Buttons

Stkvsky что то не пойму, не легче в закладках сделать?

Отсутствует

 

№1328524-05-2019 20:33:06

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 42.0

Re: Custom Buttons

func4ptch4 если нужно заморачиваться, тогда не стоит.

Отредактировано Stkvsky (25-05-2019 03:15:31)

Отсутствует

 

№1328624-05-2019 21:39:16

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

Re: Custom Buttons

Andrey_Krropotkin пишет

пункт удалить кнопку, в консоли пишет TypeError: this.getElementsByAttribute is not a function

Да, есть такое. Не представляю откуда там this прописался.
Замени его на document

Ну или вот, свежак. Понятно, что считай ничто, но просто 69-я
так весело стартовала, что может интересно посмотреть.
Оценить масштаб отвала, чуток попрактиковаться в правке кнопок.
На специально созданом профиле, конечно же.

custom_buttons-0.0.7.0.0.5a1-fx-paxmod.xpi
custom_buttons-0.0.7.0.0.5a1-fx-bootstrap.xpi

Andrey_Krropotkin пишет

на 67 перестал работать ваш исправленный инспектор Dom

Какое там исправленный. Так, затолкал кой-что обратно, как смог.

dom_inspector-7.0.2-fx-paxmod.xpi
dom_inspector-7.0.2-fx-bootstrap.xpi

Отсутствует

 

№1328725-05-2019 00:10:10

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 476
UA: Firefox 67.0

Re: Custom Buttons

Dumby спасибо утешили, dom_inspector-7.0.2-fx-paxmod.xpi заработал, а на custom_buttons-0.0.7.0.0.5a1-fx-paxmod.xpi та же ошибка осталась, хотя на ночнушке 69 все работает.
Только почти все кнопки в одном месте, неохота говорить в каком. С первого взгляда опять намудрили с  меню кнопок, окнами и многое другое. Где можно почитать про изменения?

Отредактировано Andrey_Krropotkin (25-05-2019 01:09:00)

Отсутствует

 

№1328825-05-2019 08:46:14

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

Re: Custom Buttons

Andrey_Krropotkin пишет

та же ошибка осталась

Ну тогда не знаю. Разве что может закрыть браузер и удалить
содержимое папки startupCache. Или можно перезапустить с очисткой кодом:

Services.appinfo.invalidateCachesOnRestart();
BrowserUtils.restartApplication();

Andrey_Krropotkin пишет

Где можно почитать про изменения?

На багзилле, в коде самого Firefox, может ещё где-то, я не в курсе.
Могу попробовать рассказать в общих чертах, как мне кажется.

скрытый текст
Первое.
Уже со второго билда взяли и переключили адрес браузера с browser.xul на browser.xhtml
А это значит, что метод document.createElement() стал возвращать не XUL элементы, а HTML элементы.
Это делает их не только бесполезными, но и корёжащими интерфейс.
А для любителей попросить элемент у XUL документа заботливо предусмотрен краш приложения.

Таким образом, чтобы получить XUL элемент, нужно воспользоваться другим методом.
Напрмер, заменить   document.createElement(…   на   document.createElementNS(xulns, …
Или заменить на   document.createXULElement(…   но этот метод добавлен только с Firefox 63+

Второе.
С элемента <toolbarbutton> сняли биндинг и переделали как Custom Element.
Реализацию можно посмотреть посетив адрес chrome://global/content/elements/toolbarbutton.js

Это значит, что image, label'ы и dropmarker больше не являются anonymous nodes,
а просто такими же, как другие прочие. То есть, например, чистая кнопка
с кодом alert(this.childElementCount); до 69 покажет 0, а с 69 покажет 4.

Это нужно учитывать, и если, к примеру, в кнопке определяется image
var image = document.getAnonymousElementByAttribute(this, "class", "toolbarbutton-icon");
то, наверно, можно записать как
var image = this.icon || document.getAnonymousElementByAttribute(this, "class", "toolbarbutton-icon");
И тому подобное.

Отсутствует

 

№1328925-05-2019 09:06:12

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 476
UA: Firefox 67.0

Re: Custom Buttons

Dumby на счет ошибки не заморачиваюсь, т.к. на 69 пока без проблем. На 69 у CB  в редакторе пропала кнопка сохранить, но так все работает. Так же dom работает и кнопка Attributes Inspector. 
Ваша кнопка "Консоль браузера" последняя убивает броузер в один миг. Пока разбираюсь насчет createXULElement.
Пример старый код

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

Выделить код

Код:

// Добавыть в контекстное меню страницы пункт "В начало страницы" 
(function () {
if ( document.getElementById("context-addBackToTop") ) return; 
   var icon1="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACsklEQVQ4jX2TT2gcdRTHP%2B%2F3m%2Bnszs7umk2TbjbENFpXS2K02lZoKqUKFlsoFAmlZzUHETyI54J4KJWC9KwepAdzkIAXEU968qSiBS2RttRk2RD7Z01ndmZnfr%2Bfhw1ijfrgwYP3vp8Hj%2B8T%2Fi907QK1fSMMum%2FhV1LSHqgQynsBA3e%2FRf2HVKGqH9aPn39ndunKkky9%2FBV5HuHMzh3%2FIi4RjC%2BHx949d%2BzsG%2BrFhT2EEwcevn5j86Trfv8Znp%2FgjwIW0rUdgN2UJ1eCFy6dfOrUqzK1RxMAz87VeWjmcPOXG%2F1Fu%2FHjFwT124jsAMxQa3%2Bun39%2FoXXkLKNV0A60Ai0w3w5pPPLcyK83%2B4uDjdVv0NIh7fwFmKcxv6IOX5oP9p%2BmVAYP8PUwcZAPoD1dYrx9MFq9ZRazznc%2Fka2takS9xPjCp%2FLkB4%2F6zeMoH6waCj0Fsn1mY6GfQWsioDV7qLS%2B6Z2JO1dvaWrty7TfOyilQ4hJEHG4XR5OC1pABPICMgNpDrfvFQySBKlO%2B92tyRMeqvoJzo677kelwmSPyxOv%2BVIJiZXDGKE%2FgOkGNCsQ7YLORsaXH6%2FEiFyz3si6hzPLZN1l7nwdktvVIl5q5b8XkDj8psfsXuHABJQ9CALIe5BtJT%2FYPD1KXePhBnD9MvxxtWD3olaxpTnmmJ0TjszBVAOMgcJAWIYosChV09Z6IOCx9TM4OzRQKpw6qnnljM9IBZyDLAfHsI4qEHoW0ly4XwAp3rZ4GLnhmbbm6cdgc3PIDf1hyzmoRlDVDvpmeNU8xXvA%2F05ULYDJGlT%2B8SXOQb0C1yKBtFD0c%2FA1HlS3RwYJRRqv%2F5aMra9VuJ8ZnHsQkJQVazdjbGYMxgEWQaLtkRSYeb069vbFxmgrcjazfwcAKKVUrxfH93p33kTcFRD%2BBJjxDD9ykwv%2FAAAAAElFTkSuQmCC"; 
   var AddBackToTopMenuitem = document.createElement("menuitem");   
   AddBackToTopMenuitem.setAttribute("id", "context-addBackToTop");
   AddBackToTopMenuitem.setAttribute("label", "В начало страницы");  
   AddBackToTopMenuitem.setAttribute("class", "menuitem-iconic");
   AddBackToTopMenuitem.setAttribute("image", icon1);
   AddBackToTopMenuitem.addEventListener("command", function () { window.content.scrollTo(0,0); });

  var contextMenu = document.getElementById("contentAreaContextMenu"); 
  contextMenu.appendChild( AddBackToTopMenuitem );
})();


Пример новый код
скрытый текст

Выделить код

Код:

// Добавыть в контекстное меню страницы пункт "В начало страницы" 
(function () {
if ( document.getElementById("context-addBackToTop") ) return; 
   var icon1="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACsklEQVQ4jX2TT2gcdRTHP%2B%2F3m%2Bnszs7umk2TbjbENFpXS2K02lZoKqUKFlsoFAmlZzUHETyI54J4KJWC9KwepAdzkIAXEU968qSiBS2RttRk2RD7Z01ndmZnfr%2Bfhw1ijfrgwYP3vp8Hj%2B8T%2Fi907QK1fSMMum%2FhV1LSHqgQynsBA3e%2FRf2HVKGqH9aPn39ndunKkky9%2FBV5HuHMzh3%2FIi4RjC%2BHx949d%2BzsG%2BrFhT2EEwcevn5j86Trfv8Znp%2FgjwIW0rUdgN2UJ1eCFy6dfOrUqzK1RxMAz87VeWjmcPOXG%2F1Fu%2FHjFwT124jsAMxQa3%2Bun39%2FoXXkLKNV0A60Ai0w3w5pPPLcyK83%2B4uDjdVv0NIh7fwFmKcxv6IOX5oP9p%2BmVAYP8PUwcZAPoD1dYrx9MFq9ZRazznc%2Fka2takS9xPjCp%2FLkB4%2F6zeMoH6waCj0Fsn1mY6GfQWsioDV7qLS%2B6Z2JO1dvaWrty7TfOyilQ4hJEHG4XR5OC1pABPICMgNpDrfvFQySBKlO%2B92tyRMeqvoJzo677kelwmSPyxOv%2BVIJiZXDGKE%2FgOkGNCsQ7YLORsaXH6%2FEiFyz3si6hzPLZN1l7nwdktvVIl5q5b8XkDj8psfsXuHABJQ9CALIe5BtJT%2FYPD1KXePhBnD9MvxxtWD3olaxpTnmmJ0TjszBVAOMgcJAWIYosChV09Z6IOCx9TM4OzRQKpw6qnnljM9IBZyDLAfHsI4qEHoW0ly4XwAp3rZ4GLnhmbbm6cdgc3PIDf1hyzmoRlDVDvpmeNU8xXvA%2F05ULYDJGlT%2B8SXOQb0C1yKBtFD0c%2FA1HlS3RwYJRRqv%2F5aMra9VuJ8ZnHsQkJQVazdjbGYMxgEWQaLtkRSYeb069vbFxmgrcjazfwcAKKVUrxfH93p33kTcFRD%2BBJjxDD9ykwv%2FAAAAAElFTkSuQmCC"; 
   var AddBackToTopMenuitem = document.createXULElement("menuitem");   
   AddBackToTopMenuitem.setAttribute("id", "context-addBackToTop");
   AddBackToTopMenuitem.setAttribute("label", "В начало страницы");  
   AddBackToTopMenuitem.setAttribute("class", "menuitem-iconic");
   AddBackToTopMenuitem.setAttribute("image", icon1);
   AddBackToTopMenuitem.addEventListener("command", function () { window.content.scrollTo(0,0); });

   var refItem = document.getElementById("context-sep-ctp");
  refItem.parentNode.insertBefore(AddBackToTopMenuitem, refItem);
})();


Второй вариант
скрытый текст

Выделить код

Код:

(function () {
if ( document.getElementById("context-addTopToBack") ) return; 
var icon2="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw5JREFUOE9tk3lIlHkYx593HB3Hxmk2bbpTAzWzJLssq6Uohm6SaoMdEyIskILa6KDooGN3gjTLMpsyD+y0g2ylaNuFRUPppAILWjYopKjWsbA/nKn59Exjx0YPfF9eXn7fz/tcP5Fuxao9IjHFuY4+1e0Jg88GBqTUdn6tgam1nQlpdYFYZ5lPjOR8Mcwihi0sMavZKBST9UDTjrJW7j5+R0OLn8YHX3TtoV+/v2f1b88R0+zHIpYokVg1h2Tdq5AiMTvKrldebudFJzxqg398/9erABTVvEGiFrxQl0PVFd33K6hEzL2PNHvO+Gh6Clda4I8HXdL3qw/h7n+wtVwBlvnP1WUPm0OReEQkvlTMSRXNG0+187uaTt6EU7fgtOrCPai7D1XNMGedD7Et/AIwTPoYUaMQr5jTappWHXtD5XUo/Ru8jVDepH+tD+LaFaCf20/E+FdI/E8KMKLEPkwx6QrIVMCgIjGlljbmH+2g8C/49RJsugizvOBYE0QW+ZF5AQxXG9JzTqv0nRsjww9r+ZkiEePO11lHV92yjqrsmFvaQcFZyKuBSfshaRvEbYBuvwSJXgaROZpBmscvQ/fcl6yLN8QxaqFEZh3tyN7SirviNXknArhPQE4VuA7B+BIY4gHnJohZGcSS+xbzrDaMqdrMsfWIPbVex5iU28t1+G1+7Ts8WvNaTb/gPOQqaGY5jNsLyTu1lPVgLYAoNxiTryHOCU+0ia6PzRTbyBn27GLf0uOvOXAbNl+FFRfAfTycSeZu6K/lWFeCyXUJ6ZHRoq6MsNkUKdIjW9d5zGjbyI3PFle349WxbdFmLtV+5FTCjwchYTtETKvVtFNuqispbA5FCBA3USETdCP7pERmrP735/KX7NNM1mg5eWcgS8uwzDiGWPv9qY74sPFTfALETdZ+9NcR2+NN6cvvTC96wuYG7YPXT8yUkqBYnOf0dHTY9HV8BkzSG5mo9yROy0m0GYOXNKSva6H71ML3YorVdZXQ2n0nvgVEO/WqJCukb7T8MOKQRNg9XSe/EyIfACW3trMbW6y6AAAAAElFTkSuQmCC";
   var AddTopToBackMenuitem = document.createElementNS(xulns,"menuitem");   
   AddTopToBackMenuitem.setAttribute("id", "context-addTopToBack");
   AddTopToBackMenuitem.setAttribute("label", "В конец страницы страницы");  
   AddTopToBackMenuitem.setAttribute("class", "menuitem-iconic");
   AddTopToBackMenuitem.setAttribute("image", icon2);
   AddTopToBackMenuitem.addEventListener("command", function () { window.content.scrollTo(0,999999); });
 
  var contextMenu = document.getElementById("contentAreaContextMenu"); 
   contextMenu.appendChild( AddTopToBackMenuitem );
})();

Отредактировано Andrey_Krropotkin (25-05-2019 16:29:54)

Отсутствует

 

№1329025-05-2019 19:23:36

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

Re: Custom Buttons

Andrey_Krropotkin пишет

На 69 у CB  в редакторе пропала кнопка сохранить

Вроде не должна пропасть, может галка в настройках слетела.
И Ctrl+S должно работать.

Andrey_Krropotkin пишет

Ваша кнопка "Консоль браузера" последняя убивает броузер в один миг.

У меня сейчас такая

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

Выделить код

Код:

({
    title: "Консоль браузера",
    url: "chrome://devtools/content/webconsole/index.html",
    icon: "chrome://devtools/skin/images/tool-webconsole.svg",
    init() {
        var trg = document.getElementById("browser");
        trg && addEventListener("DOMContentLoaded", this, false, trg);
        var id = "viewBrowserConsoleSidebar";

        var menuitem = this.element("menuitem", {
            type: "checkbox",
            label: this.title,
            id: "menu_browserConsoleSidebar",
            oncommand: `SidebarUI.toggle("${id}");`
        }, document.getElementById("viewSidebarMenu"));

        var btn = this.element("toolbarbutton", {
            type: "checkbox",
            label: this.title,
            id: "sidebar-switcher-browserConsole",
            oncommand: `SidebarUI.show("${id}");`,
            class: "subviewbutton subviewbutton-iconic"
        });
        document.querySelector(
            'toolbarbutton[id^="sidebar-switcher-"] + toolbarseparator'
        ).before(btn);

        SidebarUI.sidebars.set(id, {
            url: this.url,
            buttonId: btn.id,
            title: this.title,
            menuId: menuitem.id
        });
        SidebarUI.isOpen && SidebarUI.currentID == id && SidebarUI.selectMenuItem(id);

        var popupset = this.popupset = this.element("popupset", {
            id: `CB${_id.slice(20)}-browserConsole-popupset`
        }, document.documentElement);

        var css = `\
            #${btn.id} > .toolbarbutton-icon,
            #sidebar-box[sidebarcommand="${id}"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
                list-style-image: url(${this.icon});
            }`;
        var str = "data:text/css," + encodeURIComponent(css), type = windowUtils.USER_SHEET;
        windowUtils.loadSheetUsingURIString(str, type);

        addDestructor(() => {
            SidebarUI.sidebars.delete(id);
            btn.remove(); menuitem.remove(); popupset.remove();
            windowUtils.removeSheetUsingURIString(str, type);
        });
        "insertFTLIfNeeded" in MozXULElement && MozXULElement
            .insertFTLIfNeeded("toolkit/main-window/editmenu.ftl");

        self.onclick = e => {
            if (e.button == 2) return;
            if (!e.button) return SidebarUI.toggle(id);
            var st = gBrowser.selectedTab, tab;
            if (!e.ctrlKey) tab = gBrowser.visibleTabs.find(tab => {
                var br = gBrowser.getBrowserForTab(tab);
                return br.currentURI.spec == this.url || (
                    "_cachedCurrentURI" in br
                    && br._cachedCurrentURI.spec == this.url
                )
            });
            if (tab == st) return;
            if (!tab) tab = gBrowser.addTrustedTab(this.url);
            gBrowser.moveTabTo(tab, st._tPos + 1);
            gBrowser.selectedTab = tab;
        }
        for(var br of gBrowser.browsers) {
            if (br.currentURI.spec != this.url) continue;
            var doc = br.contentDocument;
            if (doc && (
                doc.readyState == "complete" ||
                doc.readyState == "interactive"
            ))
                doc.querySelector(
                    "main#app-wrapper,div#output-container"
                ).childElementCount
                    ? this.defineDocPopupset(doc)
                    : this.handleEvent({target: doc});
        }
        if (!btn.hasAttribute("checked")) return;
        var doc = SidebarUI.browser.contentDocument;
        if (doc.documentURI != this.url) btn.doCommand();
        else if (doc.readyState == "complete") this.defineDocPopupset(doc);
    },
    defineDocPopupset(doc) {
        this.definePopupset(
            doc.querySelector("popupset") ||
            doc.documentElement.appendChild(doc.createXULElement("popupset"))
        );
    },
    get definePopupset() {
        var append = customElements.get("menuitem")
            ? popup => {
                this.popupset.appendChild(popup);
                popup.setAttribute("oncommand", "event.target.cmd()");
                for(var node of [...popup.querySelectorAll("menuitem")]) {
                    var menuitem = document.importNode(node, true);
                    menuitem.cmd = Services.els.getListenerInfoFor(node)
                        .find(inf => inf.type == "command").listenerObject;
                    popup.replaceChild(menuitem, node);
                }
                return popup;
            }
            : this.popupset.appendChild.bind(this.popupset);

        delete this.definePopupset;
        return this.definePopupset = popupset => popupset.appendChild = append;
    },
    async handleEvent({target: doc}) {
        if (!doc || Cu.isCrossProcessWrapper(doc) || doc.documentURI != this.url) return;

        var win = doc.defaultView;
        if (
            win.docShell.name == "toolbox-panel-iframe-webconsole" ||
            doc.DOMContentLoadedEventHandled
        )
            return;
        doc.DOMContentLoadedEventHandled = true;
        "custombuttonsConsole" in win || Services.scriptloader.loadSubScript(
            "chrome://custombuttons/content/consoleOverlay.js", win
        );
        var cw = win.isChromeWindow;
        if (!cw && doc.visibilityState == "hidden") {
            var {focus} = win;
            win.focus = () => win.focus = focus;
        }
        this.loader.Services.ww.wins.push(win);
        var bc = await new this.loader.HUDService().toggleBrowserConsole();
        this.defineDocPopupset(doc);
        if (cw) return;

        var br = win.docShell.chromeEventHandler;
        var cmAttr = br.getAttribute("contextmenu");
        cmAttr && br.removeAttribute("contextmenu");
        win.onbeforeunload = () => {
            bc.chromeWindow = {close() {}};
            cmAttr && br.setAttribute("contextmenu", cmAttr);
        }
        var link = doc.createElement("link");
        link.setAttribute("rel", "shortcut icon");
        link.setAttribute("href", this.icon);
        doc.head.prepend(link);
    },
    get loader() {
        delete this.loader;
        var id = _id + "-browser-console";
        var url = "resource://devtools/shared/Loader.jsm?" + id;
        var loader = {exports: {}}, nsvo = Cu.import(url, loader);
        addDestructor(reason => reason[5] == "e" && Cu.unload(url));

        if (id in nsvo) return this.loader = nsvo[id];

        Services.scriptloader.loadSubScript("resource://devtools/client/webconsole/hudservice.js", loader);
        var e = new CustomEvent("DOMContentLoaded", {bubbles: false}), ww = loader.Services.ww;
        loader.Services.ww = Cu.getGlobalForObject(nsvo).Object.create(ww, {
            wins: {value: []},
            openWindow: {value: function() {
                var win = this.wins.shift();
                win.setTimeout(() => win.dispatchEvent(e), 0);
                return win;
            }}
        });
        return this.loader = nsvo[id] = loader;
    },
    element(name, attrs, parent) {
        var node = document.createXULElement(name);
        for(var attr in attrs) node.setAttribute(attr, attrs[attr]);
        parent && parent.append(node);
        return node;
    }
}).init();

Отсутствует

 

№1329125-05-2019 21:05:10

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 476
UA: Firefox 69.0

Re: Custom Buttons

Dumby Нет не пропала, пару раз нажал в настройках и появилась. Не понятно пока с алертами для кнопок - alertsService.showAlertNotification(image, filename, notification);, не хотят пока работать. И не понятно почему вот такая конструкция не работает
function $C( name, attr) {
        var el = document.createXULElement( name );
        if (attr) Object.keys(attr).forEach(function(n){ el.setAttribute(n, attr[n])});
        return el;
    };
вот в этом коде:

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

Выделить код

Код:

//Добавление в меню расширений дополнительных пунктов
(function(){

        var iconURL = "";  

 

    if (window.AM_Helper) { 
        window.AM_Helper.uninit();
        delete window.AM_Helper;
    }
    

    Cu.import("resource://gre/modules/Services.jsm");
    Cu.import("resource://gre/modules/AddonManager.jsm");

   
    window.AM_Helper = {
        init: function() {
            document.addEventListener("DOMContentLoaded", this, false);
            this.platformVersion = parseFloat(Services.appinfo.platformVersion);
        },
        uninit: function() {
            document.removeEventListener("DOMContentLoaded", this, false);
        },
          handleEvent: function(event){
            switch(event.type){
                    case "DOMContentLoaded":
                    var doc = event.target;
                    var win = doc.defaultView;
                    
                    if (["about:addons","chrome://mozapps/content/extensions/extensions.xul"].indexOf(doc.URL) == -1)
                        return;
                    this.addPopupMenu(doc);

                    win.AM_Helper = AM_Helper;
                    this.win = win;

                    var observer = new MutationObserver(function(e) {
                        e = e[e.length-1];
                        if(e.attributeName == "loading") {
                            var doc = e.target.ownerDocument;
                        }
                    });
                    observer.observe(doc.getElementById("detail-view"), {attributes: true});
                    break;
                    case "popupshowing":
                    this.getAddon(this.win.document.popupNode.value,
                                  this.setItemsAttributes,
                                  event);
                    break;
            }
        },
        
        
        //Создаем меню
        addPopupMenu: function(doc) {
        
         var mainicon2="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADwAAAAAwAAAP8AAAD/AAAA/wAAAPAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA8AAAAAAAAAD/AAAA/wAAAP8AAADwAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAAAAAA/wAAAP8AAAD/AAAA8AAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADwAAAAAAAAAP8AAAD/AAAA/wAAAPAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA8AAAAAAAAAD/AAAA/wAAAP8AAADwAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAAAAAA8wAAAP8AAAD/AAAA8AAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADwAAAAAAAAAPAAAAD/AAAA/wAAAPAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAAAAAAAAAAAHsAAAD/AAAAkAAAAAAAAADwAAAA/wAAAP8AAADwAAAA/wAAAP8AAAD/AAAA/wAAAP8AAADwAAAAAAAAAIQAAAD/AAAAjQAAAAAAAAB1AAAA/wAAAP8AAAD/AAAA8AAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA8AAAAIoAAAD/AAAAigAAAAAAAABpAAAAAAAAAAAAAAB7AAAA/wAAAIcAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAigAAAAAAAAB7AAAA8AAAAAAAAACEAAAA/wAAAIcAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAhwAAAAAAAACBAAAA/wAAAPAAAACKAAAA/wAAAIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAA/wAAAP8AAAD/AAAA/wAAAIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAIQAAAAAAAAAAAAAAAAAAAAAAB8AAAAfAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAxAAAAIgAAAAWAAAAJEAAAEDAAD+BwAA+A8AAA==";
        
        
            var ins = doc.getElementById("menuitem_uninstallItem");
            if (!ins) return;

            ins = ins.nextSibling;
            var popup = ins.parentNode;

            var menuitem = $C("menuseparator", {
                id: "AM-separator-1"
            });
            popup.insertBefore(menuitem, ins);


             menuitem = $C("menuitem", {
                id: "AM-browse-dir",
                label: "Папка установки",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.browseDir);"
            });
            popup.insertBefore(menuitem, ins);
            
            menuitem = $C("menuitem", {
                id: "AM-browse-Folder",
                label: "Файл установки",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.Folder);"
            });
            popup.insertBefore(menuitem, ins);

            menuitem = $C("menuitem", {
                id: "AM-open-url",
                label: "Страница на AMO",
                tooltiptext: null,
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.goAMO);"
            });
            popup.insertBefore(menuitem, ins);
            
            menuitem = $C("menuitem", {
                id: "AM-browse-support",
                label: "Страница поддержки",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.support);"
            });    
           popup.insertBefore(menuitem, ins);   
           
           menuitem = $C("menuitem", {
                id: "AM-browse-goHome",
                label: "Домашняя страница",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.goHome);"
            });
           popup.insertBefore(menuitem, ins);         

          // var menu = $C("menu", {
            //    id: "AM-menu",
                //class: "menu-iconic",
                //image: mainicon2,
            //    label: "Копировать",
           // });
           // popup.insertBefore(menu, ins);
           // var menuPopup = $C("menupopup", {
           //     id: "AM-menupopup",
          //  });
           // menu.appendChild(menuPopup);
            
            menuitem = $C("menuitem", {
                id: "AM-copy-name",
                label: "Копировать имя",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyName);"
            });
           // menuPopup.appendChild(menuitem);
            popup.insertBefore(menuitem, ins); 
            
             menuitem = $C("menuitem", {
                id: "AM-copy-version",
                label: "Копировать версию",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyVersion);"
            });
          //  menuPopup.appendChild(menuitem);
           popup.insertBefore(menuitem, ins); 
           
            menuitem = $C("menuitem", {
                id: "AM-copy-NameVersion",
                label: "Копировать имя и версию",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyNameVersion);"
            });
           // menuPopup.appendChild(menuitem);
           popup.insertBefore(menuitem, ins); 
           
            menuitem = $C("menuitem", {
                id: "AM-copy-id",
                label: "Копировать id",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyID);"
            });
           // menuPopup.appendChild(menuitem);
           popup.insertBefore(menuitem, ins); 

            popup.addEventListener("popupshowing", this, true);
        },
        
        
        //Указываем где и когда показывать элементы меню
        setItemsAttributes: function(aAddon, event) {
            var popup = event.target;
            var doc = popup.ownerDocument;

            var
                isExtension = (aAddon.type == "extension"),
                isTheme = (aAddon.type == "theme"),
                isPlugin = (aAddon.type == "plugin"),
                isService = (aAddon.type == "service"),
                isCustomButton = (aAddon.type == "custombuttons"),
                menuitem
            ;

            menuitem = doc.getElementById("AM-browse-dir");
            menuitem.hidden = isService || isTheme || isCustomButton;
                        
            menuitem = doc.getElementById("AM-browse-Folder");
            menuitem.hidden = isService || isTheme || isCustomButton || isPlugin;

            menuitem = doc.getElementById("AM-copy-name");
            menuitem.tooltipText = aAddon.name;
            
            
            menuitem = doc.getElementById("AM-copy-version");
            menuitem.tooltipText = aAddon.version;
            menuitem.hidden = isCustomButton || isTheme;
            
            menuitem = doc.getElementById("AM-copy-NameVersion");
            menuitem.tooltipText = aAddon.name + " " + aAddon.version;
            menuitem.hidden = isCustomButton || isTheme;
            
            menuitem = doc.getElementById("AM-copy-id");
            menuitem.tooltipText = "ID: " + aAddon.id;
            

            menuitem = doc.getElementById("AM-open-url");
            var amoURL = aAddon.reviewURL
                 ? aAddon.reviewURL.replace(/\/reviews\//, "/")
                 : null;
            menuitem.tooltipText = amoURL;
            menuitem.hidden = !amoURL || /addons.mozilla.org/.test(aAddon.homepageURL);
            
            menuitem = doc.getElementById("AM-browse-support");
            menuitem.tooltipText = aAddon.supportURL;
            menuitem.hidden = !aAddon.supportURL;
            
            menuitem = doc.getElementById("AM-browse-goHome");
            menuitem.tooltipText = aAddon.homepageURL;
            menuitem.hidden = !aAddon.homepageURL;
           
        },

        getPopupNode: function (aNode) {
            var doc = aNode.ownerDocument;
            return "triggerNode" in aNode.parentNode ? aNode.parentNode.triggerNode : doc.popupNode;
        },
        getAddon: function (aId, aCallback, aEvent) {
            var self = this;
            if (this.win.gDetailView._addon) {
                aCallback.apply(this, [this.win.gDetailView._addon, aEvent]);
                return;
            }

            (self.platformVersion < 61.0?
                new Promise((resolve, reject) => AddonManager.getAllAddons(addons => resolve(addons))):
                AddonManager.getAllAddons()
            ).then(addons => {
                for (var i = 0; i < addons.length; i++) {
                    if (addons[i].id == aId) {
                        aCallback.apply(self, [addons[i], aEvent]);
                        return;
                    }
                }
            });
        },


     //Открыть папку установки
        browseDir: function (aAddon) {
            switch (aAddon.type) {
                case "plugin":
                    var pathes = aAddon.pluginFullpath;
                    for (var i = 0; i < pathes.length; i++) {
                        this.revealPath(pathes[i]);
                    }
                    return;
                case "userchromejs":
                    var file = aAddon._script.file;
                    if (file.exists())
                        file.reveal();
                    return;
            }

            // addon
            var gecko = parseInt(Services.appinfo.platformVersion);
            var nsLocalFile = Components.Constructor("@mozilla.org/file/local;1", (gecko >= 14) ? "nsIFile" : "nsILocalFile",
                "initWithPath");

            var dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
            dir.append("extensions");
            dir.append(aAddon.id);
            var fileOrDir = dir.path + (dir.exists() ? "" : ".xpi");
           
            try {
                (new nsLocalFile(fileOrDir)).reveal();
            } catch (ex) {
                var addonDir = /.xpi$/.test(fileOrDir) ? dir.parent : dir;
                try {
                    if (addonDir.exists()) {
                        addonDir.launch();
                    }
                } catch (ex) {
                    var uri = Services.io.newFileURI(addonDir);
                    var protSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
                    getService(Ci.nsIExternalProtocolService);
                    protSvc.loadUrl(uri);
                }
            }
        },



        //Копировать имя  
        copyName: function (aAddon) {
            this.copyToClipboard(aAddon.name);
        },
         //Копировать ID 
    copyID: function (aAddon) {
            this.copyToClipboard("ID: " + aAddon.id);
        },
    //Копировать версию 
    copyVersion: function (aAddon) {
            this.copyToClipboard(aAddon.version);
        },
    //Копировать имя и версию
    copyNameVersion: function (aAddon) {
           this.copyToClipboard(aAddon.name + " " + aAddon.version);
        },
       //Открыть файл установки    
    Folder: function (aAddon) {
            var gecko = parseInt(Services.appinfo.platformVersion);
            var nsLocalFile = Components.Constructor("@mozilla.org/file/local;1", (gecko >= 14) ? "nsIFile" : "nsILocalFile",
                "initWithPath");
            var dir = Services.dirsvc.get("ProfD", Ci.nsIFile); 
                        dir.append('extensions');
                        dir.append(aAddon.id);
                            if ( dir.exists() ) dir.launch();
 
                            var file = Components.classes['@mozilla.org/file/directory_service;1']
                                      .getService(Components.interfaces.nsIProperties)
                                     .get('ProfD', Components.interfaces.nsIFile);       
                            file.append('extensions');
                            file.append( aAddon.id + '.xpi' )             
                            if ( file.exists() ) file.launch(); 
                            return; 
          },     
         //Страница на АМО
       goAMO: function (aAddon) {
        var sourceTracker = "/?src=external-Add-ons_Manager_Context_Menu-extension";
    if (aAddon.reviewURL) {
      var amoURL = aAddon.reviewURL.replace(/\/reviews\//, "/")
                             .replace(/\/(firefox|seamonkey|thunderbird|android)/, "")
                             .replace(/\/\?src\=api/, sourceTracker);
    }
    if (/personas.mozilla.org$/.test(aAddon.id)) {
      amoURL = "https://addons.mozilla.org/addon/" + aAddon.id.match(/\d+/) + sourceTracker;
      }
       openURL(amoURL);
       },
       
        //Домашняя страница
     goHome: function (aAddon) {
        var url = aAddon.homepageURL;
        if (!url) {
        if (aAddon.reviewURL) {
        url = aAddon.reviewURL.replace(/\/reviews\/.*$/, "/");
        } else {
        url = "https://addons.mozilla.org/search/?q="
            + encodeURIComponent(aAddon.name);
         }
        }
       openURL(url);
       },
       
       //Страница поддержки
       support: function(aAddon) {
        openURL(aAddon.supportURL);
       },
       //Вспомогательные функции
        get getPath() {
            var url = this.win.gViewController.viewObjects.detail._addon;
            if (!url) return false;
            return url.pluginFullpath || false;
        },
       revealPath: function(path){
            var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
            file.initWithPath(path);
            if(file.exists())
                file.reveal();
        },
        copyToClipboard: function (aString) {
            Cc["@mozilla.org/widget/clipboardhelper;1"].
                getService(Ci.nsIClipboardHelper).copyString(aString);
        }
    };


function $C( name, attr) {
        var el = document.createXULElement( name );
        if (attr) Object.keys(attr).forEach(function(n){ el.setAttribute(n, attr[n])});
        return el;
    };

    AM_Helper.init();

})();

Отредактировано Andrey_Krropotkin (25-05-2019 21:36:27)

Отсутствует

 

№1329225-05-2019 21:19:31

Quartz1t
Участник
 
Группа: Members
Зарегистрирован: 25-11-2013
Сообщений: 111
UA: Firefox 60.0

Re: Custom Buttons

Всё равно, никак не получается самостоятельно установить дополнение...
Вроде бы всё делаю правильно, по инструкции. Но никак,.. уходит в устаревшие. :(

Отредактировано Quartz1t (25-05-2019 21:20:32)

Отсутствует

 

№1329325-05-2019 21:46:54

anywho
Забанен
 
Группа: Members
Зарегистрирован: 21-05-2019
Сообщений: 54
UA: Firefox 67.0

Re: Custom Buttons

Quartz1t
Какие расширения? С xpcom xul ? Так они никак, хотя в FF 60 еще можно ...Настроек, правда, не видно будет, в ручную только...

Отсутствует

 

№1329425-05-2019 21:57:37

kokoss
Участник
 
Группа: Members
Зарегистрирован: 15-02-2018
Сообщений: 1728
UA: Firefox 52.0

Re: Custom Buttons

Quartz1t
Попробуйте в "Конфиге" включить параметр_extensions.legacy.enabled.


Win7

Отсутствует

 

№1329526-05-2019 08:18:09

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

Re: Custom Buttons

Andrey_Krropotkin пишет

Не понятно пока с алертами для кнопок - alertsService.showAlertNotification(image, filename, notification);, не хотят пока работать.

Я запустил
Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
    .showAlertNotification("about:logo", "Title", "Message");
и алерт появился.

Andrey_Krropotkin пишет

не понятно почему вот такая конструкция не работает
function $C( name, attr) {
        var el = document.createXULElement( name );
        if (attr) Object.keys(attr).forEach(function(n){ el.setAttribute(n, attr[n])});
        return el;
    };

Конструкция работает, здесь дело в другом.
menuitem тоже Custom Element уже с Firefox 68.

В этой функции элемент создаётся документом браузера,
а затем добавляется в другой документ, документ about:addons
При этом, местная CE-машинерия воротит от чужеродного элемента нос.

Будет лучше, если элемент будет создаваться тем же документом.
В данном случае, например, можно переместить function $C
внутрь метода AM_Helper.addPopupMenu() и заменить document на doc

Отсутствует

 

№1329626-05-2019 10:46:39

anywho
Забанен
 
Группа: Members
Зарегистрирован: 21-05-2019
Сообщений: 54
UA: Firefox 67.0

Re: Custom Buttons

Dumby
Что это за кнопка?  И где тут поганка завернута?
/*CODE*/
getBrowser (). loadOneTab ("https://www.bwin.com/ru/sportsbook.aspx?zoneid=38775", null, null, null, false, false);

Отсутствует

 

№1329726-05-2019 18:23:17

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 476
UA: Firefox 67.0

Re: Custom Buttons

Dumby спасибо вроде на 69 ночнушке все заработало, алерты то появляются, то нет, ну и черт с ними.
Заметил еще одну вещь, если забить ваш костыль browser.tabs.remote.autostart, то потом надо еще и в настройках его отключить, но почему то, после того   домашняя страница становится пустая. Если без костыля, то мнопроцерсоность не убирается, даже если переключаю browser.tabs.remote.autostart.
Да еще подскажите - не работает вот такое выражение:
                 if (location != "chrome://browser/content/browser.xhtml") return;
        var URLBarInput = gURLBar.mInputField;
        var locationBar = URLBarInput.parentNode.appendChild(document.createXULElement("hbox"));
пишет URLBarInput is undefined

Отсутствует

 

№1329826-05-2019 19:23:12

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

Re: Custom Buttons

anywho пишет

Что это за кнопка?

Без понятия.

anywho пишет

И где тут поганка завернута?

Здесь (#l1.12) и здесь (#l5.12).

Andrey_Krropotkin пишет

если забить ваш костыль browser.tabs.remote.autostart, то потом надо еще и в настройках его отключить

Да, в этом и смысл, чтобы настройкой переключать.
Иначе можно сразу "1" поставить, без проверки настройки.

Andrey_Krropotkin пишет

но почему то, после того   домашняя страница становится пустая

Откуда я знаю какая страница у тебя домашняя?
А если ты об этом, — так «RESOLVED WONTFIX».

Andrey_Krropotkin пишет

пишет URLBarInput is undefined

Так Quantum Bar же. gURLBar.inputField чем нехорош?

Отсутствует

 

№1329926-05-2019 19:49:09

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 476
UA: Firefox 67.0

Re: Custom Buttons

Dumby пишет

А если ты об этом, — так «RESOLVED WONTFIX».

да об этом и что с этим делать?
Я так понимаю ошибка, которую они не собираются в ближайшее время исправлять. В принципе она мне и не нужна домашняя страница (по умолчанию). Использую другое приложение.

Отредактировано Andrey_Krropotkin (26-05-2019 19:55:20)

Отсутствует

 

№1330026-05-2019 23:20:18

Smitis
Участник
 
Группа: Members
Зарегистрирован: 29-01-2016
Сообщений: 10
UA: Firefox 67.0

Re: Custom Buttons

Подскажите, какая версия CustomButtons сейчас работоспособна на FF 67.0? Версии custom_buttons-0.0.7.0.0.5a1-fx-paxmod и custom_buttons-0.0.7.0.0.4-fx-paxmod не заработали (config.js брал по ссылке на странице с 0.0.7.0.0.4).

Отсутствует

 

Board footer

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