- Создаем инструменты
- Структура XML-документа
- Как все описать, чтобы не запутаться
- И что же теперь со всем этим делать?
- Несколько слов о коде кнопок
Создаем инструменты
В предыдущей статье я уже описал, как создается кнопка меню, в принципе к структуре кода добавить нечего. Но тут проблема в том, что создание XML-дерева в программном коде - процесс малоприятный и как правило результат содержит массу ошибок, по крайней мере если дерево имеет более-менее сложную структуру и достаточно много элементов. Естественно описывать подобные структуры с помощью XML - намного удобнее, но посредством имеющихся у нас возможностей просто так взять и запихнуть XML-код в нашу кнопку не получится, а значит надо придумать что-то другое. Я эту проблему решил следующим образом: создал простой XML-формат, в котором содержится код, а также XUL-структура кнопки. Кроме того написал код для кнопки-транслятора, которая загружает готовый XML-документ, генерирует из него JavaScript-код инициализации новой кнопки и вставляет его в буфер обмена. Дальше просто создаем новую кнопку, в окне редактирования открываем вкладку Initialization и вставляем туда код из буфера. Задаем кнопке имя, иконку, сохраняем, добавляем на панель с кнопками и можно пользоваться.
Теперь по порядку.
Структура XML-документа
Корневой элемент menu-button, в него вложены три элемента code-before, button и code-after. Первый будет содержать JavaScript-код, предшествующий инициализации кнопки, содержимое второго будет повторять структуру содержимого кнопки и из него будет генерироваться JavaScript-код инициализации меню. В последнем будет код, расположенный после инициализации, в нем надо размещать подписки на события элементов меню.
Пример документа
| Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<menu-button xmlns="urn:diadiavova-schemas:menu-button"
>
<code-before>
<![CDATA[
function ael(id, evtName, handler){document.getElementById(id).addEventListener(evtName, handler, false);};
function aeln(name, clickhandler){thisbutton.querySelector('*[name="' + name + '"]').addEventListener("click", clickhandler, false);};
function aeldi(di, clickhandler){thisbutton.querySelector('*[data-id="' + di + '"]').addEventListener("click", clickhandler, false);}
]]>
</code-before>
<button>
<menupopup>
<menuitem id="mi1"
label="Cyberforum"
tooltiptext="Открывает
главную страницу киберфорума"
image="http://www.cyberforum.ru/favicon.ico"/>
<menuitem label="Пункт
меню с флажком"
autocheck="true"
type="checkbox"
checked="true"
data-id="check-item"/>
<menuitem label="Что
с флажком?"
data-id="check-query"/>
<menu label="Светофор">
<menupopup>
<menuitem label="Красный"
style="color:red;"
autocheck="true"
type="radio"
name="trafficlight"/>
<menuitem label="Желтый"
style="color:yellow;"
autocheck="true"
type="radio"
name="trafficlight"/>
<menuitem label="Зеленый"
style="color:green;"
autocheck="true"
type="radio"
name="trafficlight"/>
</menupopup>
</menu>
<menu label="Вложенное
меню">
<menupopup>
<menuitem label="item
2" id="mi2"/>
<menuitem label="item
3" id="mi3"/>
<menu label="Глубоко
вложенное меню">
<menupopup>
<menuitem label="subitem
1" name="si1"/>
<menuitem label="subitem
2" name="si2"/>
</menupopup>
</menu>
</menupopup>
</menu>
</menupopup>
</button>
<code-after>
<![CDATA[
ael("mi1", "click", function(evt){loadURI("http://www.cyberforum.ru/")});
ael("mi2", "click", function(evt){alert("item 2 clicked")});
ael("mi3", "click", function(evt){alert("item 3 clicked")});
aeln('si1', function(evt){alert('subitem 1 clicked');});
aeln('si2', function(evt){alert('subitem 2 clicked');});
aeldi("check-query",
function(evt)
{
var ft = (thisbutton.querySelector("menuitem[data-id='check-item']").getAttribute("checked") == "true") ? "установлен" : "не установлен";
alert("Флажок " + ft);
});
]]>
</code-after>
</menu-button>
Здесь требуются кое-какие пояснения. В первом блоке кода (code-before) я определил три функции, все они предназначены для подписки на события.
ael - добавляет обработчик события к элементу с заданным id, причем ищет его по всему документу
id - значение атрибута id искомого элемента
evtName - имя события, на которое надо подписаться
handler - функция-обработчик
aeln - добавляет обработчик события click эелементу с заданным атрибутом name, но поиск осуществляется в пределах данной кнопки. Это может быть полезно для того, чтобы не использовать лишний раз, поскольку этот атрибут должен быть уникальным в пределах документа.
aeldi - то же самое, что и предыдущий вариант, только вместо атрибута name, который может использоваться для других целей (как это показано в меню Светофор), используется специально выдуманный атрибут data-id.
В блоке code-after выполняется подписка путем вызова этих функций.
Внутри элемента button - обычный XUL-код нашего меню. Основные возможности здесь показаны. "Пункт меню с флажком" устанавливает и удаляет флажок при клике, а следующий за ним пункт показывает состояние. Это просто демонстрация того, как можно использовать флажки и проверять их значение программно. На примере меню "Светофор" показана работа меню типа "radio" ну и применение стилей. Остальное особых вопросов вызвать не должно.
Как все описать, чтобы не запутаться
В использовании XML, по всей видимости, было бы мало смысла, если бы не было возможности сделать процесс написания документа более комфортным. Существует множество XML-редакторов и многие из них поддерживают схемы, выдают контекстные подсказки в соответствии со схемой и умеют еще много чего. Я сам пользуюсь встроенным XML-редактором в Visual Studio, но это не принципиально, можно использовать любой другой, главное - чтобы он поддерживал XML-схему XSD.
Вот код схемы, в соответствии с которой составлен представленный выше документ.
| Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"?>
<xs:schema id="MultyButton"
targetNamespace="urn:diadiavova-schemas:menu-button"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns="urn:diadiavova-schemas:menu-button"
xmlns:mstns="urn:diadiavova-schemas:menu-button"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="menu-button">
<xs:complexType>
<xs:sequence>
<xs:element name="code-before"
type="string-element-content"/>
<xs:element name="button">
<xs:complexType>
<xs:sequence>
<xs:element name="menupopup"
type="menupopupDef"/>
</xs:sequence>
<xs:attribute name="accesskey"
type="accesskeyDef"/>
<xs:attribute name="autocheck"
type="xs:boolean"/>
<xs:attribute name="checkState"
>
<xs:simpleType>
<xs:restriction base="xs:int">
<xs:enumeration value="0"/>
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="checked"
type="xs:boolean"/>
<xs:attribute name="command"
type="xs:string"/>
<xs:attribute name="crop"
type="cropDef"/>
<xs:attribute name="dir">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="normal"/>
<xs:enumeration value="reverse"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="disabled"
type="xs:boolean"/>
<xs:attribute name="dlgtype">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="accept"/>
<xs:enumeration value="cancel"/>
<xs:enumeration value="help"/>
<xs:enumeration value="disclosure"/>
<xs:enumeration value="extra1"/>
<xs:enumeration value="extra2"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="group"
type="xs:string"/>
<xs:attribute name="icon"
type="xs:anyURI"/>
<xs:attribute name="image"
type="xs:anyURI"/>
<xs:attribute name="label"
type="xs:string"/>
<xs:attribute name="open"
type="xs:boolean"/>
<xs:attribute name="orient">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="horizontal"/>
<xs:enumeration value="vertical"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="tabindex"
type="xs:int"/>
<xs:attribute name="type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="checkbox"/>
<xs:enumeration value="menu"/>
<xs:enumeration value="menu-button"/>
<xs:enumeration value="panel"/>
<xs:enumeration value="radio"/>
<xs:enumeration value="repeat"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="code-after"
type="string-element-content"
minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="menupopupDef">
<xs:choice maxOccurs="unbounded">
<xs:element name="menuitem"
type="menuitemDef"/>
<xs:element name="menuseparator"
type="menuseparatorDef"/>
<xs:element name="menu"
type="menuDef"/>
</xs:choice>
<xs:attribute name="ignorekeys"/>
<xs:attribute name="left"/>
<xs:attribute name="onpopuphidden"/>
<xs:attribute name="onpopuphiding"/>
<xs:attribute name="onpopupshowing"/>
<xs:attribute name="onpopupshown"/>
<xs:attribute name="position"/>
<xs:attribute name="top"/>
<xs:attributeGroup ref="common-attributes"/>
</xs:complexType>
<xs:complexType name="menuDef">
<xs:all>
<xs:element name="menupopup"
type="menupopupDef"/>
</xs:all>
<xs:attribute name="acceltext"
type="xs:string"/>
<xs:attribute name="accesskey"
type="accesskeyDef"/>
<xs:attribute name="allowevents"
type="xs:boolean"/>
<xs:attribute name="command"
type="xs:string"/>
<xs:attribute name="crop"
type="cropDef"/>
<xs:attribute name="disabled"
type="xs:boolean"/>
<xs:attribute name="image"
type="xs:anyURI"/>
<xs:attribute name="label"
type="xs:string"/>
<xs:attribute name="menuactive"
type="xs:boolean"/>
<xs:attribute name="open"
type="xs:boolean"/>
<xs:attribute name="sizetopopup">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="prefs"/>
<xs:enumeration value="always"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="tabindex"
type="xs:int"/>
<xs:attribute name="value"
type="xs:string"/>
<xs:attributeGroup ref="common-attributes"/>
</xs:complexType>
<xs:complexType name="menuitemDef">
<xs:attribute name="acceltext"
type="xs:string"/>
<xs:attribute name="accesskey"
type="accesskeyDef"/>
<xs:attribute name="allowevents"
type="xs:boolean"/>
<xs:attribute name="autocheck"
type="xs:boolean"/>
<xs:attribute name="checked"
type="xs:boolean"/>
<xs:attribute name="closemenu">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="auto"/>
<xs:enumeration value="single"/>
<xs:enumeration value="none"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="command"
type="xs:string"/>
<xs:attribute name="crop"
type="cropDef"/>
<xs:attribute name="description"
type="xs:string"/>
<xs:attribute name="disabled"
type="xs:boolean"/>
<xs:attribute name="image"
type="xs:anyURI"/>
<xs:attribute name="key"
type="xs:IDREF"/>
<xs:attribute name="label"
type="xs:string"/>
<xs:attribute name="name"
type="xs:string"/>
<xs:attribute name="selected"
type="xs:boolean"/>
<xs:attribute name="tabindex"
type="xs:int"/>
<xs:attribute name="type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="checkbox"/>
<xs:enumeration value="radio"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="validate">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="always"/>
<xs:enumeration value="never"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="value"
type="xs:string"/>
<xs:attributeGroup ref="common-attributes"/>
</xs:complexType>
<xs:complexType name="menuseparatorDef">
<xs:attribute name="acceltext"/>
<xs:attribute name="accesskey"/>
<xs:attribute name="allowevents"/>
<xs:attribute name="command"/>
<xs:attribute name="crop"/>
<xs:attribute name="disabled"/>
<xs:attribute name="image"/>
<xs:attribute name="label"/>
<xs:attribute name="selected"/>
<xs:attribute name="tabindex"/>
<xs:attribute name="value"/>
<xs:attributeGroup ref="common-attributes"/>
</xs:complexType>
<xs:simpleType name="string-element-content">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="accesskeyDef">
<xs:restriction base="xs:string">
<xs:length value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="cropDef">
<xs:restriction base="xs:string">
<xs:enumeration value="start"/>
<xs:enumeration value="end"/>
<xs:enumeration value="center"/>
<xs:enumeration value="left"/>
<xs:enumeration value="right"/>
<xs:enumeration value="none"/>
</xs:restriction>
</xs:simpleType>
<xs:attributeGroup name="common-attributes">
<xs:attribute name="id"
type="xs:string"/>
<xs:attribute name="tooltiptext"
type="xs:string"/>
<xs:attribute name="style"
type="xs:string"/>
<xs:attribute name="data-id"
type="xs:string"/>
</xs:attributeGroup>
</xs:schema>
И что же теперь со всем этим делать?
Теперь нам потребуется еще одна кнопка, которую можно создать стандартными средствами и в редакторе кода которой, на вкладке Code мы разместим следующий код.
| Код javascript | Выделить |
/*CODE*/
function queryFileName()
{
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Dialog Title", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterXML);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace)
{
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
return path;
}
}
function ReadTextFromFile(fileName, charset, callback)
{
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(fileName);
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var channel = NetUtil.newChannel(file);
channel.contentType = "application/xml";
NetUtil.asyncFetch(channel, function (inputStream, status)
{
callback(NetUtil.readInputStreamToString(inputStream, file.fileSize, { charset: charset }));
});
}
function createGen(domElement)
{
/// <param name="domElement" type="Element">Description</param>
function createGenerator(domElement)
{
/// <param name="domElement" type="Element">Description</param>
var result = "element = cont.ownerDocument.createElement('"
+ domElement.tagName + "');";
result += "if(stack.length>0){stack.last().appendChild(element);}else{cont.appendChild(element)};";
result += "stack.push(element);"
var atts = domElement.attributes;
for (var
i = 0; i < atts.length; i++)
{
if (atts[i].specified)
{
var attname = atts[i].name;
if (atts[i].name == 'image')
{
result += "stack.last().classList.add('menuitem-iconic');";
}
result += "stack.last().setAttribute('" + atts[i].name + "','"
+ atts[i].value + "');";
}
}
var chnodes = domElement.childNodes;
for (var
i = 0; i < chnodes.length; i++)
{
if (chnodes[i].nodeType == 1)
{
result += createGenerator(chnodes[i], result);
}
else if
(chnodes[i].nodeType == 3 && chnodes[i].nodeValue.trim().length > 0)
{
result += "stack.last().appendChild(cont.ownerDocument.createTextNode(" + JSON.stringify(chnodes[i].nodeValue) + "));";
}
}
result += "stack.pop();";
return result
}
var result = "function generateCode(cont){var element;var stack=[];stack.last = function(){return this[this.length - 1];};"
result += createGenerator(domElement, "") + "}";
return result;
}
var initText = "this.type='menu-button';var thisbutton=this;"
function loadXml()
{
ReadTextFromFile(queryFileName(), "utf-8", function
(data)
{
var parser = new
DOMParser();
var doc = parser.parseFromString(data, "application/xml");
initText += doc.querySelector("code-before").textContent;
initText += createGen(doc.querySelector("button menupopup")) + "generateCode(this);";
initText += doc.querySelector("code-after").textContent;
gClipboard.write(initText);
});
}
loadXml();
Код, полученный из представленного документа выглядит так
| Код javascript | Выделить |
this.type = 'menu-button'; var thisbutton =
this;
function ael(id, evtName, handler) { document.getElementById(id).addEventListener(evtName, handler, false); };
function aeln(name, clickhandler) { thisbutton.querySelector('*[name="' + name + '"]').addEventListener("click",
clickhandler, false); };
function aeldi(di, clickhandler) { thisbutton.querySelector('*[data-id="' + di + '"]').addEventListener("click",
clickhandler, false); }
function generateCode(cont) { var element; var stack = []; stack.last =
function () { return this[this.length
- 1]; }; element = cont.ownerDocument.createElement('menupopup'); if (stack.length > 0) { stack.last().appendChild(element); } else
{ cont.appendChild(element) }; stack.push(element); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0) { stack.last().appendChild(element); } else
{ cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('class', 'menuitem-iconic'); stack.last().setAttribute('id',
'mi1'); stack.last().setAttribute('label', 'Cyberforum'); stack.last().setAttribute('tooltiptext',
'Открывает главную страницу киберфорума'); stack.last().classList.add('menuitem-iconic'); stack.last().setAttribute('image',
'http://www.cyberforum.ru/favicon.ico'); stack.pop(); element = cont.ownerDocument.createElement('menuitem'); if
(stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Пункт меню с флажком');
stack.last().setAttribute('autocheck', 'true'); stack.last().setAttribute('type',
'checkbox'); stack.last().setAttribute('checked', 'true'); stack.last().setAttribute('data-id',
'check-item'); stack.pop(); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0) {
stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Что с флажком?');
stack.last().setAttribute('data-id', 'check-query'); stack.pop(); element = cont.ownerDocument.createElement('menu');
if (stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label',
'Светофор'); element = cont.ownerDocument.createElement('menupopup'); if (stack.length > 0) {
stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); element = cont.ownerDocument.createElement('menuitem'); if
(stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Красный');
stack.last().setAttribute('style', 'color:red;'); stack.last().setAttribute('autocheck',
'true'); stack.last().setAttribute('type', 'radio'); stack.last().setAttribute('name',
'trafficlight'); stack.pop(); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0)
{ stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Желтый');
stack.last().setAttribute('style', 'color:yellow;'); stack.last().setAttribute('autocheck',
'true'); stack.last().setAttribute('type', 'radio'); stack.last().setAttribute('name',
'trafficlight'); stack.pop(); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0)
{ stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Зеленый');
stack.last().setAttribute('style', 'color:green;'); stack.last().setAttribute('autocheck',
'true'); stack.last().setAttribute('type', 'radio'); stack.last().setAttribute('name',
'trafficlight'); stack.pop(); stack.pop(); stack.pop(); element = cont.ownerDocument.createElement('menu'); if
(stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'Вложенное меню');
element = cont.ownerDocument.createElement('menupopup'); if (stack.length > 0) { stack.last().appendChild(element); } else
{ cont.appendChild(element) }; stack.push(element); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0) { stack.last().appendChild(element); } else
{ cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'item 2'); stack.last().setAttribute('id',
'mi2'); stack.pop(); element = cont.ownerDocument.createElement('menuitem'); if (stack.length > 0) {
stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'item 3');
stack.last().setAttribute('id', 'mi3'); stack.pop(); element = cont.ownerDocument.createElement('menu');
if (stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label',
'Глубоко вложенное меню'); element = cont.ownerDocument.createElement('menupopup'); if (stack.length > 0) {
stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); element = cont.ownerDocument.createElement('menuitem'); if
(stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label', 'subitem 1');
stack.last().setAttribute('name', 'si1'); stack.pop(); element = cont.ownerDocument.createElement('menuitem');
if (stack.length > 0) { stack.last().appendChild(element); } else { cont.appendChild(element) }; stack.push(element); stack.last().setAttribute('label',
'subitem 2'); stack.last().setAttribute('name', 'si2'); stack.pop(); stack.pop(); stack.pop();
stack.pop(); stack.pop(); stack.pop(); } generateCode(this);
ael("mi1", "click", function
(evt) { loadURI("http://www.cyberforum.ru/") });
ael("mi2", "click", function
(evt) { alert("item 2 clicked") });
ael("mi3", "click", function
(evt) { alert("item 3 clicked") });
aeln('si1', function (evt) { alert('subitem 1 clicked');
});
aeln('si2', function (evt) { alert('subitem 2 clicked');
});
aeldi("check-query", function (evt)
{
var ft = (thisbutton.querySelector("menuitem[data-id='check-item']").getAttribute("checked")
== "true") ? "установлен" : "не установлен";
alert("Флажок " + ft);
});
Несколько слов о коде кнопок
Главное отличие кода, написанного для этих кнопок от кода, используемого в букмарклетах или Greasemonkey или даже на веб-странице - это контекст. Здесь так же как и там сослаться на глобальный объект можно с помощью слова window, вот только значение у него немного другое. И с документом та же проблема - document ссылается вовсе не на документ страницы. Поэтому, естественно, возникает вопрос: "Kак обратиться объектам страницы?", - ведь в конечном итоге, все затевается именно ради возможности взаимодействовать со страницей. Простейший способ достучаться до страницы - использование объекта content (или window.conent), это ссылка на объект window страницы загруженной в активную вкладку браузера. Через него можно получить доступ ко всем остальным объектам: content.document - будет ссылаться на объект document этой страницы; а content.varname - на тот же объект, что и переменная varname, объявленная на этой странице.
Знание слова content вполне решает проблему доступа к содержимому страницы, так что останавливаться на нем не вижу особого смысла, гораздо интереснее посмотреть, что можно сделать из того, что нельзя из кода веб-страницы, но можно из привилегированного кода.
Полезные ссылки
Для начала несколько ссылок.
НОУ ИНТУИТ | Разработка приложений с помощью Mozilla | Информация
Хорошая книга о платформе, на русском языке не видел ничего лучше. Но кое-что из написанного уже устарело, так что надо сверяться с документацией. Например, там предлагается установить мозиллу для запуска stanalone-приложений, но этого проекта уже давным-давно не существует, точнее он переименован и теперь называется XulRunner, если этого не знать, то можно много времени потратить на поиски. Но по архитектуре платформы большинство информации еще актуально. Требуется авторизация на сайте, но книгу можно найти и в других местах, например здесь
Программирование - N.McFarlane: Приложения на XUL с использованием Mozilla
Документация по XUL
XUL - Mozilla | MDN
Информация об элементах XUL
XUL Reference - Mozilla | MDN
Описание основных технологий
XUL Tutorial - Mozilla | MDN
Документация по XPCOM (масса дополнительных возможностей появляется именно при использовании этих объектов).
XPCOM - Mozilla | MDN
Список интерфейсов(там в описаниях есть примеры кода)
XPCOM Interface Reference - Mozilla | MDN
Ну и несколько важных примеров.
Файлы
О доступе к файловой системе можно прочитать здесь.
File I/O - Mozilla | MDN
Чтобы сразу дать рабочий пример, приведу код, читающий текст из файла
| Код javascript | Выделить |
function ReadTextFromFile(fileName, charset, callback)
{
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(fileName);
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var channel = NetUtil.newChannel(file);
channel.contentType = "application/xml";
NetUtil.asyncFetch(channel, function (inputStream, status)
{
callback(NetUtil.readInputStreamToString(inputStream, file.fileSize, { charset: charset }));
});
}
| Код javascript | Выделить |
ReadTextFromFile("file:///C:/myfile.txt", "utf-8",
function (data)
{
alert(data);
});
Указывать файл вручную неудобно, так что неплохо было бы файл-диалогом разжиться.
nsIFilePicker - Mozilla | MDN
Опять-таки простой пример исползования
| Код javascript | Выделить |
function queryFileName()
{
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Dialog Title", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterXML);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace)
{
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
return path;
}
}
Буфер обмена
Using the clipboard - Mozilla | MDN
Использование буфера обмена так, как это описано по ссылке выше - немного муторно. В большинстве случаев требуется записать текст в буфер или получить текст из буфера. Если нужно только это, то это можно реализовать проще.
Запись текста в буфер
| Код javascript | Выделить |
gClipboard.write("Какой-то текст.");
| Код javascript | Выделить |
alert(gClipboard.read());
Preferences - Mozilla | MDN
Ну опять-таки реальный пример. Я уже писал о том, что есть у CustomButtons проблема с назначением внешнего редактора кода: если один раз его назначить, то уже на другой не поменяешь. Сбросить редактор или назначить новый можно в конфигурации. Перейти на страницу about:config и там найти ветвь extensions.custombuttons.external_editor и там либо удалить значение и получить таким образом возможность задать новый редактор при попытке вызова внешнего редактора, либо указать адрес исполняемого файла той программы, которую надо назначить внешним редактором.
Программно сбросить редактор можно так.
| Код javascript | Выделить |
var ps = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
ps = ps.QueryInterface(Components.interfaces.nsIPrefBranch);
var cbps = ps.getBranch("extensions.custombuttons.");
cbps.clearUserPref("external_editor");
| Код javascript | Выделить |
function setExternalEditor(exeFileName)
{
var ps = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
ps = ps.QueryInterface(Components.interfaces.nsIPrefBranch);
var cbps = ps.getBranch("extensions.custombuttons.");
cbps.setCharPref("external_editor", exeFileName);
}
Accessing the Windows Registry Using XPCOM | MDN
Небольшой пример
| Код javascript | Выделить |
function getVSPath(version)
{
/// <summary>Возвращает путь к каталогу установки заданной версии Visual Studio</summary>
/// <param name="version" type="String">Версия Visual Studio. Должна быть указана в полном формате, например: 11.0</param>
/// <returns type="String" />
var v7 = "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7";
var wrk = Components.classes["@mozilla.org/windows-registry-key;1"].createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, v7, wrk.ACCESS_READ);
var result = wrk.readStringValue(version);
wrk.close();
return result;
}
| Код javascript | Выделить |
var ps = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
ps = ps.QueryInterface(Components.interfaces.nsIPrefBranch);
var cbps = ps.getBranch("extensions.custombuttons.");
cbps.setCharPref("external_editor", getVSPath("11.0")
+ "Common7\\IDE\\devenv.exe");
Комментариев нет :
Отправить комментарий