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

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

№1307603-01-2019 11:35:07

okkamas_knife
We are the Borg.       Resistance is futile.
 
Группа: Members
Зарегистрирован: 21-10-2009
Сообщений: 9558
UA: Seamonkey 2.14

Re: Custom Buttons

nbuh пишет: Скажите пожалуйста, а как установить иконку кнопки, если она дана в виде кода, а не рисунка?

изъясняйтесь внятнее что есть и что хотите получить.
если есть чтото типа data:image/png;base64,.... то просто вставляете этот код в поле изображение в окне редактирования кнопки
либо ищете в коде кнопки подобные куски и заменяете своими (все что в кавычках)
если вам надо получитть такой код из картинки то в окне редактироания там где изображение выберите файл и нажмите >>base64 и в строке вместо адреса появится этот код


я помню те времена когда обновления программ убирали проблемы и исправляли баги, а не добавляли их.
toxID:05AB9B827D896AACEE7FF4573A02FB8F025F46ADC856B98F65BC1BA9BD21A81DC98BA9C36CE3

Отсутствует

 

№1307704-01-2019 18:59:09

SanchiZ
Участник
 
Группа: Members
Зарегистрирован: 11-10-2016
Сообщений: 23
UA: Firefox 52.0

Re: Custom Buttons

Подскажите есть ли такая кнопка открывающая ютюбные ссылки ЛКМ автоматически во внешнем плеере?

Отсутствует

 

№1307805-01-2019 20:30:31

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

Re: Custom Buttons

Есть вот такой код:

Открыть вкладку "Исходный код страницы" справа от текущей

Выделить код

Код:

/*Initialization Code*/

// Открыть вкладку "Исходный код страницы" справа от текущей. ..........
this.onclick = function(event) {
  if(event.button == 0) {
    var orgTab = gBrowser.selectedTab;
    var newTab = gBrowser.addTab ("view-source:" + content.location.href);
    gBrowser.moveTabTo(newTab, orgTab._tPos+1);
    setTimeout(function() { gBrowser.selectedTab = newTab, 500 });
  }
}


Как сделать тоже самое, только для "Исходный код выделенного фрагмента"?


«The Truth Is Out There»

Отсутствует

 

№1307905-01-2019 22:36:13

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

Re: Custom Buttons

unter_officer может что-то из этого подойдет с заменой "view-source:", context-inspect, view-inspect, inspect...

Отсутствует

 

№1308005-01-2019 22:48:02

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

Re: Custom Buttons

func4ptch4

Попробовал. Не работает.


«The Truth Is Out There»

Отсутствует

 

№1308117-01-2019 01:02:55

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

Re: Custom Buttons

rubel Только сейчас заметил ваше сообщение  - Save snapshot to html
У меня на 64 работает так -

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

Выделить код

Код:

// Добавить новый пункт "Сохранить страницу или выбранное как  HTML" в главном меню .....................................................
(function() {
    // блокировать дублирование пункта при открытии настройки панелей
  if ( document.getElementById("SaveHTML") ) return;

function SaveHTML(event) {
var saveToFile = function (fileContent, fileName) {
    var uc = Components.classes['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
    uc.charset = 'utf-8';
    fileContent = uc.ConvertFromUnicode(fileContent);

    var nsIFilePicker = Components.interfaces.nsIFilePicker;
    var fp = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
    fp.init(window, '', fp.modeSave);
    fp.defaultString = fileName;
    fp.appendFilters(fp.filterHTML);
    fp.appendFilters(fp.filterAll);
    fp.open(function (rv) {
  if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
    var stream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream);
    stream.init(fp.file, 0x02|0x20|0x08, 0666, 0);
    stream.write(fileContent, fileContent.length);
    stream.close();
  }
});

};
var resolveURL = function (url, base) {
    try {
        var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService);
        var baseURI = ioService.newURI(base, null, null);
        var absURI = ioService.newURI(url, null, baseURI);
        return absURI.spec;
    } catch (e) {}
};

var getSelWin = function (w) {
    if (w.getSelection().toString()) return w;
    for (var i = 0, f, r; f = w.frames[i]; i++) {
        try {
            if (r = getSelWin(f)) return r;
        } catch(e) {}
    }
};
var encodeImg = function (src, obj) {
    var canvas, img, ret = src;
    if (/^https?:\/\//.test(src)) {
        canvas = doc.createElement('canvas');
        if (!obj || obj.nodeName.toLowerCase() != 'img') {
            img = doc.createElement('img');
            img.src = src;
        } else {
            img = obj;
        };
        if (img.complete) try{
            canvas.width = img.width;
            canvas.height = img.height;
            canvas.getContext('2d').drawImage(img, 0, 0);
            ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
        } catch (e) {};
        if (img != obj) img.src = 'about:blank';
    };
    return ret;
};
var toSrc = function (obj) {
    var strToSrc = function (str) {
        var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
        while (chr = str.charAt(i++)) {
            ret += meta[chr] || chr;
        };
        return '\x22' + ret + '\x22';
    },
    arrToSrc = function (arr) {
        var ret = [];
        for (var i = 0; i < arr.length; i++) {
            ret[i] = toSrc(arr[i]) || 'null';
        };
        return '[' + ret.join(',') + ']';
    },
    objToSrc = function (obj) {
        var val, ret = [];
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
        };
        return '{' + ret.join(',') + '}';
    };

    switch (Object.prototype.toString.call(obj).slice(8, -1)) {
        case 'Array': return arrToSrc(obj);
        case 'Boolean':
        case 'Function':
        case 'RegExp': return obj.toString();
        case 'Date': return 'new Date(' + obj.getTime() + ')';
        case 'Math': return 'Math';
        case 'Number': return isFinite(obj) ? String(obj) : 'null';
        case 'Object': return objToSrc(obj);
        case 'String': return strToSrc(obj);
        default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
    }
};

var mainWin = document.commandDispatcher.focusedWindow.top == content ? document.commandDispatcher.focusedWindow : content
var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location;
var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g;

if (selWin) {
    var rng = win.getSelection().getRangeAt(0);
    pEle = rng.commonAncestorContainer;
    ele = rng.cloneContents();
} else {
    pEle = doc.documentElement;
    ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true);
};
while (pEle) {
    if (pEle.nodeType == 1) {
        clone = pEle.cloneNode(false);
        clone.appendChild(ele);
        ele = clone;
    };
    pEle = pEle.parentNode
};
var sel = doc.createElement('div');
sel.appendChild(ele);

for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
    el = all[i];
    if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
        if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
        return prev + encodeImg(url) + next;
    });
    switch (el.nodeName.toLowerCase()) {
        case 'link':
        case 'style':
        case 'script': el.parentNode.removeChild(el); break;
        case 'a': 
        case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
        case 'img':
        case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
        case 'audio':
        case 'video':
        case 'embed':
        case 'frame':
        case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
        case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
        case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
    }
};

var head = ele.insertBefore(doc.createElement('head'), ele.firstChild);
var meta = doc.createElement('meta');
meta.httpEquiv = 'content-type';
meta.content = 'text/html; charset=utf-8';
head.appendChild(meta);
var title = doc.getElementsByTagName('title')[0];
if (title) head.appendChild(title.cloneNode(true));

head.copyScript = function (unsafeWin) {
    if ('$' in unsafeWin) return;
    var f = doc.createElement('iframe');
    f.src = 'about:blank';
    f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');
    doc.documentElement.appendChild(f);
    var str, script = doc.createElement('script');
    script.type = 'text/javascript';
    for (var name in unsafeWin) {
        if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue;
        try {
            str = toSrc(unsafeWin[name]);
            if (!/\{\s*\[native code\]\s*\}/.test(str)) {
                script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n'));
            }
        } catch (e) {};
    };
    f.parentNode.removeChild(f);
    if (script.childNodes.length) this.nextSibling.appendChild(script);
};
head.copyScript(win.wrappedJSObject || win);

head.copyStyle = function (s) {
    if (!s) return;
    var style = doc.createElement('style');
    style.type = 'text/css';
    if (s.media && s.media.mediaText) style.media = s.media.mediaText;
    try {
        for (var i = 0, rule; rule = s.cssRules[i]; i++) {
            if (rule.type != 3) {
                if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) {
                    var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) {
                        if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href);
                        if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url);
                        return prev + url + next;
                    });
                    style.appendChild(doc.createTextNode(css + '\n'));
                }
            } else {
                this.copyStyle(rule.styleSheet);
            }
        }
    } catch(e) {
        if (s.ownerNode) style = s.ownerNode.cloneNode(false);
    };
    this.appendChild(style);
};
var sheets = doc.styleSheets;
for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]);
head.appendChild(doc.createTextNode('\n'));

var doctype = '', dt = doc.doctype;
if (dt && dt.name) {
    doctype += '<!DOCTYPE ' + dt.name;
    if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
    if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
    doctype += '>\n';
};
var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, '');
fileName += (function () {
    var d = new Date(), z = function(n){return '_' + (n < 10 ? '0' : '') + n};
    return z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds());
})();
if(!/\.html?$/.test(fileName))fileName += '.html';

