Andrey_Krropotkin
Я обычно и в других темах тоже вижу, просто не всегда есть время на осмысленные ответы.
Не знаю правильно это или нет, но вроде работает.
На удивление не все еще отломали. У меня было опасение, что дублирование command/oncommand (от загружаемого оверлея в старых версиях и дописанное вручную) может привести к дублирующейся отработке команд, но, вроде (как минимум, в Firefox 52 и 56), работает нормально.
Добавил, спасибо: https://github.com/Infocatcher/Custom_B … a2ec1e4d2d
Заодно оживил в новых версиях: https://github.com/Infocatcher/Custom_B … fd057a0614
Не решенным осталось еще, что не работает кнопка "Внешний редактор", вообще не как не реагирует.
Может Вы все таки посмотрите, ну просто без кнопки, такое чувство, что что-то не хватает.
Я не пользуюсь... В самых новых – и вовсе редактирование поломало, я открытием во вкладке спасаюсь. Dumby, насколько я помню, тоже говорил, что не пользуется...
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Да-да, не пользуюсь, и Source Editor'ом тоже.
Не потому, что нехорош, а просто не пользуюсь.
Открытие внешнего редактора показалось мне
настолько простым, что думал Андрей подкумекает
это если не в тот же день, то на следующий.
Я просто дописал в начало метода initWindow()
window.edit_button = function edit_button() { var panel = document.getElementById( "custombuttons-editbutton-tabbox" ).tabpanels.selectedPanel; if (/sourceEditor-(.+)/.test(panel.id)) panel = document.getElementById(RegExp.$1); if (panel.localName == "cbeditor") window.edittarget(panel); }
//var require = Components.utils.import(loader, {}).devtools.require; var g = Components.utils.import(loader, {}); var require = (g.devtools || g).require;
Отсутствует
Infocatcher и Dumby спасибо и вот еще по этому коду вопрос (может только у меня - не знаю): неправильно работает выделение в URL кнопки, Имя, Изображение. Текст мышкой не выделяется (или как у меня двойной дубль-клик), но если нажать в контекстном меню после этого копировать или выделить все и копировать, то текст нормально копируется (т.е. не видно полосы выделения текста).
Отредактировано Andrey_Krropotkin (26-08-2019 22:11:56)
Отсутствует
(может только у меня - не знаю)
Подтвержаю, у меня на Win7 выделенное там тоже не подсвечивается фоном.
И, я тебе в начало метода initWindow() добавить посоветовал,
нет, в конец, конечно же.
Выделение можно стилем задать, а кодом — ничего лучше не придумал,
тоже в конец метода initWindow()
if(this.platformVersion >= 68) (function(events, win) { var listener = function(e) { if(e.type == "unload") return destroy(); var sheets = win.document.styleSheets; for(var ind = sheets.length - 1; ind; ind--) { var sheet = sheets.item(ind); if(sheet.href != "resource://devtools/client/themes/common.css") continue; destroy(); for(let ind = 0, len = sheet.cssRules.length; ind < len; ind++) { var rule = sheet.cssRules.item(ind); if(rule.selectorText && rule.selectorText == "::selection") return sheet.deleteRule(ind); } } } var destroy = function() { events.forEach(function(type) { win.removeEventListener(type, listener, false); }); } events.forEach(function(type) { win.addEventListener(type, listener, false); }); })(["mousedown", "keydown", "unload"], window);
Отсутствует
Dumby спасибо, теперь вообще комфортнее стало.
Отсутствует
Dumby есть ещё маленький такой момент, у Infocatcher есть ещё кнопка редактировать во вкладке, при совместной работе с Source Editor-ом на 68 проблем не было, и в редакторе и во вкладке все было одинаково, на 69, Source Editor работает только в редакторе, а во вкладке как обычно, т. е. Source Editor не применяется, посмотри пожалуйста. В какой из этих двух кнопках где-то неисправность не знаю.
Отсутствует
В какой из этих двух кнопках где-то неисправность не знаю.
В коде Source Editor'а поиск:
isBrowserWindow: function(window) {
Как только увидишь — всё сразу станет ясно.
Отсутствует
Dumby спасибо, слона и не заметил, заменил, все работает.
Отсутствует
В 71 перестал работать, также как и dom inspector
DOM Inspector и правда снова отвалился...
А вот Attributes Inspector, на первый взгляд, живой:
Что конкретно не работает и что пишет в консоль (Ctrl+Shift+J)?
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Да и в 73, на данный момент, работает.
А DOMi, ну давайте попробую скинуть.
// DOMi-7.0.{4a1-5}-fx.js (async re => { var gzip = "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"; var sel = "Select"; try {sel = Services.strings.createBundle("chrome://global/locale/commonDialogs.properties") .GetStringFromName(sel);} catch(ex) {} var picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); picker.init(window, sel + " DOMi 7.0.4a1", picker.modeOpen); picker.appendFilter(null, "dom_inspector-7.0.4a1-fx-*.xpi"); await new Promise(resolve => picker.open(resolve)); var {file} = picker; if (!file) return; var ln = file.leafName; if (!re.test(ln)) return alert("???\n" + ln); var {fileURL} = picker; var xpi = file.parent.clone(); xpi.append(ln = ln.replace("4a1-", "5-")); file.copyTo(file.parent, ln); var obs = {}, data; var td = new TextDecoder(), te = new TextEncoder(); var scs = Cc["@mozilla.org/streamConverters;1"].getService(Ci.nsIStreamConverterService); var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); var sl = Cc["@mozilla.org/network/stream-loader;1"].createInstance(Ci.nsIStreamLoader); sis.data = atob(gzip); obs.onStreamComplete = (a, b, c, d, result) => data = td.decode(new Uint8Array(result)); sl.init(obs); var converter = scs.asyncConvertData("gzip", "uncompressed", sl, null); converter.onStartRequest(null, null); var args = [null, null, sis, 0, sis.data.length]; if (converter.onDataAvailable.length == 4) args.shift(); converter.onDataAvailable(...args); converter.onStopRequest(null, null, null); var zw = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter); var mt = Date.now() * 1000, cp = zw.COMPRESSION_DEFAULT; var bootstrap = ln.includes("bootstrap"); var prefix = "jar:" + fileURL.spec + "!/"; var lr = /^¦(?:\d+)?$/, sep1 = "£", sep2 = "¥"; zw.open(xpi, 0x04); // PR_RDWR for(var item of data.split(sep1)) { var [entry, val] = item.split(sep2); if (bootstrap && (entry == "manifest.json" || entry == "startup.jsm")) continue; if (val == "+") { zw.addEntryDirectory(entry, mt, false); continue; } if (zw.hasEntry(entry)) { if (val.includes("¦")) { var lines = val.split("\n"); var oldLines = (await (await fetch(prefix + entry)).text()).split("\n"); lines.forEach((line, ind) => { if (lr.test(line)) lines[ind] = oldLines[ line.length == 1 ? ind : +line.slice(1) ]; }); val = lines.join("\n"); } zw.removeEntry(entry, false); if (val == "-") continue; } var stream = Cc["@mozilla.org/io/string-input-stream;1"] .createInstance(Ci.nsISupportsCString); stream.data = String.fromCharCode(...new Uint8Array(te.encode(val))); zw.addEntryStream(entry, mt, cp, stream, false); stream.close(); } zw.close(); xpi.reveal(); })( /^dom_inspector-7\.0\.4a1-fx-(?:paxmod|bootstrap)\.xpi$/ );
Отсутствует
Infocatcher, Dumby в кнопке для 71 версии
// https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Editor_Toggle_on_Top // (c) Infocatcher 2012-2015 // version 0.1.11 - 2015-06-04 // Hotkey: Ctrl+T const watcherId = "customButtonsToggleOnTop_" + this.id; var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older var storage = (function() { if(!("Services" in window)) // Firefox 3.6 and older return Application.storage; var global = Components.utils.import("resource://gre/modules/Services.jsm", {}); var ns = "_cbEditorToggleOnTopStorage"; var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null)); return { get: function(key, defaultVal) { if(key in storage) return storage[key]; return defaultVal; }, set: function(key, val) { if(key === null) delete storage[key]; else storage[key] = val; } }; })(); var watcher = storage.get(watcherId, null); if(!watcher) { watcher = { btnPos: 0, // 0 - at top right window corner, 1 - at end of tabs, 2 - before dialog buttons spacer btnStyle: "toolbarbutton", // "button" or "toolbarbutton" btnChecked: true, // use "checked" style: true or false // http://www.iconfinder.com/icondetails/12276/16/gps_location_pin_icon icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjAx4SevkwAAAjxJREFUKM+FUk1oE0EU/mZnRU0U6cFLIT1KKkkNpIcSWwUvVty2l142hZpDoQcPgYQKQkUEz6V6sHgR/KGQQwleQg/xEKRRsIEmga4NoZa2oRGKpJaWmp2Z52UX12Dxg4838+D73ps3jzUYQycIgDc7l0oNAkgqpZ4n5uc/NgHsEEHDKZB/X5OJRGJcSplemJ6+6SZPFStvJ0Ta4eEhTNMcE0I8fDsxcQsAdPwHc6nUoBRCOz4+Rr1eR39//+18Pn/h/cjIGea+eS6VegLgKoBfRPRdKbXGOf8GIBmPx8cty0Kz2YTf74ff70cmkynpNSIAwP7+/t2ZmZnowcGBPDo6+rm1tdWo1Wo7pmnesSwLtm2jWCx+EkJwKSUnoln2wWnv9eTkMBE9TafT0b29PbTbbXDO0Wq1YNs2lpaWVpVSj8ZyuWXhaPg953CtXK6v9fVtr6ys9A4NDXU3Gg24JoVC4fPJycnjsVxu2TtMzgCUAVQAUKVSrwDtzc3NQCQS6d7d3XU7EK1W62twY+ML84i1N4zB5TvgYrVaPTs1NRXNZDKlrq4u+Hw+BAKBy1LK6zYA2xE+6/gqzhgbXl9fnw+FQrPBYPCHlBKjo6NRIjpPRD3kEdbcJWGMgTF2Y3Fx8UE4HE4yxl70WtYCEc1ms9kS51wDcM4r/FOS87BhGK90XTc0TeNuPgggFosNG4axOjAw8PJKxwIxAD26rt9XShWVUtvOWosO2h1RAJAMwCUAPseInGG6UTlm6l/8DZ+nFuAjSdH0AAAAAElFTkSuQmCC", iconPinned: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjIpwH1VRwAAAgdJREFUKM+FkkFoU0EQhr/dtxFSquLBS8CAICISQci1oBWLKaaINL300psXDxH0ItSD4L21UKgUxV4aWnKyiIGaQwio2IQ0PQQpBaEhoaEk0dSQNsnb9WAepg+LPwz/7DD/7O7MiJIQuGEAV3QIiNowV4f0HlA0BskJsI8fo75YLHIEjw3ccIInivVxX1IqcXFu7l4Lnhq4BaD4P4Y6INsHB+zlcpwfH7+zG48PavCIvj8/B64CRxoqXdg8Bd+BqG9xMVJNpaiUy5zxehkcGCC2uppV28Y44rs3V1aCFIt2p15v/MzlSj/S6eKlmZnRairFoW2zmUx+boHVBsvAtEj+fV5Iw4vbS0tBe2uLw04H2+ulUy5zaNu8X17OdOGZgkTX3TADCRumP0xNZY3fz6/9fSr5PNVGg6/x+BfdE/Y3wxJAHtj6wzt5aHsSiQv+4WFfbWcHu9VCeDzdRq32DdgQfZMQom9JjDGngcn19fWF2shI9vrYWNBqNjFCND8mk+8MTDq5s65RWUKIUKFQmA0EAtP3pawdra0xGokE0dqrwe8kvgS2e1cjpURKORyLxTaUUg+UUuciUvIWQq8hU5uYMPOQmQcuu7fAsqxr4XD4jVIqLKW0nPgV4AmEFiDzCF65hQLwK6Ueaq0/aa13e2vddVnHxV3AFsBZYKBXyPSa6bDuFdP/st/BfOD54p3eIQAAAABJRU5ErkJggg==", boxId: "cbToggleOnTopBox", btnId: "cbToggleOnTopButton", onTopAttr: "cbOnTop", naAttr: "cbOnTopNA", styleId: "cbToggleOnTopStyle", get btnTip() { var locale = (function() { if("Services" in window && "locale" in Services) { var locales = Services.locale.requestedLocales // Firefox 64+ || Services.locale.getRequestedLocales && Services.locale.getRequestedLocales(); if(locales) return locales[0]; } var prefs = "Services" in window && Services.prefs || Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); function pref(name, type) { return prefs.getPrefType(name) != prefs.PREF_INVALID ? prefs["get" + type + "Pref"](name) : undefined; } if(!pref("intl.locale.matchOS", "Bool")) { // Also see https://bugzilla.mozilla.org/show_bug.cgi?id=1414390 var locale = pref("general.useragent.locale", "Char"); if(locale && locale.substr(0, 9) != "chrome://") return locale; } return Components.classes["@mozilla.org/chrome/chrome-registry;1"] .getService(Components.interfaces.nsIXULChromeRegistry) .getSelectedLocale("global"); })().match(/^[a-z]*/)[0]; if(locale == "ru") return "Поверх всех окон (Ctrl+T)"; return "Always on top (Ctrl+T)"; }, REASON_STARTUP: 1, REASON_SHUTDOWN: 2, REASON_WINDOW_LOADED: 3, REASON_WINDOW_CLOSED: 4, get obs() { delete this.obs; return this.obs = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); }, get ww() { delete this.ww; return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); }, get wm() { delete this.wm; return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); }, init: function(reason) { this.obs.addObserver(this, "quit-application-granted", false); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.initWindow(ws.getNext(), reason); this.ww.registerNotification(this); }, destroy: function(reason) { this.obs.removeObserver(this, "quit-application-granted"); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.destroyWindow(ws.getNext(), reason); this.ww.unregisterNotification(this); }, initWindow: function(window, reason) { if(!this.isTargetWindow(window)) return; window.addEventListener("keypress", this, true); if(this.hasSizeModeChangeEvent) window.addEventListener("sizemodechange", this, false); else { window.addEventListener("resize", this, false); // Can detect only maximize/restore this.legacySizeModeChange(window); } var document = window.document; this.removeStyle(document); this.addStyle(document); var box = document.getElementById(this.boxId); box && box.parentNode.removeChild(box); box = document.createXULElement("hbox"); box.id = this.boxId; var btn = document.createXULElement(this.btnStyle); btn.id = this.btnId; if(this.btnChecked) { btn.setAttribute("type", "checkbox"); btn.setAttribute("autoCheck", "false"); } btn.tooltipText = this.btnTip; btn.addEventListener("command", this, false); box.appendChild(btn); switch(this.btnPos) { default: box.setAttribute("cbOnTopFloat", "true"); document.documentElement.appendChild(box); break; case 1: box.setAttribute("align", "right"); let tabbox = document.getElementById("custombuttons-editbutton-tabbox"); let tabs = tabbox.getElementsByTagName("tabs")[0]; tabs.parentNode.insertBefore(box, tabs); box.style.marginBottom = -btn.boxObject.height + "px"; break; case 2: box.setAttribute("align", "center"); let btnBox = document.documentElement.getButton("accept").parentNode; let insPos = btnBox.firstChild; for(let node = insPos; node; node = node.nextSibling) { if(node.localName == "spacer") { insPos = node; break; } } btnBox.insertBefore(box, insPos); } this.checkWindowStatus(window, box); }, destroyWindow: function(window, reason) { if(reason == this.REASON_WINDOW_CLOSED) window.removeEventListener("DOMContentLoaded", this, false); // Window can be closed before DOMContentLoaded if(!this.isTargetWindow(window)) return; window.removeEventListener("keypress", this, true); if(this.hasSizeModeChangeEvent) window.removeEventListener("sizemodechange", this, false); else window.removeEventListener("resize", this, false); var document = window.document; var btn = document.getElementById(this.btnId); btn.removeEventListener("command", this, false); if(reason == this.REASON_SHUTDOWN) { let box = btn.parentNode; box.parentNode.removeChild(box); this.removeStyle(document); let xulWin = this.getXulWin(window); xulWin.zLevel = xulWin.normalZ; } }, isTargetWindow: function(window) { return window.location.href.substr(0, 41) == "chrome://custombuttons/content/editor.xul"; }, observe: function(subject, topic, data) { if(topic == "quit-application-granted") this.destroy(); else if(topic == "domwindowopened") subject.addEventListener("DOMContentLoaded", this, false); else if(topic == "domwindowclosed") this.destroyWindow(subject, this.REASON_WINDOW_CLOSED); }, handleEvent: function(e) { var trg = e.originalTarget || e.target; var window; switch(e.type) { case "DOMContentLoaded": window = trg.defaultView; window.removeEventListener("DOMContentLoaded", this, false); this.initWindow(window, this.REASON_WINDOW_LOADED); break; case "keypress": if( !( (e.ctrlKey || e.metaKey) && !e.altKey && !e.shiftKey && String.fromCharCode(e.charCode).toLowerCase() == "t" ) ) break; e.preventDefault(); e.stopPropagation(); case "command": window = trg.ownerDocument.defaultView.top; this.toggleOnTop(window); break; case "sizemodechange": case "resize": window = trg; this.checkWindowStatus(window); } }, get hasSizeModeChangeEvent() { var appinfo = "Services" in window && Services.appinfo; delete this.hasSizeModeChangeEvent; return this.hasSizeModeChangeEvent = appinfo && ( appinfo.name == "Pale Moon" || parseFloat(appinfo.platformVersion) >= 8 ); }, legacySizeModeChange: function(window) { var lastState = window.windowState; window.setInterval(function(window, _this) { var state = window.windowState; if(state != lastState) _this.checkWindowStatus(window); lastState = state; }, 150, window, this); }, checkWindowStatus: function(window, box) { if(!box) box = window.document.getElementById(this.boxId); var na = String(window.windowState != window.STATE_NORMAL); if(box.getAttribute(this.naAttr) == na) return; box.setAttribute(this.naAttr, na); //LOG("Set n/a: " + na); this.setOnTop(box.firstChild); }, addStyle: function(document) { var style = '\ %box%[cbOnTopFloat] {\n\ position: fixed !important;\n\ top: 0 !important;\n\ right: 0 !important;\n\ }\n\ %btn%, %btn% * {\n\ margin: 0 !important;\n\ padding: 0 !important;\n\ }\n\ %btn% > .button-box > .button-icon {\n\ margin: -3px -1px !important;\n\ }\n\ toolbarbutton%btn% {\n\ padding: 0 2px !important;\n\ }\n\ %btn% {\n\ position: relative !important;\n\ z-index: 2147483647 !important;\n\ list-style-image: url("%icon%") !important;\n\ -moz-user-focus: ignore !important;\n\ min-height: 0 !important;\n\ min-width: 0 !important;\n\ }\n\ %btn%[%onTopAttr%="true"] {\n\ list-style-image: url("%iconPinned%") !important;\n\ }\n\ %box%[%naAttr%="true"] image {\n\ opacity: 0.75 !important;\n\ }' .replace(/%box%/g, "#" + this.boxId) .replace(/%btn%/g, "#" + this.btnId) .replace(/%onTopAttr%/g, this.onTopAttr) .replace(/%icon%/g, this.icon) .replace(/%iconPinned%/g, this.iconPinned) .replace(/%naAttr%/g, this.naAttr); document.insertBefore(document.createProcessingInstruction( "xml-stylesheet", 'id="' + this.styleId + '" href="' + "data:text/css," + encodeURIComponent(style) + '" type="text/css"' ), document.documentElement); }, removeStyle: function(document) { var mark = 'id="' + this.styleId + '"'; for(var child = document.documentElement; child = child.previousSibling; ) { if( child.nodeType == child.PROCESSING_INSTRUCTION_NODE && child.data.substr(0, mark.length) == mark ) { document.removeChild(child); break; } } }, getXulWin: function(window) { return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) .QueryInterface(Components.interfaces.nsIDocShellTreeItem) .treeOwner .QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIXULWindow); }, setOnTop: function(btn, toggle) { var document = btn.ownerDocument; var root = document.documentElement; var onTop = root.getAttribute(this.onTopAttr) == "true"; var xs = Components. classes ["@mozilla.org/xul/xulstore;1"]. getService (Components. interfaces. nsIXULStore); if(toggle) { onTop = !onTop; root.setAttribute(this.onTopAttr, onTop); if(root.id) { if (!("persist" in document)) { if (!("persist" in xs)) xs = ChromeUtils. import ("resource://gre/modules/Services.jsm"). Services. xulStore; document.persist = function (id, attrr){ var node = document. getElementById (root.id); if (node) xs. persist (root.id, this.onTopAttr); } } } } else if(!onTop) // Just opened or restored window always have zLevel == normalZ return; var window = document.defaultView; var state = window.windowState; var restore = onTop && state == window.STATE_MINIMIZED; if(restore || state == window.STATE_NORMAL) { if(restore) onTop = false; let xulWin = this.getXulWin(window); xulWin.zLevel = onTop ? xulWin.raisedZ : xulWin.normalZ; } this.checkButton(btn, onTop); }, toggleOnTop: function(window) { this.setOnTop(window.document.getElementById(this.btnId), true); }, checkButton: function(btn, onTop) { btn.setAttribute(this.onTopAttr, onTop); this.btnChecked && btn.setAttribute("checked", onTop); } }; storage.set(watcherId, watcher); watcher.init(watcher.REASON_STARTUP); } function destructor(reason) { if(reason == "update" || reason == "delete") { watcher.destroy(watcher.REASON_SHUTDOWN); storage.set(watcherId, null); } } if( typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+ && addDestructor != ("addDestructor" in window && window.addDestructor) ) addDestructor(destructor, this); else this.onDestroy = destructor;
// https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Source_Editor // http://infocatcher.ucoz.net/js/cb/cbSourceEditor.js // (c) Infocatcher 2012-2019 // version 0.1.0a9 - 2019-03-01 var options = { cssInHelp: true, codeMirror: { lineNumbers: true, enableCodeFolding: true, showTrailingSpace: true, lineWrapping: false, autocomplete: true, fontSize: 14 }, orion: { lineNumbers: true } }; const watcherId = "customButtonsSourceEditor_" + this.id; var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older var storage = (function() { if(!("Services" in window)) // Firefox 3.6 and older return Application.storage; var global = Components.utils.import("resource://gre/modules/Services.jsm", {}); var ns = "_cbSourceEditorStorage"; var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null)); return { get: function(key, defaultVal) { if(key in storage) return storage[key]; return defaultVal; }, set: function(key, val) { if(key === null) delete storage[key]; else storage[key] = val; } }; })(); var watcher = storage.get(watcherId, null); if(!watcher) { watcher = { REASON_STARTUP: 1, REASON_SHUTDOWN: 2, REASON_WINDOW_LOADED: 3, REASON_WINDOW_CLOSED: 4, get obs() { delete this.obs; return this.obs = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); }, get ww() { delete this.ww; return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); }, get wm() { delete this.wm; return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); }, get platformVersion() { delete this.platformVersion; return this.platformVersion = parseFloat(Services.appinfo.platformVersion); }, get hasCodeMirror() { delete this.hasCodeMirror; return this.hasCodeMirror = Services.appinfo.name == "Pale Moon" //~ todo: test || this.platformVersion >= 27; }, init: function(reason) { if(!this.hasCodeMirror) { this.isBrowserWindow = function() { return false; }; } this.obs.addObserver(this, "quit-application-granted", false); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.initWindow(ws.getNext(), reason); this.ww.registerNotification(this); }, destroy: function(reason) { this.obs.removeObserver(this, "quit-application-granted"); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.destroyWindow(ws.getNext(), reason); this.ww.unregisterNotification(this); }, initWindow: function(window, reason, isFrame) { if(this.isBrowserWindow(window)) { this.initBrowserWindow(window, reason); return; } if(!this.isEditorWindow(window)) return; _log("initWindow(): isFrame: " + isFrame); var document = window.document; if(isFrame) window.addEventListener("unload", this, false); Components.classes["@mozilla.org/moz/jssubscript-loader;1"] .getService(Components.interfaces.mozIJSSubScriptLoader) .loadSubScript("chrome://global/content/globalOverlay.js", window); var isCodeMirror = false; try { // See chrome://browser/content/devtools/scratchpad.js Components.utils.import("resource:///modules/source-editor.jsm", window); } catch(e) { var loader = this.platformVersion >= 44 // See https://bugzilla.mozilla.org/show_bug.cgi?id=912121 ? "resource://devtools/shared/Loader.jsm" : "resource://gre/modules/devtools/Loader.jsm"; var g = Components.utils.import(loader, {}); var require = (g.devtools || g).require; [ "devtools/sourceeditor/editor", "devtools/client/sourceeditor/editor", // Firefox 44+ "devtools/client/shared/sourceeditor/editor" // Firefox 68+ ].some(function(path) { try { return window.SourceEditor = require(path); } catch(e) { } return null; }); isCodeMirror = true; } var SourceEditor = window.SourceEditor; // See view-source:chrome://browser/content/devtools/scratchpad.xul // + view-source:chrome://browser/content/devtools/source-editor-overlay.xul var psXUL = (isCodeMirror ? '<!DOCTYPE popupset [\ <!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">\ %editMenuStrings;\ <!ENTITY % sourceEditorStrings SYSTEM "' + ( Services.appinfo.name == "Pale Moon" || Services.appinfo.name == "Basilisk" ? this.platformVersion >= 4.1 ? "chrome://devtools/locale/sourceeditor.dtd" : "chrome://global/locale/devtools/sourceeditor.dtd" : this.platformVersion >= 45 ? "chrome://devtools/locale/sourceeditor.dtd" : "chrome://browser/locale/devtools/sourceeditor.dtd" ) + '">\ %sourceEditorStrings;\ ]>\ <popupset id="sourceEditorPopupset"\ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\ <menupopup id="sourceEditorContext"\ onpopupshowing="goUpdateSourceEditorMenuItems()">\ <menuitem id="menu_undo" label="&undoCmd.label;" accesskey="&undoCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_undo\')" />\ <menuitem id="menu_redo" label="&redoCmd.label;" accesskey="&redoCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_redo\')" />\ <menuseparator/>\ <menuitem id="menu_cut" label="&cutCmd.label;" accesskey="&cutCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_cut\')" />\ <menuitem id="menu_copy" label="©Cmd.label;" accesskey="©Cmd.accesskey;"\ oncommand="goDoCommand(\'cmd_copy\')" />\ <menuitem id="menu_paste" label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_paste\')" />\ <menuitem id="menu_delete" label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_delete\')" />\ <menuseparator/>\ <menuitem id="menu_selectAll" label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;"\ oncommand="goDoCommand(\'cmd_selectAll\')" />\ <menuseparator/>\ <menuitem id="menu_find" label="&findCmd.label;" accesskey="&findCmd.accesskey;" />\ <menuitem id="menu_findAgain" label="&findAgainCmd.label;" accesskey="&findAgainCmd.accesskey;" />\ <menuseparator/>\ <menuitem id="se-menu-gotoLine"\ label="&gotoLineCmd.label;"\ accesskey="&gotoLineCmd.accesskey;"\ key="key_gotoLine"\ oncommand="goDoCommand(\'cmd_gotoLine\')"/>\ </menupopup>\ </popupset>' : '<popupset id="sourceEditorPopupset"\ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\ <menupopup id="sourceEditorContext"\ onpopupshowing="goUpdateSourceEditorMenuItems()">\ <menuitem id="se-menu-undo"/>\ <menuitem id="se-menu-redo"/>\ <menuseparator/>\ <menuitem id="se-menu-cut"/>\ <menuitem id="se-menu-copy"/>\ <menuitem id="se-menu-paste"/>\ <menuitem id="se-menu-delete"/>\ <menuseparator/>\ <menuitem id="se-menu-selectAll"/>\ <menuseparator/>\ <menuitem id="se-menu-find"/>\ <menuitem id="se-menu-findAgain"/>\ <menuseparator/>\ <menuitem id="se-menu-gotoLine"/>\ </menupopup>\ </popupset>' ).replace(/>\s+</g, "><"); var ps = this.parseXULFromString(psXUL); document.documentElement.appendChild(ps); window.setTimeout(function() { function appendNode(nodeName, id) { var node = document.createElementNS(xulns, nodeName); node.id = id; document.documentElement.appendChild(node); } appendNode("commandset", "editMenuCommands"); appendNode("commandset", "sourceEditorCommands"); appendNode("keyset", "sourceEditorKeys"); appendNode("keyset", "editMenuKeys"); this.loadOverlays( window, function done() { window.setTimeout(function() { var mp = document.getElementById("sourceEditorContext"); if(mp.state == "closed") return; Array.prototype.forEach.call( mp.getElementsByAttribute("command", "*"), function(mi) { var cmd = mi.getAttribute("command"); var controller = document.commandDispatcher .getControllerForCommand(cmd); if(controller && !controller.isCommandEnabled(cmd)) mi.setAttribute("disabled", "true"); } ); }, 0); if(!isCodeMirror) return; // See view-source:chrome://browser/content/devtools/scratchpad.xul in Firefox 27.0a1 window.goUpdateSourceEditorMenuItems = function() { goUpdateGlobalEditMenuItems(); var commands = ["cmd_undo", "cmd_redo", "cmd_cut", "cmd_paste", "cmd_delete"]; commands.forEach(goUpdateCommand); }; var cmdsMap = { "se-menu-undo": "cmd_undo", "se-menu-redo": "cmd_redo", "se-menu-cut": "cmd_cut", "se-menu-copy": "cmd_copy", "se-menu-paste": "cmd_paste", "se-menu-delete": "cmd_delete", __proto__: null }; for(var id in cmdsMap) { var mi = document.getElementById(id); mi && mi.setAttribute("command", cmdsMap[id]); } // We can't use command="cmd_selectAll", menuitem will be wrongly disabled sometimes var enabledCmdsMap = { "se-menu-selectAll": "cmd_selectAll", "se-menu-findAgain": "cmd_findAgain", __proto__: null }; for(var id in enabledCmdsMap) { var mi = document.getElementById(id); if(mi) { mi.removeAttribute("command"); mi.removeAttribute("disabled"); mi.setAttribute("oncommand", "goDoCommand('" + enabledCmdsMap[id] + "');"); } } // Workaround: emulate keyboard shortcut var keyCmdsMap = { "menu_find": { keyCode: KeyboardEvent.DOM_VK_F, charCode: "f".charCodeAt(0), ctrlKey: true }, "menu_findAgain": { keyCode: KeyboardEvent.DOM_VK_G, charCode: "g".charCodeAt(0), ctrlKey: true }, __proto__: null }; var _key = function() { var e = this._keyData; var evt = document.createEvent("KeyboardEvent"); evt.initKeyEvent( "keydown", true /*bubbles*/, true /*cancelable*/, window, e.ctrlKey || false, e.altKey || false, e.shiftKey || false, e.metaKey || false, e.keyCode || 0, e.charCode || 0 ); document.commandDispatcher.focusedElement.dispatchEvent(evt); }; for(var id in keyCmdsMap) { var mi = document.getElementById(id); if(mi) { mi.removeAttribute("command"); mi.removeAttribute("disabled"); mi.setAttribute("oncommand", "this._key();"); mi._keyData = keyCmdsMap[id]; mi._key = _key; } } // Fix styles for autocomplete tooltip function css(uri) { document.insertBefore(document.createProcessingInstruction( "xml-stylesheet", 'href="' + uri + '" type="text/css"' ), document.documentElement); } css("resource://devtools/client/themes/variables.css"); css("resource://devtools/client/themes/common.css"); css("chrome://devtools/skin/tooltips.css"); }, ["chrome://global/content/editMenuOverlay.xul", function check(window) { return window.document.getElementById("editMenuCommands").hasChildNodes(); }], ["chrome://browser/content/devtools/source-editor-overlay.xul", function check(window) { return window.document.getElementById("sourceEditorCommands").hasChildNodes(); }] ); }.bind(this), 500); // We should wait to not break other extensions with document.loadOverlay() var tabs = document.getElementById("custombuttons-editbutton-tabbox"); var selectedPanel = tabs.selectedPanel; Array.prototype.slice.call(document.getElementsByTagName("cbeditor")).forEach(function(cbEditor) { if("__sourceEditor" in cbEditor) return; var code = cbEditor.value; var isCSS = options.cssInHelp && cbEditor.id == "help"; if(isCodeMirror) { var opts = { mode: isCSS ? SourceEditor.modes.css : SourceEditor.modes.js, value: code, lineNumbers: true, enableCodeFolding: true, showTrailingSpace: true, autocomplete: true, contextMenu: "sourceEditorContext" }; var optsOvr = options.codeMirror; for(var opt in optsOvr) if(optsOvr.hasOwnProperty(opt)) opts[opt] = optsOvr[opt]; var se = new SourceEditor(opts); if("codeMirror" in se) window.setTimeout(function() { if("insertCommandsController" in se) se.insertCommandsController(); // Pale Moon and Basilisk else this.insertCommandsController(se); }.bind(this), 200); } else { var se = new SourceEditor(); } se.__isCodeMirror = isCodeMirror; var seElt = document.createElementNS(xulns, "hbox"); if(cbEditor.id) seElt.id = "sourceEditor-" + cbEditor.id; seElt.className = "sourceEditor"; seElt.setAttribute("flex", 1); seElt.__sourceEditor = se; cbEditor.parentNode.insertBefore(seElt, cbEditor); //cbEditor.setAttribute("hidden", "true"); cbEditor.setAttribute("collapsed", "true"); cbEditor.parentNode.appendChild(cbEditor); cbEditor.__sourceEditor = se; cbEditor.__sourceEditorElt = seElt; cbEditor.__defineGetter__("value", function() { if("__sourceEditor" in this) { var se = this.__sourceEditor; if(!se.__initialized) return se.__value; return se.getText().replace(/\r\n?|\n\r?/g, "\n"); } return this.textbox.value; }); cbEditor.__defineSetter__("value", function(v) { if("__sourceEditor" in this) { var se = this.__sourceEditor; if(!se.__initialized) { var _this = this; se.__onLoadCallbacks.push(function() { _this.value = v; }); return se.__value = v; } return se.setText(v.replace(/\r\n?|\n\r?/g, "\n")); } return this.textbox.value = v; }); cbEditor.selectLine = function(lineNumber) { if("__sourceEditor" in this) { var se = this.__sourceEditor; if(!se.__initialized) { var _this = this, args = arguments; se.__onLoadCallbacks.push(function() { _this.selectLine.apply(_this, args); }); return undefined; } if(se.__isCodeMirror) { //se.focus(); //se.setCursor({ line: lineNumber - 1, ch: 0 }); //~ todo: optimize var val = this.value; var lines = val.split("\n"); var line = Math.min(lineNumber - 1, lines.length); var ch = lines[line].length; se.focus(); return se.setSelection({ line: line, ch: 0 }, { line: line, ch: ch }); } else { var selStart = se.getLineStart(lineNumber - 1); var selEnd = se.getLineEnd(lineNumber - 1, false); se.focus(); return se.setSelection(selStart, selEnd); } } return this.__proto__.selectLine.apply(this, arguments); }; // For edit_button() from chrome://custombuttons/content/editExternal.js seElt.__cbEditor = cbEditor; seElt.__defineGetter__("localName", function() { return "cbeditor"; }); seElt.__defineGetter__("value", function() { return this.__cbEditor.value; }); seElt.__defineSetter__("value", function(val) { this.__cbEditor.value = val; }); se.__initialized = false; se.__onLoadCallbacks = []; se.__value = code; var onTextChanged = se.__onTextChanged = function() { window.editor.changed = true; }; var isLoaded = reason == this.REASON_WINDOW_LOADED; function done() { se.__initialized = true; se.__onLoadCallbacks.forEach(function(fn) { try { fn(); } catch(e) { Components.utils.reportError(e); } }); delete se.__onLoadCallbacks; delete se.__value; } if(isCodeMirror) { se.appendTo(seElt).then(function() { try { se.setupAutoCompletion(); } catch(e) { Components.utils.reportError(e); } if("setFontSize" in se) try { se.setFontSize(options.codeMirror.fontSize); } catch(e) { Components.utils.reportError(e); } window.setTimeout(function() { window.editor.changed = false; // Strange... window.setTimeout(function() { // Workaround for unexpected onTextChanged() calls if(window.editor.changed && cbEditor.value == code) window.editor.changed = false; }, 100); se.on("change", onTextChanged); if(isLoaded) { if("clearHistory" in se) se.clearHistory(); else { var seGlobal = Components.utils.getGlobalForObject(SourceEditor.prototype); // Note: this is resource://app/modules/devtools/gDevTools.jsm scope in Firefox 34+ var cm = seGlobal.editors.get(se); cm.clearHistory(); } } }, isFrame ? 50 : 15); // Oh, magic delays... done(); // See resource:///modules/devtools/sourceeditor/editor.js // doc.defaultView.controllers.insertControllerAt(0, controller(this, doc.defaultView)); var controllers = window.controllers; // nsIControllers var controller = se.__cmdController = controllers.getControllerAt(0); if("__cmdControllers" in tabs) tabs.__cmdControllers.push(controller); else { tabs.__cmdControllers = [controller]; var onSelect = tabs.__onSelect = function() { var seElt = tabs.selectedPanel; if(!seElt || !("__sourceEditor" in seElt)) return; var se = seElt.__sourceEditor; var curController = se.__cmdController; tabs.__cmdControllers.forEach(function(controller) { try { if(controller == curController) controllers.insertControllerAt(0, controller); else controllers.removeController(controller); } catch(e) { } }); }; tabs.addEventListener("select", onSelect, false); window.setTimeout(onSelect, 0); // Activate controller from selected tab } }); } else { var opts = { mode: isCSS ? SourceEditor.MODES.CSS : SourceEditor.MODES.JAVASCRIPT, showLineNumbers: true, initialText: code, placeholderText: code, // For backward compatibility contextMenu: "sourceEditorContext" }; var optsOvr = options.orion; for(var opt in optsOvr) if(optsOvr.hasOwnProperty(opt)) opts[opt] = optsOvr[opt]; se.init(seElt, opts, function callback() { done(); isLoaded && se.resetUndo && se.resetUndo(); se.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onTextChanged); // Hack to use selected editor var controller = se.ui._controller; controller.__defineGetter__("_editor", function() { var seElt = tabs.selectedPanel; var se = seElt && seElt.__sourceEditor || document.getElementsByTagName("cbeditor")[0].__sourceEditor; return se; }); controller.__defineSetter__("_editor", function() {}); }); } }, this); // Force select correct panel (prevent bugs, if selected "Button settings" tab) tabs.selectedPanel = selectedPanel.__sourceEditorElt || selectedPanel; var origExecCmd = window.editor.execute_oncommand_code; window.editor.execute_oncommand_code = function() { var cd = document.commandDispatcher; var cdFake = { __proto__: cd, get focusedElement() { var selectedTab = tabs.selectedTab; if(selectedTab && selectedTab.id == "code-tab") return document.getElementById("code").textbox.inputField; return cd.focusedElement; } }; document.__defineGetter__("commandDispatcher", function() { return cdFake; }); try { var ret = origExecCmd.apply(this, arguments); } catch(e) { Components.utils.reportError(e); } // document.hasOwnProperty("commandDispatcher") == false, so we cat just delete our fake property delete document.commandDispatcher; return ret; }; window.addEventListener("load", function ensureObserversAdded() { window.removeEventListener("load", ensureObserversAdded, false); window.setTimeout(function() { window.editor.removeObservers(); }, 0); window.setTimeout(function() { window.editor.addObservers(); }, 0); }, false); // Fix for Ctrl+S hotkey (catched by CodeMirror) var hke = this.handleKeyEvent; window.addEventListener("keydown", hke, true); window.addEventListener("keypress", hke, true); window.addEventListener("keyup", hke, true); if(this.platformVersion >= 68) (function(events, win) { var listener = function(e) { if(e.type == "unload") return destroy(); var sheets = win.document.styleSheets; for(var ind = sheets.length - 1; ind; ind--) { var sheet = sheets.item(ind); if(sheet.href != "resource://devtools/client/themes/common.css") continue; destroy(); for(let ind = 0, len = sheet.cssRules.length; ind < len; ind++) { var rule = sheet.cssRules.item(ind); if(rule.selectorText && rule.selectorText == "::selection") return sheet.deleteRule(ind); } } } var destroy = function() { events.forEach(function(type) { win.removeEventListener(type, listener, false); }); } events.forEach(function(type) { win.addEventListener(type, listener, false); }); })(["mousedown", "keydown", "unload"], window); }, insertCommandsController: function(se) { this.insertCommandsController = insertCommandsController; return insertCommandsController(se); // devtools/client/sourceeditor/editor-commands-controller in Pale Moon/Basilisk function createController(ed) { return { supportsCommand: function (cmd) { switch (cmd) { case "cmd_find": case "cmd_findAgain": case "cmd_gotoLine": case "cmd_undo": case "cmd_redo": case "cmd_delete": case "cmd_selectAll": return true; } return false; }, isCommandEnabled: function (cmd) { let cm = ed.codeMirror; switch (cmd) { case "cmd_find": case "cmd_gotoLine": case "cmd_selectAll": return true; case "cmd_findAgain": return cm.state.search != null && cm.state.search.query != null; case "cmd_undo": return ed.canUndo(); case "cmd_redo": return ed.canRedo(); case "cmd_delete": return ed.somethingSelected(); } return false; }, doCommand: function (cmd) { let cm = ed.codeMirror; let map = { "cmd_selectAll": "selectAll", "cmd_find": "find", "cmd_undo": "undo", "cmd_redo": "redo", "cmd_delete": "delCharAfter", "cmd_findAgain": "findNext" }; if (map[cmd]) { cm.execCommand(map[cmd]); return; } if (cmd == "cmd_gotoLine") { ed.jumpToLine(); } }, onEvent: function () {} }; } function insertCommandsController(sourceEditor) { let input = sourceEditor.codeMirror.getInputField(); let controller = createController(sourceEditor); input.controllers.insertControllerAt(0, controller); } }, destroyWindow: function(window, reason, isFrame) { if(reason == this.REASON_WINDOW_CLOSED) window.removeEventListener("DOMContentLoaded", this, false); // Window can be closed before DOMContentLoaded if(this.isBrowserWindow(window)) { this.destroyBrowserWindow(window, reason); return; } if(!this.isEditorWindow(window) || !("SourceEditor" in window)) return; _log("destroyWindow(): isFrame: " + isFrame); var document = window.document; if(isFrame) window.removeEventListener("unload", this, false); var tabs = document.getElementById("custombuttons-editbutton-tabbox"); if("__onSelect" in tabs) { tabs.removeEventListener("select", tabs.__onSelect, false); delete tabs.__onSelect; delete tabs.__cmdControllers; } Array.prototype.slice.call(document.getElementsByTagName("cbeditor")).forEach(function(cbEditor) { if(!("__sourceEditor" in cbEditor)) return; var se = cbEditor.__sourceEditor; var isCodeMirror = se.__isCodeMirror; if(isCodeMirror) se.off("change", se.__onTextChanged); else se.removeEventListener(window.SourceEditor.EVENTS.TEXT_CHANGED, se.__onTextChanged); delete se.__onTextChanged; if(reason == this.REASON_SHUTDOWN) { var val = cbEditor.value; delete cbEditor.value; delete cbEditor.selectLine; var seElt = cbEditor.__sourceEditorElt; seElt.parentNode.insertBefore(cbEditor, seElt); seElt.parentNode.removeChild(seElt); delete cbEditor.__sourceEditorElt; delete cbEditor.__sourceEditor; delete seElt.__sourceEditor; delete seElt.__cbEditor; cbEditor.value = val; window.setTimeout(function() { cbEditor.removeAttribute("collapsed"); }, 0); } se.destroy(); if("__cmdController" in se) { try { window.controllers.removeController(se.__cmdController); } catch(e) { } delete se.__cmdController; } }, this); if(reason == this.REASON_SHUTDOWN) { delete window.editor.execute_oncommand_code; [ "sourceEditorPopupset", "editMenuCommands", "sourceEditorCommands", "sourceEditorKeys", "editMenuKeys" ].forEach(function(id) { var node = document.getElementById(id); node && node.parentNode.removeChild(node); }); [ "closeWindow", "canQuitApplication", "goQuitApplication", "goUpdateCommand", "goDoCommand", "goSetCommandEnabled", "goSetMenuValue", "goSetAccessKey", "goOnEvent", "visitLink", "setTooltipText", "NS_ASSERT", "goUpdateGlobalEditMenuItems", "goUpdateUndoEditMenuItems", "goUpdatePasteMenuItems" ].forEach(function(p) { delete window[p]; }); for(var child = document.documentElement; child = child.previousSibling; ) { if( child.nodeType == child.PROCESSING_INSTRUCTION_NODE && child.data.indexOf("://devtools/") != -1 ) { setTimeout(function(child) { child.parentNode.removeChild(child); }, 0, child); } } delete window.SourceEditor; } var hke = this.handleKeyEvent; window.removeEventListener("keydown", hke, true); window.removeEventListener("keypress", hke, true); window.removeEventListener("keyup", hke, true); //~ todo: we have one not removed controller! //LOG("getControllerCount(): " + window.controllers.getControllerCount()); }, initBrowserWindow: function(window, reason) { _log("initBrowserWindow()"); window.addEventListener("DOMContentLoaded", this, false); Array.prototype.forEach.call(window.frames, function(frame) { this.initWindow(frame, reason, true); }, this); }, destroyBrowserWindow: function(window, reason) { _log("destroyBrowserWindow()"); window.removeEventListener("DOMContentLoaded", this, false); Array.prototype.forEach.call(window.frames, function(frame) { this.destroyWindow(frame, reason, true); }, this); }, isEditorWindow: function(window) { return window.location.href.substr(0, 41) == "chrome://custombuttons/content/editor.xul"; }, isBrowserWindow: function(window) { var loc = window.location.href; return loc == "chrome://browser/content/browser.xhtml" || loc == "chrome://navigator/content/navigator.xul"; }, observe: function(subject, topic, data) { if(topic == "quit-application-granted") this.destroy(); else if(topic == "domwindowopened") subject.addEventListener("DOMContentLoaded", this, false); else if(topic == "domwindowclosed") this.destroyWindow(subject, this.REASON_WINDOW_CLOSED); }, handleEvent: function(e) { switch(e.type) { case "DOMContentLoaded": var window = e.target.defaultView || e.target; window.removeEventListener(e.type, this, false); var isFrame = window != e.currentTarget; this.initWindow(window, this.REASON_WINDOW_LOADED, isFrame); break; case "unload": //var window = e.currentTarget; var window = e.target.defaultView || e.target; window.removeEventListener(e.type, this, false); this.destroyWindow(window, this.REASON_WINDOW_CLOSED, true); } }, handleKeyEvent: function(e) { if( (e.keyCode == e.DOM_VK_S || String.fromCharCode(e.charCode).toUpperCase() == "S") && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) { e.preventDefault(); e.stopPropagation(); if(e.type == "keydown") { var window = e.currentTarget; window.editor.updateButton(); } } }, loadOverlays: function() { this.runGenerator(this.loadOverlaysGen, this, arguments); }, get loadOverlaysGen() { var fn = this._loadOverlaysGen.toString() .replace(/__yield/g, "yield"); try { new Function("function test() { yield 0; }"); } catch(e) { // Firefox 58+: SyntaxError: yield expression is only valid in generators fn = fn.replace("function", "function*"); // Firefox 26+ } delete this.loadOverlaysGen; return this.loadOverlaysGen = eval("(" + fn + ")"); }, _loadOverlaysGen: function loadOverlaysGen(window, callback/*, overlayData1, ...*/) { var gen = loadOverlaysGen.__generator; for(var i = 2, l = arguments.length; i < l; ++i) { var overlayData = arguments[i]; this.loadOverlay(window, overlayData[0], overlayData[1], function() { gen.next(); }); __yield(0); } callback(); __yield(0); }, loadOverlay: function(window, uri, check, callback) { var document = window.document; var stopWait = Date.now() + 4500; window.setTimeout(function load() { _log("loadOverlay(): " + uri); var tryAgain = Date.now() + 800; try { document.loadOverlay(uri, null); } catch(e) { window.setTimeout(callback, 0); return; } window.setTimeout(function ensureLoaded() { if(check(window)) window.setTimeout(callback, 0); else if(Date.now() > stopWait) return; else if(Date.now() > tryAgain) window.setTimeout(load, 0); else window.setTimeout(ensureLoaded, 50); }, 50); }, 0); }, runGenerator: function(genFunc, context, args) { var gen = genFunc.apply(context, args); genFunc.__generator = gen; gen.next(); }, parseXULFromString: function(xul) { xul = xul.replace(/>\s+</g, "><"); try { return new DOMParser().parseFromString(xul, "application/xml").documentElement; } catch(e) { var dummy = document.createElement("dummy"); dummy.innerHTML = xul.trimLeft(); return dummy.firstChild; } } }; storage.set(watcherId, watcher); setTimeout(function() { watcher.init(watcher.REASON_STARTUP); }, 50); } function destructor(reason) { if(reason == "update" || reason == "delete") { watcher.destroy(watcher.REASON_SHUTDOWN); storage.set(watcherId, null); } } if( typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+ && addDestructor != ("addDestructor" in window && window.addDestructor) ) addDestructor(destructor, this); else this.onDestroy = destructor; function ts() { var d = new Date(); var ms = d.getMilliseconds(); return d.toTimeString().replace(/^.*\d+:(\d+:\d+).*$/, "$1") + ":" + "000".substr(("" + ms).length) + ms + " "; } function _log(s) { Services.console.logStringMessage("[Custom Buttons :: Source Editor] " + ts() + s); }
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Add_New_Buttons_After_This_Button // Add New Buttons After This Button button for Custom Buttons // (c) Infocatcher 2012, 2014 // version 0.1.2 - 2014-01-25 var cbs = custombuttons.cbService; var windowId = cbs.getWindowId(document.documentURI); var notificationPrefix = cbs.getNotificationPrefix(windowId); this.toggleEnabled = function() { this.checked = !this.checked; if("persist" in document) document.persist(this.id, "checked"); else // Firefox 63+ Services.xulStore.persist(this, "checked"); }; this.setAttribute("oncommand", "this.toggleEnabled();"); var observer = { button: this, observe: function(button, topic, data) { if(topic != notificationPrefix + "installButton") return; if(!this.button.checked) return; var toolbar = this.button.parentNode; toolbar.insertBefore(button, this.button.nextSibling); custombuttons.persistCurrentSets(toolbar.id, this.button.id, button.id || button.getAttribute("id")); } }; var os = Components.classes["@mozilla.org/observer-service;1"] .getService (Components.interfaces.nsIObserverService); os.addObserver(observer, notificationPrefix + "installButton", false); var hasObserver = true; this.onDestroy = function(reason) { if(hasObserver) { hasObserver = false; os.removeObserver(observer, notificationPrefix + "installButton"); } if(reason == "delete" && this.checked) this.toggleEnabled(); };
Отредактировано Andrey_Krropotkin (16-12-2019 12:52:47)
Отсутствует
CB_Editor_Toggle_on_Top
Эта та кнопка, которой я пользуюсь сам, и там много чего уже накопилось.
Всё собирался написать Infocatcher'у, но так и не собрался.
А теперь уже страшно даже и подумать, там целый трактат нужен,
и далеко не всё и обосновать бы смог.
Давай скопирую у себя из 73, но следует понимать, что это будет
гораздо хуже, чем если бы правил сам Автор, так что просто на всякий случай.
// https://forum.mozilla-russia.org/viewtopic.php?id=56040 // http://infocatcher.ucoz.net/js/cb/cbEditorToggleOnTop.js // https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Editor_Toggle_on_Top // Custom Buttons Editor: Toggle on Top button for Custom Buttons // (code for "initialization" section) // (c) Infocatcher 2012-2015 // version 0.1.11 - 2015-06-04 // Hotkey: Ctrl+T const watcherId = "customButtonsToggleOnTop_" + this.id; var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older var storage = (function() { if(!("Services" in window)) // Firefox 3.6 and older return Application.storage; // Simple replacement for Application.storage // See https://bugzilla.mozilla.org/show_bug.cgi?id=1090880 //var global = Components.utils.getGlobalForObject(Services); // Ensure, that we have global object (because window.Services may be overwritten) var global = Components.utils.import("resource://gre/modules/Services.jsm", {}); var ns = "_cbEditorToggleOnTopStorage"; // Note: Firefox 57+ returns NonSyntacticVariablesObject w/o .Object property var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null)); return { get: function(key, defaultVal) { if(key in storage) return storage[key]; return defaultVal; }, set: function(key, val) { if(key === null) delete storage[key]; else storage[key] = val; } }; })(); var watcher = storage.get(watcherId, null); if(!watcher) { watcher = { btnPos: 0, // 0 - at top right window corner, 1 - at end of tabs, 2 - before dialog buttons spacer btnStyle: "button", // "button" or "toolbarbutton" btnChecked: true, // use "checked" style: true or false // Fogue icons, http://p.yusukekamiyamane.com/ // http://www.iconfinder.com/icondetails/12276/16/gps_location_pin_icon icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjAx4SevkwAAAjxJREFUKM+FUk1oE0EU/mZnRU0U6cFLIT1KKkkNpIcSWwUvVty2l142hZpDoQcPgYQKQkUEz6V6sHgR/KGQQwleQg/xEKRRsIEmga4NoZa2oRGKpJaWmp2Z52UX12Dxg4838+D73ps3jzUYQycIgDc7l0oNAkgqpZ4n5uc/NgHsEEHDKZB/X5OJRGJcSplemJ6+6SZPFStvJ0Ta4eEhTNMcE0I8fDsxcQsAdPwHc6nUoBRCOz4+Rr1eR39//+18Pn/h/cjIGea+eS6VegLgKoBfRPRdKbXGOf8GIBmPx8cty0Kz2YTf74ff70cmkynpNSIAwP7+/t2ZmZnowcGBPDo6+rm1tdWo1Wo7pmnesSwLtm2jWCx+EkJwKSUnoln2wWnv9eTkMBE9TafT0b29PbTbbXDO0Wq1YNs2lpaWVpVSj8ZyuWXhaPg953CtXK6v9fVtr6ys9A4NDXU3Gg24JoVC4fPJycnjsVxu2TtMzgCUAVQAUKVSrwDtzc3NQCQS6d7d3XU7EK1W62twY+ML84i1N4zB5TvgYrVaPTs1NRXNZDKlrq4u+Hw+BAKBy1LK6zYA2xE+6/gqzhgbXl9fnw+FQrPBYPCHlBKjo6NRIjpPRD3kEdbcJWGMgTF2Y3Fx8UE4HE4yxl70WtYCEc1ms9kS51wDcM4r/FOS87BhGK90XTc0TeNuPgggFosNG4axOjAw8PJKxwIxAD26rt9XShWVUtvOWosO2h1RAJAMwCUAPseInGG6UTlm6l/8DZ+nFuAjSdH0AAAAAElFTkSuQmCC", iconPinned: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjIpwH1VRwAAAgdJREFUKM+FkkFoU0EQhr/dtxFSquLBS8CAICISQci1oBWLKaaINL300psXDxH0ItSD4L21UKgUxV4aWnKyiIGaQwio2IQ0PQQpBaEhoaEk0dSQNsnb9WAepg+LPwz/7DD/7O7MiJIQuGEAV3QIiNowV4f0HlA0BskJsI8fo75YLHIEjw3ccIInivVxX1IqcXFu7l4Lnhq4BaD4P4Y6INsHB+zlcpwfH7+zG48PavCIvj8/B64CRxoqXdg8Bd+BqG9xMVJNpaiUy5zxehkcGCC2uppV28Y44rs3V1aCFIt2p15v/MzlSj/S6eKlmZnRairFoW2zmUx+boHVBsvAtEj+fV5Iw4vbS0tBe2uLw04H2+ulUy5zaNu8X17OdOGZgkTX3TADCRumP0xNZY3fz6/9fSr5PNVGg6/x+BfdE/Y3wxJAHtj6wzt5aHsSiQv+4WFfbWcHu9VCeDzdRq32DdgQfZMQom9JjDGngcn19fWF2shI9vrYWNBqNjFCND8mk+8MTDq5s65RWUKIUKFQmA0EAtP3pawdra0xGokE0dqrwe8kvgS2e1cjpURKORyLxTaUUg+UUuciUvIWQq8hU5uYMPOQmQcuu7fAsqxr4XD4jVIqLKW0nPgV4AmEFiDzCF65hQLwK6Ueaq0/aa13e2vddVnHxV3AFsBZYKBXyPSa6bDuFdP/st/BfOD54p3eIQAAAABJRU5ErkJggg==", boxId: "cbToggleOnTopBox", btnId: "cbToggleOnTopButton", onTopAttr: "cbOnTop", naAttr: "cbOnTopNA", styleId: "cbToggleOnTopStyle", get btnTip() { var locale = (function() { if("Services" in window && "locale" in Services) { var locales = Services.locale.requestedLocales // Firefox 64+ || Services.locale.getRequestedLocales && Services.locale.getRequestedLocales(); if(locales) return locales[0]; } var prefs = "Services" in window && Services.prefs || Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); function pref(name, type) { return prefs.getPrefType(name) != prefs.PREF_INVALID ? prefs["get" + type + "Pref"](name) : undefined; } if(!pref("intl.locale.matchOS", "Bool")) { // Also see https://bugzilla.mozilla.org/show_bug.cgi?id=1414390 var locale = pref("general.useragent.locale", "Char"); if(locale && locale.substr(0, 9) != "chrome://") return locale; } return Components.classes["@mozilla.org/chrome/chrome-registry;1"] .getService(Components.interfaces.nsIXULChromeRegistry) .getSelectedLocale("global"); })().match(/^[a-z]*/)[0]; if(locale == "ru") return "Поверх всех окон (Ctrl+T)"; return "Always on top (Ctrl+T)"; }, REASON_STARTUP: 1, REASON_SHUTDOWN: 2, REASON_WINDOW_LOADED: 3, REASON_WINDOW_CLOSED: 4, get obs() { delete this.obs; return this.obs = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); }, get ww() { delete this.ww; return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); }, get wm() { delete this.wm; return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); }, init: function(reason) { this.obs.addObserver(this, "quit-application-granted", false); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.initWindow(ws.getNext(), reason); this.ww.registerNotification(this); }, destroy: function(reason) { this.obs.removeObserver(this, "quit-application-granted"); var ws = this.wm.getEnumerator(null); while(ws.hasMoreElements()) this.destroyWindow(ws.getNext(), reason); this.ww.unregisterNotification(this); }, checkOnTopAttr: function(window) { if(this.version < 71) return this.checkOnTopAttr = function() {} var attr = this.onTopAttr; var id = window.document.documentElement.id; var xs = Cu.import("resource://gre/modules/Services.jsm", {}) .Services.xulStore; (this.checkOnTopAttr = function(window) { var de = window.document.documentElement; if(de.hasAttribute(attr)) return; var url = window.location.href; if(xs.hasValue(url, id, attr)) de.setAttribute(attr, xs.getValue(url, id, attr)); })(window); }, initWindow: function(window, reason) { if(!this.isTargetWindow(window)) return; window.addEventListener("keypress", this, true); if(this.hasSizeModeChangeEvent) window.addEventListener("sizemodechange", this, false); else { window.addEventListener("resize", this, false); // Can detect only maximize/restore this.legacySizeModeChange(window); } this.checkOnTopAttr(window); var document = window.document; this.removeStyle(document); this.addStyle(document); var box = document.getElementById(this.boxId); box && box.parentNode.removeChild(box); box = document.createElementNS(xulns, "hbox"); box.id = this.boxId; var btn = document.createElementNS(xulns, this.btnStyle); btn.id = this.btnId; if(this.btnChecked) { btn.setAttribute("type", "checkbox"); btn.setAttribute("autoCheck", "false"); } btn.tooltipText = this.btnTip; btn.addEventListener("command", this, false); box.appendChild(btn); switch(this.btnPos) { default: box.setAttribute("cbOnTopFloat", "true"); document.documentElement.appendChild(box); break; case 1: box.setAttribute("align", "right"); let tabbox = document.getElementById("custombuttons-editbutton-tabbox"); let tabs = tabbox.getElementsByTagName("tabs")[0]; tabs.parentNode.insertBefore(box, tabs); box.style.marginBottom = -(btn.boxObject || btn.getBoundingClientRect()).height + "px"; break; case 2: box.setAttribute("align", "center"); let btnBox = document.documentElement.getButton("accept").parentNode; let insPos = btnBox.firstChild; for(let node = insPos; node; node = node.nextSibling) { if(node.localName == "spacer") { insPos = node; break; } } btnBox.insertBefore(box, insPos); } this.checkWindowStatus(window, box); //this.setOnTop(btn); }, destroyWindow: function(window, reason) { if(reason == this.REASON_WINDOW_CLOSED) window.removeEventListener("DOMContentLoaded", this, false); // Window can be closed before DOMContentLoaded if(!this.isTargetWindow(window)) return; window.removeEventListener("keypress", this, true); if(this.hasSizeModeChangeEvent) window.removeEventListener("sizemodechange", this, false); else window.removeEventListener("resize", this, false); var document = window.document; var btn = this.shadow(document).getElementById(this.btnId); btn.removeEventListener("command", this, false); if(reason == this.REASON_SHUTDOWN) { let box = btn.parentNode; box.parentNode.removeChild(box); this.removeStyle(document); let xulWin = this.getXulWin(window); xulWin.zLevel = xulWin.normalZ; } }, isTargetWindow: function(window) { return window.location.href.substr(0, 41) == "chrome://custombuttons/content/editor.xul"; }, observe: function(subject, topic, data) { if(topic == "quit-application-granted") this.destroy(); else if(topic == "domwindowopened") subject.addEventListener("DOMContentLoaded", this, false); else if(topic == "domwindowclosed") this.destroyWindow(subject, this.REASON_WINDOW_CLOSED); }, handleEvent: function(e) { var trg = e.originalTarget || e.target; var window; switch(e.type) { case "DOMContentLoaded": window = trg.defaultView; window.removeEventListener("DOMContentLoaded", this, false); this.initWindow(window, this.REASON_WINDOW_LOADED); break; case "keypress": if( !( (e.ctrlKey || e.metaKey) && !e.altKey && !e.shiftKey && String.fromCharCode(e.charCode).toLowerCase() == "t" ) ) break; e.preventDefault(); e.stopPropagation(); case "command": window = trg.ownerDocument.defaultView.top; this.toggleOnTop(window); break; case "sizemodechange": case "resize": window = trg; this.checkWindowStatus(window); } }, version: 0, get hasSizeModeChangeEvent() { var appinfo = "Services" in window && Services.appinfo; delete this.hasSizeModeChangeEvent; return this.hasSizeModeChangeEvent = appinfo && ( appinfo.name == "Pale Moon" || (this.version = parseFloat(appinfo.platformVersion)) >= 8 ); }, legacySizeModeChange: function(window) { var lastState = window.windowState; window.setInterval(function(window, _this) { var state = window.windowState; if(state != lastState) _this.checkWindowStatus(window); lastState = state; }, 150, window, this); }, checkWindowStatus: function(window, box) { if(!box) box = this.shadow(window.document).getElementById(this.boxId); var na = String(window.windowState != window.STATE_NORMAL); if(box.getAttribute(this.naAttr) == na) return; box.setAttribute(this.naAttr, na); //LOG("Set n/a: " + na); this.setOnTop(box.firstChild); }, addStyle: function(document) { var style = '\ %box%[cbOnTopFloat] {\n\ display: block !important;\n\ position: fixed !important;\n\ top: 0 !important;\n\ right: 0 !important;\n\ }\n\ %btn%, %btn% * {\n\ margin: 0 !important;\n\ padding: 0 !important;\n\ }\n\ %btn% > .button-box > .button-icon {\n\ margin: -3px -1px !important;\n\ }\n\ toolbarbutton%btn% {\n\ padding: 0 2px !important;\n\ }\n\ %btn% {\n\ position: relative !important;\n\ z-index: 2147483647 !important;\n\ list-style-image: url("%icon%") !important;\n\ -moz-user-focus: ignore !important;\n\ min-height: 0 !important;\n\ min-width: 0 !important;\n\ }\n\ %btn%[%onTopAttr%="true"] {\n\ list-style-image: url("%iconPinned%") !important;\n\ }\n\ %box%[%naAttr%="true"] image {\n\ opacity: 0.75 !important;\n\ }' .replace(/%box%/g, "#" + this.boxId) .replace(/%btn%/g, "#" + this.btnId) .replace(/%onTopAttr%/g, this.onTopAttr) .replace(/%icon%/g, this.icon) .replace(/%iconPinned%/g, this.iconPinned) .replace(/%naAttr%/g, this.naAttr); if(this.shadow(document, style)) return; document.insertBefore(document.createProcessingInstruction( "xml-stylesheet", 'id="' + this.styleId + '" href="' + "data:text/css," + encodeURIComponent(style) + '" type="text/css"' ), document.documentElement); }, removeStyle: function(document) { if(this.shadow(document, false)) return; var mark = 'id="' + this.styleId + '"'; for(var child = document.documentElement; child = child.previousSibling; ) { if( child.nodeType == child.PROCESSING_INSTRUCTION_NODE && child.data.substr(0, mark.length) == mark ) { document.removeChild(child); break; } } }, shadow: function(document, arg) { var sr = document.documentElement.shadowRoot; if(this.btnPos != 2 || !sr) return (this.shadow = function(document, arg) { return arg === undefined ? document : false; })(document, arg); if(arg === undefined) return sr; var st = sr.querySelector("style"), id = this.styleId; if(arg) { st.append(arg); st.lastChild[id] = true; } else { var tn = Array.from(st.childNodes).find(function(node) { return id in node; }); tn && tn.remove(); } return true; }, getXulWin: function(window) { return ( window.docShell && window.docShell instanceof Components.interfaces.nsIDocShell ? window.docShell : window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) .QueryInterface(Components.interfaces.nsIDocShellTreeItem) ) .treeOwner .QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIXULWindow || Ci.nsIAppWindow); }, setOnTop: function(btn, toggle) { var document = btn.ownerDocument; var root = document.documentElement; var onTop = root.getAttribute(this.onTopAttr) == "true"; if(toggle) { onTop = !onTop; root.setAttribute(this.onTopAttr, onTop); if(root.id) { if("persist" in document) document.persist(root.id, this.onTopAttr); else // Firefox 63+ Services.xulStore.persist(root, this.onTopAttr); } } else if(!onTop) // Just opened or restored window always have zLevel == normalZ return; var window = document.defaultView; var state = window.windowState; // Strange glitches with minimized "raisedZ" window... var restore = onTop && state == window.STATE_MINIMIZED; if(restore || state == window.STATE_NORMAL) { if(restore) onTop = false; let xulWin = this.getXulWin(window); xulWin.zLevel = onTop ? xulWin.raisedZ : xulWin.normalZ; //LOG("Set on top: " + onTop); } this.checkButton(btn, onTop); }, toggleOnTop: function(window) { this.setOnTop(this.shadow(window.document).getElementById(this.btnId), true); }, checkButton: function(btn, onTop) { btn.setAttribute(this.onTopAttr, onTop); this.btnChecked && btn.setAttribute("checked", onTop); } }; storage.set(watcherId, watcher); watcher.init(watcher.REASON_STARTUP); } function destructor(reason) { if(reason == "update" || reason == "delete") { watcher.destroy(watcher.REASON_SHUTDOWN); storage.set(watcherId, null); } } if( typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+ && addDestructor != ("addDestructor" in window && window.addDestructor) ) addDestructor(destructor, this); else this.onDestroy = destructor;
var ps = this.parseXULFromString(psXUL); if(isFrame && "parseFromSafeString" in window.DOMParser.prototype) ps = window.MozXULElement.parseXULToFragment(ps.outerHTML); document.documentElement.appendChild(ps);
//toolbar.insertBefore(button, this.button.nextSibling); toolbar.insertBefore( custombuttons.cbCloneNode(button), this.button.nextSibling );
Отсутствует
Dumby да все работает, спасибо.
Отсутствует
Dumby
О! Спасибо!
Начал потихоньку внедрять: https://github.com/Infocatcher/Custom_B … 6e8a3ad252, пока только Toggle on Top (и то частично).
Добавлено 19-12-2019 00:11:24
P.S. И отдельное спасибо за оживленный DOMi – попробовать успел, но не отписался...
Отредактировано Infocatcher (19-12-2019 00:11:24)
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Infocatcher
Насчёт Source Editor'а и Firefox 73.
В нелокализированной версии покромсали editMenuOverlay.dtd
и там вместо <popupset>'а образуется <parsererror>, длиннющий такой,
больше двадцати тысяч пикселей.
По всему коду лазать не стал, записал всё про контекстное меню
отдельным блоком в if, а существующее задвинул в else.
Получилось не очень, «Найти ещё раз» находит не следующее,
а перескакивает через одно.
var SourceEditor = window.SourceEditor; if(this.platformVersion >= 73) { var psXUL = '<popupset id="sourceEditorPopupset">\ <linkset>\ <html:link rel="localization" href="toolkit/global/textActions.ftl" />\ </linkset>\ <menupopup id="sourceEditorContext"\ onpopupshowing="popupHandler(event.target)"\ oncommand="popupHandler(event.target)">\ \ <menuitem id="menu_undo" data-l10n-id="text-action-undo" />\ <menuitem id="menu_redo" data-l10n-id="text-action-redo" />\ <menuseparator/>\ <menuitem id="menu_cut" data-l10n-id="text-action-cut" />\ <menuitem id="menu_copy" data-l10n-id="text-action-copy" />\ <menuitem id="menu_paste" data-l10n-id="text-action-paste" />\ <menuitem id="menu_delete" data-l10n-id="text-action-delete" />\ <menuseparator/>\ <menuitem id="menu_selectAll" data-l10n-id="text-action-select-all" />\ <menuseparator/>\ <menuitem id="menu_find" label="&findCmd.label;" accesskey="&findCmd.accesskey;" />\ <menuitem id="menu_findAgain" label="&findAgainCmd.label;" accesskey="&findAgainCmd.accesskey;" />\ <menuseparator/>\ <menuitem id="se-menu-gotoLine" label="&gotoLineCmd.label;"\ accesskey="&gotoLineCmd.accesskey;" key="key_gotoLine" />\ </menupopup>\ </popupset>'; var ps = window.MozXULElement.parseXULToFragment(psXUL, [ "chrome://global/locale/editMenuOverlay.dtd", "chrome://devtools/locale/sourceeditor.dtd" ]); if(!this.popupHandler) { var commands = {module: {}}; ps.querySelectorAll("menuitem").forEach(function(menuitem) { commands[menuitem.id] = menuitem.id.replace(/^(se-)?menu./, "cmd_"); }); Services.scriptloader.loadSubScript( "resource://devtools/client/shared/sourceeditor/editor-commands-controller.js", commands ); var createController = function(editor) { var res = editor.popupCmdController = commands.createController(editor); res.docShell = editor.container.contentWindow.docShell; return res; } var getObj = function(cmd, controller) { return controller.supportsCommand(cmd) ? controller: controller.docShell; } var setDisabled = function(menuitem) { var cmd = commands[menuitem.id]; menuitem.disabled = !getObj(cmd, this).isCommandEnabled(cmd); } this.popupHandler = function(node) { var ed = node.ownerDocument.activeElement.contentWindow.editor; var controller = ed.popupCmdController || createController(ed); var cmd = commands[node.id]; if(cmd) getObj(cmd, controller).doCommand(cmd); else node.menuitems.forEach(setDisabled, controller); } } var popup = ps.querySelector("menupopup"); popup.menuitems = popup.querySelectorAll("menuitem"); popup.popupHandler = this.popupHandler; } else { // See view-source:chrome://browser/content/devtools/scratchpad.xul // + view-source:chrome://browser/content/devtools/source-editor-overlay.xul var psXUL = (isCodeMirror // ....... } document.documentElement.appendChild(ps);
Лисе сделали XUL content type лоботомию, и теперь
документ CB-редактора это XMLDocument .
Скрипты не исполняются и кустомэлементщина окно игнорирует.
Приходится подгружать скриптлоадером вручную.
И вот здесь нарисовалась какая-то нестыковка.
При попытке открыть окно редактора, браузер зависает, грузит процессор,
и Windows дописывает ему в заголовок своё «(Не отвечает)».
Причём, если открыть во вкладке, то такого не происходит.
Так вот, если воспроизводится, хотел попросить добавить
что-нибудь для предотвращения.
Например, помогает даже нулевой таймаут
//initWindow: function(window, reason, isFrame) { initWindow: function(window, reason, isFrame, again) { if(this.isBrowserWindow(window)) { this.initBrowserWindow(window, reason); return; } if(!this.isEditorWindow(window)) return; if(!again && !isFrame && window.document.contentType == "text/xml") { window.setTimeout(function(_this) { _this.initWindow(window, reason, isFrame, true); }, 0, this); return; } _log("initWindow(): isFrame: " + isFrame);
Отсутствует
При попытке открыть окно редактора, браузер зависает, грузит процессор,
и Windows дописывает ему в заголовок своё «(Не отвечает)».
Причём, если открыть во вкладке, то такого не происходит.
Упс.
Я попробовал поиграться, еще помогает
if(!again && !isFrame && window.document.contentType == "text/xml") { // Firefox 73+ //window.setTimeout(function(_this) { // _this.initWindow(window, reason, isFrame, true); //}, 0, this); Services.tm.mainThread.dispatch(function() { this.initWindow(window, reason, isFrame, true); }.bind(this), Components.interfaces.nsIThread.DISPATCH_NORMAL); return; }
Но на глаз все равно есть задержка (хотя она и от самого CodeMirror'а).
И еще перестает зависать после замены "DOMContentLoaded" -> "load", так что я пока такую распорку воткнул.
А с меню надо бы еще посмотреть, куда и как доотламывать будут. Хотя, наверное, это еще локализации не обновились.
Меня особенно удивили постоянные перепиливания бедного Eyedropper'а без каких бы то ни было приростов в возможностях: https://github.com/Infocatcher/Custom_B … 1358-L1397 А вы, друзья, как ни садитесь...
Отредактировано Infocatcher (07-01-2020 23:09:15)
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Infocatcher на 72 обновил кнопки с вашей страницы:
1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label"); - document.getAnonymousElementByAttribute is not a function
2. Check for Addons Updates (использую в составе Toggle Restartless Add-ons) - постоянно крутится и выдает ошибку inProgress is null для btn.tooltipText = inProgress.getAttribute("value");
3. Source Editor - version 0.1.0a10 - 2019-12-25 работает нормально, но показывает ошибку - InvalidAccessError: A parameter or an operation is not supported by the underlying object для for(var j = 0, len = sheet.cssRules.length; j < len; ++j)
Отредактировано Andrey_Krropotkin (08-01-2020 19:08:53)
Отсутствует
3. Source Editor - version 0.1.0a10 - 2019-12-25 работает нормально, но показывает ошибку - InvalidAccessError: A parameter or an operation is not supported by the underlying object для for(var j = 0, len = sheet.cssRules.length; j < len; ++j)
Там таймаут надо поднимать.
Или вот, ещё вариант. Вроде работает (как идея).
css("resource://devtools/client/themes/variables.css"); css("resource://devtools/client/themes/common.css"); css("chrome://devtools/skin/tooltips.css"); if(this.platformVersion >= 68) { var eKey = "styleSheetChangeEventsEnabled"; var notVal = !document[eKey]; if(notVal) document[eKey] = true; document.addEventListener("StyleSheetApplicableStateChanged", function change(e) { var sheet = e.stylesheet; if(sheet.href != "resource://devtools/client/themes/common.css") return; document.removeEventListener(e.type, change); if(notVal && document[eKey]) document[eKey] = false; for(var i = 0, len = sheet.cssRules.length; i < len; ++i) if(sheet.cssRules[i].selectorText == "::selection") { sheet.deleteRule(i); break; } }); }
Отсутствует
1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label"); - document.getAnonymousElementByAttribute is not a function
Решаемая в этом месте кода проблема с тултипом,
наверно уже давно потеряла актуальность после этого:
Bug 1461798 - Migrate <tooltip> away from XBL
Может так
Отсутствует
1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label"); - document.getAnonymousElementByAttribute is not a function
(Долго я писал, да...)
Вроде, распорка эта больше и не требуется... отключил: https://github.com/Infocatcher/Custom_B … 63b9ee2fe2
2. Check for Addons Updates (использую в составе Toggle Restartless Add-ons) - постоянно крутится и выдает ошибку inProgress is null для btn.tooltipText = inProgress.getAttribute("value");
Там все печально, в браузере браузер (вкладочный) с браузером (с содержимым). Я начал делать, но все ссылки на элементы управления в менеджере дополнений отломались:
--- a/checkForAddonsUpdates.js +++ b/checkForAddonsUpdates.js @@ -169,8 +169,6 @@ } progressIcon.loading(); - var inProgress = $("updates-progress"); - btn.tooltipText = inProgress.getAttribute("value"); var origIcon = tab.image; tab.image = image; @@ -180,12 +178,36 @@ if(!updEnabled) Services.prefs.setBoolPref(updEnabledPref, true); - var notFound = $("updates-noneFound"); - var updated = $("updates-installed"); + var fu = $("cmd_findAllUpdates"); + if(!fu) { // Firefox 72+ + var win = doc.defaultView; + var vb = doc.getElementById("html-view-browser"); + if(!vb) { + win.setTimeout(processAddonsTab, 20, win); + return; + } + var vbDoc = vb.contentDocument; + fu = vbDoc.querySelector('[action="check-for-updates"]'); + } + + var notFound = $("updates-noneFound"); //~fixme + var updated = $("updates-installed"); //~fixme // Avoid getting false results from the past update check (may not be required for "noneFound") - notFound.hidden = updated.hidden = true; + //~fixme notFound.hidden = updated.hidden = true; - $("cmd_findAllUpdates").doCommand(); + //fu.doCommand(); + fu.click(); + + var inProgress = $("updates-progress"); + if(!inProgress) { // Firefox 72+ + var um = vbDoc.getElementById("updates-message"); + inProgress = um.shadowRoot.querySelector('[data-l10n-id="addon-updates-updating"]'); + if(!inProgress) { + win.setTimeout(processAddonsTab, 5, win); + return; + } + } + btn.tooltipText = inProgress.getAttribute("value") || inProgress.textContent; var waitTimer = setInterval(function() { if(!doc.defaultView || doc.defaultView.closed) {
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Или вот, ещё вариант. Вроде работает (как идея).
работает без ошибок
Вроде, распорка эта больше и не требуется
работает без ошибок
Какой-то неработающий черновик
если сначала нажимаю на кнопку выдает ошибки
1. TypeError: el.closest(...) is null дляview-source:chrome://mozapps/content/extensions/aboutaddons.js в строке
function getTelemetryViewName(el) {
return el.closest("[current-view]").getAttribute("current-view");
}
2. TypeError: autoUpdate is nul для var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"; и зацикливается
При перезагрузке открывается вкладка Управление дополнениями - доступные обновления
Если сначала открываю вкладку Управление дополнениями и затем нажимаю на кнопку, то находит обновления, но все равно кнопка и вкладка зацикливаются и выдают только вторую ошибку, если после этого закрываю вкладку, то выскакивает сообщение "Tab with addon-manager was closed" и тогда кнопка перестает работать
Не знаю поможет Вам или нет - вот на www.camp-firefox.de нашел работающий кусок кода
let frameScript = function() { addEventListener('pageshow', function onPageshow(event) { let document = event.target; if (document.URL != 'about:addons') return; removeEventListener('pageshow', onPageshow); content.setTimeout(function() { content.getHtmlBrowser().contentDocument.querySelector('[action="check-for-updates"]').click(); let item = document.getElementById('category-availableUpdates'); item.click(); let categories = item.parentNode; categories.addEventListener('mousedown', function onMousedown(event) { if (event.target != item && event.target.parentNode != item) { item.hidden = true; categories.removeEventListener('mousedown', onMousedown); }; }); }, 0); }); }; let frameScriptURI = 'data:,(' + frameScript.toString() + ')()'; let window = event.target.ownerGlobal; window.openTrustedLinkIn('about:addons', 'tab'); window.gBrowser.selectedBrowser.messageManager.loadFrameScript(frameScriptURI, true);
Отредактировано Andrey_Krropotkin (09-01-2020 14:40:20)
Отсутствует
Хочу добавить про Toggle on Top, раз обсуждалось.
Это для btnPos: 1 (at end of tabs).
В Firefox 72 переписали использование [align="right"],
и кнопка съехала в противоположную сторону, влево.
Причём, переписали неправильно, затем исправили,
и даже в бету пришлось вмешиваться.
Таким образом, наверно так
case 1: //box.setAttribute("align", "right"); if(this.platformVersion >= 72) box.setAttribute("pack", "end"); else box.setAttribute("align", "right");
Отсутствует
Хочу добавить про Toggle on Top, раз обсуждалось.
Это для btnPos: 1 (at end of tabs).В Firefox 72 переписали использование [align="right"],
и кнопка съехала в противоположную сторону, влево.
Спасибо! Обновил.
У меня причем ощущение, что я проверял (на какой-то бэта-версии) – и было нормально.
Причём, переписали неправильно, затем исправили,
и даже в бету пришлось вмешиваться.
Чик-чик – и в продакшн! ©
Добавлено 12-01-2020 23:35:00
А в 73.0b3 новая напасть: упорно лезет нулевая высота у свежедобавленной кнопки даже после волшебного пинка таймаута:
tabs.parentNode.insertBefore(box, tabs); LOG("xxx " + window.getComputedStyle(btn, null).height); LOG("xxx " + btn.getBoundingClientRect().height); setTimeout(function() { LOG("xxx " + window.getComputedStyle(btn, null).height); LOG("xxx " + btn.getBoundingClientRect().height); }, 5); box.style.marginBottom = -(btn.boxObject || btn.getBoundingClientRect()).height + "px";
Отредактировано Infocatcher (12-01-2020 23:35:00)
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует