Dumby
BrowserTryToCloseWindow(); Этот код в кн. плеер надо ,хотя бы на левый клик. Плеер запустился, а браузер - закрылся...И никак толком..
(func => { var sysPlayerName = "Pot Player"; var path = "D:\\PotPlayer\\PotPlayerMini64.exe"; var videoMoved = "Видео перенесено в " + sysPlayerName; var noFound = "Не найдено видео на странице, доступное для переноса в " + sysPlayerName; this.label = "Открыть видео в " + sysPlayerName; this.tooltipText = "Л: Видео в плеер\nП: Видео из Clipboard"; this._handleClick = () => { var msgName = _id + ":Player"; var listener = ({data}) => data ? run([data]) : notify(); messageManager.addMessageListener(msgName, listener); addDestructor(() => messageManager.removeMessageListener(msgName, listener)); var url = "data:charset=utf-8," + encodeURIComponent( `(${func})()`.replace("MSG_NAME", msgName) .replace("VIDEO_MOVED", encodeURIComponent(videoMoved)) .replace("CONFIRM", encodeURIComponent("Открыть ссылку в плеере ?")) ); (this._handleClick = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); } this.onauxclick = e => e.button != 1 || gShowPopup(this); this.oncontextmenu = e => { if (e.ctrlKey || e.shiftKey || e.altKey) return; e.preventDefault(); custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет") && run([gClipboard.read(),]); } var popup = document.getElementById("contentAreaContextMenu"); addEventListener("popupshowing", { get hidden() { return !(gContextMenu.onLink || gContextMenu.onVideo || gContextMenu.onPlainTextLink); }, handleEvent() { if (this.hidden) return; var menuitem = document.createXULElement("menuitem"); for(var args of Object.entries({ image: self.image, oncommand: "play()", class: "menuitem-iconic", label: "Открыть в " + sysPlayerName })) menuitem.setAttribute(...args); menuitem.play = () => play(gContextMenu.linkURL || gContextMenu.mediaURL); document.getElementById("context-savelink").before(menuitem); addDestructor(() => menuitem.remove()); this.handleEvent = e => { if (e.target == popup) menuitem.hidden = this.hidden; } } }, false, popup || 1); var play = link => custombuttons.confirmBox(null, "Открыть ссылку в плеере ?", "Да", "Отмена") && run([link]); var run = args => { var file = FileUtils.File(path); (run = args => { if (!file.exists()) return custombuttons.alertBox("File not exists!", path); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(file); process.runwAsync(args, args.length); })(args); } var notify = () => { var name = _id + "-noFound"; var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); (notify = () => setTimeout(as.closeAlert, 1150, name, as.showAlertNotification( "chrome://global/skin/icons/question-48.png", "", noFound, false, "", null, name )))(); } })(() => { var found, videoMoved, SEND = msg => { found = true; if (!msg || Cc["@mozilla.org/embedcomp/prompt-service;1"] .getService(Ci.nsIPromptService) .confirm(content, null, decodeURIComponent("CONFIRM")) ) { if (msg) videoMoved = decodeURIComponent("VIDEO_MOVED"); sendAsyncMessage("MSG_NAME", msg); } else return true; } var YoutubeID = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:\W|$)/; var tmp = '', tmpp = '', innerA = '<div style="display:block!important;color:#00ff00!important;width:250px!important;font:bold 16px serif!important;z-index:999!important;opacity:1!important;visibility: visible!important;', innerB = 'left:5px!important;position:absolute!important;height:auto!important;box-sizing:border-box!important;padding:5px!important;margin:5px!important;', //stopPl = "javascript:(function(){v=document.getElementById('movie_player');if(v){v.stopVideo()}else{v=document.getElementsByTagName('video');if(v){v[0].src='';try{v[0].load()}catch(e){}};}})();", ytIMGouter = function (ytID) { return '<div width="100%"><br /><a target="_blank" href="https://www.youtube.com/watch?v=' + ytID + '"><img src="https://i.ytimg.com/vi/' + ytID + '/hqdefault.jpg"></a><br />' + innerA + 'background-color:black!important;position:relative!important;bottom:20px!important;"> ' + videoMoved + '</div><br /></div><br />' }, handlWin = function (currentWin) { tmp = ''; var elem = currentWin.document.getElementsByTagName('video'), currLoc = currentWin.location; if (elem.length > 0) { if (currLoc.hostname.indexOf('youtu') != -1 && (tmp = currLoc.toString().match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:-15px!important;"><b>' + videoMoved + '</b></div>'; //loadURI(stopPl); (function(d){var v=d.getElementById('movie_player');if(v){try{v.stopVideo()}catch{}} else{v=d.getElementsByTagName('video');if(v[0]){v[0].src='';try{v[0].load()}catch{}};}})(currentWin.document); currentWin.document.getElementById('eow-title').appendChild(videoMovedbox); return true; }; for (i = 0; i < elem.length; i++) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) && tmp.length > 2) || (i == 0 && currentWin.document.body.innerHTML.substring(0, 7) == '<video ' && (tmp = currLoc.toString()))) { if (SEND(tmp)) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:20px!important;background-color:black!important;">' + videoMoved + '</div>'; if (currLoc.hostname == 'www.youtube.com') { elem[i].parentNode.parentNode.appendChild(videoMovedbox); } else { elem[i].parentNode.appendChild(videoMovedbox); }; elem[i].src = ''; try { elem[i].load() } catch (e) {}; return true; } } }; currentWin._elems = currentWin.document.getElementsByTagName('iframe'); if (currentWin._elems.length > 0) { for (currentWin._iCounter = 0; currentWin._iCounter < currentWin._elems.length; currentWin._iCounter++) { if ((currentWin._elems[currentWin._iCounter].src.indexOf('youtube.com') > -1) && (tmp = currentWin._elems[currentWin._iCounter].src.match(YoutubeID)) && (tmp[1].length == 11)) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; currentWin._elems[currentWin._iCounter].outerHTML = ytIMGouter(tmp[1]); return true; }; if (currentWin._elems[currentWin._iCounter].clientWidth > 80 && currentWin._elems[currentWin._iCounter].clientHeight > 40 && handlWin(currentWin._elems[currentWin._iCounter].contentWindow)) return true; } }; elem = currentWin.document.getElementsByTagName('object'); currLoc = currentWin.location; if (elem.length == 0) { elem = currentWin.document.getElementsByTagName('embed') }; if (elem.length > 0) { for (i = 0; i < elem.length; i++) { if (elem[i].innerHTML.indexOf('youtu') != -1 && (tmp = elem[i].innerHTML.match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; elem[i].outerHTML = ytIMGouter(tmp[1]); return true; } else { if (elem[i].clientWidth > 80 && elem[i].clientHeight > 40) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) || (tmp = getLink(elem[i], currLoc))) && tmp.length > 2) { if (SEND(tmp)) return; elem[i].outerHTML = innerA + 'background-color:black!important;bottom:20px!important;"> ' + videoMoved + '</div>'; return true; }; }; } }; }; return false; }; function restProtHost(lnkR, curLoc) { if (lnkR.length == 0) return ''; let tr = lnkR.replace(/^:\/\//, curLoc.protocol + "//"); if (!tr.match(/^https?:\/\//i)) { lnkR = tr.replace(/^\/+/, ''); if (lnkR.split('/')[0].split('?')[0].split('#')[0].toLowerCase().match(/^(?:[-a-z\d]+\.)+[a-z\d]{2,6}$/)) { tr = curLoc.protocol + '//' + lnkR; } else { tr = curLoc.protocol + '//' + curLoc.host + "/" + lnkR; } }; return tr; }; function getSrc(vobj, currentLoc) { var t = '', tt = ''; if ((((t = vobj.innerHTML.match(/<video.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*))/i)) && (t) && (tt = t[1] || t[2] || t[3]) && tt.indexOf('blob:') == -1) || ((t = vobj.innerHTML.match(/<source.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*)).*?\stype=['"]?video\//i)) && (t) && (tt = t[1] || t[2] || t[3]))) && tt.length > 2 && tt.indexOf('blob:') == -1) { if (tt.indexOf(".mp4/?") == -1) { tt = tt.replace(/&/g, "&") }; t = restProtHost(tt, currentLoc); return t; }; return ''; }; function getLink(obj, curLocation) { if (!obj || !obj.tagName) return ''; q = obj.tagName.toLowerCase(); var getParam = function (e, n) { var v = '', r = new RegExp('^(' + n + ')$', 'i'), param = e.getElementsByTagName('param'); for (var igp = 0, p; p = param[igp]; igp++) { if (p.hasAttribute('name') && p.getAttribute('name').match(r)) { v = p.getAttribute('value'); break }; }; return v; }; var restPath = function (f, s) { return (f.substring(0, 4) == 'http') ? f : s.replace(/[#?].*$/, '').replace(/[^\/]*$/, f) }; function videoLinkExtract(fl) { alert(fl); var linkArr = [], outLinks = [], jj = 0, lba = '', lbb = '', decodeURL = gBrowser.currentURI.spec; { try { return decodeURIComponent(s) } catch (e) { return unescape(s) } }; for (var ij = 0; ij < 3; ij++) { lba = lba + String.fromCharCode(parseInt((Math.random() * 15 + 1) + '', 10)); lbb = lbb + String.fromCharCode(parseInt((Math.random() * 15 + 16) + '', 10)); }; function pushWithMerit(lnk) { var merit = -11; if (lnk.match(/^https?:\/\//i)) merit = merit + 40; if (outLinks.length == 0) merit = merit + 1; if (lnk.match(/^\//)) merit = merit + 7; if (lnk.match(/^\/\//)) merit = merit + 30; if (lnk.match(/240p([^a-z]|$)/i)) merit = merit + 1; if (lnk.match(/[^a-z]240([^a-z0-9]|$)/i)) merit = merit + 1; if (lnk.match(/360p([^a-z]|$)/i)) merit = merit + 3; if (lnk.match(/[^a-z]360([^a-z0-9]|$)/i)) merit = merit + 3; if (lnk.match(/480p([^a-z]|$)/i)) merit = merit + 5; if (lnk.match(/[^a-z]480([^a-z0-9]|$)/i)) merit = merit + 5; if (lnk.match(/720p([^a-z]|$)/i)) merit = merit + 7; if (lnk.match(/[^a-z]720([^a-z0-9]|$)/i)) merit = merit + 7; if (lnk.match(/\.mp4([^a-z]|$)/i)) merit = merit + 8; if (lnk.match(/_hd([^a-z]|$)/i)) merit = merit + 6; if (lnk.match(/\.(jpg|xml)([^a-z]|$)/i)) merit = merit - 40; if (merit > 0) outLinks.push(merit + lba + lnk); Services.console.logStringMessage('merit:' + merit + ' lnk->' + lnk); }; linkArr.push(fl); while (linkArr.length > jj && jj < 30) { var testPaths = []; testPaths = linkArr[jj].split(/(\.(?:flv|mp4|m3u8))/i); if (testPaths[testPaths.length - 1] == '') testPaths.pop(); for (k = 1; k < testPaths.length; k = k + 2) { if (testPaths[k - 1].indexOf(lba) > -1) { pref = testPaths[k - 1]; } else { var testAboutDom = testPaths[k - 1].toLowerCase().split(/(https?:\/\/)/); if (testAboutDom[testAboutDom.length - 1] == '') testAboutDom.pop(); var pTest = testAboutDom[testAboutDom.length - 1].split(/(\?[^\?]*?&)/); if (pTest.length > 2) { pTest.pop(); pTest.pop(); }; testAboutDom[testAboutDom.length - 1] = pTest.join(''); pref = testPaths[k - 1].substring(testAboutDom.join('').lastIndexOf("&") + 1); }; t2 = pref.lastIndexOf(lbb); if (t2 > -1) { pref = pref.substring(t2 + 3); } else { t2 = pref.lastIndexOf('{"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf('["'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf(',"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.toLowerCase().lastIndexOf('"http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('"https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(';http'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('*https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(' or '); if (t2 > -1) pref = pref.substring(t2 + 4); pref = pref.substring(pref.split('/')[0].toLowerCase().split('%2f')[0].lastIndexOf('=') + 1); } if (pref.length > 0) { if (pref.split('?')[0].toLowerCase().match(/%[2-3][0-9a-f]/)) { t2 = pref.indexOf('"') if (t2 > -1) pref = pref.substring(t2 + 1); suff = testPaths[k + 1] ? testPaths[k + 1].split('&')[0].split('"')[0].split(';')[0].split(/,http/i)[0] : ''; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3) linkArr.push(decodeURL(pref + testPaths[k] + suff)); } else { testPaths[k + 1] = (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } else { suff = testPaths[k + 1] ? testPaths[k + 1].split(';')[0].split('"]')[0].split('"}')[0].split('",')[0].split(/,https?:\/\//i)[0].split('*https://')[0].split(' or ')[0] : ''; t2 = suff.indexOf('&'); if ((t2 > -1) && (pref != testPaths[k - 1])) { if (t2 == 0) suff = ''; if (suff.charAt(0) != '?') suff = suff.split(/(&[^&]+=https?:\/\/)/i)[0]; }; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3); pushWithMerit(pref + testPaths[k] + suff); } else { testPaths[k + 1] = lba + (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } } }; jj = jj + 1; }; if (outLinks.length == 0) return ''; function srt(a, b) { a = parseInt(a.substr(0, a.indexOf(lba)), 10); b = parseInt(b.substr(0, b.indexOf(lba)), 10); if (a < b) return 1; if (a > b) return -1; return 0 }; outLinks.sort(srt); outLinks[0] = outLinks[0].substr(outLinks[0].indexOf(lba) + 3) if (outLinks[0].indexOf('_hq.mp4/?time=') > 0) outLinks[0] = outLinks[0].replace(/&/g, '&'); return outLinks[0]; }; if (!ol) return ''; //ol = ol.replace(/^:?\/\//, curLocation.protocol + "//"); //return restPath(ol, src); return restProtHost(ol, curLocation); }; try {handlWin(content);} finally {found || SEND();} }); var style = custombutton.buttonGetHelp(self).replace(/id/g, _id); var uri = makeURI('data:text/css,'+ encodeURIComponent(style)); var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(uri, 0);
Отсутствует
хотя бы на левый клик. Плеер запустился, а браузер - закрылся...И никак толком..
Не браузер, а окно браузера. Это не всегда одно и то же.
Закрыть браузер (выход из приложения) — это goQuitApplication({});
Не знаю что там за «никак толком», добавил после
process.runwAsync(args, args.length);
и всё работает как написано.
А вот чтобы только «на левый клик», чуть сложнее,
нужно аргументом пробросить, что запуск именно оттуда.
Но, судя по «хотя бы», видимо, необязательная задача.
Отсутствует
Dumby
Ну, так просто как-то. Выход с любого действа с плеером...В Browser.js вставил это:
Отсутствует
Хотелось бы только на левый клик
Тогда так
// var listener = ({data}) => data ? run([data]) : notify(); var listener = ({data}) => data ? run([data], true) : notify();
/* var run = args => { var file = FileUtils.File(path); (run = args => { if (!file.exists()) return custombuttons.alertBox("File not exists!", path); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(file); process.runwAsync(args, args.length); })(args); } */ var run = (...a) => { var file = FileUtils.File(path); (run = (args, quit) => { if (!file.exists()) return custombuttons.alertBox("File not exists!", path); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(file); process.runwAsync(args, args.length); quit && goQuitApplication({}); })(...a); }
Отсутствует
Dumby
От это класс ! Не постоjе речи ! А, это не косяк? . А то вечно хлопнешь и браузер закрылся , может скрипт какой?
Отсутствует
var cmd = Services.dirsvc.get("SysD", Ci.nsIFile); cmd.append("cmd.exe"); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(cmd); var args = ["/k", "echo Как видно?", "&&", "pause"]; process.runwAsync(args, args.length);
Великолепно, от всей души БЛАГОДАРЮ Dumby!
Можно ли дополнить ваш код, моментальным сворачиванием cmd, пример: cmd ярлык, если в свойствах ярлыка выбрать "Свёрнутое в значок" то произойдет сворачивание, без вывода консоли.
Отредактировано Senflex (25-09-2021 21:10:11)
Отсутствует
может скрипт какой?
Так у тебя под спойлером и есть скрипт.
Если его в custom_script_win.js добавить, то будет
вылезать подтверждение при закрытии (каждого) browser.xhtml окна.
Но только при закрытии лисьей кнопкой закрытия окна,
а не кнопкой в Windows-заголовке окна, Alt+F4 и прочим.
А если «вечно хлопнешь и браузер закрылся» дофантазировать как вывод
подтверждения перед закрытием последнего нормального браузерного окна,
то можно попробовать в инициализацию CB-кнопки добавить
window.toolbar.visible && (lst => ["close", "DOMWindowClose"].forEach( type => addEventListener(type, lst, true) ))(e => e.target == window && !Services.startup.restarting && Array.from(browserWindows()) .filter(w => !w.closed && w.toolbar.visible).length == 1 && !Services.prompt.confirm(null, "ВНИМАНИЕ !", "Закрыть?") && e.preventDefault() );
Можно ли дополнить ваш код, моментальным сворачиванием cmd
Уж не знаю можно или нет, но, скажем так:
в самом браузере, напрямую, такая возможность не предусмотрена.
Хорошо бы покопаться, возможно ли через ctypes, но это долго и сложно.
Отредактировано Dumby (26-09-2021 18:41:22)
Отсутствует
можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false.
Подтянутся гуру и может что-то получше посоветуют.
А мне только такой "костыль" в голову приходит:
/*Initialization Code*/ this.onmousedown =e=> { if (e.button == 0) { network(); } if ( e.button == 2 ) { gShowPopup(this); } } self.onclick =e=> e.preventDefault(); function network() { if (navigator.onLine) { alert('TRUE'); } else { alert('FALSE'); } }
«The Truth Is Out There»
Отсутствует
Dumby
Ставил это ,вроде норм.
//
function BrowserTryToCloseWindow() {
if (Services.prompt.confirm(null, "ВНИМАНИЕ !", "Закрыть Браузер ?"))
if (WindowIsClosing()) {
goQuitApplication({});
} // WindowIsClosing does all the necessary checks
}
Отсутствует
Senflex пишетможно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false.
Подтянутся гуру и может что-то получше посоветуют.
А мне только такой "костыль" в голову приходит:скрытый текстВыделить кодКод:
/*Initialization Code*/ this.onmousedown =e=> { if (e.button == 0) { network(); } if ( e.button == 2 ) { gShowPopup(this); } } self.onclick =e=> e.preventDefault(); function network() { if (navigator.onLine) { alert('TRUE'); } else { alert('FALSE'); } }
Этот вариант, подходит когда у роутера не горят огоньки, а когда начинаются загораться (ещё огонёк интернета не горит), уже показывает true.
Отсутствует
можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false. Типа команды ping в cmd.
Наверно можно HEAD-запрос на какой-нибудь адрес отправить
var url = "http://example.com"; var xhr = new XMLHttpRequest(); xhr.open("HEAD", url, true); xhr.timeout = 2000; var {channel} = xhr; channel.loadFlags |= channel.LOAD_BYPASS_CACHE; xhr.onreadystatechange = () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) { xhr.onreadystatechange = null; alert(true); } else if (xhr.readyState == xhr.DONE) alert(false); } xhr.send();
Отсутствует
Senflex пишетможно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false. Типа команды ping в cmd.
Наверно можно HEAD-запрос на какой-нибудь адрес отправить
скрытый текстВыделить кодКод:
var url = "http://example.com"; var xhr = new XMLHttpRequest(); xhr.open("HEAD", url, true); xhr.timeout = 2000; var {channel} = xhr; channel.loadFlags |= channel.LOAD_BYPASS_CACHE; xhr.onreadystatechange = () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) { xhr.onreadystatechange = null; alert(true); } else if (xhr.readyState == xhr.DONE) alert(false); } xhr.send();
Код попробовал. Бывают ложные срабатывания.
Версия лисы 56.0.2
Отредактировано Senflex (27-09-2021 10:48:50)
Отсутствует
Подскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин) как её убрать. И ещё не работает кнопка, "Добавить новую кнопку".
Отредактировано Senflex (29-09-2021 17:23:10)
Отсутствует
Подскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин)
Win7
Отсутствует
Senflex пишетПодскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин)
Благодарю за ответ. Не получилось.
Отсутствует
Жизнь иногда такое выкидывает, что хочется подобрать...
Отсутствует
Dumby
После каждого редактирования кнопки Информация о странице пункты в контекстном меню клонируются. Можешь подправить?
Отсутствует
фишка "optionsType = 1" в manifest.json
Это где такое?
Есть <em:optionsType>1</em:optionsType> в install.rdf,
но там несовместимо с моим антиподписячим кодом.
В смысле разве что пришлось бы их объединить,
а это отдельный и неозвученный вопрос.
Вобщем, тяп-ляп скопировал файло,
но без добавления свойства "OPTIONS_TYPE_DIALOG" в AddonManager.
Могут быть проблемы, если какой-нибудь код будет написан так,
как если бы это свойство в нём есть, то есть ссылаться на него.
// bootstrap-loader.js 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[action="preferences"]').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);}
После каждого редактирования кнопки Информация о странице пункты в контекстном меню клонируются.
Так это и не кнопка. Это код для ucf custom_script_win.js
Но можно и в кнопке использовать, если добавить предпоследней строкой
addDestructor(() => menuitem.remove());
Custom Buttons 0.0.7.0.0.20
paxmod и bootstrap в zip-папке.
Отсутствует
Так это и не кнопка. Это код для ucf custom_script_win.js
Но можно и в кнопке использовать, если добавить предпоследней строкой
addDestructor(() => menuitem.remove());
Понятно. Спасибо.
Отсутствует
Здравствуйте. Можно ли эту кнопку заставить работать на актуальной версии Лисы (93.0 x64)?
custombutton://%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0D%0A%3Ccustombutton%20xmlns%3Acb%3D%22http%3A//xsms.nm.ru/custombuttons/%22%3E%0A%20%20%3Cname%3E%u041C%u043D%u043E%u0433%u043E%u0446%u0432%u0435%u0442%u043D%u043E%u0435%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%3C/name%3E%0A%20%20%3Cimage%3E%3C%21%5BCDATA%5Bdata%3Aimage/x-icon%3Bbase64%2CAAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD+//4B////APX69AV9vWaPVJsR9GabDPiQrDGn6OzRG////wD+/v0B////AP///gL///0B////AP///wD///8A////AP3+/gP///8ActGnhRyqVP83q0r8PqMu/kqZDf9yoh7Wu8t8YNjan07m4bM9+ffqCf///wD///4B////AP///wD8/v8C//7/AOL5/A4d1NrUFc/G/yHEov0qt3b/NKtM/UGeHv9WlAD/eJcB/peZAP+nphzH5+bAIP///wD+/v8A/fz/A/3+/wH///8A5/b+CyG8/c4Jz/z/ENfm/RrLuf8nu4b/M61S/UGgJvtbnBH8fqAQ/IKXAP+lrjax/v76AP7+/gH///8A7fD9CNni/Bb///8Ac7/4dQCn9/8P1f38ENbh/xzIsf8ouoD/NKtL/0GeHf9enA79dJ0M/4yiGOrx8d0c/f3/AJaT7105O+TOHTjn5GuP8n7u9v4AOK/3ugW/+/8N2vX9FdDN/yDDoP8rtXD/N6c//kqdGPpcnBD/dqIZ3/L05BCVht5rLhXE/zMj0f0nKuL/EkHp7+Dm/Bq64/wyBKz4/w/W/fwP1+X/Gsq4/ya+kPwwsWH+M58o/jSSAP+exXNr////AFkwrepXMrT8SjPG9icg2v8xRunM8fX+BfL5/gcdpPbOBMH8/w/b9vwX0dH+FcKk/yS3evFZum2rpdOaUPj79gD///8BZy6R8lsqn/8/Hbb/NSfS6MPE+DDj7PwI8vP9ALHe/DQXs/nWAs/9/wDV5v9C1Mmwye/jI////wD///8B////AP7+/gG/oclQgVSpuIhuy4/OyfIheHbpgx815+4vZ+7C2uf8Fuz5/we+8P83y/f8Jvb9/QL///8A8vv4CZPUpGO837Y5////AP///wD6+f0D////AIVy1IsgEsz/Lini+Qow5/+Gq/Vq////AM/t/Ruo6/4+6/z9C////wB817lzE6dQ/zGeJfOs05lI/Pv9Bf///wDXy+knTCOu7UYvxf89KtH4Fhrh/4yb9GTB3/sjCJH07QC9+v854Pio9///AFHOsrMktnX/NKtO/0KoPsz9/P0D////AMey2ElWJaL/UzG4+k0vv/4sGs/5vLn1Mkl68aoLcvD/G6j4+QDH+v+29f8xY9zUkA/Eo/8swpH3JL6M/P38/gL///8A08DcNF8kkPlgNar9Sh2m/3texp/i4PsPKTDk7hxP7P8eefD7Apb1/47Z/E2x8vw2AMrv/wXN5/8O0Ova////AP///wH8+/wAoHe0iWkukfN+U66v8+31C9PL7iA3H8n6KSzi/yNF6/sMVe3/rc35PP///wB/zv1qM7L7u53e/j7///8A////AP///wD7+fsB6NzqD/n2+gT///8A7uf0EV80rONFKcD/LCTU/zxC47/u8P4D////Af7+/gH//f4A///+AP///wD///8A//7/AP79/gL///8A/v7/Av38/gT///8AsZHEZmAplv5gOrPQzMbwKf///wD9/f4C/f//A/7//wL9/v8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%5D%5D%3E%3C/image%3E%0A%20%20%3Cmode%3E0%3C/mode%3E%0A%20%20%3Cinitcode%3E%3C%21%5BCDATA%5B//%20%u041D%u0430%u0441%u0442%u0440%u043E%u0439%u043A%u0430%20%u0444%u0443%u043D%u043A%u0446%u0438%u0439%20%u043A%u043B%u0438%u043A%u043E%u0432%20%u043C%u044B%u0448%u0438%20%u0434%u043B%u044F%20%u043A%u043D%u043E%u043F%u043A%u0438%20..........%0Athis.onclick%20%3De%3D%3E%20%7B%0A%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u041B%u041A%u041C%20....%0A%20%20%20if%20%28%20e.button%20%3D%3D%200%20%29%20%7B%0A%20%20%20%20%20%20%20%20cbu.setPrefs%28s%2C%20%21cbu.getPrefs%28s%29%29%3B%0A%20%20%20%20%20%20%20%20cbu.getPrefs%28s%29%20%3F%20doHighlight%28%29%20%3A%20clearHighlight%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u0421%u041A%u041C%20....%20%20%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%201%20%29%20toggleHighlightValue%28%27add%27%29%3B%0A%20%20%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u041F%u041A%u041C%20....%20%20%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%202%20%26%26%20%21e.ctrlKey%20%26%26%20%21e.shiftKey%20%26%26%20%21e.altKey%20%26%26%20%21e.metaKey%20%29%20%7B%20%0A%20%20%20%20%20%20%20%20e.preventDefault%28%29%3B%20%20%0A%20%20%20%20%20%20%20%20toggleHighlightValue%28%27clear%27%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20Ctrl%20+%20%u041F%u041A%u041C%20....%20.%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%202%20%26%26%20%21e.ctrlKey%20%26%26%20e.shiftKey%20%26%26%20%21e.altKey%20%26%26%20%21e.metaKey%20%29%20%7B%20%0A%20%20%20%20%20%20%20%20e.preventDefault%28%29%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20//%20%u043E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0440%u0435%u0434%u0430%u043A%u0442%u043E%u0440%20%u043A%u043D%u043E%u043F%u043A%u0438%20%u0432%20%u0432%u043A%u043B%u0430%u0434%u043A%u0435%20%u0421%u043F%u0440%u0430%u0432%u043A%u0430%0A%20%20%20%20%20%20%20%20function%20observer%28subject%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20subject.addEventListener%28%22load%22%2C%20%28e%2C%20doc%20%3D%20e.target%29%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20doc.URL.startsWith%28%22chrome%3A//custombuttons%22%29%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Services.ww.unregisterNotification%28observer%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20doc.getElementsByTagName%28%22tabs%22%29%5B0%5D.children%5B2%5D.click%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20Services.ww.registerNotification%28observer%29%3B%0A%0A%20%20%20%20%20%20%20%20custombuttons.editButton%28this%29%3B%20//%20%u043E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0440%u0435%u0434%u0430%u043A%u0442%u043E%u0440%0A%20%20%20%20%20%20%20%20%7D%20%0A%7D%3B%0A%0A%0A//%20%u0426%u0432%u0435%u0442%u0430%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%2C%20%u043C%u043E%u0436%u043D%u043E%20%u043C%u0435%u043D%u044F%u0442%u044C%20%u0438%20%u0434%u043E%u0431%u0430%u0432%u043B%u044F%u0442%u044C%20..........%0Avar%20colors%20%3D%20%5B%22%23FFFF00%22%2C%20%22%23FF0000%22%2C%20%22%2366FFFF%22%2C%20%22%2366FF99%22%2C%20%22%23FFA500%22%2C%20%22%23FF00FF%22%5D%3B%0A%0A%0A//%20%u041F%u043E%u0434%u0441%u043A%u0430%u0437%u043A%u0430%20%u0434%u043B%u044F%20%u043A%u043D%u043E%u043F%u043A%u0438%20...........%0Athis.onmouseover%20%3D%28%29%3D%3E%20%7B%0A%20%20%20var%20array%20%3D%20getArray%28%29.map%28str%3D%3E%20%7B%20return%20str.slice%280%2C50%29%20+%20%28str.length%20%3E%2050%20%3F%20%22...%22%20%3A%20%22%22%29%20%7D%29%3B%0A%20%20%20this.tooltipText%20%3D%20self.label%20+%20%22%5Cn%u041B%3A%20B%u043A%u043B%u044E%u0447%u0438%u0442%u044C/%u0432%u044B%u043A%u043B%u044E%u0447%u0438%u0442%u044C%20%5Cn%u0421%3A%20%u0414%u043E%u0431%u0430%u0432%u0438%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%5Cn%u041F%3A%20%u0421%u0431%u0440%u043E%u0441%u0438%u0442%u044C%20%u0432%u0435%u0441%u044C%20%u0442%u0435%u043A%u0441%u0442%5CnShift+%u041F%3A%20%u0420%u0435%u0434%u0430%u043A%u0442%u0438%u0440%u043E%u0432%u0430%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Cn%5Cn%u0422%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%20%5Cn%22%20+%20array.join%28%22%5Cn%22%29%3B%0A%7D%3B%0A%0A%0A//%20%u0423%u0441%u0442%u0430%u043D%u043E%u0432%u0438%u0442%u044C%20%u043D%u0443%u0436%u043D%u0443%u044E%20%u0438%u043A%u043E%u043D%u043A%u0443%20%u043A%u043D%u043E%u043F%u043A%u0438%20%u043F%u0440%u0438%20%u0441%u0442%u0430%u0440%u0442%u0435%20%u0431%u0440%u0430%u0443%u0437%u0435%u0440%u0430%20%u0438%u043B%u0438%20%u043F%u0440%u0438%20%u0438%u0437%u043C%u0435%u043D%u0435%u043D%u0438%u044F%u0445%20%u043D%u0430%u0441%u0442%u0440%u043E%u0435%u043A%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Avar%20s%20%3D%20%22CB.autoHighlight%22%3B%0Afunction%20toggleImage%28%29%20%7B%0A%20%20%20self.ownerDocument.getAnonymousElementByAttribute%28self%2C%20%22class%22%2C%20%22toolbarbutton-icon%22%29.%0A%20%20%20src%20%3D%20cbu.getPrefs%28s%29%0A%20%20%20%3F%20self.image%0A%20%20%20%3A%20%22data%3Aimage/x-icon%3Bbase64%2CAAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A/v7+AP39/QH9/f0B/f39Af39/QH///8AmJiYKQAAALEDAwPqAAAA1gAAAGm7u7sA////Afv7+wD///8A////AP39/QH9/f0D////AP///wD7+/sB////AKKiogoAAADJAAAA/wAAAP0BAQH+AAAA/wsLC3HDw8MA////Bfb29gP+/v4A////Aefn5wDT09MJ1tbWCdTU1AT///8AXFxcSwAAAPsEBAT/AAAA/gAAAPwFBQX/AAAA63Nzczvu7u4A/Pz8Avz8/AHr6+sJMjIyZgAAAL8CAgLCAAAApsbGxhhoaGhLAAAA/gUFBf8AAAD+AAAA/wAAAP0DAwP/AAAA6hMTE5eHh4cr3d3dACsrK4gAAAD/AwMD/gMDA/wAAAD/Hh4emy0tLQwAAADwAQEB/wAAAP4AAAD/AAAA/wAAAP8BAQH9AwMD/wAAAOcrKys5AwMD7gYGBv4AAAD7BAQE/QAAAP9KSkp5////ABUVFcIAAAD/AQEB/QAAAP8AAAD/AAAA/wAAAP4BAQH6BwcH/wICAr8BAQGfAAAA/wEBAf8AAADrBQUFeMbGxgX///8AGhoawQAAAP8BAQH+AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD9BgYG66+vrwc1NTVCDw8PPgAAADUREREt0tLSF+7u7gAdHR27AAAA/wICAvsBAQH9AQEB/QEBAf0BAQH8AQEB+wICAv8BAQHC////AsHBwQAICAhzAAAA6AQEBP8AAADqWlpaL25ubj8AAAD4AgIC/wAAAP4AAAD/AAAA/wAAAP4CAgL/AAAA3VtbWznHx8cABgYGbwAAAP8BAQH+AAAA+AUFBf8AAAB18PDwAIyMjDwYGBiuGRkZwxgYGJQTExNzFhYWdQUFBWRgYGAb////ALS0tBwAAADXBQUF/wAAAPgFBQX/AAAA90tLSzunp6cApqamDaurqwP///8A////AP///wD///8A////AP///wD7+/sBtLS0LQAAAOwLCwv/CQkJ+wAAAP88PDyLkJCQDAICAqYAAADdAAAAkJCQkAP///8AW1tbLUlJSXFnZ2cR////AP7+/gHNzc0HDAwMpAAAAP8AAADuJycneRMTEw8PDw/AAQEB/wsLC/8AAAD6cnJyPHR0dB4AAADiAAAA/yAgIJX///8A/v7+Av///wC7u7sPgoKCRYqKii3T09MACgoKaAAAAP8BAQH4AgIC/AAAAP8vLy9GKysreQAAAP8FBQX/BwcH3LS0tAj///8A/f39Af39/QH///8A////AOzs7AYDAwPEAAAA/wAAAPsCAgL/AAAA8mtrawlMTEygAAAA/wgICP8AAADpampqGP///wD///8A/Pz8Afn5+QT+/v4B7e3tBAQEBMEAAAD/AQEB/QAAAP8TExNv////AFJSUosAAAD/AwMD/wEBAauurq4A////Av///wD///8A/v7+AP///wLv7+8AAwMDSwAAAOADAwPZAAAAZZOTkwP///8AT09PIQAAALYAAAC3oaGhIf///wD9/f0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%22%3B%0A%7D%3B%0AtoggleImage%28%29%3B%0AgPrefService.addObserver%28s%2C%20toggleImage%2C%20false%29%3B%0AaddDestructor%28%28%29%3D%3E%20gPrefService.removeObserver%28s%2C%20toggleImage%29%29%3B%20%0A%0A%0A//%20%u0410%u0432%u0442%u043E%u043C%u0430%u0442%u0438%u0447%u0435%u0441%u043A%u0438%20%u0432%u044B%u0434%u0435%u043B%u0438%u0442%u044C%20%u0432%u0441%u0435%20%u0438%u0441%u043A%u043E%u043C%u044B%u0435%20%u0442%u0435%u043A%u0441%u0442%u044B%20%u0440%u0430%u0437%u043D%u044B%u043C%u0438%20%u0446%u0432%u0435%u0442%u0430%u043C%u0438%20..........%0Afunction%20doHighlight%28node%20%3D%20content.document.body%29%20%7B%0A%20%20%20if%20%28%20%21cbu.getPrefs%28s%29%20%7C%7C%20%21node%29%20return%3B%0A%20%20%20%0A%20%20%20//%20%u0435%u0441%u043B%u0438%20%u044D%u0442%u043E%20%u043D%u0435%20%u043F%u0443%u0441%u0442%u043E%u0439%20%u0442%u0435%u043A%u0441%u0442%u043E%u0432%u043E%u0439%20%u0443%u0437%u0435%u043B%0A%20%20%20if%20%28%20node.nodeName%20%3D%3D%20%22%23text%22%20%26%26%20node.data.replace%28/%5E%5Cs+%7C%5Cs+%24/g%2C%22%22%29%20%21%3D%20%22%22%20%29%20%7B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20data%20%3D%20node.data.toLowerCase%28%29%2C%20highlightText%20%3D%20getArray%28%29%3B%20%0A%20%20%20%20%20%20%20%20var%20arr%20%3D%20highlightText.filter%28str%3D%3E%20%7B%20return%20%7Edata.indexOf%28str%29%20%7D%29%3B%0A%0A%20%20%20%20%20%20%20%20if%20%28%20arr%20%21%3D%20%22%22%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20arr.reverse%28%29.forEach%28str%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20parentNode%20%3D%20node.parentNode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20color%20%3D%20colors%5BhighlightText.indexOf%28str%29%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20parentNode.className%20%3D%3D%20_id%20%29%20%7B%20//%20%u0435%u0441%u043B%u0438%20%u043D%u0430%u0434%20%u0442%u0435%u043A%u0441%u0442%u043E%u0432%u044B%u043C%20%u0443%u0437%u043B%u043E%u043C%20%u043A%u043D%u043E%u043F%u043A%u0430%20%u0443%u0436%u0435%20%u0441%u043E%u0437%u0434%u0430%u043B%u0430%20span%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20parentNode.style.backgroundColor%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20span%20%3D%20content.document.createElement%28%22span%22%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.style.backgroundColor%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.className%20%3D%20_id%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20middle%20%3D%20node.splitText%28data.indexOf%28str%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20middle.splitText%28str.length%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20node.parentNode.insertBefore%28span%2C%20middle%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.appendChild%28middle%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20else%20if%20%28%20node.nodeType%20%3D%3D%201%20%26%26%20node.firstChild%20%26%26%20%21/script%7Cstyle/.test%28node.localName%29%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B...node.childNodes%5D.forEach%28node%3D%3E%20doHighlight%28node%29%20%29%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%7D%3B%0AdoHighlight%28%29%3B%0A%0A%0A//%20%u041E%u0431%u0440%u0430%u0431%u043E%u0442%u0447%u0438%u043A%20%u0441%u043B%u0435%u0434%u0438%u0442%20%u0437%u0430%20%u0437%u0430%u0433%u0440%u0443%u0437%u043A%u043E%u0439%20%u0438%20%u043F%u0435%u0440%u0435%u043A%u043B%u044E%u0447%u0435%u043D%u0438%u0435%u043C%20%u0432%u043A%u043B%u0430%u0434%u043A%u0438%20%u0438%20%u0437%u0430%u043F%u0443%u0441%u043A%u0430%u044E%u0442%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%20..........%0Avar%20listener%20%3D%20%7B%20%0A%20%20%20onStateChange%3A%20function%28wpr%2C%20req%2C%20flag%29%20%7B%0A%20%20%20%20%20%20if%20%28%20gBrowser.currentURI.spec.startsWith%28%22http%22%29%20%26%26%20flag%20%26%20Ci.nsIWebProgressListener.STATE_STOP%20%29%0A%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%3D%3E%20doHighlight%28%29%2C%200%29%3B%0A%20%20%20%7D%2C%0A%20%20%20%20%20%20%0A%20%20%20onLocationChange%3A%20function%28wpr%2C%20req%29%20%7B%0A%20%20%20%20%20%20if%20%28%20gBrowser.currentURI.spec.startsWith%28%22http%22%29%20%26%26%20%21req%20%26%26%20wpr.document.readyState%20%3D%3D%20%22complete%22%20%29%0A%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%3D%3E%20doHighlight%28%29%2C%200%29%3B%0A%20%20%20%7D%20%20%20%20%20%20%20%0A%7D%3B%0AgBrowser.addProgressListener%28listener%29%3B%0AaddDestructor%28%28%29%3D%3E%20gBrowser.removeProgressListener%28listener%29%20%29%3B%0A%0A%0A//%20%u041F%u0435%u0440%u0435%u0431%u0440%u0430%u0442%u044C%20%u0432%u0441%u0435%20%u043E%u0442%u043A%u0440%u044B%u0442%u044B%u0435%20%u0441%u0442%u0440%u0430%u043D%u0438%u0446%u044B%20%u0438%20%u0443%u0434%u0430%u043B%u0438%u0442%u044C%20%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%20..........%0Afunction%20clearHighlight%28reason%20%3D%20false%29%20%7B%0A%20%20%20if%20%28%20reason%20%3D%3D%20%27destructor%27%20%29%20return%3B%0A%20%20%20%0A%20%20%20for%20%28%20var%20br%20of%20gBrowser.browsers%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%5B...br.contentDocument.querySelectorAll%28%27SPAN%5Bclass%3D%27%20+%20_id%20+%20%27%5D%27%29%5D%0A%20%20%20%20%20%20%20%20%20.forEach%28el%3D%3E%20el.style%20%3D%20%22%22%20%29%3B%0A%20%20%20%20%20%20%20%20%20%7D%0A%7D%3B%20%0AaddDestructor%28clearHighlight%29%3B%0A%0A%20%0A//%20%u0414%u043E%u0431%u0430%u0432%u0438%u0442%u044C%20%u0438%u043B%u0438%20%u0443%u0434%u0430%u043B%u0438%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Afunction%20toggleHighlightValue%28arg%29%20%7B%20%0A%20%20if%20%28%20arg%20%3D%3D%20%27clear%27%20%29%0A%20%20%20%20%20%20%20var%20text%20%3D%20%27%27%2C%20notification%20%3D%20%22%u0422%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20%u0443%u0434%u0430%u043B%u0451%u043D%22%3B%0A%20%20%0A%20%20if%20%28%20arg%20%3D%3D%20%27add%27%20%29%20%7B%20%20%0A%20%20%20%20%20%20%20var%20sel%20%3D%20getSelect%28%29%2C%20array%20%3D%20getArray%28%29%3B%20%0A%20%20%20%20%20%20%20var%20array%20%3D%20%28array%20%3D%3D%20%22%22%29%20%3F%20%22%22%20%3A%20array.join%28%22%5Cn%22%29%20+%20%22%5Cn%22%3B%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20if%20%28%20sel%20%29%20%7B%20var%20text%20%3D%20array%20+%20sel%20%7D%0A%20%20%20%20%20%20%20else%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20val%20%3D%20custombuttons.promptBox%28self.label%2C%20%27B%u0432%u0435%u0434%u0438%u0442%u0435%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%27%29%5B1%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20val%20%3D%3D%20%22%22%20%29%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20text%20%3D%20array%20+%20val.split%28%22%7C%22%29.join%28%22%5Cn%22%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20goDoCommand%28%22cmd_selectNone%22%29%3B%0A%20%20%20%20%20%20%20var%20notification%20%3D%20%22%u0414%u043E%u0431%u0430%u0432%u0438%u043B%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%20%5Cn%22%20+%20%28sel%20%3F%20sel%20%3A%20val.replace%28/%5C%7C/g%2C%22%5Cn%22%29%29%3B%0A%20%20%20%20%20%20%20%7D%0A%20%20%20%0A%20%20saveTextToHelp%28text.toLowerCase%28%29%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%0A%20%20cbu.setPrefs%28s%2C%20%28arg%20%3D%3D%20%27add%27%29%29%3B%0A%20%20cbu.getPrefs%28s%29%20%3F%20doHighlight%28%29%20%3A%20clearHighlight%28%29%3B%0A%20%20%0A%20%20var%20alertsService%20%3D%20Cc%5B%22@mozilla.org/alerts-service%3B1%22%5D.getService%28Ci.nsIAlertsService%29%3B%0A%20%20alertsService.showAlertNotification%28self.image%2C%20self.label%2C%20notification%29%3B%0A%20%20setTimeout%28%28%29%3D%3E%20alertsService.closeAlert%28%29%2C%201500%29%3B%0A%7D%3B%0A%0A%0A//%20%u0417%u0430%u043F%u0438%u0441%u0430%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%20%u0432%20%u0432%u043A%u043B%u0430%u0434%u043A%u0443%20%27%u0421%u043F%u0440%u0430%u0432%u043A%u0430%27%20..........%0Afunction%20saveTextToHelp%28text%29%20%7B%0A%20%20%20var%20dir%20%3D%20Services.dirsvc.get%28%22ProfD%22%2C%20Ci.nsIFile%29%3B%0A%20%20%20dir.initWithPath%28dir.path%20+%20%22%5C%5Cextensions%5C%5Ccustombuttons@xsms.org%5C%5Ccomponents%5C%5CCustomButtonsService.js%22%29%3B%0A%20%20%20var%20url%20%3D%20Services.io.newFileURI%28dir%29.spec%2C%20cbs%20%3D%20%7B%7D%3B%0A%20%20%20Services.scriptloader.loadSubScript%28url%2C%20cbs%29%3B%0A%20%20%20var%20AO%20%3D%20new%20cbs.AppObject%28%29%2C%20btn%20%3D%20AO.getButton%28_id%29%3B%0A%0A%20%20%20self.setAttribute%28%22Help%22%2C%20text%29%3B%0A%20%20%20btn.setAttribute%28%22Help%22%2C%20text%29%3B%0A%20%20%20AO.overlay.saveOverlayToProfile%28%29%3B%20%20%20%0A%7D%3B%0A%0A%0A//%20%u041F%u043E%u043B%u0443%u0447%u0438%u0442%u044C%20%u0438%u0437%20%u0432%u043A%u043B%u0430%u0434%u043A%u0438%20%27%u0421%u043F%u0440%u0430%u0432%u043A%u0430%27%20%u043C%u0430%u0441%u0441%u0438%u0432%20%u0441%20%u0442%u0435%u043A%u0441%u0442%u0430%u043C%u0438%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Afunction%20getArray%28%29%20%7B%0A%20%20%20return%20%28custombutton.buttonGetHelp%28self%29%29.toLowerCase%28%29.split%28%22%5Cn%22%29.filter%28el%3D%3E%20/%5CS/.test%28el%29%29%3B%0A%7D%3B%0A%0A%0A//%20%u041F%u043E%u043B%u0443%u0447%u0438%u0442%u044C%20%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u043D%u044B%u0439%20%u0442%u0435%u043A%u0441%u0442%20%u0438%u0437%20%u0441%u0442%u0440%u0430%u043D%u0438%u0446%u044B%20%u0438%u043B%u0438%20false%20..........%0Afunction%20getSelect%28%29%20%7B%0A%20%20%20var%20el%20%3D%20document.commandDispatcher.focusedElement%3B%0A%20%20%20try%20%7B%20return%20el.value.substring%28el.selectionStart%2C%20el.selectionEnd%29%20%7D%20catch%28e%29%20%7B%7D%3B%0A%20%20%20var%20sel%20%3D%20document.commandDispatcher.focusedWindow.getSelection%28%29%3B%0A%20%20%20return%20%28sel%20%3D%3D%20%27%27%29%20%3F%20false%20%3A%20sel.toString%28%29.replace%28/%5E%5Cs+%7C%5Cs+%24/g%2C%22%22%29%3B%0A%7D%3B%5D%5D%3E%3C/initcode%3E%0A%20%20%3Ccode%3E%3C%21%5BCDATA%5B/*CODE*/%5D%5D%3E%3C/code%3E%0A%20%20%3Caccelkey%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/accelkey%3E%0A%20%20%3Chelp%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/help%3E%0A%20%20%3Cattributes/%3E%0A%3C/custombutton%3E
иконка
data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD+//4B////APX69AV9vWaPVJsR9GabDPiQrDGn6OzRG////wD+/v0B////AP///gL///0B////AP///wD///8A////AP3+/gP///8ActGnhRyqVP83q0r8PqMu/kqZDf9yoh7Wu8t8YNjan07m4bM9+ffqCf///wD///4B////AP///wD8/v8C//7/AOL5/A4d1NrUFc/G/yHEov0qt3b/NKtM/UGeHv9WlAD/eJcB/peZAP+nphzH5+bAIP///wD+/v8A/fz/A/3+/wH///8A5/b+CyG8/c4Jz/z/ENfm/RrLuf8nu4b/M61S/UGgJvtbnBH8fqAQ/IKXAP+lrjax/v76AP7+/gH///8A7fD9CNni/Bb///8Ac7/4dQCn9/8P1f38ENbh/xzIsf8ouoD/NKtL/0GeHf9enA79dJ0M/4yiGOrx8d0c/f3/AJaT7105O+TOHTjn5GuP8n7u9v4AOK/3ugW/+/8N2vX9FdDN/yDDoP8rtXD/N6c//kqdGPpcnBD/dqIZ3/L05BCVht5rLhXE/zMj0f0nKuL/EkHp7+Dm/Bq64/wyBKz4/w/W/fwP1+X/Gsq4/ya+kPwwsWH+M58o/jSSAP+exXNr////AFkwrepXMrT8SjPG9icg2v8xRunM8fX+BfL5/gcdpPbOBMH8/w/b9vwX0dH+FcKk/yS3evFZum2rpdOaUPj79gD///8BZy6R8lsqn/8/Hbb/NSfS6MPE+DDj7PwI8vP9ALHe/DQXs/nWAs/9/wDV5v9C1Mmwye/jI////wD///8B////AP7+/gG/oclQgVSpuIhuy4/OyfIheHbpgx815+4vZ+7C2uf8Fuz5/we+8P83y/f8Jvb9/QL///8A8vv4CZPUpGO837Y5////AP///wD6+f0D////AIVy1IsgEsz/Lini+Qow5/+Gq/Vq////AM/t/Ruo6/4+6/z9C////wB817lzE6dQ/zGeJfOs05lI/Pv9Bf///wDXy+knTCOu7UYvxf89KtH4Fhrh/4yb9GTB3/sjCJH07QC9+v854Pio9///AFHOsrMktnX/NKtO/0KoPsz9/P0D////AMey2ElWJaL/UzG4+k0vv/4sGs/5vLn1Mkl68aoLcvD/G6j4+QDH+v+29f8xY9zUkA/Eo/8swpH3JL6M/P38/gL///8A08DcNF8kkPlgNar9Sh2m/3texp/i4PsPKTDk7hxP7P8eefD7Apb1/47Z/E2x8vw2AMrv/wXN5/8O0Ova////AP///wH8+/wAoHe0iWkukfN+U66v8+31C9PL7iA3H8n6KSzi/yNF6/sMVe3/rc35PP///wB/zv1qM7L7u53e/j7///8A////AP///wD7+fsB6NzqD/n2+gT///8A7uf0EV80rONFKcD/LCTU/zxC47/u8P4D////Af7+/gH//f4A///+AP///wD///8A//7/AP79/gL///8A/v7/Av38/gT///8AsZHEZmAplv5gOrPQzMbwKf///wD9/f4C/f//A/7//wL9/v8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
инициализация
// Настройка функций кликов мыши для кнопки .......... this.onclick =e=> { // действие при клике ЛКМ .... if ( e.button == 0 ) { cbu.setPrefs(s, !cbu.getPrefs(s)); cbu.getPrefs(s) ? doHighlight() : clearHighlight(); } // действие при клике СКМ .... if ( e.button == 1 ) toggleHighlightValue('add'); // действие при клике ПКМ .... if ( e.button == 2 && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey ) { e.preventDefault(); toggleHighlightValue('clear'); } // действие при клике Ctrl + ПКМ .... . if ( e.button == 2 && !e.ctrlKey && e.shiftKey && !e.altKey && !e.metaKey ) { e.preventDefault(); // открыть редактор кнопки в вкладке Справка function observer(subject) { subject.addEventListener("load", (e, doc = e.target)=> { if ( doc.URL.startsWith("chrome://custombuttons") ) { Services.ww.unregisterNotification(observer); doc.getElementsByTagName("tabs")[0].children[2].click(); } }); }; Services.ww.registerNotification(observer); custombuttons.editButton(this); // открыть редактор } }; // Цвета для автовыделение, можно менять и добавлять .......... var colors = ["#FFFF00", "#FF0000", "#66FFFF", "#66FF99", "#FFA500", "#FF00FF"]; // Подсказка для кнопки ........... this.onmouseover =()=> { var array = getArray().map(str=> { return str.slice(0,50) + (str.length > 50 ? "..." : "") }); this.tooltipText = self.label + "\nЛ: Bключить/выключить \nС: Добавить текст\nП: Сбросить весь текст\nShift+П: Редактировать текст\ \n\nТекст для автовыделения: \n" + array.join("\n"); }; // Установить нужную иконку кнопки при старте браузера или при изменениях настроек автовыделения .......... var s = "CB.autoHighlight"; function toggleImage() { self.ownerDocument.getAnonymousElementByAttribute(self, "class", "toolbarbutton-icon"). src = cbu.getPrefs(s) ? self.image : "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A/v7+AP39/QH9/f0B/f39Af39/QH///8AmJiYKQAAALEDAwPqAAAA1gAAAGm7u7sA////Afv7+wD///8A////AP39/QH9/f0D////AP///wD7+/sB////AKKiogoAAADJAAAA/wAAAP0BAQH+AAAA/wsLC3HDw8MA////Bfb29gP+/v4A////Aefn5wDT09MJ1tbWCdTU1AT///8AXFxcSwAAAPsEBAT/AAAA/gAAAPwFBQX/AAAA63Nzczvu7u4A/Pz8Avz8/AHr6+sJMjIyZgAAAL8CAgLCAAAApsbGxhhoaGhLAAAA/gUFBf8AAAD+AAAA/wAAAP0DAwP/AAAA6hMTE5eHh4cr3d3dACsrK4gAAAD/AwMD/gMDA/wAAAD/Hh4emy0tLQwAAADwAQEB/wAAAP4AAAD/AAAA/wAAAP8BAQH9AwMD/wAAAOcrKys5AwMD7gYGBv4AAAD7BAQE/QAAAP9KSkp5////ABUVFcIAAAD/AQEB/QAAAP8AAAD/AAAA/wAAAP4BAQH6BwcH/wICAr8BAQGfAAAA/wEBAf8AAADrBQUFeMbGxgX///8AGhoawQAAAP8BAQH+AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD9BgYG66+vrwc1NTVCDw8PPgAAADUREREt0tLSF+7u7gAdHR27AAAA/wICAvsBAQH9AQEB/QEBAf0BAQH8AQEB+wICAv8BAQHC////AsHBwQAICAhzAAAA6AQEBP8AAADqWlpaL25ubj8AAAD4AgIC/wAAAP4AAAD/AAAA/wAAAP4CAgL/AAAA3VtbWznHx8cABgYGbwAAAP8BAQH+AAAA+AUFBf8AAAB18PDwAIyMjDwYGBiuGRkZwxgYGJQTExNzFhYWdQUFBWRgYGAb////ALS0tBwAAADXBQUF/wAAAPgFBQX/AAAA90tLSzunp6cApqamDaurqwP///8A////AP///wD///8A////AP///wD7+/sBtLS0LQAAAOwLCwv/CQkJ+wAAAP88PDyLkJCQDAICAqYAAADdAAAAkJCQkAP///8AW1tbLUlJSXFnZ2cR////AP7+/gHNzc0HDAwMpAAAAP8AAADuJycneRMTEw8PDw/AAQEB/wsLC/8AAAD6cnJyPHR0dB4AAADiAAAA/yAgIJX///8A/v7+Av///wC7u7sPgoKCRYqKii3T09MACgoKaAAAAP8BAQH4AgIC/AAAAP8vLy9GKysreQAAAP8FBQX/BwcH3LS0tAj///8A/f39Af39/QH///8A////AOzs7AYDAwPEAAAA/wAAAPsCAgL/AAAA8mtrawlMTEygAAAA/wgICP8AAADpampqGP///wD///8A/Pz8Afn5+QT+/v4B7e3tBAQEBMEAAAD/AQEB/QAAAP8TExNv////AFJSUosAAAD/AwMD/wEBAauurq4A////Av///wD///8A/v7+AP///wLv7+8AAwMDSwAAAOADAwPZAAAAZZOTkwP///8AT09PIQAAALYAAAC3oaGhIf///wD9/f0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; }; toggleImage(); gPrefService.addObserver(s, toggleImage, false); addDestructor(()=> gPrefService.removeObserver(s, toggleImage)); // Автоматически выделить все искомые тексты разными цветами .......... function doHighlight(node = content.document.body) { if ( !cbu.getPrefs(s) || !node) return; // если это не пустой текстовой узел if ( node.nodeName == "#text" && node.data.replace(/^\s+|\s+$/g,"") != "" ) { var data = node.data.toLowerCase(), highlightText = getArray(); var arr = highlightText.filter(str=> { return ~data.indexOf(str) }); if ( arr != "" ) { arr.reverse().forEach(str=> { var parentNode = node.parentNode; var color = colors[highlightText.indexOf(str)]; if ( parentNode.className == _id ) { // если над текстовым узлом кнопка уже создала span parentNode.style.backgroundColor = color; return; } var span = content.document.createElement("span"); span.style.backgroundColor = color; span.className = _id; var middle = node.splitText(data.indexOf(str)); middle.splitText(str.length); node.parentNode.insertBefore(span, middle); span.appendChild(middle); }); } } else if ( node.nodeType == 1 && node.firstChild && !/script|style/.test(node.localName) ) { [...node.childNodes].forEach(node=> doHighlight(node) ); } }; doHighlight(); // Обработчик следит за загрузкой и переключением вкладки и запускают автовыделение .......... var listener = { onStateChange: function(wpr, req, flag) { if ( gBrowser.currentURI.spec.startsWith("http") && flag & Ci.nsIWebProgressListener.STATE_STOP ) setTimeout(()=> doHighlight(), 0); }, onLocationChange: function(wpr, req) { if ( gBrowser.currentURI.spec.startsWith("http") && !req && wpr.document.readyState == "complete" ) setTimeout(()=> doHighlight(), 0); } }; gBrowser.addProgressListener(listener); addDestructor(()=> gBrowser.removeProgressListener(listener) ); // Перебрать все открытые страницы и удалить выделение .......... function clearHighlight(reason = false) { if ( reason == 'destructor' ) return; for ( var br of gBrowser.browsers ) { [...br.contentDocument.querySelectorAll('SPAN[class=' + _id + ']')] .forEach(el=> el.style = "" ); } }; addDestructor(clearHighlight); // Добавить или удалить текст для автовыделения .......... function toggleHighlightValue(arg) { if ( arg == 'clear' ) var text = '', notification = "Текст для автовыделения удалён"; if ( arg == 'add' ) { var sel = getSelect(), array = getArray(); var array = (array == "") ? "" : array.join("\n") + "\n"; if ( sel ) { var text = array + sel } else { var val = custombuttons.promptBox(self.label, 'Bведите текст для автовыделения:')[1]; if ( val == "" ) return; var text = array + val.split("|").join("\n"); } goDoCommand("cmd_selectNone"); var notification = "Добавил текст для автовыделения: \n" + (sel ? sel : val.replace(/\|/g,"\n")); } saveTextToHelp(text.toLowerCase()); cbu.setPrefs(s, (arg == 'add')); cbu.getPrefs(s) ? doHighlight() : clearHighlight(); var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); alertsService.showAlertNotification(self.image, self.label, notification); setTimeout(()=> alertsService.closeAlert(), 1500); }; // Записать текст в вкладку 'Справка' .......... function saveTextToHelp(text) { var dir = Services.dirsvc.get("ProfD", Ci.nsIFile); dir.initWithPath(dir.path + "\\extensions\\custombuttons@xsms.org\\components\\CustomButtonsService.js"); var url = Services.io.newFileURI(dir).spec, cbs = {}; Services.scriptloader.loadSubScript(url, cbs); var AO = new cbs.AppObject(), btn = AO.getButton(_id); self.setAttribute("Help", text); btn.setAttribute("Help", text); AO.overlay.saveOverlayToProfile(); }; // Получить из вкладки 'Справка' массив с текстами для автовыделения .......... function getArray() { return (custombutton.buttonGetHelp(self)).toLowerCase().split("\n").filter(el=> /\S/.test(el)); }; // Получить выделенный текст из страницы или false .......... function getSelect() { var el = document.commandDispatcher.focusedElement; try { return el.value.substring(el.selectionStart, el.selectionEnd) } catch(e) {}; var sel = document.commandDispatcher.focusedWindow.getSelection(); return (sel == '') ? false : sel.toString().replace(/^\s+|\s+$/g,""); };
Отсутствует
Всем привет!
Ребята, сразу прошу прощения, возможно этот вопрос уже поднимался, а я просто не внимательно эту тему читал. Есть скрипт вставки скопированного текста по мидл клик, описан в посте https://forum.mozilla-russia.org/viewto … 32#p778032, так вот с недавних пор он перестал у меня работать. Вставка по мидл клик работает, но если в строке поиска есть уже текст, то выделение его и вставка нового по мидл клик не срабатывает - старый текст не удаляется, а просто дополняется новым из буфера обмена. Может кто в курсе как заставить скрипт снова работать?
Отредактировано Алексей78 (09-10-2021 16:06:10)
Отсутствует
возможно этот вопрос уже поднимался, а я просто не внимательно эту тему читал
Да нет, вроде никто не спрашивал.
Я когда-то давно его в jsm'ку перекладывал, а затем родили баг ,
и пришлось на mousedown переделывать.
Вобщем, сейчас такой. В новом UCF, импортировать созданный модуль
можно добавив в конфигурационный CustomStylesScripts.jsm
в массив UcfStylesScripts.scriptsbackground объект вида
{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/MMPaste.jsm");' },
var MMPasteChild, EXPORTED_SYMBOLS = ["MMPasteChild"]; var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); var sel = ":is(textarea,input):not([readonly],[disabled])"; var x, y, sn = Ci.nsISelectionController.SELECTION_NORMAL; var inRect = r => y > r.top && y < r.bottom && x < r.right && x > r.left; var mousedown = e => { if (e.button != 1) return; var trg = (e.originalTarget || e.target).closest(sel); if (!trg) return; var ed = trg.editor; if (!ed || ed.selection.isCollapsed || !ed.canPaste(sn)) return; x = e.clientX; y = e.clientY; var rng = ed.selection.getRangeAt(0); if (!inRect(rng.getBoundingClientRect())) return; var list = rng.getClientRects(); if (list.length == 1 || Array.from(list).some(inRect)) ed.paste(sn), trg.ownerGlobal.addEventListener(...args); } var args = ["auxclick", e => e.preventDefault(), {once: true, capture: true}]; if (ChromeUtils.domProcessChild.childID) (MMPasteChild = class extends JSWindowActorChild {}).prototype.handleEvent = mousedown; else ((pref, gmon) => ({ mousedown, get pref() { return Services.prefs.getBoolPref(pref); }, startup() { this.handleEvent = e => this[e.type](e); this.unload = e => this.listen(e.target.ownerGlobal); this.e10s = Services.appinfo.browserTabsRemoteAutostart; Services.prefs.addObserver(pref, this); Services.obs.addObserver(this, "quit-application-granted", false); this.pref && this.init(); this.gmon(); }, async gmon() { Cu.importGlobalProperties(["fetch"]); var url = "chrome://custombuttons/content/editExternal.js"; try {var src = await (await fetch(url)).text();} catch {return;} src = src.replace(/function gmon_edit_mouseclick[^}]+?}/, gmon); var arr = [["override", url, "data:," + encodeURIComponent(src)]]; url = Services.io.getProtocolHandler("resource").getSubstitution("gre"); this.gmonHelper = Cc["@mozilla.org/addons/addon-manager-startup;1"] .getService(Ci.amIAddonManagerStartup).registerChrome(url, arr); }, shutdown() { this.pref && this.destroy(); Services.prefs.removeObserver(pref, this); Services.obs.removeObserver(this, "quit-application-granted"); this.gmonHelper?.destruct(); }, opts: { allFrames: true, remoteTypes: ["w", "file", "extension", "privileged"], child: {moduleURI: __URI__, events: {mousedown: {capture: true}}} }, init() { this.e10s && ChromeUtils.registerWindowActor("MMPaste", this.opts); Services.obs.addObserver(this, "widget-first-paint", false); this.wins("addEventListener"); }, destroy() { this.e10s && ChromeUtils.unregisterWindowActor("MMPaste"); Services.obs.removeObserver(this, "widget-first-paint"); this.wins(); }, observe(subj, topic, data) { var char = topic[0]; if (char == "w") return this.listen(subj, "addEventListener"); if (char == "q") return this.shutdown(); data == pref && this[this.pref ? "init" : "destroy"](); }, wins(arg) { for(var win of Services.wm.getEnumerator(null)) this.listen(win, arg); }, listen(trg, meth = "removeEventListener") { trg[meth]("mousedown", this, true); trg[meth]("unload", this); } }).startup())("middlemouse.paste", `function gmon_edit_mouseclick(e) { var mmp = Cc["@mozilla.org/preferences-service;1"] .getService(Ci.nsIPrefBranch).getBoolPref.bind(null, "middlemouse.paste"); (gmon_edit_mouseclick = e => e.button != 1 || mmp() || edittarget(e.target))(e); }`);
Отсутствует