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

Многие проблемы быстрее решаются поиском по форуму и чтением FAQ, чем созданием новой темы и томительным ожиданием ответа.

№1627609-03-2022 23:39:35

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

Re: Custom Buttons

ВВП пишет

Нет.

Нет так нет. PlacesTransactions.clearTransactionsHistory();
сбросит вообще всё, в том числе и пункт.

Отсутствует

 

№1627710-03-2022 00:25:38

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

Re: Custom Buttons

Dumby
Годится. Класс !

Отсутствует

 

№1627810-03-2022 09:04:00

vv07
Участник
 
Группа: Members
Зарегистрирован: 07-11-2007
Сообщений: 689
UA: Firefox 54.0

Re: Custom Buttons

Здравствуйте! для старых FF, была отличная кнопка для переключения Proxy
С таким

Выделить код

Код:

// Proxy, от 25.02.2016.


// Настройка функций кликов мыши для кнопки ...................
this.onmousedown =e=> {
   
   this.onmouseup =e=>{                  // левый клик
        if ( e.button ) return;
        clearTimeout(self.timer);           

        switch( cbu.getPrefs("network.proxy.type") ) {
           case 0:  var data = 1; break;            
           case 1:  var data = 2; break; 
           case 2:  var data = 0; break;
           default: var data = 0; 
        }
        cbu.setPrefs("network.proxy.type", data);  
   }

   if ( e.button == 0 )                  // длинный левый клик
        self.timer = setTimeout(()=>{     
           self.onmouseup = '';
           cbu.getPrefs("CB.Proxy.connectionsInTab") ? openConnectionsInTab() : openConnections();
        }, 500);
   
   if ( e.button == 2 )                  // правый клик
        menuPopup.showPopup(self, -1, -1, "popup", "bottomleft", "topleft");
};
self.onclick =e=> e.preventDefault();



// Подсказка для кнопки ...................
this.onmouseover =()=> {
   this.tooltipText = "Proxy \nЛ: Переключить прокси \nДЛ: Открыть настройки прокси"
                    + "\nП: Mеню кнопки \n\nТекущие настройки прокси: " 
                    + "\nIP: " + Services.prefs.getComplexValue("network.proxy.http", Ci.nsISupportsString).data
                    + "\nПорт: "+ cbu.getPrefs("network.proxy.http_port");                                                    
};



// Создать меню для кнопки ...................
var array = [
   { label: "Добавление прокси в контекстом меню", value: 'CB.Proxy.inContextMenu' },
   { label: "Открывать настройки прокси как вкладку", value: 'CB.Proxy.connectionsInTab' },
   { label: "Переключать на режим 'Без прокси' при закрытии браузера ", value: 'CB.Proxy.reset' }    
];

var menuPopup = document.getElementById('mainPopupSet').appendChild(document.createElement("menupopup"));
array.forEach((m)=> {
   var mItem = document.createElement("menuitem");
   mItem.setAttribute("label", m.label);
   mItem.setAttribute('type', 'checkbox');
   mItem.setAttribute('checked', cbu.getPrefs(m.value) );
   mItem.onclick =()=> cbu.setPrefs(m.value, !cbu.getPrefs(m.value));  
   menuPopup.appendChild(mItem);
});
addDestructor(()=> menuPopup.remove() );

// добавить стандартное контекстное меню 
menuPopup.appendChild(document.createElement("menuseparator"));
menuPopup.appendChild(document.createElement("menu")).setAttribute("label", "Меню кнопки");
var clone = menuPopup.lastChild.appendChild(document.getElementById("custombuttons-contextpopup").cloneNode(true));
clone.setAttribute("onpopupshowing", "document.popupNode = document.getElementById('" + _id + "')");



// Функция открывает настройки прокси в окне ...................
function openConnections() {
   for ( var win, nm = Services.wm.getEnumerator(null); win = nm.getNext(); ) 
         if ( win.name == 'Proxy') {
              win.focus();  
              break;
              }
   var win = openDialog("chrome://browser/content/preferences/connection.xul", "Proxy", "centerscreen");

   // добавить атрибут "prefwindow"
   win.addEventListener("load", function f(e) {
       this.removeEventListener("load", f, true); 
       e.target.documentElement.setAttribute("type", "prefwindow");
   }, true );
   
   // закрыть настройки прокси по клику на странице 
   gBrowser.addEventListener("click", function c() {
      this.removeEventListener("click", c );
      try { win.close() } catch(e) {}; 
   }, true );
};



// Функция открывает настройки прокси в вкладке ...................
function openConnectionsInTab() { 
   var connections = gBrowser.getBrowserForTab( gBrowser.selectedTab = gBrowser.addTab("chrome://browser/content/preferences/connection.xul") ); 
          
   // oбработчик ждет пока откроется прокси, удаляет себя и добавляет атрибут
   connections.addEventListener("pageshow", function c(e) {         
      this.removeEventListener(e.type, c);         
      e.originalTarget.documentElement.setAttribute("type", "prefwindow");
   })   
};



// Установливать нужную иконку кнопки при старте баузера или при изменениях в 'about:config' ...................
var s = "network.proxy.type";
function toggleImage() {
   var icon = self.ownerDocument.getAnonymousElementByAttribute(self, "class", "toolbarbutton-icon");
   switch( cbu.getPrefs(s) ) {
      case 0: icon.src = self.image; break;          
      case 1: icon.src = 'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAQAAAAAIAA4GQAAnFmkAHQ5fAAQAKAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQECAgICAgICAgICAgMBAQEAAAQBJBFQOLQ+nX38ecev/mu6of8gfFvtAyUXewAAAAsAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEBAAAAAAMLUza7PKd//3S7oP2849X9zuvh/mW4mf4XiV7/AB4QZwAAAAAAAAAAAQEBAQAAAAAZGhgTKSwnLyYqJDErKCUnID8vXgaAUP9BlnH8b7ea/ojHrv+q2sf/a7CT+zGWbf8PakXGAAIAAAEBAQMHBwcbAAAAD2BiXn76/ff/4ebg/Onm4f+DtZ7/A31L/j2Wb/+Fwaj/ls23/7rh0v9+vaT+M5du/xKGWO0DIxIdAAAAAAEBAQEAAAABBgYFEaSmorbP08z239vX/4OwnP4ail3/RJlz/5HHsP/T7+X/ms64/3K2mP40k2r/D3tM7AUiExwhIx8meXt1mXt+eKJ3eXSce353n6esouDMysf/o7eq/kOdeP92uZv/vuPT/9Hs4P/C4tT/eLWa+juWbv8bcEq7AAYBAF1gWn7//////f39/P//////////////////////////lcKx/3i9pf/K7OD/2O/l/7ndzf6Fw6n/VK+I/AsxHj8AAAAAXF5bf+Ln2P+AnU/4fZ1N+4WiV/yKp13+jKlh/5CsZf+GpFj/Zpxh/4C5nf+i0L7+k8ex/1udf+UTOCZFAAAAAAEBAQJfYF5/3uXR/16IIPxciiH/Z5Iv/26YOP9ymz3/cpw+/3OcPf9wlzb/YI80/1mKNPu707X/YmZjiAAAAAAEBAQGAAAAAF9gXoDg5tT/cJU6/HGaPf9/pE3/hqtX/4uvXf+NsF7/iKxZ/4KoU/9+o0r/cpY6+9zjyf9ubm2JAAAAAAMEAwQAAAAAX2BegODm1P94nEX8e6JJ/4quXP+Vt2n/nb1z/6LBef+jwXz/ob96/527d/+NrGP71+HG/2praogAAAAABAQDBAAAAABfYF2A4ObU/4WlVvyPsmP/osB7/7HMj/+81J3/wdik/8DXo/+60pz/tc6X/6G6ffvY4sf/amtqiAAAAAAEBAMEAAAAAF9gXn/e5dL/n7l7/LLMk//B16X/zuG2/9jpwv/b68b/2enE/9Pkvv/Q4bv/uMud+9jhyP9qa2mHAAAAAAQEAwQAAAAAX2Bef97k0v+gt335sceT/LrNnfzC1Kj8ydmx/MzatfzK2bP8xtav/MbVr/ywwpL41+HH/2tsaokAAAAABAQDBAAAAABZW1Z3+fz1/+Dm1Pzj6db/5u3b/+nv3v/r8eD/6vDe/+ft2//k69j/4efT/9ngzPv7//b/ZGdhfwAAAAADBAMEAAAAABcZFRpZW1Z6YGFfgWBiYIBhYmGAYmJhgGJjYoBiY2KAYmJhgGFiYYBgYWCAX2BegF1fWoAbHRkeAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='; break;
      case 2: icon.src = 'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAQAAAAAIAAcHQAAYGGkAEBBfAAEBKAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAgICAAICAgACAgMBAQEAAAEBJAA6OrQAf3/8ALCw/gCiov8AXl7tABkZewAAAAsAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEBAAAAAAMAOTm7AIOD/wCjo/0A1tb9AOHh/gCcnP4AY2P/ABISZwAAAAAAAAAAAAEBAQAAAAAAGBgTACgoLwAmJjEAJiYnADIyXgBWVv8Adnb8AJ2d/gCxsf8Aycn/AJeX+wBycv8ASkrGAAACAAABAQMABwcbAAAADwBfX34A+fn/AOHh/ADj4/8AoaH/AFJS/gB1df8Aq6v/ALm5/wDT0/8Ap6f+AHNz/wBeXu0AFRUdAAAAAAABAQEAAAABAAUFEQCjo7YAzs72ANnZ/wCfn/4AY2P/AHl5/wCzs/8A5ub/ALu7/wCcnP4AcHD/AFNT7AAVFRwAICAmAHd3mQB6eqIAdXWcAHl5nwClpeAAyMj/AK2t/gB9ff8An5//ANXV/wDh4f8A1tb/AJ6e+gB0dP8AUFC7AAEGAABcXH4A////AP39/AD///8A////AP///wD///8A////ALOz/wCnp/8A4eH/AObm/wDPz/4ArKz/AI2N/AAhIT8AAAAAAFxcfwDd3f8Aa2v4AGpq+wBycvwAeHj+AHt7/wB/f/8Ac3P/AHNz/wCiov8AwMD+ALS0/wCEhOUAKSlFAAAAAAABAQIAXl5/ANjY/wBGRvwARkb/AFJS/wBaWv8AX1//AF9f/wBfX/8AWVn/AFRU/wBRUfsAvr7/AGNjiAAAAAAABAQGAAAAAABeXoAA2tr/AFtb/ABeXv8AbGz/AHV1/wB6ev8Ae3v/AHd3/wBxcf8Aamr/AFtb+wDS0v8AbW2JAAAAAAADAwQAAAAAAF5egADa2v8AZGT8AGlp/wB5ef8AhYX/AI2N/wCTk/8AlJT/AJKS/wCPj/8AfX37AM/P/wBqaogAAAAAAAMDBAAAAAAAXl6AANra/wBycvwAf3//AJOT/wClpf8AsLD/ALa2/wC1tf8Ar6//AKqq/wCTk/sA0ND/AGpqiAAAAAAAAwMEAAAAAABeXn8A2dn/AJGR/ACnp/8At7f/AMXF/wDQ0P8A09P/ANHR/wDLy/8AyMj/AK2t+wDR0f8AaWmHAAAAAAADAwQAAAAAAF5efwDY2P8AkpL5AKWl/ACurvwAuLj8AL+//ADCwvwAwMD8AL29/AC8vPwAo6P4ANDQ/wBqaokAAAAAAAMDBAAAAAAAV1d3APf3/wDa2vwA3d3/AOHh/wDk5P8A5ub/AOTk/wDh4f8A39//ANra/wDT0/sA+fn/AGNjfwAAAAAAAwMEAAAAAAAWFhoAV1d6AF9fgQBgYIAAYWGAAGFhgABiYoAAYmKAAGFhgABhYYAAYGCAAF5egABbW4AAGhoeAAAAAAABAQEAAAAA/oOAQeEAgEH6AIBBoACAQQAAAEGAAABBAACAQQAAgEEAAQBBAAKAQQACgEEAAoBBAAKAQQACgEEAAoBBAAKAQQACgEE='; break;
      default:icon.src = self.image; 
   }
};  
toggleImage();
gPrefService.addObserver(s, toggleImage, false);
addDestructor(()=> gPrefService.removeObserver(s, toggleImage) );



// Переключать на режим 'Без прокси' при закрытии браузера если это разрешено в 'about:config' ...................
var switchOffProxy = {
    observe: function(subject, topic, data) {
       if ( data == "shutdown" && cbu.getPrefs("CB.Proxy.reset") ) cbu.setPrefs("network.proxy.type", 0);  
    }
};
Services.obs.addObserver(switchOffProxy, "quit-application", false);



// Создаем меню для добавление прокси в контекстном меню выделенного текста на странице ...................
((contextMenu)=> {

  // создать новый пункт меню
  var menuitem = contextMenu.appendChild( document.createElement("menuitem") );      
  menuitem.setAttribute("label", "Добавить прокси"); 
  menuitem.setAttribute("class", "menuitem-iconic");
  menuitem.setAttribute("image", self.image);
  menuitem.onclick =()=> addNewProxy();
  addDestructor(()=> menuitem.remove() );
  
  // устанавливаем где показывать пункт меню 
  addEventListener("popupshowing", ()=>{
     menuitem.hidden = !cbu.getPrefs("CB.Proxy.inContextMenu") || !gContextMenu.isContentSelected; // !gContextMenu.isTextSelected;
  }, false, contextMenu);
  
  
  // добавление прокси
  function addNewProxy(sel) {  
     var selection = document.commandDispatcher.focusedWindow.getSelection().toString();
     var sel = ( sel == undefined ) ? selection : sel.toString();
     sel = sel.replace(/^\s+|\s+$/g, ""); // удалить пробелы, слева и справа от строки
     sel = sel.replace(/\s+/g,":"); // заменить пробелы внутри строки

     // если только порт ...
     if ( sel.length < 6 && isFinite(sel) ) { 
          sel = sel.replace(/:/g, "");
          var lab = 'порт';
          cbu.setPrefs("network.proxy.http_port", +sel);                    
          }
     
     // если только адрес ...
     if ( sel.length > 5 && !/:/.test(sel) && sel.split(".").length == 4 ) {   
          var lab = 'адрес';
          cbu.setPrefs("network.proxy.http", convertFromUnicode("UTF-8", sel));  
          }   
     
     // если адрес и порт ...    
     if ( sel.length > 5 && /:/.test(sel) && sel.split(":").length == 2 && sel.split(".").length == 4 ) {
          var lab = 'адрес и порт';
          var array = sel.split(":");  
          array.forEach((str)=> addNewProxy(str) );          
          }     

     if ( lab == undefined ) return;

     // всплывающая подсказка рядом с выделенным текстом ...     
     function showTooltip() {
        var tooltip = gBrowser.appendChild( document.createElement("tooltip") );
        tooltip.style.cssText = "color: red !important; font-weight: bold !important; font-size: 14px !important; -moz-box-orient: horizontal; text-align: center;";
   
        var image = tooltip.appendChild( document.createElement("image") );
        image.setAttribute("src", self.image);
   
        var label = tooltip.appendChild( document.createElement("label"));
        label.setAttribute("value", "Установлен " + lab + " прокси: " + sel);
    
        var focused = document.commandDispatcher.focusedWindow;
        var selection = focused.getSelection().getRangeAt(0).getBoundingClientRect();
        var posX = focused.mozInnerScreenX + selection.left;
        var posY = focused.mozInnerScreenY + selection.bottom - 5;   

        tooltip.showPopup(gBrowser, posX, posY);
        setTimeout(()=> gBrowser.removeChild(tooltip), 3000);
     };
     showTooltip();    
  };
   
})(document.getElementById("contentAreaContextMenu"));

К сожалению на FF 84 и выше, перестала работать.
Помогите пожалуйста. Поправьте

Отсутствует

 

№1627910-03-2022 12:10:51

sonyas75
Участник
 
Группа: Members
Откуда: Ставрополь
Зарегистрирован: 22-03-2011
Сообщений: 557
UA: Firefox 91.0

Re: Custom Buttons

vv07
я что-то там менял, не помню что, то ли цвета кнопки, то ли еще что-то по-мелочи

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

Выделить код

Код:

/*Initialization Code*/
/* https://forum.mozilla-russia.org/viewtopic.php?pid=783830#p783830 */
// Настройка функций кликов мыши для кнопки ...................
this.onmousedown = e => {

    if (e.button == 0) // длинный левый клик
        self.timer = setTimeout(() => {
            self.onmouseup = null;
            cbu.getPrefs("CB.Proxy.connectionsInTab")
                ? openConnectionsInTab() : openConnections();
        }, 500);

    this.onmouseup = e => { // левый клик
        if (e.button) return;
        clearTimeout(self.timer);

        switch(cbu.getPrefs("network.proxy.type")) {
            case 0:  var data = 1; break;
            case 1:  var data = 2; break;
            case 2:  var data = 0; break;
            default: var data = 0; 
        }
        cbu.setPrefs("network.proxy.type", data);
    }
}
// правый клик
this.oncontextmenu = e => e.detail == 2 && !menuPopup.hidePopup()
    || e.ctrlKey || !!menuPopup.openPopup(this, "after_start");


// Подсказка для кнопки ...................
this.onmouseover = () => this.tooltipText =
    "Proxy \nЛ:  Переключить прокси \nДЛ: Открыть настройки прокси" +
    "\nП:  Mеню кнопки \nДП: CB меню \n\nТекущие настройки прокси: " +
    "\nIP: " + cbu.getPrefs("network.proxy.http") + "\nПорт: " +
    cbu.getPrefs("network.proxy.http_port");


// Создать меню для кнопки ...................
var array = [
    {label: "Добавление прокси в контекстом меню", value: 'CB.Proxy.inContextMenu'},
    {label: "Открывать настройки прокси как вкладку", value: 'CB.Proxy.connectionsInTab'},
    {label: "Переключать на режим 'Без прокси' при закрытии браузера ", value: 'CB.Proxy.reset'}
];

var menuPopup = document.getElementById('mainPopupSet').appendChild(document.createElementNS(xulns, "menupopup"));
array.forEach(m => {
    var mItem = document.createElementNS(xulns, "menuitem");
    mItem.setAttribute("label", m.label);
    mItem.setAttribute("type", "checkbox");
    mItem.setAttribute("checked", cbu.getPrefs(m.value));
    mItem.onclick = () => cbu.setPrefs(m.value, !cbu.getPrefs(m.value));  
    menuPopup.appendChild(mItem);
});
addDestructor(() => menuPopup.remove());


var listenClick = win => {
    var args = ["click", win.close.bind(win), true];
    var unload = () => gBrowser.removeEventListener(...args);
    gBrowser.addEventListener(...args);
    win.addEventListener("unload", unload, {once: true});
}
var version = parseInt(Services.appinfo.platformVersion);
var url = `chrome://browser/content/preferences/${version >= 77 ? "dialogs/" : ""}connection.x${version >= 72 ? "htm" : "u"}l`;

var fox73 = version >= 73, noop = () => {};
if (fox73) var grid = win => {
    var url = "data:text/css;charset=utf-8," + encodeURIComponent(`
        #proxy-grid, #dnsOverHttps-grid {
            display: grid;
            grid-template-columns: auto 1fr;
            align-items: center;
        }
        #proxy-grid > .thin {
            grid-column-end: 3;
            height: 20px;
        }
        #dnsOverHttps-grid.custom-container-hidden #networkCustomDnsOverHttpsInput,
        #dnsOverHttps-grid.custom-container-hidden #networkCustomDnsOverHttpsInputLabelContainer {
            display: none;
        }
    `);
    (grid = win => win.windowUtils.loadSheetUsingURIString(url, win.windowUtils.AUTHOR_SHEET))(win);
}
var winOpen = win => win.addEventListener("readystatechange", winReady, {once: true, capture: true});
var winReady = e => {
    var win = e.target.ownerGlobal, cw = win.isChromeWindow;
    if (cw || (win.location == url && !win.docShell.name)) winPatch(win, cw);
}
var winPatch = (win, cw) => {
    win.opener = {gSubDialog: {_dialogs: [{
        _frame: {get contentDocument() {
            cw && listenClick(win);
            delete this.contentDocument;
            return this.contentDocument = win.document;
        }},
        resizeVertically: cw ? () => win.sizeToContent() : noop
    }]}};
    fox73 && grid(win);
}
addEventListener("MozBeforeInitialXULLayout", winReady, false, gBrowser.tabpanels || 1);

for(var {contentWindow: win} of gBrowser.browsers)
    win && win.location == url && !win.opener && winPatch(win);


// Функция открывает настройки прокси в окне ...................  
function openConnections() {
    var win = [...Services.wm.getEnumerator(null)].find(w => w.location == url);
    win ? win.focus() : winOpen(openDialog(url, "Proxy", "centerscreen"));
}

// Функция открывает настройки прокси в вкладке ...................
function openConnectionsInTab() {
    var connections = gBrowser.getBrowserForTab(
        gBrowser.selectedTab = gBrowser.addTrustedTab(url)
    );

    // oбработчик ждет пока откроется прокси, удаляет себя и добавляет атрибут
    connections.addEventListener("pageshow",
        e => e.target.documentElement.setAttribute("type", "prefwindow")
    , {once: true});
}


// Установливать нужную иконку кнопки при старте баузера или при изменениях в 'about:config' ...................
var s = "network.proxy.type";
function toggleImage() {
    var {icon} = self;
    switch( cbu.getPrefs(s) ) {
        //case 0: icon.src = self.image; break;
        case 2: icon.src = 'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAQAAAAAIAA4GQAAnFmkAHQ5fAAQAKAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQECAgICAgICAgICAgMBAQEAAAQBJBFQOLQ+nX38ecev/mu6of8gfFvtAyUXewAAAAsAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEBAAAAAAMLUza7PKd//3S7oP2849X9zuvh/mW4mf4XiV7/AB4QZwAAAAAAAAAAAQEBAQAAAAAZGhgTKSwnLyYqJDErKCUnID8vXgaAUP9BlnH8b7ea/ojHrv+q2sf/a7CT+zGWbf8PakXGAAIAAAEBAQMHBwcbAAAAD2BiXn76/ff/4ebg/Onm4f+DtZ7/A31L/j2Wb/+Fwaj/ls23/7rh0v9+vaT+M5du/xKGWO0DIxIdAAAAAAEBAQEAAAABBgYFEaSmorbP08z239vX/4OwnP4ail3/RJlz/5HHsP/T7+X/ms64/3K2mP40k2r/D3tM7AUiExwhIx8meXt1mXt+eKJ3eXSce353n6esouDMysf/o7eq/kOdeP92uZv/vuPT/9Hs4P/C4tT/eLWa+juWbv8bcEq7AAYBAF1gWn7//////f39/P//////////////////////////lcKx/3i9pf/K7OD/2O/l/7ndzf6Fw6n/VK+I/AsxHj8AAAAAXF5bf+Ln2P+AnU/4fZ1N+4WiV/yKp13+jKlh/5CsZf+GpFj/Zpxh/4C5nf+i0L7+k8ex/1udf+UTOCZFAAAAAAEBAQJfYF5/3uXR/16IIPxciiH/Z5Iv/26YOP9ymz3/cpw+/3OcPf9wlzb/YI80/1mKNPu707X/YmZjiAAAAAAEBAQGAAAAAF9gXoDg5tT/cJU6/HGaPf9/pE3/hqtX/4uvXf+NsF7/iKxZ/4KoU/9+o0r/cpY6+9zjyf9ubm2JAAAAAAMEAwQAAAAAX2BegODm1P94nEX8e6JJ/4quXP+Vt2n/nb1z/6LBef+jwXz/ob96/527d/+NrGP71+HG/2praogAAAAABAQDBAAAAABfYF2A4ObU/4WlVvyPsmP/osB7/7HMj/+81J3/wdik/8DXo/+60pz/tc6X/6G6ffvY4sf/amtqiAAAAAAEBAMEAAAAAF9gXn/e5dL/n7l7/LLMk//B16X/zuG2/9jpwv/b68b/2enE/9Pkvv/Q4bv/uMud+9jhyP9qa2mHAAAAAAQEAwQAAAAAX2Bef97k0v+gt335sceT/LrNnfzC1Kj8ydmx/MzatfzK2bP8xtav/MbVr/ywwpL41+HH/2tsaokAAAAABAQDBAAAAABZW1Z3+fz1/+Dm1Pzj6db/5u3b/+nv3v/r8eD/6vDe/+ft2//k69j/4efT/9ngzPv7//b/ZGdhfwAAAAADBAMEAAAAABcZFRpZW1Z6YGFfgWBiYIBhYmGAYmJhgGJjYoBiY2KAYmJhgGFiYYBgYWCAX2BegF1fWoAbHRkeAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='; break;
        case 1: icon.src = 'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAQAAAAAIAAcHQAAYGGkAEBBfAAEBKAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAgICAAICAgACAgMBAQEAAAEBJAA6OrQAf3/8ALCw/gCiov8AXl7tABkZewAAAAsAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEBAAAAAAMAOTm7AIOD/wCjo/0A1tb9AOHh/gCcnP4AY2P/ABISZwAAAAAAAAAAAAEBAQAAAAAAGBgTACgoLwAmJjEAJiYnADIyXgBWVv8Adnb8AJ2d/gCxsf8Aycn/AJeX+wBycv8ASkrGAAACAAABAQMABwcbAAAADwBfX34A+fn/AOHh/ADj4/8AoaH/AFJS/gB1df8Aq6v/ALm5/wDT0/8Ap6f+AHNz/wBeXu0AFRUdAAAAAAABAQEAAAABAAUFEQCjo7YAzs72ANnZ/wCfn/4AY2P/AHl5/wCzs/8A5ub/ALu7/wCcnP4AcHD/AFNT7AAVFRwAICAmAHd3mQB6eqIAdXWcAHl5nwClpeAAyMj/AK2t/gB9ff8An5//ANXV/wDh4f8A1tb/AJ6e+gB0dP8AUFC7AAEGAABcXH4A////AP39/AD///8A////AP///wD///8A////ALOz/wCnp/8A4eH/AObm/wDPz/4ArKz/AI2N/AAhIT8AAAAAAFxcfwDd3f8Aa2v4AGpq+wBycvwAeHj+AHt7/wB/f/8Ac3P/AHNz/wCiov8AwMD+ALS0/wCEhOUAKSlFAAAAAAABAQIAXl5/ANjY/wBGRvwARkb/AFJS/wBaWv8AX1//AF9f/wBfX/8AWVn/AFRU/wBRUfsAvr7/AGNjiAAAAAAABAQGAAAAAABeXoAA2tr/AFtb/ABeXv8AbGz/AHV1/wB6ev8Ae3v/AHd3/wBxcf8Aamr/AFtb+wDS0v8AbW2JAAAAAAADAwQAAAAAAF5egADa2v8AZGT8AGlp/wB5ef8AhYX/AI2N/wCTk/8AlJT/AJKS/wCPj/8AfX37AM/P/wBqaogAAAAAAAMDBAAAAAAAXl6AANra/wBycvwAf3//AJOT/wClpf8AsLD/ALa2/wC1tf8Ar6//AKqq/wCTk/sA0ND/AGpqiAAAAAAAAwMEAAAAAABeXn8A2dn/AJGR/ACnp/8At7f/AMXF/wDQ0P8A09P/ANHR/wDLy/8AyMj/AK2t+wDR0f8AaWmHAAAAAAADAwQAAAAAAF5efwDY2P8AkpL5AKWl/ACurvwAuLj8AL+//ADCwvwAwMD8AL29/AC8vPwAo6P4ANDQ/wBqaokAAAAAAAMDBAAAAAAAV1d3APf3/wDa2vwA3d3/AOHh/wDk5P8A5ub/AOTk/wDh4f8A39//ANra/wDT0/sA+fn/AGNjfwAAAAAAAwMEAAAAAAAWFhoAV1d6AF9fgQBgYIAAYWGAAGFhgABiYoAAYmKAAGFhgABhYYAAYGCAAF5egABbW4AAGhoeAAAAAAABAQEAAAAA/oOAQeEAgEH6AIBBoACAQQAAAEGAAABBAACAQQAAgEEAAQBBAAKAQQACgEEAAoBBAAKAQQACgEEAAoBBAAKAQQACgEE='; break;
        default: icon.src = self.image;
    }
}
toggleImage();
Services.prefs.addObserver(s, toggleImage, false);
addDestructor(() => Services.prefs.removeObserver(s, toggleImage));