saveToFile(doctype + sel.innerHTML + '\n<!-- This document saved from ' + (loc.protocol != 'data:' ? loc.href : 'data:uri') + ' -->', fileName);
 }   
     var menuItem = document.createElement("toolbarbutton");
    menuItem.id = "SaveHTML";               
    menuItem.setAttribute("label", "Сохранить страницу или выбранное как HTML");
    menuItem.setAttribute("class", "subviewbutton subviewbutton-iconic");
    menuItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII=");
    menuItem.addEventListener("command", SaveHTML, false);  
   var it = document.getElementById("appMenu-print-button");
    it.parentNode.insertBefore(menuItem, it); 
  
 })();

Отсутствует

 

№1308217-01-2019 07:50:41

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

Re: Custom Buttons

Andrey_Krropotkin пишет

У меня на 64 работает так -

Попробовал. Не работает, ваш код вставлял и во вкладку Код и в Инициализацию. Нигде нет этого пункта:
"Сохранить страницу или выбранное как  HTML" в главном меню
. Извиняюсь, нашел в кнопочке справа. А я искал в меню Инструменты.:) Спасибо Вам За эту чудесную кнопку!

Отредактировано rubel (17-01-2019 08:01:08)

Отсутствует

 

№1308317-01-2019 10:48:55

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

Re: Custom Buttons

Подправил под 64 - Добавление на вкладке дополнения в меню расширений дополнительных пунктов

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

Выделить код

Код:

//Добавление на вкладке дополнения в меню расширений дополнительных пунктов
(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);
            
             menuitem = $C("menuitem", {
                id: "AM-copy-version",
                label: "Копировать версию",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyVersion);"
            });
            menuPopup.appendChild(menuitem);
 
            menuitem = $C("menuitem", {
                id: "AM-copy-NameVersion",
                label: "Копировать имя и версию",
                oncommand: "AM_Helper.getAddon(AM_Helper.getPopupNode(this).value, AM_Helper.copyNameVersion);"
            });
            menuPopup.appendChild(menuitem);
           
            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.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);
        }
    };

 
    AM_Helper.init();

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

Отсутствует

 

№1308428-01-2019 18:24:00

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

Re: Custom Buttons

«Merge Date»
Описание исходной концепции (Firefox 63+) здесь.
В коде для конфигурационного файла заменён запускатор,
поскольку рассылка использовавшегося топика прекращена (#l4.31).

config.js

Выделить код

Код:

//
try {(jsval => {
    var data = {
        MOZ_REQUIRE_SIGNING: false,
        MOZ_ALLOW_LEGACY_EXTENSIONS: true,
        get MOZ_UNSIGNED_SCOPES() {
            return 31; // AddonManager.SCOPE_ALL
        }
    };
    var o = Cu.getGlobalForObject(jsval).Object, {freeze} = o;
    o.freeze = obj => {
        if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
            return freeze(obj);
        for(let key in data)
            o.defineProperty(obj, key, o.getOwnPropertyDescriptor(data, key));
        return (o.freeze = freeze)(obj);
    }
    lockPref("extensions.legacy.enabled", true);
    lockPref("xpinstall.signatures.required", false);
    lockPref("extensions.langpacks.signatures.required", false);

    Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {})
        .shouldVerifySignedState = addon => !addon.id;
    
})(Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}));}
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);}


custom_buttons-0.0.7.0.0.2-fx-paxmod.xpi

Dumby пишет

из Firefox 65 вырезали аддон-менеджерские коды
обслуживавшие bootstrapped extensions

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

скрытый текст
Захотелось и себе завести что-то подобное.

Но идея подгрузки через config.js многочисленного файла́ из профиля
показалась немного запутанной, а bootstrapLoader.xpi, который лежит
среди опекаемых расширений, тоже слегка не понравился,
да и вообще xpi, как таковой, пока не слишком хорошо подходит для таких дел.

Однако, взял из него код, чуть-чуть изменил, дополнил про providesUpdatesSecurely,
собрал адресом в одну строку, и записал как scriptloader'ом в sandbox.

Получилсь компактно, и не нужно ничего ни устанавливать,
ни создавать никаких файлов, а просто добавить код в config.js

Private Tab 0.2.3pre прямо из-под make.bat установился без проблем.

Соответственно, и Custom Buttons без paxmod-оболочки, если что.
custom_buttons-0.0.7.0.0.2-fx-bootstrap.xpi

bootstrap-loader.js

Выделить код

Код:

//
try {
    Cu.createDocumentEncoder && Cc["@mozilla.org/moz/jssubscript-loader;1"]
        .getService(Ci.mozIJSSubScriptLoader).loadSubScript(String.raw`
            data:,var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0Avar {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A%09Services: "resource://gre/modules/Services.jsm",%0A%09ConsoleAPI: "resource://gre/modules/Console.jsm",%0A%09Blocklist: "resource://gre/modules/Blocklist.jsm",%0A%09AddonInternal: "resource://gre/modules/addons/XPIDatabase.jsm",%0A%09RDFDataSource: "resource://gre/modules/addons/RDFDataSource.jsm"%0A});%0AXPCOMUtils.defineLazyGetter(this, "BOOTSTRAP_REASONS", () =>%0A%09ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {})%0A%09%09.XPIProvider.BOOTSTRAP_REASONS%0A);%0AXPCOMUtils.defineLazyGetter(this, "logger", () =>%0A%09ChromeUtils.import("resource://gre/modules/Log.jsm", {})%0A%09%09.Log.repository.getLogger("addons.bootstrap")%0A);%0A%0A%0A// RDFManifestConverter.jsm%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A%09return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0Afunction getValue(literal) {%0A%09return literal && literal.getValue();%0A}%0Afunction getProperty(resource, property) {%0A%09return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A%09constructor(ds) {%0A%09%09this.ds = ds;%0A%09}%0A%09static loadFromString(text) {%0A%09%09return new this(RDFDataSource.loadFromString(text));%0A%09}%0A%09static loadFromBuffer(buffer) {%0A%09%09return new this(RDFDataSource.loadFromBuffer(buffer));%0A%09}%0A%09static async loadFromFile(uri) {%0A%09%09return new this(await RDFDataSource.loadFromFile(uri));%0A%09}%0A}%0A%0Aclass InstallRDF extends Manifest {%0A%09_readProps(source, obj, props) {%0A%09%09for (let prop of props) {%0A%09%09%09let val = getProperty(source, prop);%0A%09%09%09if (val != null) {%0A%09%09%09%09obj[prop] = val;%0A%09%09%09}%0A%09%09}%0A%09}%0A%09_readArrayProp(source, obj, prop, target, decode = getValue) {%0A%09%09let result = Array.from(%0A%09%09%09source.getObjects(EM_R(prop)), target => decode(target)%0A%09%09);%0A%09%09if (result.length) {%0A%09%09%09obj[target] = result;%0A%09%09}%0A%09}%0A%09_readArrayProps(source, obj, props, decode = getValue) {%0A%09%09for (let [prop, target] of Object.entries(props)) {%0A%09%09%09this._readArrayProp(source, obj, prop, target, decode);%0A%09%09}%0A%09}%0A%09_readLocaleStrings(source, obj) {%0A%09%09this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A%09%09this._readArrayProps(source, obj, {%0A%09%09%09locale: "locales",%0A%09%09%09developer: "developers",%0A%09%09%09translator: "translators",%0A%09%09%09contributor: "contributors",%0A%09%09});%0A%09}%0A%09decode() {%0A%09%09let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A%09%09let result = {};%0A%0A%09%09let props = [%0A%09%09%09"id", "version", "type", "updateURL", "optionsURL",%0A%09%09%09"optionsType", "aboutURL", "iconURL",%0A%09%09%09"bootstrap", "unpack", "strictCompatibility"%0A%09%09];%0A%09%09this._readProps(root, result, props);%0A%0A%09%09let decodeTargetApplication = source => {%0A%09%09%09let app = {maxVersion: "*"};%0A%09%09%09this._readProps(source, app, ["id", "minVersion"]);%0A%09%09%09return app;%0A%09%09};%0A%0A%09%09let decodeLocale = source => {%0A%09%09%09let localized = {};%0A%09%09%09this._readLocaleStrings(source, localized);%0A%09%09%09return localized;%0A%09%09};%0A%0A%09%09this._readLocaleStrings(root, result);%0A%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"targetPlatform": "targetPlatforms"}%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"targetApplication": "targetApplications"}, decodeTargetApplication%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"localized": "localized"}, decodeLocale%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"dependency": "dependencies"}, source => getProperty(source, "id")%0A%09%09);%0A%09%09return result;%0A%09}%0A}%0A// fim RDFManifestConverter.jsm%0A%0A%0A// BootstrapLoader.jsm%0A/**%0A * Valid IDs fit this pattern.%0A */%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-\._]*\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA%09= [%0A%09"id", "version", "type", "internalName", "updateURL",%0A%09"optionsURL", "optionsType", "aboutURL", "iconURL"%0A];%0Aconst PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];%0Aconst PROP_LOCALE_MULTI%09= ["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%09extension: 2,%0A%09dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A%09extension: true,%0A%09dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A%09let ext = filename.slice(-4).toLowerCase();%0A%09return ext === ".xpi" || ext === ".zip";%0A}%0A%0A/**%0A * Gets an nsIURI for a file within another file, either a directory or an XPI%0A * file. If aFile is a directory then this will return a file: URI, if it is an%0A * XPI file then it will return a jar: URI.%0A *%0A * @param {nsIFile} aFile%0A *%09%09The file containing the resources, must be either a directory or an XPI file%0A *%0A * @param {string} aPath%0A *%09%09The path to find the resource at, "/" separated. If aPath is empty%0A *%09%09then the uri to the root of the contained files will be returned%0A *%0A * @returns {nsIURI}%0A *%09%09An nsIURI pointing at the resource%0A */%0Afunction getURIForResourceInFile(aFile, aPath) {%0A%09if (!isXPI(aFile.leafName)) {%0A%09%09let resource = aFile.clone();%0A%09%09if (aPath)%0A%09%09%09aPath.split("/").forEach(part => resource.append(part));%0A%0A%09%09return Services.io.newFileURI(resource);%0A%09}%0A%09return buildJarURI(aFile, aPath);%0A}%0A%0A/**%0A * Creates a jar: URI for a file inside a ZIP file.%0A *%0A * @param {nsIFile} aJarfile%0A *%09%09The ZIP file as an nsIFile%0A *%0A * @param {string} aPath%0A *%09%09The path inside the ZIP file%0A *%0A * @returns {nsIURI}%0A *%09%09An nsIURI for the file%0A */%0Afunction buildJarURI(aJarfile, aPath) {%0A%09let uri = Services.io.newFileURI(aJarfile);%0A%09uri = "jar:" + uri.spec + "!/" + aPath;%0A%09return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A%09name: "bootstrap",%0A%09manifestFile: "install.rdf",%0A%09async loadManifest(pkg) {%0A%09%09/**%0A%09%09 * Reads locale properties from either the main install manifest root%0A%09%09 * or an em:localized section in the install manifest.%0A%09%09 *%0A%09%09 * @param {Object} aSource%0A%09%09 *%09%09The resource to read the properties from.%0A%09%09 *%0A%09%09 * @param {boolean} isDefault%0A%09%09 *%09%09True if the locale is to be read from the main install manifest root%0A%09%09 *%0A%09%09 * @param {string[]} aSeenLocales%0A%09%09 *%09%09An array of locale names already seen for this install manifest.%0A%09%09 *%09%09Any locale names seen as a part of this function will be added to this array%0A%09%09 *%0A%09%09 * @returns {Object}%0A%09%09 *%09%09An object containing the locale properties%0A%09%09 */%0A%09%09function readLocale(aSource, isDefault, aSeenLocales) {%0A%09%09%09let locale = {};%0A%09%09%09if (!isDefault) {%0A%09%09%09%09locale.locales = [];%0A%09%09%09%09for (let localeName of aSource.locales || []) {%0A%09%09%09%09%09if (!localeName) {%0A%09%09%09%09%09%09logger.warn("Ignoring empty locale in localized properties");%0A%09%09%09%09%09%09continue;%0A%09%09%09%09%09}%0A%09%09%09%09%09if (aSeenLocales.includes(localeName)) {%0A%09%09%09%09%09%09logger.warn("Ignoring duplicate locale in localized properties");%0A%09%09%09%09%09%09continue;%0A%09%09%09%09%09}%0A%09%09%09%09%09aSeenLocales.push(localeName);%0A%09%09%09%09%09locale.locales.push(localeName);%0A%09%09%09%09}%0A%0A%09%09%09%09if (locale.locales.length == 0) {%0A%09%09%09%09%09logger.warn("Ignoring localized properties with no listed locales");%0A%09%09%09%09%09return null;%0A%09%09%09%09}%0A%09%09%09}%0A%09%09%09for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A%09%09%09%09if (hasOwnProperty(aSource, prop)) {%0A%09%09%09%09%09locale[prop] = aSource[prop];%0A%09%09%09%09}%0A%09%09%09}%0A%09%09%09return locale;%0A%09%09}%0A%0A%09%09let manifestData = await pkg.readString("install.rdf");%0A%09%09let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A%09%09let addon = new AddonInternal();%0A%09%09for (let prop of PROP_METADATA) {%0A%09%09%09if (hasOwnProperty(manifest, prop)) {%0A%09%09%09%09addon[prop] = manifest[prop];%0A%09%09%09}%0A%09%09}%0A%09%09if (!addon.type) {%0A%09%09%09addon.type = "extension";%0A%09%09} else {%0A%09%09%09let type = addon.type;%0A%09%09%09addon.type = null;%0A%09%09%09for (let name in TYPES) {%0A%09%09%09%09if (TYPES[name] == type) {%0A%09%09%09%09%09addon.type = name;%0A%09%09%09%09%09break;%0A%09%09%09%09}%0A%09%09%09}%0A%09%09}%0A%09%09if (!(addon.type in TYPES))%0A%09%09%09throw new Error("Install manifest specifies unknown type: " + addon.type);%0A%0A%09%09if (!addon.id)%0A%09%09%09throw new Error("No ID in install manifest");%0A%09%09if (!gIDTest.test(addon.id))%0A%09%09%09throw new Error("Illegal add-on ID " + addon.id);%0A%09%09if (!addon.version)%0A%09%09%09throw new Error("No version in install manifest");%0A%0A%09%09addon.strictCompatibility =%0A%09%09%09!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A%09%09%09"strictCompatibility" in manifest && manifest.strictCompatibility == "true";%0A%0A%09%09// Only read these properties for extensions.%0A%09%09if (addon.type == "extension") {%0A%09%09%09if (manifest.bootstrap != "true") {%0A%09%09%09%09throw new Error("Non-restartless extensions no longer supported");%0A%09%09%09}%0A%09%09%09if (%0A%09%09%09%09addon.optionsType &&%0A%09%09%09%09addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A%09%09%09%09addon.optionsType != AddonManager.OPTIONS_TYPE_TAB%0A%09%09%09) {%0A%09%09%09%09throw new Error("Install manifest specifies unknown optionsType: " + addon.optionsType);%0A%09%09%09}%0A%09%09} else {%0A%09%09%09// Convert legacy dictionaries into a format the WebExtension%0A%09%09%09// dictionary loader can process.%0A%09%09%09if (addon.type === "dictionary") {%0A%09%09%09%09addon.loader = null;%0A%09%09%09%09let dictionaries = {};%0A%09%09%09%09await pkg.iterFiles(({path}) => {%0A%09%09%09%09%09let match = /^dictionaries\/([^\/]+)\.dic$/.exec(path);%0A%09%09%09%09%09if (match) {%0A%09%09%09%09%09%09let lang = match[1].replace(/_/g, "-");%0A%09%09%09%09%09%09dictionaries[lang] = match[0];%0A%09%09%09%09%09}%0A%09%09%09%09});%0A%09%09%09%09addon.startupData = {dictionaries};%0A%09%09%09}%0A%0A%09%09%09// Only extensions are allowed to provide an optionsURL, optionsType,%0A%09%09%09// optionsBrowserStyle, or aboutURL. For all other types they are silently ignored%0A%09%09%09addon.aboutURL = null;%0A%09%09%09addon.optionsBrowserStyle = null;%0A%09%09%09addon.optionsType = null;%0A%09%09%09addon.optionsURL = null;%0A%09%09}%0A%0A%09%09addon.defaultLocale = readLocale(manifest, true);%0A%09%09let defaultLocaleEntries = Object.entries(addon.defaultLocale);%0A%0A%09%09let seenLocales = [];%0A%09%09addon.locales = [];%0A%09%09for (let localeData of manifest.localized || []) {%0A%09%09%09let locale = readLocale(localeData, false, seenLocales);%0A%09%09%09if (locale) {%0A%09%09%09%09for(let [key, val] of defaultLocaleEntries)%0A%09%09%09%09%09key in locale || Reflect.set(locale, key, val);%0A%09%09%09%09addon.locales.push(locale);%0A%09%09%09}%0A%09%09}%0A%0A%09%09addon.dependencies = Object.freeze(%0A%09%09%09"dependencies" in manifest ? Array.from(new Set(manifest.dependencies)) : []%0A%09%09);%0A%0A%09%09let seenApplications = [];%0A%09%09addon.targetApplications = [];%0A%09%09for (let targetApp of manifest.targetApplications || []) {%0A%09%09%09if (!targetApp.id || !targetApp.minVersion || !targetApp.maxVersion) {%0A%09%09%09%09logger.warn("Ignoring invalid targetApplication entry in install manifest");%0A%09%09%09%09continue;%0A%09%09%09}%0A%09%09%09if (seenApplications.includes(targetApp.id)) {%0A%09%09%09%09logger.warn(%0A%09%09%09%09%09"Ignoring duplicate targetApplication entry for " + targetApp.id + " in install manifest"%0A%09%09%09%09);%0A%09%09%09%09continue;%0A%09%09%09}%0A%09%09%09seenApplications.push(targetApp.id);%0A%09%09%09addon.targetApplications.push(targetApp);%0A%09%09}%0A%0A%09%09// Note that we don't need to check for duplicate targetPlatform entries since%0A%09%09// the RDF service coalesces them for us.%0A%09%09addon.targetPlatforms = [];%0A%09%09for (let targetPlatform of manifest.targetPlatforms || []) {%0A%09%09%09let platform = {%0A%09%09%09%09os: null,%0A%09%09%09%09abi: null,%0A%09%09%09};%0A%0A%09%09%09let pos = targetPlatform.indexOf("_");%0A%09%09%09if (pos != -1) {%0A%09%09%09%09platform.os = targetPlatform.substring(0, pos);%0A%09%09%09%09platform.abi = targetPlatform.substring(pos + 1);%0A%09%09%09} else {%0A%09%09%09%09platform.os = targetPlatform;%0A%09%09%09}%0A%09%09%09addon.targetPlatforms.push(platform);%0A%09%09}%0A%0A%09%09addon.userDisabled = false;%0A%09%09addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A%09%09addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A%09%09addon.userPermissions = null;%0A%0A%09%09addon.icons = {};%0A%09%09if (await pkg.hasResource("icon.png")) {%0A%09%09%09addon.icons[32] = "icon.png";%0A%09%09%09addon.icons[48] = "icon.png";%0A%09%09}%0A%0A%09%09if (await pkg.hasResource("icon64.png")) {%0A%09%09%09addon.icons[64] = "icon64.png";%0A%09%09}%0A%0A%09%09return addon;%0A%09},%0A%0A%09loadScope(addon, file) {%0A%09%09let uri = getURIForResourceInFile(file, "bootstrap.js").spec;%0A%09%09let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A%09%09let sandbox = new Cu.Sandbox(principal, {%0A%09%09%09sandboxName: uri,%0A%09%09%09addonId: addon.id,%0A%09%09%09wantGlobalProperties: ["ChromeUtils"],%0A%09%09%09metadata: { addonID: addon.id, URI: uri },%0A%09%09});%0A%0A%09%09try {%0A%09%09%09Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A%09%09%09XPCOMUtils.defineLazyGetter(%0A%09%09%09%09sandbox, "console", () => new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 })%0A%09%09%09);%0A%09%09%09Services.scriptloader.loadSubScript(uri, sandbox);%0A%0A%09%09} catch (e) {%0A%09%09%09logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A%09%09}%0A%0A%09%09function findMethod(name) {%0A%09%09%09if (sandbox.name) {%0A%09%09%09%09return sandbox.name;%0A%09%09%09}%0A%09%09%09try {%0A%09%09%09%09let method = Cu.evalInSandbox(name, sandbox);%0A%09%09%09%09return method;%0A%09%09%09} catch (err) { }%0A%0A%09%09%09return () => {%0A%09%09%09%09logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A%09%09%09};%0A%09%09}%0A%0A%09%09let install = findMethod("install");%0A%09%09let uninstall = findMethod("uninstall");%0A%09%09let startup = findMethod("startup");%0A%09%09let shutdown = findMethod("shutdown");%0A%0A%09%09return {%0A%09%09%09install: (...args) => install(...args),%0A%09%09%09uninstall: (...args) => uninstall(...args),%0A%0A%09%09%09startup(...args) {%0A%09%09%09%09if (addon.type == "extension") {%0A%09%09%09%09%09logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A%09%09%09%09%09Components.manager.addBootstrappedManifestLocation(file);%0A%09%09%09%09}%0A%09%09%09%09return startup(...args);%0A%09%09%09},%0A%0A%09%09%09shutdown(data, reason) {%0A%09%09%09%09try {%0A%09%09%09%09%09return shutdown(data, reason);%0A%09%09%09%09} catch (err) {%0A%09%09%09%09%09throw err;%0A%09%09%09%09} finally {%0A%09%09%09%09%09if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A%09%09%09%09%09%09logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A%09%09%09%09%09%09Components.manager.removeBootstrappedManifestLocation(file);%0A%09%09%09%09%09}%0A%09%09%09%09}%0A%09%09%09},%0A%09%09};%0A%09},%0A};%0A// fim BootstrapLoader.jsm%0A%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0AObject.defineProperty(%0A%09AddonInternal.prototype,%0A%09"providesUpdatesSecurely",%0A%09{enumerable: true, value: true}%0A);%0A
        `.trim(), new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils"]}));
} catch(ex) {Cu.reportError(ex);}

Отредактировано Dumby (20-02-2019 17:56:44)

Отсутствует

 

№1308528-01-2019 21:32:54

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 318
UA: Firefox 52.0

Re: Custom Buttons

Dumby
Я дико извиняюсь за вопрос.
А куда поместить bootstrap-loader.js? Дописать в config.js?

Отредактировано Garalf (28-01-2019 22:59:38)

Отсутствует

 

№1308629-01-2019 01:42:44

Coroner
Участник
 
Группа: Members
Зарегистрирован: 29-10-2012
Сообщений: 4001
UA: Firefox 63.0

Re: Custom Buttons

Dumby пишет

Firefox 63+

Dumby пишет

Firefox 65

Приветствую. Можно попросить объяснить в двух словах бестолковому — эти метóды годятся для 65 или нет? :blush:
И что куда (присоединяясь в ковпросу Garalf).

В конец запутался я в этих версиях. Мало того что кнопки не работают ещё и стили перепиливать придётся.
Спасибо заранее, уж простите, в конец запутался я в версиях :angel:

Отсутствует

 

№1308729-01-2019 08:39:04

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

Re: Custom Buttons

Garalf пишет

А куда поместить bootstrap-loader.js? Дописать в config.js?

Да, всё верно, bootstrap-loader.js задуман
как предназначеный быть добавленным в config.js

Coroner пишет

эти метóды годятся для 65 или нет?

Да, я надеюсь, что обе эти метóды годятся для Firefox 65.

скрытый текст
Firefox 63+ — это имеется в виду нижняя граница
совместимости paxmod-варианта, ну то есть,
вроде, должно работать на 63, 64, 65 и 66.

И не будет работать на 62, 61, 60, 59, и.т.д.
Можно, конечно, было бы попробовать расширить диапазон,
но не вижу большого смысла, да и всё равно дальше 60 не сдвинуть.


Firefox 65 — это имеется в виду рубеж, когда нужно
предпринимать какие-то дополнительные действия,

поскольку bootstrap-расширения перестанут работать не потому,
что неподписаны или легаси, а просто потому, что некоторые необходимые
служебные коды высланы из Firefox куда-то в Thunderbird'ьи края.

Иначе говоря, с точки зрения Firefox 65, попытка установить
такое расширение будет воспринята так, как будто какой-то лопух
пытается поставить аддон вообще без инсталляционного манифеста.

Таким образом, чтобы установить
на вылеченный Firefox 65 bootstrap-расширение, нужно:

либо оформить его как paxmod, и тут для некоторых (зависит от расширения)
весьма желателен какой-нибудь запускатор, ну просто чтобы
стартовало побыстрее, а не тогда, когда запустится само,

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

Отсутствует

 

№1308829-01-2019 14:15:52

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 318
UA: Firefox 65.0

Re: Custom Buttons

Dumby
Очередной раз огромное спасибо, что не даете умереть FF (считаю, без CB - это уже другой браузер).

Отредактировано Garalf (29-01-2019 14:16:40)

Отсутствует

 

№1308929-01-2019 15:43:45

Rag
Участник
 
Группа: Members
Откуда: Краснодон
Зарегистрирован: 06-03-2017
Сообщений: 247
UA: Firefox 60.0

Re: Custom Buttons

Доброго дня всем.
Сможет кто нибудь переделать Clipboard? Чтоб контекстное меню открывалось не при нажатии на кнопку а был пункт в контекстном меню страницы как в стареньком расширении Clipple
Нужно для Palemoon 27 и 28 версий.

Отредактировано Rag (29-01-2019 15:59:17)

Отсутствует

 

№1309030-01-2019 00:13:09

Coroner
Участник
 
Группа: Members
Зарегистрирован: 29-10-2012
Сообщений: 4001
UA: Firefox 63.0

Re: Custom Buttons

Dumby пишет

Да, я надеюсь, что обе эти метóды годятся для Firefox 65.

Огромное спасибо, уважаемый! Объяснение более чем доходчивое.

Да я и сам виноват — ухитрился вставить код bootstrap-loader.js так, что Firefox вообще запускаться перестал.
Да ещё и custombuttons@xsms.org.xpi обозвал как custombuttons@xsms.org.xpi.xpi
Начудил, в общем... Буду Ctrl-C Ctrl-V осваивать... :(

Garalf пишет

без CB - это уже другой браузер

скрытый текст
Без CB я почти не вижу разницы с [chrome], с которым мне приходится иметь дело каждый день. Да ещё и не знаю теперь что лучше.
Если ещё не будет возможности внешний вид настраивать — тогда вообще труба настанет. :(

Отсутствует

 

№1309130-01-2019 08:23:08

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

Re: Custom Buttons

Dumby пишет

Затейник xiaoxiaoflood продемонстрировал,

Без этого куска config.js отвалились расширения WinSizeNPosition и Video Speed Controller, а после добавления кода в начало config.js они снова подхватились.

Отсутствует

 

№1309230-01-2019 12:43:30

drage2
Забанен
 
Группа: Members
Откуда: Донецк
Зарегистрирован: 23-11-2017
Сообщений: 392
UA: Firefox 64.0

Re: Custom Buttons

Чем он круче 64? Если смысл его ломать? Кнопки падают? Заманали они своими шарадами ...Теперь еще и панели...
Unread - непрочитанная вкладка - опять ломать? Если нет новой скорости или еще какой-то фишки, то на ... он нужен?
MP4 декодер когда?  Никогда ! А это главная фича хрома....От уроды.

Отредактировано drage2 (30-01-2019 12:44:08)

Отсутствует

 

№1309331-01-2019 10:26:15

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

Re: Custom Buttons

Dumby не подскажите?
1. В 65 убрали файл chrome://global/content/platformHTMLBindings.xml из которого можно было вывести команды. Сейчас в 65 такое возможно или нет?

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

Выделить код

Код:

var tagNames = ["command", "broadcaster", "key", "menuitem"];
  var collection = new Object();
  for (var i = 0; i < tagNames.length; i++) {
    collection[tagNames[i]] = document.getElementsByTagName(tagNames[i]);
  }
  var out = new Array();
  var xai = Components.classes["@mozilla.org/xre/app-info;1"].
    getService(Components.interfaces.nsIXULAppInfo);
  out.push(xai.name + " " + xai.version);
  out.push(document.documentElement.getAttribute("windowtype"));
  var menuitems = document.getElementsByTagName("menuitem");
  for (var i in collection) {
    var count = 0;
    out.push("_____ ^ " + i + " " + Array(72 - i.length).join("_"));
    for (var j = 0; j < collection[i].length; j++) {
      var id = collection[i][j].getAttribute("id");
      var oncommand = collection[i][j].getAttribute("oncommand");
      var label = collection[i][j].getAttribute("label");
      var tooltiptext = collection[i][j].getAttribute("tooltiptext");
      var text = label || tooltiptext;
      if (id && oncommand) {
        count++;
        var tagNames = ["command", "key", "observes"];
        for (var m = 0; m < tagNames.length; m++) {
          if (!text) {
            for (var n = 0; n < menuitems.length; n++) {
              if (menuitems[n].getAttribute(tagNames[m]) == id) {
                text = menuitems[n].getAttribute("label");
                break;
              }
            }
          }
        }
        id = text ? id + " \u25ba " + text : id;
        oncommand = oncommand.search(/\x29$/) > -1 ? oncommand + ";" : oncommand;
        oncommand = oncommand.replace(/\s\s+/g, " ");
        out.push(id + "\n" + oncommand);
      }
    }
    out.push(i + " count " + count);
  }
  out.push("_____ ^ handler " + Array(65).join("_"));
  var request =  new XMLHttpRequest();
  request.open("GET", "chrome://global/content/platformHTMLBindings.xml", false);
  request.send(null);
  var els = request.responseXML.getElementsByTagName("handler");
  var cmd = new Array();
  var count = 0;
  for (var i = 0; i < els.length; i++) {
    var command = els[i].getAttribute("command");
    var controller = document.commandDispatcher.getControllerForCommand(command);
    var element = document.getElementById(command);
    var label = element && element.getAttribute("label");
    var tooltiptext = element && element.getAttribute("tooltiptext");
    var text = label || tooltiptext;
    if (command && (controller || element) && cmd.indexOf(command) == -1) {
      count++;
      var tagNames = ["command", "key", "observes"];
      for (var m = 0; m < tagNames.length; m++) {
        if (!text) {
          for (var n = 0; n < menuitems.length; n++) {
            if (menuitems[n].getAttribute(tagNames[m]) == command) {
              text = menuitems[n].getAttribute("label");
              break;
            }
          }
        }
      }
      cmd.push(command);
      var id = text ? command + " \u25ba " + text : command;
      var oncommand = "goDoCommand('" + command + "');";
      out.push(id + "\n" + oncommand);
    }
  }
  out.push("handler count " + count);
  for (var i = 0; i < out.length; i++) {
    var suc = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
      createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
    suc.charset = "UTF-8";
    out[i] = suc.ConvertFromUnicode(out[i]);
    out[i] = out[i].replace(/&/g, "&amp;");
    out[i] = out[i].replace(/>/g, "&gt;");
    out[i] = out[i].replace(/</g, "&lt;");
    out[i] = out[i].replace(/"/g, "&quot;");
    out[i] = out[i].replace(/'/g, "&apos;");
  }
  var data = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">';
  data += "<html><head><title>" + out[1] + "</title>";
  data += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">';
  data += "</head><body><pre>" + out.join("\n\n") + "</pre></body></html>";
  if (xai.name == "Firefox" || xai.name == "SeaMonkey") {
    gBrowser.selectedTab = gBrowser.addTab("data:text/html;charset=utf-8;base64," + btoa(data), {
    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
  }
  if (xai.name == "Thunderbird") {
    openContentTab("data:text/html;charset=utf-8;base64," + btoa(data));
  }


2. Перестал на на 65 работать вот такой код работы с Clipboard, вроде на 65 с Clipboard ничего не меняли
скрытый текст

Выделить код

Код:

function readFromClipboard() {
  var string;
  try {
      var clipboard = Cc["@mozilla.org/widget/clipboard;1"].
                      getService(Ci.nsIClipboard);
             
      var trans = Cc["@mozilla.org/widget/transferable;1"].
                  createInstance(Ci.nsITransferable);
      trans.addDataFlavor("text/unicode");
      if (clipboard.supportsSelectionClipboard()) {
        clipboard.getData(trans, clipboard.kSelectionClipboard);
      } else {
        clipboard.getData(trans, clipboard.kGlobalClipboard);
      }
      var data = {};
      var dataLen = {};
      trans.getTransferData("text/unicode", data, dataLen);
      if (data) {
        data = data.value.QueryInterface(Ci.nsISupportsString);
        string = data.data.substring(0, dataLen.value / 2);
      }
  } catch (ex) {
  }
  return string;
}

Отредактировано Andrey_Krropotkin (31-01-2019 10:26:48)

Отсутствует

 

№1309431-01-2019 14:01:34

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

Re: Custom Buttons

Andrey_Krropotkin пишет

В 65 убрали файл chrome://global/content/platformHTMLBindings.xml из которого можно было вывести команды. Сейчас в 65 такое возможно или нет?

Bug 1419091 - Figure out an alternative to the key handling defined with XBL in the platformHTMLBindings.xml files
То есть их зашили в C++. Так что, вряд ли.
Нет, ну можешь попробовать по сети за командами слазать,
типа как-то так (не проверял)

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

Выделить код

Код:

(async () => {
    var url = `${
        AppConstants.SOURCE_REVISION_URL.replace("rev", "raw-file")
    }/dom/xbl/builtin/${
        {win: "win", linux: "unix", macosx: "mac"}[AppConstants.platform] || "unix"
    }/ShortcutKeyDefinitions.cpp`;

    var text = await (await fetch(url)).text();
    var commands = text.match(/cmd_[^"]+/g);

    alert(commands.join("\n"));
})();

Andrey_Krropotkin пишет

Перестал на на 65 работать вот такой код работы с Clipboard, вроде на 65 с Clipboard ничего не меняли

С Clipboard, может, и не меняли, а вот с nsITransferable меняли.
Bug 1493292 - Remove aDataLen parameters from nsITransferable
Значит, наверно, так

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

Выделить код

Код:

//string = data.data.substring(0, dataLen.value / 2);
        string = trans.getTransferData.length == 2
          ? data.data : data.data.substring(0, dataLen.value / 2);

Отсутствует

 

№1309531-01-2019 14:31:11

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

Re: Custom Buttons

Dumby пишет:
типа как-то так (не проверял)

нормально все показывает.
А с richlistbox, listcol и richlistitem то же что изменили?
У меня есть код на 64 работал, на 65 перестал, т.е функции все отрабатываются, кнопки все работают, но в полях ничего не видно, словно текст стал белым.

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

Выделить код

Код:

/*CODE*/
 
var btn = this, br = gBrowser;
var img2 = "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAABwWjpocFs8zHFbPP9xWzz/cVs8/3FbPP9xWzz/cVs8/3FbPP9xWzz/cVs8/3FbPP9xWzz/cVs8/3BbPMxwWjpodWBB4bWwqv/o6Oj/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+rq6v+1sKr/dWBB4XtlR//f39//6Ojo/8e7q/+0oIf/po1t/6aNbf+mjW3/po1t/6aNbf+mjW3/po1t/+rq6v/s7Oz/39/f/3tlR/+Ba07/29vb/+fn5//f39//3Nzc/9nZ2f/Z2dn/2dnZ/9nZ2f/Z2dn/2dnZ/9nZ2f/l5eX/5+fn/9vb2/+Ba07/h3FV/9bW1v/k5OT/5OTk/+Dg4P+bhGb/k3hX/5N4WP+TeFf/m4Rm/+Dg4P+WfF3/rp2H/+Dg4P/U1NT/h3FV/454XP/Z2dn/5+fn/+Pj4//j4+P/ysK3/8nCtv/Jwrb/xr2w/8e9sP/d29j/2NjY/5d9Xv+vnon/1dXV/454XP+UgGP/3Nzc/+rq6v/m5ub/lHlY/5d8XP+dhmf/lXpa/5R5WP+dhmf/uqyb/+bm5v+UeVj/r56I/9jY2P+UgGP/m4dr/97e3v/t7e3/6enp/93d3f/d3d3/3t7e/yOC5P+AsOD/3t7e/+Pj4/+lkHX/rp6J/+Li4v/c3Nz/m4dr/6KNc//h4eH/8PDw//Dw8P/w8PD/7Ozs/zKK6f/h4eH/C3To/+zs7P/s7Oz/4+Pj/+bm5v/u7u7/4eHh/6KNc/+olHr/5OTk//Pz8//z8/P/8/Pz//Dw8P8Seen/8PDw/xJ56f8Seen/8PDw//Dw8P/z8/P/8/Pz/+Tk5P+olHr/r5uC/+fn5//29vb/9vb2//b29v/z8/P/lL3o/xl96f/n5+f/grTo/yWE6v/z8/P/9vb2//b29v/n5+f/r5uC/7Whif/p6en/+fn5//n5+f/5+fn/+fn5//Dw8P+JuOr/JYXr/+/v7/+60ev/9fX1//n5+f/5+fn/6enp/7Whif+6p5D/7Ozs//v7+//7+/v/+/v7//v7+//6+vr/8/Pz/4y77f83juz/9fX1//v7+//7+/v/+/v7/+zs7P+6p5D/v6yW/+3t7f/8/Pz//f39//39/f/9/f3//f39//z8/P/09PT/xNnw//r6+v/9/f3//f39//z8/P/t7e3/v6yW/8Owm8ze2tX/9fX1//f39//39/f/9/f3//f39//39/f/9/f3//T09P/39/f/9/f3//f39//19fX/3trV/8Owm8zGsp9oyLWezMi1n//ItZ//yLWf/8i1n//ItZ//yLWf/8i1n//ItZ//yLWf/8i1n//ItZ//yLWf/8i1nszGsp9oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; 
var img1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC";
var trigger, trigger1, dialog, drives, count;
var icon1 = "data:image/png;base64,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";
var icon2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACa0lEQVR42oWTXUhTYRjH/+/50H1mZ/kRs5WstTKCcsFiTIVYUETUneAupEAqwSuD2obJokS78WawLrppu+uiSymCwFr2ceEcQoWM/MjK2odzNfd1ds7pbKCo2Py/HHh5nuf9v7/znOcQrKvP0sY1NPgMXJOdpiiRgCQAIsqZ2qxY4BZXlmdyyfR1+Kc+YpPIxu72mYtWm328y3IOGqpWzpCNgoV8AoHpF8XU1+/OnOvls50NZOked30622ZrtXBGoqBYSPLKCEWE5qbzodDEg+L9N8PYJrI9oAt0xy+1ddaf0BhQhIDX32YwEZp4wt99dQ07iGiGL3hMzS23GImUqQWVsYkz6wyMSdWIoiTg7coslheW0sKffOlHfDm46hof2GJwyO+MOh1XTJxCAwZU5SmKJZQksVKQD06hmoje1zVrs9nMsZ+/0cDppMONBqKU3z/B/0V07Rfs4wX09PTseDgYDILU3HP0quu0/alYclS9V9t92ma9rFGpEUnNI5ZJYTBytGJQLt6s9RiBy27B6GS4Eh3q9NWdaunnlRSy2SzAUvCGj1Un2BIZ7PDjYF0f9inl8WHAsCwG3xt3IXC390KSwhUKT/sjGPbchF4LSlkLDa3AQEi/C8GIY46oaiRpNbcENXsEzVo9WBpE/qw1NAP3jLk6ARk7/5k9vr+1lC1ALPCQaYD51HNk+AhoAi/jcFcloIc6MqL1gFqi5HbwAhCXmxdNXsXDd4FykdfrlaoSUHccomivJ2AZ+UIKdLYE/kvsqZRcu4GxD+l1g//3wNMZhYo2leeYVH4OApEX0ijwJzEyuVg2qDaJ/wCoeQGVklbGmAAAAABJRU5ErkJggg==";
var icon3 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAB3RJTUUH1wcREBAg9OD7NAAAAAlwSFlzAAAewQAAHsEBw2lUUwAAAARnQU1BAACxjwv8YQUAAAH6SURBVHjaxVNBaxNBFP4mGRNp06bSaGu9qXhRLOSQX6Dg0d48SMWC4Mmbv8AfYA7Sgp6UgteCF29CKahBsKKWglgam4rRxGxiks3u7Mz6zZIlSVHRk7N8vLcz7/vemzczwP8eorSCgtLJJeXpUGtMBQHgK5RptxbuYJUx5o8CL5bx+OzCkyuZ2cLIwtPihbVW9a1GiCyFQyusFD4S2zfv4x5DtI2TAphOZk8zbXFE4NKt5ctInDmY8OLd6zNrtI+IlhURG0Xs55dezZXXF9Gq7CA9NQvNbAH19W/wo+6gWXeaN1aQkybEXLv2Bb36Z+SvfUAynYLgB9Y+GLEv6Sl4HTdcvX0yy4kZqdkit1nFWC4Pme4ydvMXxNiOwwTfIVP5OCBlK0Cv8Y4C5xnXYM+bB0ixTwjWHwpUdzbh+ijZSWlYQefrcxwrLJJcIZxRAWM33qP1KTCOwJ9ErbyLno8trvqRgOrsITV5ik6pT2IXvbZdINmjVr8CmYMx09h7/8wKMBtUtIWgu4/DE8e5lxob4pLsDkhhvxJrjYfE2Am065/Et1ZUQRAJTBxJIDQJZrgKZDiRwYA03AlxCF1Ho92oYGM7ElCS5/qmVjXzLx+cg71tMex5D/8Pw+ng4etd2GYpER0ucJRI26v9l2/Ivg82CI2YIP+BPCyifwI91AzKs/qQkQAAAABJRU5ErkJggg==";
var icon4 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC5UlEQVR4AWIgFvxfaXzz/0tNC5wKbgpHCINoS9V+NW2TpVNlnPfc5Q0CsEkOwHItURRd3bfHeBh927Zt27Zt27Zt2/6xbdvJWFcnj8Xsqo1C42Ccw/mLnfNeeH2R5PcRWbG/J7+vcbC8GE3QAwPQP3L1DsnwlAcOWOv+waVg5F4dMdbu2Un8GlwX0nHO3vazHDoOGC07b/aHBEb8CLVj6MXw1HmvLVzvAJm89V7S/9B9ZdGV68gD11wtXLZcuNuW/kN2FckfIFI4usOPFn/5/iITEqcC6J+514St8oGpQIFNkovYIzORXNtinFqMtSOLwFJ8u+BQ8GaC73fQQ/lh3OSGnwDokGlc2GrVN7KUB6EOht0O+jyg3mHM2oegFTw1+UpmLbfBng6uB56L0SktY7IPGaPdcy3toZSA6WHQ46PQnlw//gH8lAtWis2//JvZh+9Btr0AZmPwBJHYZUZgNQEAXE8zc0GafrVdeWHFqdTDFqgKEKPhtXHmH0/z567nQ6YOwY1QEmyzzmLfO1LRZbF4sMw8FUJ8xZ+lrRhd3gwU4AtuE6RappavcdPun4LTBBtwLWVsLzSy6YYPdf0AKa9B3FR5eLtP2d8fQm7tEuGsx3V97qXvzDXZev3hEADyAAUQ3zXAwIqdOLTjEhJ2HQlAOZ9g69XmsrCU5LVhR7F0oZAsTOS+8z+CAlADxAWKX2vgg0K9zS3bCTw7gCobwhXNp/225+5PjqfvoAxru/34+pzn2CM1HfJAHSh7QrlxJQAdu/DIuNazpbje3iKbbS1y4PoiZ2dFboqJPKFFXkXk3Q6+38O3EP/JUAlAAwB3L6qs8cPyWoa6HYXFHVwQg/lxmJPoyGFYoiEPrFAdNL5fCJ4BoIDeX5hkuHD/2i0zb8vGFutIsAbpOrQ1oMWGmEPNMYgEh8WilUPVy8UVrAp9Q9dtOCNzwtPFdfee4G6yXVO23kLslbNU/v3cSvHRZVOdcnT1ADucXxaSvokQAAAAAElFTkSuQmCC";

function jarLoaded(e) {        var val = gURLBar.value;
    if(val.search(/(file|jar):/) == 0 && val.search(/\.(ja|jar|xpi|zip)$/i) > -1) loadURI("jar:" + val + "!/");
}
function jarClicked(e) {    if(e.button == 0) {
    var tabUrl = gURLBar.value;
    if(tabUrl.search(/(file|jar):/) == 0 && tabUrl.search(/\/$/) > -1 || tabUrl.search(/view-source:/) == 0) {
        var node = e.target.href;
        if(node && node.search(/\.(exe|dll|sqlite|sqlite-shm|sqlite-wal)$/i) == -1) {
            if(tabUrl.search(/view-source:/) == 0 &&
                 node.search(/view-source:/) == 0    || node.search(/\.(ja|jar|xpi|zip)$/i) > -1) {
                e.preventDefault();    e.stopPropagation();    br.selectedTab = br.addTab(node);
            } else if(node.search(/\/$/) == -1) {
                e.preventDefault();    e.stopPropagation();    br.selectedTab = br.addTab("view-source:" + un(node));
}}}}}


function intf(drives, count) {    
    var data = '<?xml version="1.0"?>';
    data += '<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>';
    data += '<window title="' + btn.label + '" onload="self.load()" xmlns="' + xulns + '">';
    data +=   '<keyset>';
    data +=     '<key keycode="VK_ESCAPE" oncommand="close()"/>';
    data +=   '</keyset>';
    data +=   '<vbox flex="1">';
    data +=     '<richlistbox id="listbox" flex="1" >';
    data +=       '<listcols>';
    data +=         '<listcol/>';
    data +=         '<listcol flex="1" />';
    data +=       '</listcols>';
    data +=     '</richlistbox>';
    data +=     '<hbox>';
    data +=       '<button image="' + icon2 + '" label=" Add-on" oncommand="self.addon(event)" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=       '<button image="' + img2 + '" label=" 1 omni.ja" oncommand="self.omni(event)" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=       '<button image="' + img2 + '" label=" 2 omni.ja" oncommand="self.omni1(event)" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=       '<button image="' + icon4 + '" label=" Firefox" oncommand="self.folder(event,' + "'GreD'" + ')" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=       '<button image="' + icon3 + '" label=" Профиль" oncommand="self.folder(event,' + "'ProfD'" + ')" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=       '<button label="Закрыть" oncommand="self.close()"/>';
    data +=     '</hbox>';
    data +=     '<hbox>';
    for(i = 0; i < count; i++)    data += '<button image="' + icon1 + '" label="   ' + drives[i] + '" oncommand="self.mydrives(event,' +
                        "'" + drives[i] + "'" + ')" tooltiptext="Ctrl+ = Без закрытия диалога"/>';
    data +=     '</hbox>';        
    data +=   '</vbox>';
    data += '</window>';
    return data.replace(/self/g, "opener.document.getElementById(&quot;" + self.id + "&quot;)");
}
this.load = function() {
    if(!("AddonManager" in window))
            Components.utils.import("resource://gre/modules/AddonManager.jsm");
            
    if(!("Services" in window))
            Components.utils.import("resource://gre/modules/Services.jsm");
        
    var then, promise = AddonManager.getAddonsByTypes(["extension"], then = function(addons) {
        var list = new Array();
        addons.forEach( function(addon) { list.push(addon); });
        var options = {
        addonTypes: ["extension"]
        };
        
        function key(addon) {
                return options.addonTypes.indexOf(["extension"])
                + "\n" + addon.name.toLowerCase();
            }    
        
        list.sort( function(a, b){
                        var ka = key(a);
                var kb = key(b);
                return ka == kb ? 0 : ka < kb ? -1 : 1;
                
            });
                
            for(var i = 0; i < list.length; i++) {
            var item = document.createElement("richlistitem");
            var cell = document.createElement("treecol");
            cell.setAttribute("label", list[i].name);
            item.appendChild(cell);
            dialog.document.getElementById("listbox").appendChild(item);
            dialog.document.getElementById("listbox").focus();
            dialog.document.getElementById("listbox").selectAll();
            }
            });
        promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+    

}
this.addon = function(e) {
    if(!("AddonManager" in window))
            Components.utils.import("resource://gre/modules/AddonManager.jsm");
    if(!("Services" in window))
            Components.utils.import("resource://gre/modules/Services.jsm");
    var name = dialog.document.getElementById("listbox").selectedItem.firstChild.getAttribute("label");
    //AddonManager.getAddonsByTypes(["extension"], function(addons) {
    var then, promise = AddonManager.getAddonsByTypes(["extension"], then = function(addons) {
            addons.forEach( function(addon) { 
            if(addon.name == name) {
                var uri = addon.getResourceURI();
                var file = uri.QueryInterface(Ci.nsIFileURL).file;
                if(file.isDirectory()) br.selectedTab = br.addTab(uri.spec);
                else br.selectedTab = br.addTrustedTab("jar:" + uri.spec + "!/");
}});});    if(!e.ctrlKey) dialog.close();
promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+    
}

this.omni1 = function(e) {
    //var file = Services.dirsvc.get("GreD", Ci.nsIFile);
    var profileDir = Components.classes["@mozilla.org/file/directory_service;1"]
    .getService(Components.interfaces.nsIProperties)
   .get("GreD", Components.interfaces.nsIFile)
    .path;
    var file = profileDir + "\\browser\\omni.ja";
    var vert="jar:" + "file:///" + file + "!/";
    br.selectedTab = br.addTrustedTab(vert);
    if(!e.ctrlKey) dialog.close();
}
this.omni = function(e) {
    var file = Services.dirsvc.get("GreD", Ci.nsIFile);
    file.append("omni.ja");
    var uri = Services.io.newFileURI(file);
    br.selectedTab = br.addTrustedTab("jar:" + uri.spec + "!/");
    if(!e.ctrlKey) dialog.close();
}
this.folder = function(e, shortcut) {
    var uri = Services.io.newFileURI(Services.dirsvc.get(shortcut, Ci.nsIFile));
    br.selectedTab = br.addTrustedTab(uri.spec);
    if(!e.ctrlKey) dialog.close();
}
this.mydrives = function(e, letter) {    
        br.selectedTab = br.addTrustedTab("file:///" + letter + "/");
    if(!e.ctrlKey) dialog.close();
}
this.close = function() {    dialog.close();
}


Cu.import("resource://gre/modules/FileUtils.jsm");    
var root = new FileUtils.File("\\\\.");
var drivesEnum = root.directoryEntries;            
drives = [];
while (drivesEnum.hasMoreElements()) { drives.push(drivesEnum.getNext().QueryInterface(Ci.nsIFile).path); }
count = drives.length;
var url = "data:application/vnd.mozilla.xul+xml;text/plain," + encodeURIComponent(intf(drives, count));

var feature = "chrome,centerscreen,width=580,height=410,alwaysRaised";

dialog = window.openDialog(url, "", feature);    
//dialog = window.openDialog(url, document.nodePrincipal, "", feature);    

this.onDestroy = function(reason) {
    if(reason == "update" || reason == "delete") {
        br.removeEventListener("click", jarClicked, false);
        br.tabContainer.removeEventListener("TabSelect", jarLoaded, false);
        if(reason == "delete") try { this.PS.clearUserPref(unzp); } catch (e) {}
}}

Отредактировано Andrey_Krropotkin (31-01-2019 14:32:58)

Отсутствует

 

№1309631-01-2019 19:12:35

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

Re: Custom Buttons

Andrey_Krropotkin пишет

А с richlistbox, listcol и richlistitem то же что изменили?

А причём здесь они? Проблемы с отображением у treecol'ов,
которым в richlistbox'е делать нечего, даже хотя бы исходя из названия.

И вообще как-то наворочено, может будь проще

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

Выделить код

Код:

/*
            for(var i = 0; i < list.length; i++) {
            var item = document.createElement("richlistitem");
            var cell = document.createElement("treecol");
            cell.setAttribute("label", list[i].name);
            item.appendChild(cell);
            dialog.document.getElementById("listbox").appendChild(item);
            dialog.document.getElementById("listbox").focus();
            dialog.document.getElementById("listbox").selectAll();
            }
*/
    var box = dialog.document.getElementById("listbox");
    for(var addon of list) box.appendItem(addon.name, "").addon = addon;
    box.focus();

            });
        promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+    

}
this.addon = e => {
    var item = dialog.document.querySelector("richlistitem[selected]");
    var uri = item.addon.getResourceURI();
    var file = uri.QueryInterface(Ci.nsIFileURL).file;
    uri = file.isDirectory() ? uri.spec : "jar:" + uri.spec + "!/";
    if (!e.ctrlKey) {
        dialog.close();
        dialog = null;
    }
    br.selectedTab = br.addTrustedTab(uri);
}
/*
this.addon = function(e) {
    if(!("AddonManager" in window))
            Components.utils.import("resource://gre/modules/AddonManager.jsm");
    if(!("Services" in window))
            Components.utils.import("resource://gre/modules/Services.jsm");
    var name = dialog.document.getElementById("listbox").selectedItem.firstChild.getAttribute("label");
    //AddonManager.getAddonsByTypes(["extension"], function(addons) {
    var then, promise = AddonManager.getAddonsByTypes(["extension"], then = function(addons) {
            addons.forEach( function(addon) { 
            if(addon.name == name) {
                var uri = addon.getResourceURI();
                var file = uri.QueryInterface(Ci.nsIFileURL).file;
                if(file.isDirectory()) br.selectedTab = br.addTab(uri.spec);
                else br.selectedTab = br.addTrustedTab("jar:" + uri.spec + "!/");
}});});    if(!e.ctrlKey) dialog.close();
promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+    
}
*/

Отредактировано Dumby (31-01-2019 19:16:11)

Отсутствует

 

№1309731-01-2019 19:49:01

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

Re: Custom Buttons

Dumby спасибо да действительно так лучше, а еще также спасибо за DOM Inspector

Отсутствует

 

№1309803-02-2019 09:19:18

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

Re: Custom Buttons

Как сделать динамическую ширину вкладок (табов) из кода [CB] или userChrome.css?
Есть ли работающий код? Расширение TabMix у меня НЕ установлено (оно мне не нужно).
Дано: панель вкладок сверху, она может быть в одну/несколько строк. Браузер: Basilisk 2018.12.18.
Надо: ширину табов к примеру от 350 до 50 пикселей, ширина уменьшается в зависимости от количества открытых вкладок.
При переполнении табов появляется следующая строка панели вкладок - это уже решено и работает в CSS-стиле.

Я сделал код [CB], но проявляется эффект: при открытии последней вкладки мелькает вторая строка панели вкладок, затем ширина табов уменьшается и 2 строка пропадает.

Выделить код

Код:

function TabCollapsed() {
    try { window.clearTimeout(TabCollapsed.timeout) } catch(e) {};
    TabCollapsed.timeout = window.setTimeout(()=> {
        var tabbrowser = document.getElementById('tabbrowser-tabs');
        var Tabs = gBrowser.tabs.length;
        const maxTabs = 16, maxTabWidth = 320;
        Tabs = Math.min(Tabs, maxTabs); // возвращает минимальное число из группы чисел
        document.getElementById("TabsToolbar").collapsed = (Tabs == 1);
        if ( Tabs == 1 ) return;
        var tabWidth = Math.min(( tabbrowser.clientWidth - (48 + maxTabs) ) / Tabs, maxTabWidth); // вычислить ширину вкладки
        custombuttons.setPrefs('browser.tabs.tabMaxWidth', tabWidth);
        custombuttons.setPrefs('browser.tabs.tabMinWidth', tabWidth);
    }, 50);
};
addEventListener("TabOpen", function() {TabCollapsed(true)}, false, gBrowser.tabContainer);
addEventListener("TabClose", function() {TabCollapsed(false)}, false, gBrowser.tabContainer);
setTimeout(function() TabCollapsed(false), 1000); // старт браузера

Отсутствует

 

№1309903-02-2019 11:00:30

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

Re: Custom Buttons

Вопрос у вас в Clipboard такой код? я там немного добавил BBCode(вроде он вообще не пашет), в самом Clipboard попап есть но не копируется.
Если будет время займусь может смогу с моим скудным знанием что-то сделать.

Clipboard+BBCode

Выделить код

Код:

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Блок инициализации глоб. переменных и функций
// срок существования - один сеанс браузера
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
bbBBCodeStat=true                                                   // разрешить/включить BBCode
window.arycb_BBCodeClipboardStrings = this.clipboardStrings = [];   // Массив буфера обмена

//
//********************************************************
//--------Общие функции для вставки BBCode----------------
//*********************************************************

//++++++++++++++++++BEGIN DEF++++++++++++++++++++++++++++++
//--------------------------------------------------------
// Функция lb_BBCodePrim 
// [bbCode]выделенный[/bbCode]
//  по умолчанию от ЛКМ)
lb_BBCodePrim = function (open,close){
var theBox = document.commandDispatcher.focusedElement;
var startPos = theBox.selectionStart;
var endPos = theBox.selectionEnd;
var oPosition = theBox.scrollTop;
var oHeight = theBox.scrollHeight;
var text=theBox.value.substring(0,startPos);
text +=open+theBox.value.substring(startPos, endPos)+close;
text +=theBox.value.substring(endPos, theBox.value.length);
theBox.value = text;
var nHeight = theBox.scrollHeight - oHeight;
theBox.scrollTop = oPosition + nHeight;
};

//---------------------------------------------------------
// Функция mb_BBCodePrim 
// Резерв
// по умолчанию от CКМ
mb_BBCodePrim = function (open,close) {};

// -------------------------------------------------------
// Функция rb_BBCodePrim 
// [bbCode]параметр PASTE по умолчанию из буфер обмена[/bbCode]
// по умолчанию от ПКМ
rb_BBCodePrim = function (open,close,paste){
if (paste == undefined)  { open +=close;} //буфер обмена пуст
   else                 { open +=paste+close;}

var theBox = document.commandDispatcher.focusedElement;
var startPos = theBox.selectionStart;
var endPos = theBox.selectionEnd;
var selectionLen = endPos - startPos
var oPosition = theBox.scrollTop;
var oHeight = theBox.scrollHeight;
var text=theBox.value.substring(0,startPos);
var nHeight = theBox.scrollHeight - oHeight;
text +=open;
text +=theBox.value.substring(endPos, theBox.value.length);
theBox.value = text; 
theBox.selectionStart = endPos + open.length;
theBox.selectionEnd = endPos + open.length - selectionLen;
theBox.scrollTop = oPosition + nHeight;
};

// -------------------------------------------------------
// Функция fHLD_Get_ListClipboardPrim
// получить список сохранёного буфера обмена
//
fnHLD_Get_ListClipboardPrim = function (){
var cs = arycb_BBCodeClipboardStrings;
return (cs.slice(0));
};


//++++++++++++++++++END DEF+++++++++++++++++++++++++++++++++++

//Список сохранёного буфера обмена
//Не вызываем это, обработка по таймеру или по событию ЛКМ
//-----Обработчики буфера обмена

this.MAX_ENTRIES = 5;               //Максимальное число записей 5
this.CHECK_INTERVAL = 1000;      //Время опроса буфера 1000 миллисекунд
this.type = "menu";
this.orient = "horizontal";
this._menupopup = this.appendChild(document.createElement("menupopup"));
this._menupopup.setAttribute("oncommand", "this.parentNode.handleCommand(event.target);");



this.checkClipboard = function() {
    var clipStr = readFromClipboard();
    if(clipStr && this.clipboardStrings.indexOf(clipStr) == -1)
        this.storeString(clipStr);
};


this.storeString = function(str) {
    var cs = this.clipboardStrings;
    cs.push(str);
    while(cs.length > this.MAX_ENTRIES)
        cs.shift(); 

    var mi = document.createElement("menuitem");
    mi.setAttribute("label", str);


    this._menupopup.appendChild(mi);
    while(this._menupopup.childNodes.length > this.MAX_ENTRIES)
        this._menupopup.removeChild(this._menupopup.firstChild);
};


this.handleCommand = function(mi) {
    var indx = Array.indexOf(mi.parentNode.childNodes, mi);
    if(indx != -1)
        this.insertText(this.clipboardStrings[indx]);
};

this.insertText = function(str) {
    var cmd = "cmd_insertText";
    var controller = document.commandDispatcher.getControllerForCommand(cmd);
    if(controller && controller.isCommandEnabled(cmd)) {
        controller = controller.QueryInterface(Ci.nsICommandController);
        var params = Cc["@mozilla.org/embedcomp/command-params;1"]
            .createInstance(Ci.nsICommandParams);
        params.setStringValue("state_data", str);
        controller.doCommandWithParams(cmd, params);
    }
};

setInterval(function(button) {
    button.checkClipboard();
}, this.CHECK_INTERVAL, this);

this.setAttribute("onpopupshowing", "this.checkClipboard();");

Отредактировано func4ptch4 (03-02-2019 21:35:28)

Отсутствует

 

№1310004-02-2019 21:23:47

drage2
Забанен
 
Группа: Members
Откуда: Донецк
Зарегистрирован: 23-11-2017
Сообщений: 392
UA: Firefox 61.0

Re: Custom Buttons

Dumby
Привет! Такая проблема : добавить ссылку в закладки , в 61 еще работает, т.е. favicon появляется . В 64 уже нет,только дефолтная.
Где поганка зарыта? Какое JS ковырять? или JSM ? Заманали они своими шарадами...

Отредактировано drage2 (04-02-2019 22:24:22)

Отсутствует

 

Board footer

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