- Внесение изменений в редактор кода
- Воспользуемся встроенным в браузер редактором JavaScript
- Получение и изменение кода кнопки
- Привязка редактора к кнопке
- Редактирование букмарклета с помощью встроенного редактора
- Использование внешнего редактора для редактирования кода.
Внесение изменений в редактор кода
Редактор кода в CustomButtons, мягко говоря, оставляет желать лучшего. По большому счету для более-менее сложного кода его лучше вообще не использовать, а вместо этого воспользоваться функцией редактирования во внешнем редакторе. Тем не менее иногда его использовать проще, в силу того, что он встроен в расширение и легко запускается, при этом можно сохранить код (Ctrl+s) и тут же пользоваться им. Поэтому было бы неплохо иметь возможность дополнить его каким-то своими элементами также как мы можем это делать в главном окне браузера. Но как получить доступ к DOM другого окна?
Для решения этой задачи мы создадим наблюдателя, который будет следить за появлением и сокрытием XUL-окон xul-window-visible. Кроме того нам нужно будет найти открытые окна, для этого понадобится nsIWindowMediator, пример перебора окна можно посмотреть здесь. Для того, чтобы найти именно то окно, которое надо, необходимо знать о нем что-то что можно будет проверить у всех окон при переборе. Я уже сделал предварительную работу и код окна получил, выглядит он так
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://custombuttons/content/codeeditor.css" type="text/css"?>
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="custombuttonsEditor"
title="Редактирование кнопки: modal dialog example"
persist="width height screenX screenY sizemode"
buttons="extra1,extra2,accept,cancel"
buttonlabelextra1="Внешний редактор…"
buttonlabelextra2="Сохранить"
ondialogextra1="edit_button()"
ondialogextra2="editor.updateButton()"
buttonaccesskeyextra1="E"
onload="editor.init();"
ondialogaccept="return editor.onAccept();"
ondialogcancel="return editor.onCancel();"
onbeforeunload="editor.destroy()"
onunload="editor.destroy();"
screenX="0"
screenY="0"
width="450"
height="450"
sizemode="maximized"
chromehidden="menubar toolbar location directories status extrachrome "
defaultButton="accept">
<script type="application/x-javascript"
src="editor2.js"/>
<script type="application/x-javascript"
src="editExternal.js"/>
<commandset id="custombuttonsEditorCommandSet">
<command id="cbUpdateButtonCommand"
oncommand="editor.updateButton()"/>
<command id="cbExecuteCode"
oncommand="editor.execute_oncommand_code()"/>
<command id="cbFullScreen"
oncommand="editor.fullScreen()"/>
<command id="cbCloseDialog"
oncommand="editor.acceptDialog()"/>
</commandset>
<keyset id="custombuttonsEditorKeySet">
<key id="cbUpdateButtonKey"
command="cbUpdateButtonCommand"
key="s"
modifiers="control"/>
<key id="cbExecuteCode"
command="cbExecuteCode"
keycode="VK_F9"/>
<key id="cbFullScreen"
command="cbFullScreen"
keycode="VK_F11"/>
<key id="cbCloseDialog"
command="cbCloseDialog"
keycode="VK_RETURN"
modifiers="control"/>
</keyset>
<grid id="urlfield">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row align="center">
<label value="URL
кнопки:" accesskey="U"
control="urlfield-textbox"/>
<textbox id="urlfield-textbox"
flex="1"/>
</row>
</rows>
</grid>
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row align="center">
<label value="Имя:"
tooltiptext="Задаёт надпись на кнопке и всплывающую подсказку"
accesskey="N"
control="name"/>
<textbox id="name"
flex="1"
focused="true"/>
</row>
<row align="center">
<label value="Изображение:"
accesskey="m"
control="image"/>
<hbox>
<menulist id="image"
class="menulist-iconic"
flex="1"
onselect="editor.imageChanged()"
sizetopopup="pref"
value="custombuttons-stdicon-1"
src="">
<menupopup>
<menuitem value="custombuttons-stdicon-1"/>
<menuitem value="custombuttons-stdicon-2"/>
<menuitem value="custombuttons-stdicon-3"/>
<menuitem value="custombuttons-stdicon-4"/>
</menupopup>
</menulist>
<button label="Обзор…"
accesskey="o"
oncommand="editor.selectImage();"/>
<button label="в‡’
base64" accesskey="b"
oncommand="editor.convert_image();"
tooltiptext="Преобразовать изображение в формат base64 (data:image/png;base64,...)"/>
</hbox>
</row>
</rows>
</grid>
<tabbox flex="1"
id="custombuttons-editbutton-tabbox"
handleCtrlTab="true"
handleCtrlPageUpDown="true"
persist="selectedIndex"
selectedIndex="0">
<tabs oncommand="editor.tabSelect(event);"
orient="horizontal"
value="">
<tab id="code-tab"
accesskey="C"
cbcontrol="code"
label="Код"
tooltiptext="Выполняется при нажатии на кнопку"
first-tab="true"
selected="true"
visuallyselected="true"/>
<tab id="init-tab"
label="Инициализация"
accesskey="I"
cbcontrol="initCode"
tooltiptext="Выполняется при открытии окна приложения"
afterselected="true"/>
<tab label="Справка"
accesskey="H"
cbcontrol="help"
tooltiptext="Справка"/>
<tab label="Настройки
кнопки" accesskey="s"
tooltiptext="Панель настроек кнопки"
last-tab="true"/>
</tabs>
<tabpanels flex="1"
selectedIndex="0">
<cbeditor class="custombuttons-editor-codeBox"
id="code"
multiline="true"
flex="1"
onclick="gmon_edit_mouseclick(event);"
value="/*CODE*/"/>
<cbeditor class="custombuttons-editor-codeBox"
id="initCode"
multiline="true"
flex="1"
onclick="gmon_edit_mouseclick(event);"
value="/*Initialization Code*/"/>
<cbeditor class="custombuttons-editor-codeBox"
id="help"
multiline="true"
flex="1"
onclick="gmon_edit_mouseclick(event);"
value=""/>
<vbox>
<groupbox flex="1">
<caption label=""Горячая"
клавиша"/>
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row align="center">
<label value="Сочетание
клавиш:"/>
<textbox id="accelkey"
flex="1"/>
</row>
</rows>
</grid>
<checkbox id="disableDefaultKeyBehavior"
label="Запретить стандартное действие для указанного сочетания клавиш"/>
</groupbox>
</vbox>
</tabpanels>
</tabbox>
</dialog>
Код javascript | Выделить |
var topicV = "xul-window-visible";
var observer =
{
observe: function (subject, topic, data)
{
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var enumerator = wm.getEnumerator(null);
while (enumerator.hasMoreElements())
{
var win = enumerator.getNext();
if (win.document.documentElement.getAttribute("id")
== "custombuttonsEditor")
{
var btn = win.document.querySelector("#myButton");
if (!btn)
{
btn = win.document.createElement("button");
win.document.documentElement.appendChild(btn);
btn.setAttribute("label", "Моя
кнопка");
btn.id = "myButton";
btn.onclick = function ()
{
win.alert("Та вообще все мое.");
}
}
}
}
this.unregister()
},
register: function ()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, topicV, false);
},
unregister: function ()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, topicV);
}
}
observer.register();
Воспользуемся встроенным в браузер редактором JavaScript
Редактор JavaScript Scratchpad - достаточно неплохой, с точки зрения функциональности, редактор. В нем есть подсветка синтаксиса, автозавершение, всплывающие справочные сведения, возможность немедленного выполнения кода, а так же(что особенно важно для рассматриваемой темы) возможность выполнения chrome-кода в контексте браузера. Кроме того можно загружать, сохранять код и еще куча мелких приятностей. В этом смысле он намного лучше встроенного редактора CustomButtons, а потому "оседлать" этот редактор было бы совсем недурственно.
Редактор можно открыть в главном меню браузера Веб-разработка > Простой редактор JavaScript или сочетанием клавиш Shift+F4. Следует заметить, что по-умолчанию запуск кода в контексте браузера недоступен, а для того, чтобы сделать его доступным надо открыть инструменты разработки Веб-разработка > Инструменты разработки (или Ctrl+Shift+I), в верхнем правом углу панели разработки клацнуть иконку с шестеренкой, чтобы открыть настройки инструментов, в разделе Дополнительные параметры настроек надо установить флажок на пункте Включить инструменты отладки browser chrome и дополнений. Если после этого вызвать редактор, то в его главном меню появится меню Окружение, в котором можно будет выбрать контекст исполнения кода. Функции автозавершения при выборе режима Браузер, к сожалению, останутся такими как и были и подсказки будут выдаваться такие же, но выполняться будет chrome-код в контексте окна браузера. Более подробное описание редактора можно найти здесь
Черновик - Инструменты разработчика Firefox | MDN
Теперь начнем подбираться к самому редактору. Сначала мы скопируем код пункта меню, вызывающего редактор.
Код javascript | Выделить |
gClipboard.write(new XMLSerializer().serializeToString(document.querySelector("menuitem[label^='Простой
редактор']")));
Код javascript | Выделить |
Scratchpad.openScratchpad();
Код xml | Выделить |
<script type="application/javascript"
src="chrome://devtools/content/scratchpad/scratchpad.js"/>
Как можно запустить окно редактора и получить ссылку на объект этого окна - мы выяснили, но это не совсем то, что нам надо. Если просмотреть код функции, вызывающей редактор, ну например так
Код javascript | Выделить |
alert(Scratchpad.openScratchpad);
Код javascript | Выделить |
function SP_openScratchpad()
{
return this.ScratchpadManager.openScratchpad();
}
Код javascript | Выделить |
function SPM_openScratchpad(aState)
{
"use strict";
let params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
.createInstance(Ci.nsIDialogParamBlock);
params.SetNumberStrings(2);
params.SetString(0, this.createUid());
if (aState)
{
if (typeof
aState != 'object')
{
return;
}
params.SetString(1, JSON.stringify(aState));
}
let win = Services.ww.openWindow(null,
SCRATCHPAD_WINDOW_URL, "_blank",
SCRATCHPAD_WINDOW_FEATURES, params);
// Only add the shutdown observer if we've opened a scratchpad window.
ShutdownObserver.init();
return win;
}
text - это код, который будет находиться в открытом окне для редактирования.
filename - этот параметр можно задать вместо предыдущего, если нужно, чтобы редактировался файл.
saved - если он имеет значение true, значит редактор можно будет закрыть, ничего не меняя, и при этом он не будет запрашивать сохранение. В случае, если параметр не задан, имеет значение false или если код был изменен, но не сохранен, при закрытии будет появляться окно с запросом на сохранение.
executionContext - по умолчанию имеет значение 1 и это означает, что исполнение кода будет производиться в контексте документа, если задать значение 2, код будет выполняться в контексте браузера.
Таким образом мы имеем следующий код запуска редактора
Код javascript | Выделить |
var aState = { text: "/* Здесь будет код кнопки, которую мы собираемся редактировать. */", executionContext: 2, saved: true,
};
var win = Scratchpad.ScratchpadManager.openScratchpad(aState);
Получение и изменение кода кнопки
Для того, чтобы понять, где расположен код кнопки, надо ее более внимательно изучить. У нас есть два инструмента для сериализации кнопки: XMLSerializer и JSON. Первый позволяет получить XML-код кнопки, с помощью второго можно сериализовать кнопку как JavaScript-объект. Изучив все это "хозяйство" можно прийти к трем способам получения кода кнопки.
Код javascript | Выделить |
code = this.cbCommand;
Код javascript | Выделить |
code = this.parameters.code;
Код javascript | Выделить |
code = this.getAttribute("cb-oncommand");
Код javascript | Выделить |
init = this.cbInitCode;
Код javascript | Выделить |
init = this.paraneters.initCode;
Код javascript | Выделить |
init = this.getAttribute("cb-init");
С сохранением, к сожалению все не так просто. Точнее можно задать атрибуту cb-oncommand значение в виде кода, и это даже будет работать, но при открытии окна редактирования эти изменения там не отобразятся, а при перезапуске браузера и вовсе будут потеряны. Изучение исходников CustomButtons привело меня к следующему коду
Код javascript | Выделить |
this.setAttribute("cb-oncommand", this.cbCommand +
"\n\n/* Добавленный код */\n\nalert('Текст сообщения добавленного кода');");
this.name = "Новое
название";
custombuttons.cbService.parseButtonURI(this);
var link = "custombutton://buttons/Firefox/update/" + this.id;
custombuttons.cbService.updateButton(link, this.URI);
Привязка редактора к кнопке
Привязка, это, конечно, громко сказано, но всеми предварительными знаниями для использования Scratchpad'а для редактирования кнопки мы уже обладаем, осталось только собрать все воедино.
Вызов редактора лучше всего осуществлять через контекстное меню. Если мы посмотрим код любой кнопки, то там есть атрибут context, его значение(custombuttons-contextpopup) - это и есть id контекстного меню кнопок. Туда мы и будем добавлять элементы.
Создадим оверлей следующего содержания
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
function cbCtxMenu_editWithScratchpad(mitem)
{
var srcObj = document.querySelector("#custombuttons-contextpopup").triggerNode;
var code, codeType;
switch (mitem.getAttribute("id"))
{
case "editCommandWIthScratchpad":
code = srcObj.getAttribute("cb-oncommand");
codeType = "command";
break;
case "editInitWIthScratchpad":
code = srcObj.getAttribute("cb-init")
codeType = "init";
break;
default:
}
var aState = { text: code, executionContext: 2, saved: true
};
var win = Scratchpad.ScratchpadManager.openScratchpad(aState);
var spObserver =
{
onReady: function (sp)
{
sp._initialWindowTitle = "Редактирование кода
" + (codeType == "command" ? "команды"
: "инициализации") + "
кнопки: " + srcObj.name;
sp._updateTitle();
sp.removeObserver(spObserver);
}
}
win.onload = function (evt)
{
win.Scratchpad.addObserver(spObserver);
var toolbar = win.document.querySelector("#sp-toolbar");
var saveBtn = win.document.createElement("toolbarbutton");
saveBtn.setAttribute("label", "Сохранить
код кнопки");
toolbar.appendChild(saveBtn);
saveBtn.onclick = function ()
{
srcObj.setAttribute(codeType == "command" ? "cb-oncommand"
: "cb-init", win.Scratchpad.getText());
custombuttons.cbService.parseButtonURI(srcObj)
var link = "custombutton://buttons/Firefox/update/"
+ srcObj.id;
custombuttons.cbService.updateButton(link, srcObj.URI);
win.Scratchpad.dirty = false;
}
}
}
]]>
</script>
<menupopup id="custombuttons-contextpopup">
<menu label="Scratchpad"
id="scratchpadMenu"
position="1">
<menupopup>
<menuitem label="Редактировать
код команды"
id="editCommandWIthScratchpad"
onclick="cbCtxMenu_editWithScratchpad(this);"/>
<menuitem label="Редактировать
код инициализации"
id="editInitWIthScratchpad"
onclick="cbCtxMenu_editWithScratchpad(this);"/>
</menupopup>
</menu>
</menupopup>
</overlay>
Несколько пояснений по коду. В общем и целом код достаточно прост, а большинство мест, которые могли бы вызывать вопросы, я уже объяснил ранее. Самым непонятным здесь, по всей видимости, является использование объекта Scratchpad. В контексте окна редактора это не такой же самый объект, что и одноименный объект в контексте главного окна. Полный код этого объекта можно посмотреть, перейдя в файрфоксе по адресу
Естественно, интерфейс редактора можно обогатить с помощью оверлея и напичкать туда всего, чего душа пожелает, думаю, вопроса "как это сделать?" здесь возникнуть не должно.
Редактирование букмарклета с помощью встроенного редактора
В принципе применить описанное ранее к букмарклетам - совсем несложно, тем не менее небольшой пример не помешает. Я взял оверлей, использовавшийся ранее для копирования закладки как BB-код, добавил в него модифицированный код из предыдущего примера, а также фильтр по протоколам. Для фильтрации я использовал дополнительный атрибут data-protocols, в его значение можно заносить через запятую список протоколов адресов, для которых элемент будет виден. Формат значения такой: "http:,https:,javascript:" . Элементы, для которых задан этот атрибут будут видны только для соответствующих закладок, остальные - будут видны везде.
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
function copyAsBB(src)
{
var node = src.parentElement.triggerNode;
var pnode = node._placesNode;
gClipboard.write("[URL=\"" + pnode.uri + "\"]"
+ pnode.title + "[/URL]");
}
function placesCtxMenu_editWithScratchpad(placesNode)
{
var code = decodeURIComponent(placesNode.uri.substring("javascript:".length));
var aState = { text: code, saved: true
};
var win = Scratchpad.ScratchpadManager.openScratchpad(aState);
var spObserver =
{
onReady: function (sp)
{
sp._initialWindowTitle = "Редактирование кода
букмарклета: " + placesNode.title;
sp._updateTitle();
sp.removeObserver(spObserver);
}
}
win.onload = function (evt)
{
win.Scratchpad.addObserver(spObserver);
var toolbar = win.document.querySelector("#sp-toolbar");
var saveBtn = win.document.createElement("toolbarbutton");
saveBtn.setAttribute("label", "Сохранить
код букмарклета");
toolbar.appendChild(saveBtn);
saveBtn.onclick = function ()
{
var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var newUri = ios.newURI("javascript:"
+ encodeURIComponent(win.Scratchpad.getText()), null, null);
bmsvc.changeBookmarkURI(placesNode.itemId, newUri);
win.Scratchpad.dirty = false;
}
}
}
function editBookmarklet(src)
{
var pn = document.querySelector("#placesContext").triggerNode._placesNode;
placesCtxMenu_editWithScratchpad(pn);
}
function placesContext_onpopupshowing(src)
{
var pn = src.triggerNode._placesNode;
var mitems = src.querySelectorAll("menuitem");
for(mi of
mitems)
{
var dprotocol = mi.getAttribute("data-protocols");
if (dprotocol && dprotocol.length > 0)
{
var protocols = dprotocol.split(",");
var contains = (protocols.indexOf(pn.uri.split(":")[0]
+ ":") > -1);
mi.setAttribute("hidden", !contains);
} else mi.setAttribute("hidden",
false);
}
}
]]>
</script>
<menupopup id="placesContext"
onpopupshowing="placesContext_onpopupshowing(this);"
data-del="editBookmarkletWithScratchpad">
<menuitem label="Копировать
как BB-код"
onclick="copyAsBB(this);"
id="copyAsBB"/>
<menuitem label="Редактировать
букмарклет" id="editBookmarklet"
onclick="editBookmarklet(this);"
data-protocols="javascript:"/>
</menupopup>
</overlay>
Как и в прошлый раз, на тулбаре редактора появится новая кнопка для сохранения результата(обновления кода закладки).
Использование внешнего редактора для редактирования кода.
Несмотря на то, что Scratchpad - неплохой редактор и его вполне можно использовать для написания и редактирования скриптов, да еще и есть возможность дополнить собственными инструментами, тем не менее, сравнивать его с профессиональными инструментами разработки, я думаю не стоит. Поэтому есть смысл рассмотреть возможность редактирования кода во внешнем редакторе. Посмотрим, что можно сделать для этого на примере редактирования букмарклета(уж коль скоро возможность редактировать кнопку во внешнем редакторе хоть реализована и не очень хорошо, но все-таки она есть и пользоваться ей вполне можно).
Проблема реализации этого замысла, в отличие от использования Scratchpad, состоит в том, что последний является частью браузера и может быть запущен в том же процессе, а это означает, что мы вполне можем добавить туда дополнительные элементы интерфейса, подписаться на события и использовать другие инструменты обмена данными между объектами одного приложения. С внешним приложением все иначе, однако проблема эта вполне решаема.
Для реализации нашей задачи, код букмарклета мы просто будем сохранять во временный файл, далее откроем этот файл во внешнем приложении, а когда приложение сохранит файл, при переходе к окну браузера, мы прочитаем содержимое временного файла и обновим им код букмарклета. Нам нужно позаботиться о том, чтобы для букмарклета существовал режим редактирования, в котором и будет происходить отслеживание изменений. В примере ниже, я создал специальную панель, которая содержит сведения о редактируемой закладке (id и title), адрес временного файла, кнопку для поиска внешнего приложения и кнопку для запуска. Когда панель появляется, в систему уведомлений добавляется наблюдатель топика xul-window-visible, который будет обновлять код букмарклета кодом временного файла. При сокрытии панели - наблюдателя убираем из системы уведомлений. Панель вызывается из контекстного меню букмарклета, при этом получает все данные о нужной закладке, после запуска внешнего приложения и сохранения изменений можно переходить к окну браузера и клацать по закладке.
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
for(var old of
document.querySelectorAll("[data-del = 'x01']")) old.parentElement.removeChild(old);
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function placesContext_onpopupshowing(src)
{
var pn = src.triggerNode._placesNode;
var mitems = src.querySelectorAll("menuitem");
for(mi of
mitems)
{
var dprotocol = mi.getAttribute("data-protocols");
if (dprotocol && dprotocol.length > 0)
{
var protocols = dprotocol.split(",");
var contains = (protocols.indexOf(pn.uri.split(":")[0]
+ ":") > -1);
mi.setAttribute("hidden", !contains);
} else mi.setAttribute("hidden",
false);
}
}
function bookmarkletEditPanelSaveChanges()
{
var panel = document.querySelector("#bookmarkletEditPanel");
var file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(panel.tempFile);
var data = "";
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Components.interfaces.nsIConverterInputStream);
fstream.init(file, -1, 0, 0);
cstream.init(fstream, "UTF-8", 0, 0); // you can use another encoding here if you wish
let str = {}
{
let read = 0;
do
{
read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
data += str.value;
} while (read != 0);
}
cstream.close(); // this closes fstream
var uriTxt = "javascript:"
+ encodeURIComponent(data);
var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var newUri = ios.newURI(uriTxt, null,
null);
bmsvc.changeBookmarkURI(panel.itemId, newUri);
}
function editBookmarkletWithExternalEditor()
{
var panel = document.querySelector("#bookmarkletEditPanel");
var idLabel = document.querySelector("#labelBookmarkId");
var titleLabel = document.querySelector("#labelBookmarkTitle");
var pathTextbox = document.querySelector("#textboxExternalEditorPath");
var pn = document.querySelector("#placesContext").triggerNode._placesNode;
idLabel.textContent = pn.itemId;
var file = FileUtils.getFile("TmpD",
["bookmarkletForEdit.js"]);
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
var ostream = FileUtils.openSafeFileOutputStream(file);
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var istream = converter.convertToInputStream(decodeURIComponent(pn.uri.substring("javascript:".length)));
NetUtil.asyncCopy(istream, ostream, function (status)
{
if (!Components.isSuccessCode(status))
{
// Handle error!
return;
}
});
panel.tempFile = file.path;
panel.itemId = pn.itemId;
idLabel.textContent = pn.itemId;
titleLabel.textContent = pn.title;
panel.openPopupAtScreen(200, 200, false)
}
function buttonExternalEditorFilePicker_click()
{
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Выбор внешнего
редактора", nsIFilePicker.modeOpen);
fp.appendFilter("Исполняемые файлы",
"*.exe");
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace)
{
var file = fp.file;
var path = fp.file.path;
document.querySelector("#textboxExternalEditorPath").value = path;
}
}
function buttonRunExternalEditor_click()
{
var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(document.querySelector("#textboxExternalEditorPath").value);
var process = Components.classes["@mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(file);
var args = [document.querySelector("#bookmarkletEditPanel").tempFile];
process.run(false, args, args.length);
}
var editBookmarkletObserver = {
observe: function (subject, topic, data)
{
bookmarkletEditPanelSaveChanges();
},
register: function ()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "xul-window-visible",
false);
},
unregister: function ()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "xul-window-visible");
}
};
]]>
</script>
<menupopup id="placesContext"
onpopuphiding="placesContext_onpopupshowing();">
<menuitem label="Редактировать
букмарклет во внешнем
редакторе" data-del="x01"
data-protocols="javascript:"
id="editWithExternalEditor"
onclick="editBookmarkletWithExternalEditor();"/>
</menupopup>
<popupset id="mainPopupSet">
<panel id="bookmarkletEditPanel"
onpopupshowing="editBookmarkletObserver.register()"
onpopuphiding="editBookmarkletObserver.unregister()"
noautohide="true"
titlebar="normal"
data-del="x01"
type="drag"
backdrag="true"
label="Редактирование
букмарклета" close="true">
<html:table xmlns:html="http://www.w3.org/1999/xhtml"
border="1"
cellpadding="0"
cellspacing="0">
<html:tr>
<html:td>
<label>ID</label>
</html:td>
<html:td>
<label id="labelBookmarkId"/>
</html:td>
</html:tr>
<html:tr>
<html:td>
<label>Название</label>
</html:td>
<html:td>
<label id="labelBookmarkTitle"></label>
</html:td>
</html:tr>
</html:table>
<vbox>
<hbox>
<textbox id="textboxExternalEditorPath"
width="300"/>
<button label="Обзор..."
id="buttonExternalEditorFilePicker"
onclick="buttonExternalEditorFilePicker_click();"/>
</hbox>
<hbox>
<button label="Запустить"
id="buttonRunExternalEditor"
onclick="buttonRunExternalEditor_click();"/>
</hbox>
</vbox>
</panel>
</popupset>
</overlay>
Естественно, в реальном примере стоило бы позаботиться о каких-то дополнительных вещах, как то: удаление временного файла при выходе из режима редактирования или вставка пути ко внешнему редактору CustomButtons при первоначальной инициализации панели. Но и без этих нюаносов пример достаточно показателен.
Комментариев нет :
Отправить комментарий