// Переключать на режим 'Без прокси' при закрытии браузера если это разрешено в 'about:config' ...................
var toggleButton = {
    observe(s, t, data) {
        cbu.getPrefs("CB.Proxy.reset") && data == "shutdown" && cbu.setPrefs("network.proxy.type", 0);
    }
};
Services.obs.addObserver(toggleButton, "quit-application", false);


// Создаем меню для добавление прокси в контекстном меню выделенного текста на странице ...................
var contextMenu = document.getElementById("contentAreaContextMenu");
var menuitem = document.createElementNS(xulns,"menuitem");
menuitem.setAttribute("label", "Добавить прокси");
menuitem.setAttribute("class", "menuitem-iconic");
menuitem.setAttribute("image", self.image);
menuitem.onclick =()=> addNewProxy(menuitem);
contextMenu.appendChild(menuitem); // как последний пункт меню
addDestructor(() => menuitem.remove());
addEventListener("popupshowing", () => {
    menuitem.hidden = !cbu.getPrefs("CB.Proxy.inContextMenu") || !gContextMenu.isContentSelected; // !gContextMenu.isTextSelected;
}, false, contextMenu);


// добавление прокси ...................
function addNewProxy(menuitem) {
    var sel = gBrowser.contentDocument
        ? gBrowser.contentDocument.defaultView.getSelection().toString() // Pale Moon  
        : gContextMenu.selectionInfo.fullText; // Firefox

    sel = sel.replace(/^\s+|\s+$/g, ""); // удалить пробелы, слева и справа от строки
    sel = sel.replace(/\s+/g, ":");      // заменить пробелы внутри строки

    // если только порт ...
    if (sel.length < 6 && isFinite(sel)) { 
        var lab = 'порт';
        cbu.setPrefs("network.proxy.http_port", +sel);                    
    }

    // если только адрес ...
    if (sel.length > 5 && !/:/.test(sel) && sel.split(".").length == 4) {
        var lab = 'адрес';
        cbu.setPrefs("network.proxy.http", convertFromUnicode("UTF-8", sel));  
    }

    // если адрес и порт ...    
    if (sel.length > 5 && /:/.test(sel) && sel.split(":").length == 2 && sel.split(".").length == 4) {
        var lab = 'адрес и порт';
        var arr = sel.split(":"), IP = arr[0], port = arr[1];
        cbu.setPrefs("network.proxy.http_port", +port);
        cbu.setPrefs("network.proxy.http", convertFromUnicode("UTF-8", IP));
    }

    // всплывающая подсказка рядом с выделенным текстом ...
    var mainPopupSet = document.getElementById('mainPopupSet');
    var tooltip = mainPopupSet.appendChild(document.createElementNS(xulns,"tooltip"));
    tooltip.style.cssText = "color: red !important; font-weight: bold !important; font-size: 14px !important; -moz-box-orient: horizontal; text-align: center;";

    var image = tooltip.appendChild(document.createElementNS(xulns, "image"));
    image.setAttribute("src", self.image);

    var label = tooltip.appendChild(document.createElementNS(xulns, "label"));
    label.setAttribute("value", "Установлен " + lab + " прокси: " + sel);

    tooltip.openPopup(menuitem.parentNode, "before_start");
    setTimeout(() => mainPopupSet.removeChild(tooltip), 3000);
};


// Конвертировать текст в юникод .............  
function convertFromUnicode(charset, str) {
    var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
    converter.charset = charset;
    str = converter.ConvertFromUnicode(str);
    return str + converter.Finish();
}

а... ну в коде есть ссылка, откуда я эту кнопку выкопал и чуть ниже скрипт, сделанный из этой кнопки

Отредактировано sonyas75 (10-03-2022 12:13:40)

Отсутствует

 

№1628010-03-2022 12:19:55

vv07
Участник
 
Группа: Members
Зарегистрирован: 07-11-2007
Сообщений: 689
UA: Firefox 54.0

Re: Custom Buttons

sonyas75
Сработало. Благодарю

Отсутствует

 

№1628111-03-2022 17:26:11

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

Re: Custom Buttons

Dumby
Откуда появляется дополнительный border или не border при select / И в #loginTextbox тоже. Причем в loginTextbox сходу появляется без выделения ....

Выделить код

Код:

data:image/jpeg;charset=utf-8;base64,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

Отсутствует

 

№1628211-03-2022 21:38:10

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

Re: Custom Buttons

ВВП пишет

Откуда появляется дополнительный border или не border при select

В текстовых полях панельки со скриншота — это, наверно, outline.
Добавляется по :focus-visible этим фрагментом из chrome://browser/skin/browser.css

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

Выделить код

Код:

#editBookmarkPanelRows > vbox > html|input:focus-visible,
#editBookmarkPanelRows > vbox > hbox > html|input:focus-visible {
  outline: 2px solid var(--focus-outline-color);
}

И в #loginTextbox тоже. Причем в loginTextbox сходу появляется без выделения ....

Это не знаю. Если его документ в своём отдельном окне, то такого не вижу,
есть outline только в subdialog'ах, видимо из chrome://global/skin/in-content/common.css

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

Выделить код

Код:

html|input:is([type="email"], [type="tel"], [type="text"], [type="password"], [type="url"], [type="number"]):focus,
html|textarea:focus,
xul|search-textbox[focused],
xul|tree:focus-visible,
xul|richlistbox:focus-visible {
  border-color: transparent;
  outline: 2px solid var(--in-content-focus-outline-color);
  outline-offset: -1px; /* Prevents antialising around the corners */
}

Отсутствует

 

№1628311-03-2022 22:26:18

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

Re: Custom Buttons

Dumby
Как же я проглядел... outline: none !important; , какой к чертям border.... Благодарю.

Отсутствует

 

№1628418-03-2022 22:53:52

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

Re: Custom Buttons

Dumby пишет

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

Выделить код

Код:

(async repl => {
	var obj = `{\n  ${
		(await (await fetch("chrome://browser/content/places/controller.js")).text())
			.match(/async _removeRange\(.+?\n\ +}(?=,\n)/s)[0]
			.replace("// This is a common bookmark item.", repl)
	}\n}`
	var ps = await ChromeUtils.compileScript("data:,(" + encodeURIComponent(`${obj => {
		var patch = async ctor => {
			var proto = ctor.prototype, meth = proto?._removeRange;
			meth && Object.assign(proto, obj);
		}
		var key = "PlacesController";
		var desc = Object.getOwnPropertyDescriptor(window, key);
		if (!desc) return;

		var {get} = desc;
		if (get)
			desc.get = () => {
				var val = get();
				patch(val);
				return val;
			},
			Object.defineProperty(window, key, desc);
		else
			patch(desc.value);
	}})(${obj});`));

	var obs = doc => "PlacesController" in doc.ownerGlobal && ps.executeInGlobal(doc);
	var topic = "chrome-document-loaded";
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})(
		`$&
        if (!removedFolders.ignore) {
          let info = await PlacesUtils.bookmarks.fetch(node.bookmarkGuid);
          if (
            info?.parentGuid == "${PlacesUtils.bookmarks.toolbarGuid}" &&
            !(removedFolders.ignore ??= Services.prompt.confirm(
              null, null, "Удалять с панели закладок?"
            ))
          ) {
            totalItems--;
            continue;
          }
        }`
);

Dumby
Возможно ли добавить, чтобы при удалении закладки находящейся внутри папки, тоже появлялось предупреждение.


«The Truth Is Out There»

Отсутствует

 

№1628519-03-2022 02:36:49

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

Re: Custom Buttons

unter_officer пишет

Возможно ли добавить, чтобы при удалении закладки находящейся внутри папки, тоже появлялось предупреждение.

Есть вариант получше - можно подключить через UCF скрипт восстановления удалённых закладок/папок:

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

Выделить код

Код:

// ==UserScript==
// @name          undoBookmarksContextMenu.uc.js
// @namespace     http://space.geocities.yahoo.co.jp/gl/alice0775
// @description   add undo menu in Bookmarks Context Menu
// @include       *
// @compatibility Firefox 91
// @author        alice0775
// @version       2021/10/02 00:00 do not close popup when middle mouse click on the undo/redo menu
// @version       2019/11/20 23:00 Bug 1553188 - Rename browser.xul to browser.xhtml
// @version       2019/11/20 23:00 fix redeclaration error
// @version       2019/07/10 10:00 fix 70 Bug 1558914 - Disable Array generics in Nightly
// @version       2018/10/04 20:00 remove conflict shortcuts key for main window
// @version       2018/10/04 60+
// ==/UserScript==
if (typeof window.undobookmarksmenu == "undefined") {
	window.undobookmarksmenu = {
		popup: null,

		handleEvent: function(event) {
			switch (event.type) {
				case 'unload':
					this.uninit();
					break;
				case 'popupshown':
					this.popupshown(event);
					break;
			}
		},

		init: function() {
			window.addEventListener('unload', this, false);
			this.popup = document.getElementById("placesContext");
			if (!this.popup)
				return;
			this.popup.addEventListener('popupshown', this, false);
			let template = (location.href == "chrome://browser/content/browser.xhtml"
										 || location.href == "chrome://browser/content/browser.xul") ?
								[
									["menuitem", {id: "undobookmarksmenuUndo",
																disabled: "true",
																label: "Откат удаления",
																key: "key_undo",
																oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
																accesskey: "U",
																selection: "any",
																onmouseup: "undobookmarksmenu.shouldPreventHide(event);"
									}],
									["menuitem", {id:"undobookmarksmenuRedo",
																disabled: "true",
																label: "Удалить снова",
																key: "key_redo",
																oncommand: "PlacesTransactions.redo().catch(Cu.reportError);",
																accesskey: "R",
																selection: "any",
																onmouseup: "undobookmarksmenu.shouldPreventHide(event);"
									}]
								] : [
									["menuitem", {id: "undobookmarksmenuUndo",
																disabled: "true",
																label: "Откат удаления",
																key: "key_undo",
																oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
																acceltext: "Ctrl+Z",
																accesskey: "U",
																selection: "any",
																onmouseup: "undobookmarksmenu.shouldPreventHide(event);"
									}],
									["menuitem", {id:"undobookmarksmenuRedo",
																disabled: "true",
																label: "Удалить снова",
																key: "key_redo",
																oncommand: "PlacesTransactions.redo().catch(Cu.reportError);",
																acceltext: "Ctrl+Y",
																accesskey: "R",
																selection: "any",
																onmouseup: "undobookmarksmenu.shouldPreventHide(event);"
									}]
								];

			let ref = document.getElementById("placesContext_deleteSeparator");
			ref.parentNode.insertBefore(this.jsonToDOM(template, document, {}), ref);
		},

		uninit: function() {
			window.removeEventListener('unload', this, false);
			if (!this.popup)
				return;
			this.popup.removeEventListener('popupshown', this, false);
		},

		popupshown: function(event){
			var menuitem = document.getElementById("undobookmarksmenuUndo");
			if (menuitem)
				menuitem.setAttribute('disabled', PlacesTransactions.topUndoEntry == null);
			menuitem = document.getElementById("undobookmarksmenuRedo");
			if (menuitem)
				menuitem.setAttribute('disabled', PlacesTransactions.topRedoEntry == null);
		},

		shouldPreventHide: function(aEvent) {
			const menuitem = event.target;
			if (event.button == 1)
			{
				menuitem.setAttribute('closemenu', 'none');
				menuitem.parentNode.addEventListener('popuphidden', () => {
					menuitem.removeAttribute('closemenu');
				}, { once: true });
				if (event.ctrlKey)
					menuitem.parentNode.hidePopup();
			}
		},

		jsonToDOM: function(jsonTemplate, doc, nodes) {
			jsonToDOM.namespaces = {
			html: "http://www.w3.org/1999/xhtml",
			xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
			};
			jsonToDOM.defaultNamespace = jsonToDOM.namespaces.xul;
			function jsonToDOM(jsonTemplate, doc, nodes) {
				function namespace(name) {
						var reElemNameParts = /^(?:(.*):)?(.*)$/.exec(name);
						return { namespace: jsonToDOM.namespaces[reElemNameParts[1]], shortName: reElemNameParts[2] };
				}

				// Note that 'elemNameOrArray' is: either the full element name (eg. [html:]div) or an array of elements in JSON notation
				function tag(elemNameOrArray, elemAttr) {
					// Array of elements?  Parse each one...
					if (Array.isArray(elemNameOrArray)) {
						var frag = doc.createDocumentFragment();
						Array.prototype.forEach.call(arguments, function(thisElem) {
							frag.appendChild(tag.apply(null, thisElem));
						});
						return frag;
					}

					// Single element? Parse element namespace prefix (if none exists, default to defaultNamespace), and create element
					var elemNs = namespace(elemNameOrArray);
					var elem = doc.createElementNS(elemNs.namespace || jsonToDOM.defaultNamespace, elemNs.shortName);

					// Set element's attributes and/or callback functions (eg. onclick)
					for (var key in elemAttr) {
						var val = elemAttr[key];
						if (nodes && key == "key") {
								nodes[val] = elem;
								continue;
						}

						var attrNs = namespace(key);
						if (typeof val == "function") {
							// Special case for function attributes; don't just add them as 'on...' attributes, but as events, using addEventListener
							elem.addEventListener(key.replace(/^on/, ""), val, false);
						} else {
							// Note that the default namespace for XML attributes is, and should be, blank (ie. they're not in any namespace)
							elem.setAttributeNS(attrNs.namespace || "", attrNs.shortName, val);
						}
					}

					// Create and append this element's children
					var childElems = Array.prototype.slice.call(arguments, 2);
					childElems.forEach(function(childElem) {
						if (childElem != null) {
							elem.appendChild(
									childElem instanceof doc.defaultView.Node ? childElem :
											Array.isArray(childElem) ? tag.apply(null, childElem) :
													doc.createTextNode(childElem));
						}
					});
					return elem;
				}
				return tag.apply(null, jsonTemplate);
			}

			return jsonToDOM(jsonTemplate, doc, nodes);
		}
	}


	window.undobookmarksmenu.init();
}

Отсутствует

 

№1628619-03-2022 03:32:36

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

Re: Custom Buttons

Dobrov
Восстановление сделал Dumby по аналогии с приведённым скриптом от alice0775 (там выше и ниже поста обсуждение) https://forum.mozilla-russia.org/viewto … 78#p798678
Как я понял, unter_officer спрашивает про подтверждение перед удалением.

Отредактировано xrun1 (19-03-2022 03:43:59)

Отсутствует

 

№1628719-03-2022 11:11:55

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

Re: Custom Buttons

unter_officer пишет

Возможно ли добавить, чтобы при удалении закладки находящейся внутри папки, тоже появлялось предупреждение.

Почему бы нет

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

Выделить код

Код:

/*
          let info = await PlacesUtils.bookmarks.fetch(node.bookmarkGuid);
*/
          let args = [node.bookmarkGuid];
          let isBookmark = PlacesUtils.nodeIsBookmark(node);

          isBookmark && args.push(null, {includePath: true});
          let info = await PlacesUtils.bookmarks.fetch(...args);
          if (isBookmark) info.parentGuid = info.path[0].guid;

Отсутствует

 

№1628819-03-2022 13:50:20

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

Re: Custom Buttons

Dumby пишет

Почему бы нет

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

Выделить код

Код:

/*
          let info = await PlacesUtils.bookmarks.fetch(node.bookmarkGuid);
*/
          let args = [node.bookmarkGuid];
          let isBookmark = PlacesUtils.nodeIsBookmark(node);

          isBookmark && args.push(null, {includePath: true});
          let info = await PlacesUtils.bookmarks.fetch(...args);
          if (isBookmark) info.parentGuid = info.path[0].guid;

Большое спасибо.


«The Truth Is Out There»

Отсутствует

 

№1628920-03-2022 14:05:32

Deriax
Участник
 
Группа: Members
Зарегистрирован: 27-03-2021
Сообщений: 37
UA: Yandex 22

Re: Custom Buttons

Нужен код который прерывает все уже отправленный запросы. Наподобие этого XMLHttpRequest.abort() только для всех запросов.

Отсутствует

 

№1629022-03-2022 21:21:58

pandarianin
Участник
 
Группа: Members
Зарегистрирован: 04-01-2015
Сообщений: 43
UA: Firefox 98.0

Re: Custom Buttons

Здравствуйте.
А где собственно скачать этот Custom Buttons? Все три ссылки в начале темы ведут в никуда.

Отсутствует

 

№1629123-03-2022 00:03:19

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

Отсутствует

 

№1629223-03-2022 09:39:24

pandarianin
Участник
 
Группа: Members
Зарегистрирован: 04-01-2015
Сообщений: 43
UA: Firefox 98.0

Re: Custom Buttons

xrun1 пишет

pandarianinhttps://www.upload.ee/files/13987518/cu … x.zip.html

Спасибо. А как установить это чудо? Пишет что дополнение повреждено и не может быть установлено.

Отсутствует

 

№1629323-03-2022 11:55:22

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

Re: Custom Buttons

pandarianin
Нужны два файла: config.js и config-prefs.js. Как создать и куда положить смотрите здесь.
Для config.js актуальный код нужно брать отсюда. После этого запускаете [firefox] и ставите версию custom_buttons-0.0.7.0.0.23-fx-bootstrap.xpi.

Отсутствует

 

№1629423-03-2022 12:30:04

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

Re: Custom Buttons

без bootstrap не взлетит, нужен такой config.js

Войдите или зарегистрируйтесь, чтобы увидеть скрытый текст.

для 99 [firefox] будет такой антиподписальщик

Войдите или зарегистрируйтесь, чтобы увидеть скрытый текст.

все спасибы Dumby

Выделить код

Код:

//
try {(nsvo => {
	var o = Cu.getGlobalForObject(nsvo).Object, {freeze} = o, NEW;
	o.freeze = obj => {
		if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
			return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;
		if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj))
			lockPref("extensions.experiments.enabled", true);
		else
			obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
			lockPref("extensions.legacy.enabled", true);

		return (o.freeze = freeze)(obj);
	}
	lockPref("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

	nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
	var shouldVerify = nsvo.shouldVerifySignedState;
	nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);

	if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;
})(
	"permitCPOWsInScope" in Cu
		? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu
);}
catch(ex) {Cu.reportError(ex);}

// bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196
try {(ios => {
	var subst = "bootstrap-loader-config-script";
	ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw`
		data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A  return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A  return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A  if (element.hasAttributeNS(NS_RDF, name))%0A    return element.getAttributeNS(NS_RDF, name);%0A  if (element.hasAttribute(name))%0A    return element.getAttribute(name);%0A  return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A  constructor(subject, predicate, object) {%0A    // The subject on this assertion, an RDFSubject%0A    this._subject = subject;%0A    // The predicate, a string%0A    this._predicate = predicate;%0A    // The object, an RDFNode%0A    this._object = object;%0A    // The datasource this assertion exists in%0A    this._ds = this._subject._ds;%0A    // Marks that _DOMnode is the subject's element%0A    this._isSubjectElement = false;%0A    // The DOM node that represents this assertion. Could be a property element,%0A    // a property attribute or the subject's element for rdf:type%0A    this._DOMNode = null;%0A  }%0A%0A  getPredicate() {%0A    return this._predicate;%0A  }%0A%0A  getObject() {%0A    return this._object;%0A  }%0A}%0A%0Aclass RDFNode {%0A  equals(rdfnode) {%0A    return (rdfnode.constructor === this.constructor &&%0A            rdfnode._value == this._value);%0A  }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A  constructor(value) {%0A    super();%0A    this._value = value;%0A  }%0A%0A  getValue() {%0A    return this._value;%0A  }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A  constructor(ds) {%0A    super();%0A    // A lookup of the assertions with this as the subject. Keyed on predicate%0A    this._assertions = {};%0A    // A lookup of the assertions with this as the object. Keyed on predicate%0A    this._backwards = {};%0A    // The datasource this subject belongs to%0A    this._ds = ds;%0A    // The DOM elements in the document that represent this subject. Array of Element%0A    this._elements = [];%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the given Element from the DOM document%0A   %2A/%0A  /%2A eslint-disable complexity %2A/%0A  _parseElement(element) {%0A    this._elements.push(element);%0A%0A    // There might be an inferred rdf:type assertion in the element name%0A    if (element.namespaceURI != NS_RDF ||%0A        element.localName != "Description") {%0A      var assertion = new RDFAssertion(this, RDF_R("type"),%0A                                       this._ds.getResource(element.namespaceURI + element.localName));%0A      assertion._DOMnode = element;%0A      assertion._isSubjectElement = true;%0A      this._addAssertion(assertion);%0A    }%0A%0A    // Certain attributes can be literal properties%0A    for (let attr of element.attributes) {%0A      if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A          attr.nodeName == "xmlns")%0A        continue;%0A      if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A          (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A        continue;%0A      var object = null;%0A      if (attr.namespaceURI == NS_RDF) {%0A        if (attr.localName == "type")%0A          object = this._ds.getResource(attr.nodeValue);%0A      }%0A      if (!object)%0A        object = new RDFLiteral(attr.nodeValue);%0A      assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A      assertion._DOMnode = attr;%0A      this._addAssertion(assertion);%0A    }%0A%0A    var child = element.firstChild;%0A    element.listCounter = 1;%0A    while (child) {%0A      if (isElement(child)) {%0A        object = null;%0A        var predicate = child.namespaceURI + child.localName;%0A        if (child.namespaceURI == NS_RDF) {%0A          if (child.localName == "li") {%0A            predicate = RDF_R(%60_%24{element.listCounter}%60);%0A            element.listCounter++;%0A          }%0A        }%0A%0A        // Check for and bail out on unknown attributes on the property element%0A        for (let attr of child.attributes) {%0A          // Ignore XML namespaced attributes%0A          if (attr.namespaceURI == NS_XML)%0A            continue;%0A          // These are reserved by XML for future use%0A          if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A            continue;%0A          // We can handle these RDF attributes%0A          if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A              ["resource", "nodeID"].includes(attr.localName))%0A            continue;%0A          // This is a special attribute we handle for compatibility with Mozilla RDF%0A          if (attr.namespaceURI == NS_NC &&%0A              attr.localName == "parseType")%0A            continue;%0A        }%0A%0A        var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A        var resource = getRDFAttribute(child, "resource");%0A        var nodeID = getRDFAttribute(child, "nodeID");%0A%0A        if (resource !== undefined) {%0A          var base = Services.io.newURI(element.baseURI);%0A          object = this._ds.getResource(base.resolve(resource));%0A        } else if (nodeID !== undefined) {%0A          object = this._ds.getBlankNode(nodeID);%0A        } else {%0A          var hasText = false;%0A          var childElement = null;%0A          var subchild = child.firstChild;%0A          while (subchild) {%0A            if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A              hasText = true;%0A            } else if (isElement(subchild)) {%0A              childElement = subchild;%0A            }%0A            subchild = subchild.nextSibling;%0A          }%0A%0A          if (childElement) {%0A            object = this._ds._getSubjectForElement(childElement);%0A            object._parseElement(childElement);%0A          } else%0A            object = new RDFLiteral(child.textContent);%0A        }%0A%0A        assertion = new RDFAssertion(this, predicate, object);%0A        this._addAssertion(assertion);%0A        assertion._DOMnode = child;%0A      }%0A      child = child.nextSibling;%0A    }%0A  }%0A  /%2A eslint-enable complexity %2A/%0A%0A  /%2A%2A%0A   %2A Adds a new assertion to the internal hashes. Should be called for every%0A   %2A new assertion parsed or created programmatically.%0A   %2A/%0A  _addAssertion(assertion) {%0A    var predicate = assertion.getPredicate();%0A    if (predicate in this._assertions)%0A      this._assertions[predicate].push(assertion);%0A    else%0A      this._assertions[predicate] = [ assertion ];%0A%0A    var object = assertion.getObject();%0A    if (object instanceof RDFSubject) {%0A      // Create reverse assertion%0A      if (predicate in object._backwards)%0A        object._backwards[predicate].push(assertion);%0A      else%0A        object._backwards[predicate] = [ assertion ];%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Returns all objects in assertions with this subject and the given predicate.%0A   %2A/%0A  getObjects(predicate) {%0A    if (predicate in this._assertions)%0A      return Array.from(this._assertions[predicate],%0A                        i => i.getObject());%0A%0A    return [];%0A  }%0A%0A  /%2A%2A%0A   %2A Retrieves the first property value for the given predicate.%0A   %2A/%0A  getProperty(predicate) {%0A    if (predicate in this._assertions)%0A      return this._assertions[predicate][0].getObject();%0A    return null;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A  constructor(ds, uri) {%0A    super(ds);%0A    // This is the uri that the resource represents.%0A    this._uri = uri;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A  constructor(ds, nodeID) {%0A    super(ds);%0A    // The nodeID of this node. May be null if there is no ID.%0A    this._nodeID = nodeID;%0A  }%0A%0A  /%2A%2A%0A   %2A Sets attributes on the DOM element to mark it as representing this node%0A   %2A/%0A  _applyToElement(element) {%0A    if (!this._nodeID)%0A      return;%0A    if (USE_RDFNS_ATTR) {%0A      var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A      element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A    } else {%0A      element.setAttribute("nodeID", this._nodeID);%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Creates a new Element in the document for holding assertions about this%0A   %2A subject. The URI controls what tagname to use.%0A   %2A/%0A  _createNewElement(uri) {%0A    // If there are already nodes representing this in the document then we need%0A    // a nodeID to match them%0A    if (!this._nodeID && this._elements.length > 0) {%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    return super._createNewElement.call(uri);%0A  }%0A%0A  /%2A%2A%0A   %2A Adds a reference to this node to the given property Element.%0A   %2A/%0A  _addReferenceToElement(element) {%0A    if (this._elements.length > 0 && !this._nodeID) {%0A      // In document elsewhere already%0A      // Create a node ID and update the other nodes referencing%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    if (this._nodeID) {%0A      if (USE_RDFNS_ATTR) {%0A        let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A        element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A      } else {%0A        element.setAttribute("nodeID", this._nodeID);%0A      }%0A    } else {%0A      // Add the empty blank node, this is generally right since further%0A      // assertions will be added to fill this out%0A      var newelement = this._ds._addElement(element, RDF_R("Description"));%0A      newelement.listCounter = 1;%0A      this._elements.push(newelement);%0A    }%0A  }%0A%0A    /%2A%2A%0A     %2A Removes any reference to this node from the given property Element.%0A     %2A/%0A    _removeReferenceFromElement(element) {%0A      if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A        element.removeAttributeNS(NS_RDF, "nodeID");%0A      if (element.hasAttribute("nodeID"))%0A        element.removeAttribute("nodeID");%0A    }%0A%0A  getNodeID() {%0A    return this._nodeID;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A  constructor(document) {%0A    // All known resources, indexed on URI%0A    this._resources = {};%0A    // All blank nodes%0A    this._allBlankNodes = [];%0A%0A    // The underlying DOM document for this datasource%0A    this._document = document;%0A    this._parseDocument();%0A  }%0A%0A  static loadFromString(text) {%0A    let parser = new DOMParser();%0A    let document = parser.parseFromString(text, "application/xml");%0A%0A    return new this(document);%0A  }%0A%0A  /%2A%2A%0A   %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A   %2A been seen before a new one is created.%0A   %2A/%0A  _getSubjectForElement(element) {%0A    var about = getRDFAttribute(element, "about");%0A%0A    if (about !== undefined) {%0A      let base = Services.io.newURI(element.baseURI);%0A      return this.getResource(base.resolve(about));%0A    }%0A    return this.getBlankNode(null);%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the document for subjects at the top level.%0A   %2A/%0A  _parseDocument() {%0A    var domnode = this._document.documentElement.firstChild;%0A    while (domnode) {%0A      if (isElement(domnode)) {%0A        var subject = this._getSubjectForElement(domnode);%0A        subject._parseElement(domnode);%0A      }%0A      domnode = domnode.nextSibling;%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A   %2A If a nodeID is given then the blank node with that ID is returned or created.%0A   %2A/%0A  getBlankNode(nodeID) {%0A    var rdfnode = new RDFBlankNode(this, nodeID);%0A    this._allBlankNodes.push(rdfnode);%0A    return rdfnode;%0A  }%0A%0A  /%2A%2A%0A   %2A Gets the resource for the URI. The resource is created if it has not been%0A   %2A used already.%0A   %2A/%0A  getResource(uri) {%0A    if (uri in this._resources)%0A      return this._resources[uri];%0A%0A    var resource = new RDFResource(this, uri);%0A    this._resources[uri] = resource;%0A    return resource;%0A  }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A  return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A  return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A  return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A  constructor(ds) {%0A    this.ds = ds;%0A  }%0A%0A  static loadFromString(text) {%0A    return new this(RDFDataSource.loadFromString(text));%0A  }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A  _readProps(source, obj, props) {%0A    for (let prop of props) {%0A      let val = getProperty(source, prop);%0A      if (val != null) {%0A        obj[prop] = val;%0A      }%0A    }%0A  }%0A%0A  _readArrayProp(source, obj, prop, target, decode = getValue) {%0A    let result = Array.from(source.getObjects(EM_R(prop)),%0A                            target => decode(target));%0A    if (result.length) {%0A      obj[target] = result;%0A    }%0A  }%0A%0A  _readArrayProps(source, obj, props, decode = getValue) {%0A    for (let [prop, target] of Object.entries(props)) {%0A      this._readArrayProp(source, obj, prop, target, decode);%0A    }%0A  }%0A%0A  _readLocaleStrings(source, obj) {%0A    this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A    this._readArrayProps(source, obj, {%0A      locale: "locales",%0A      developer: "developers",%0A      translator: "translators",%0A      contributor: "contributors",%0A    });%0A  }%0A%0A  decode() {%0A    let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A    let result = {};%0A%0A    let props = ["id", "version", "type", "updateURL", "optionsURL",%0A                 "optionsType", "aboutURL", "iconURL",%0A                 "bootstrap", "unpack", "strictCompatibility"];%0A    this._readProps(root, result, props);%0A%0A    let decodeTargetApplication = source => {%0A      let app = {};%0A      this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A      return app;%0A    };%0A%0A    let decodeLocale = source => {%0A      let localized = {};%0A      this._readLocaleStrings(source, localized);%0A      return localized;%0A    };%0A%0A    this._readLocaleStrings(root, result);%0A%0A    this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A    this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A                         decodeTargetApplication);%0A    this._readArrayProps(root, result, {"localized": "localized"},%0A                         decodeLocale);%0A    this._readArrayProps(root, result, {"dependency": "dependencies"},%0A                         source => getProperty(source, "id"));%0A%0A    return result;%0A  }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");%0Avar {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A  ConsoleAPI: "resource://gre/modules/Console.jsm",%0A  Blocklist: "resource://gre/modules/Blocklist.jsm",%0A  XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A  if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A      doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {%0A    const win = doc.defaultView;%0A    let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A    win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A      if (e.type === 'click' &&%0A          e.target.getAttribute('action') === 'preferences' &&%0A          this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A        var windows = Services.wm.getEnumerator(null);%0A        while (windows.hasMoreElements()) {%0A          var win2 = windows.getNext();%0A          if (win2.closed) {%0A            continue;%0A          }%0A          if (win2.document.documentURI == this.addon.optionsURL) {%0A            win2.focus();%0A            return;%0A          }%0A        }%0A        var features = 'chrome,titlebar,toolbar,centerscreen';%0A        var instantApply = Services.prefs.getBoolPref('browser.preferences.instantApply');%0A        features += instantApply ? ',dialog=no' : '';%0A        win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A      } else {%0A        handleEvent_orig.apply(this, arguments);%0A      }%0A    }%0A    let update_orig = win.customElements.get('addon-options').prototype.update;%0A    win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A      update_orig.apply(this, arguments);%0A      if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A        this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;%0A    }%0A  }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A  if (!this.isActive) {%0A    return null;%0A  }%0A%0A  let addon = this.__AddonInternal__;%0A  let hasOptionsURL = !!this.optionsURL;%0A%0A  if (addon.optionsType) {%0A    switch (parseInt(addon.optionsType, 10)) {%0A      case OPTIONS_TYPE_DIALOG:%0A      case AddonManager.OPTIONS_TYPE_TAB:%0A      case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A        return hasOptionsURL ? addon.optionsType : null;%0A    }%0A    return null;%0A  }%0A%0A  return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA      = ['id', 'version', 'type', 'internalName', 'updateURL',%0A                            'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI  = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A  extension: 2,%0A  dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A  extension: true,%0A  dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A  let ext = filename.slice(-4).toLowerCase();%0A  return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A        The file containing the resources, must be either a directory or an%0A %2A        XPI file%0A %2A @param {string} aPath%0A %2A        The path to find the resource at, '/' separated. If aPath is empty%0A %2A        then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A        An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A  if (!isXPI(aFile.leafName)) {%0A    let resource = aFile.clone();%0A    if (aPath)%0A      aPath.split('/').forEach(part => resource.append(part));%0A%0A    return Services.io.newFileURI(resource);%0A  }%0A%0A  return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A        The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A        The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A        An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A  let uri = Services.io.newFileURI(aJarfile);%0A  uri = 'jar:' + uri.spec + '!/' + aPath;%0A  return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A  name: 'bootstrap',%0A  manifestFile: 'install.rdf',%0A  async loadManifest(pkg) {%0A    /%2A%2A%0A     %2A Reads locale properties from either the main install manifest root or%0A     %2A an em:localized section in the install manifest.%0A     %2A%0A     %2A @param {Object} aSource%0A     %2A        The resource to read the properties from.%0A     %2A @param {boolean} isDefault%0A     %2A        True if the locale is to be read from the main install manifest%0A     %2A        root%0A     %2A @param {string[]} aSeenLocales%0A     %2A        An array of locale names already seen for this install manifest.%0A     %2A        Any locale names seen as a part of this function will be added to%0A     %2A        this array%0A     %2A @returns {Object}%0A     %2A        an object containing the locale properties%0A     %2A/%0A    function readLocale(aSource, isDefault, aSeenLocales) {%0A      let locale = {};%0A      if (!isDefault) {%0A        locale.locales = [];%0A        for (let localeName of aSource.locales || []) {%0A          if (!localeName) {%0A            logger.warn('Ignoring empty locale in localized properties');%0A            continue;%0A          }%0A          if (aSeenLocales.includes(localeName)) {%0A            logger.warn('Ignoring duplicate locale in localized properties');%0A            continue;%0A          }%0A          aSeenLocales.push(localeName);%0A          locale.locales.push(localeName);%0A        }%0A%0A        if (locale.locales.length == 0) {%0A          logger.warn('Ignoring localized properties with no listed locales');%0A          return null;%0A        }%0A      }%0A%0A      for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A        if (hasOwnProperty(aSource, prop)) {%0A          locale[prop] = aSource[prop];%0A        }%0A      }%0A%0A      return locale;%0A    }%0A%0A    let manifestData = await pkg.readString('install.rdf');%0A    let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A    let addon = new AddonInternal();%0A    for (let prop of PROP_METADATA) {%0A      if (hasOwnProperty(manifest, prop)) {%0A        addon[prop] = manifest[prop];%0A      }%0A    }%0A%0A    if (!addon.type) {%0A      addon.type = 'extension';%0A    } else {%0A      let type = addon.type;%0A      addon.type = null;%0A      for (let name in TYPES) {%0A        if (TYPES[name] == type) {%0A          addon.type = name;%0A          break;%0A        }%0A      }%0A    }%0A%0A    if (!(addon.type in TYPES))%0A      throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A    if (!addon.id)%0A      throw new Error('No ID in install manifest');%0A    if (!gIDTest.test(addon.id))%0A      throw new Error('Illegal add-on ID ' + addon.id);%0A    if (!addon.version)%0A      throw new Error('No version in install manifest');%0A%0A    addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A                                 manifest.strictCompatibility == 'true');%0A%0A    // Only read these properties for extensions.%0A    if (addon.type == 'extension') {%0A      if (manifest.bootstrap != 'true') {%0A        throw new Error('Non-restartless extensions no longer supported');%0A      }%0A%0A      if (addon.optionsType &&%0A          addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A            throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A      }%0A%0A      if (addon.optionsType)%0A        addon.optionsType = parseInt(addon.optionsType);%0A    }%0A%0A    addon.defaultLocale = readLocale(manifest, true);%0A%0A    let seenLocales = [];%0A    addon.locales = [];%0A    for (let localeData of manifest.localized || []) {%0A      let locale = readLocale(localeData, false, seenLocales);%0A      if (locale)%0A        addon.locales.push(locale);%0A    }%0A%0A    let dependencies = new Set(manifest.dependencies);%0A    addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A    let seenApplications = [];%0A    addon.targetApplications = [];%0A    for (let targetApp of manifest.targetApplications || []) {%0A      if (!targetApp.id || !targetApp.minVersion ||%0A          !targetApp.maxVersion) {%0A            logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A            continue;%0A      }%0A      if (seenApplications.includes(targetApp.id)) {%0A        logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A                    ' in install manifest');%0A        continue;%0A      }%0A      seenApplications.push(targetApp.id);%0A      addon.targetApplications.push(targetApp);%0A    }%0A%0A    // Note that we don't need to check for duplicate targetPlatform entries since%0A    // the RDF service coalesces them for us.%0A    addon.targetPlatforms = [];%0A    for (let targetPlatform of manifest.targetPlatforms || []) {%0A      let platform = {%0A        os: null,%0A        abi: null,%0A      };%0A%0A      let pos = targetPlatform.indexOf('_');%0A      if (pos != -1) {%0A        platform.os = targetPlatform.substring(0, pos);%0A        platform.abi = targetPlatform.substring(pos + 1);%0A      } else {%0A        platform.os = targetPlatform;%0A      }%0A%0A      addon.targetPlatforms.push(platform);%0A    }%0A%0A    addon.userDisabled = false;%0A    addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A    addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A    addon.userPermissions = null;%0A%0A    addon.icons = {};%0A    if (await pkg.hasResource('icon.png')) {%0A      addon.icons[32] = 'icon.png';%0A      addon.icons[48] = 'icon.png';%0A    }%0A%0A    if (await pkg.hasResource('icon64.png')) {%0A      addon.icons[64] = 'icon64.png';%0A    }%0A%0A    return addon;%0A  },%0A%0A  loadScope(addon) {%0A    let file = addon.file || addon._sourceBundle;%0A    let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A    let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A    let sandbox = new Cu.Sandbox(principal, {%0A      sandboxName: uri,%0A      addonId: addon.id,%0A      wantGlobalProperties: ['ChromeUtils'],%0A      metadata: { addonID: addon.id, URI: uri },%0A    });%0A%0A    try {%0A      Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A      XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A        new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A      Services.scriptloader.loadSubScript(uri, sandbox);%0A    } catch (e) {%0A      logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A    }%0A%0A    function findMethod(name) {%0A      if (sandbox[name]) {%0A        return sandbox[name];%0A      }%0A%0A      try {%0A        let method = Cu.evalInSandbox(name, sandbox);%0A        return method;%0A      } catch (err) { }%0A%0A      return () => {%0A        logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A      };%0A    }%0A%0A    let install = findMethod('install');%0A    let uninstall = findMethod('uninstall');%0A    let startup = findMethod('startup');%0A    let shutdown = findMethod('shutdown');%0A%0A    return {%0A      install(...args) {%0A        install(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      uninstall(...args) {%0A        uninstall(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      startup(...args) {%0A        if (addon.type == 'extension') {%0A          logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A          Components.manager.addBootstrappedManifestLocation(file);%0A        }%0A        return startup(...args);%0A      },%0A%0A      shutdown(data, reason) {%0A        try {%0A          return shutdown(data, reason);%0A        } catch (err) {%0A          throw err;%0A        } finally {%0A          if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A            logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A            Components.manager.removeBootstrappedManifestLocation(file);%0A          }%0A        }%0A      },%0A    };%0A  },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0AObject.defineProperty(%0A  AddonInternal.prototype,%0A  "providesUpdatesSecurely",%0A  {enumerable: true, value: true}%0A);%0A
	`.trim()));
	Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
		.loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]}));
})(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);}

//
try {({
	ids: [
		"custombuttons@xsms.org",
	],
	init(xrt) {
		if (xrt.inSafeMode) return;
		Cu.import("resource://gre/modules/addons/XPIProvider.jsm", this);
		var load = async file => {
			var rootURI = this.XPIInternal.getURIForResourceInFile(file, "");
			Cu.import(rootURI.resolve("startup.jsm"), {}).start(rootURI);
		}
		var proto = this.XPIInternal.BootstrapScope.prototype;
		var func = proto._beforeCallBootstrapMethod;

		proto._beforeCallBootstrapMethod = () => {
			proto._beforeCallBootstrapMethod = func;
			for(var addon of this.XPIInternal.XPIStates.enabledAddons())
				this.ids.includes(addon.id) && !addon.loader && load(addon.file);
		}
	}
}).init(Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime));}
catch(ex) {Cu.reportError(ex);}

Отредактировано Farby (23-03-2022 12:38:12)


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

На форуме

 

№1629524-03-2022 00:04:11

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

Re: Custom Buttons

Farby пишет

для 99 [firefox] будет такой антиподписальщик

Да вроде и в [firefox] 98 работает


Win7

Отсутствует

 

№1629627-03-2022 13:41:23

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

Re: Custom Buttons

Всем добра!
Много лет пользуюсь этой сборкой FF.
http://www1.plala.or.jp/tete009/en-US/software.html
Начиная с 94-ой версии после установки Сustom Buttons интерфейс FF частично становится на английском языке.
Может кто знает как это побороть?

Отсутствует

 

№1629730-03-2022 20:16:51

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

Re: Custom Buttons

questman
lockPref("intl.locale.requested", "ru");
И саму locale смени правильно...В обоих omni.ja

Отсутствует

 

№1629831-03-2022 17:58:47

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

Re: Custom Buttons

ВВП пишет

questmanlockPref("intl.locale.requested", "ru");И саму locale смени правильно...В обоих omni.ja

lockPref("intl.locale.requested", "ru");
Этот параметр само сабой стоит в RU
Как менять locale в omni.ja к сожалениию не знаю.Всегда обходился установкой русского языка с офф.сайта
Есть ещё одна проблемка.
После установки Сustom Buttons начиная с 94-ой версии FF на странице "Дополнения и темы" в разделе <<Сustom Buttons>>
отсутствуют кнопки.Вместо кнопок дублированный раздел <<Расширения>>
https://hkar.ru/15lBt

Отредактировано questman (31-03-2022 18:03:32)

Отсутствует

 

№1629901-04-2022 09:56:46

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

Re: Custom Buttons

questman
https://anonfiles.com/x37b50S5x9/custom … ms.org_xpi
omni.ja - вскрыть winrar сменить все папки с языком . в нижнем omni.ja сменить папку res

Отредактировано ВВП (01-04-2022 09:58:25)

Отсутствует

 

№1630003-04-2022 20:49:59

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

Re: Custom Buttons

Отсутствует

 

Board footer

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