Преамбула
Я перешел на FF с IE очень давно — мне нужен был браузер с удобными средствами разработки и отладки, позволяющий полностью настраиваться «под себя» так как это был мой основной рабочий инструмент — инструмент должен быть удобным.
И FF полностью справился со своими обязанностями — возможно, я кому-то покажусь странным — но я потратил около 2-х месяцев на чтение сайта с дополнениями и нахождения для меня лучшего их набора, настройки и т.д.
В результате я получил мощную хорошо настроенную и удобную программу для своих нужд.
Не смотря на то, что я знаю JS, мне ни разу не пришло в голову писать какие-либо расширения под ff по одной простой причине — чтобы я не захотел — это уже было в депозитории расширений. Так продолжалось до вчера :)
Вчера мне подумалось вот о чем — во время работы мне все время нужно переключаться и заходить на localhost, где крутится мой апач с текущим проектом, и на адрес где текущий релиз проекты выложен в интернете — вполне стандартная ситуация. Вариантов решения масса — можно ( и как сделано у меня ) повесить это дело на жесты мыши, но проблема в том что я одновременно веду не один проект а несколько десятков.
Можно использовать избранное… но как-то много у меня там всякого не относящегося к работе… папки там — тоже вариант — но вот не нравится мне оно как-то.
Поиски...
И я стал искать как можно это все сделать красиво и удобно.
Для начала перерыв сайт с расширениями ничего конкретно подходящего я не обнаружил и был в несколько шоковом состоянии x_X, но по скольку борцы на просторах всемирной паутины никогда не унывают, несколько удивившись я понял что момент создания своего расширения под ff наконец-то настал, но как всегда подвел двигатель прогресса — а именно лень.
Было абсолютно не охота разбираться с тем как его упаковывать как устанавливать и писать локализации к нему — однако выход нашелся быстро — есть такое замечательное расширение, которое наверняка стоит у многих программеров Custom Buttons, есть еще более навороченный вариант Custom Buttons 2 — кому что нравится, я же выбрал первый, так как руководствуюсь принципом если несколько сущностей одинаково удовлетворяют ваши потребности — используйте простейшую — ну да это я — а Вы можете выбрать исходя из своих принципов :)
Далее все просто — данные расширения — это некоторая обвязка, позволяющая создавать свои собственные кнопки на тулбаре и реализовывать в них собственный функционал. Он пишется на JS, расширенном внутренними объектами FF.
Т.е. особо пытливым хабралюдям все-таки придется лезть и читать документацию :)
Алгоритм добавления кнопки прост: правый клик по тулбару -> добавить кнопку, позднее после заполнения всех нужных полей и сохранения кнопки надо будет еще вытащить ее в нужное место на нужный тулбар, используя правый клик по тулбару -> настроить, а дальше найдя свою кнопку перетащить ее в нужное место.
Что касается полей:
Имя — имя вашей кнопки
Картинка — путь до картинки, которая и будет кнопкой ( вы можете указав путь нажать base64 — тогда за место пути до картинки у вас образуется dataURL, и картинка будет хранится в самой кнопке )
Далее вкладки
Код — тот код, что выполняется по нажатию
Инициализация — код, выполняемый один раз при загрузке браузера
Соотвественно вся мощь FF у вас в руках, делать можно все что хочешь :)
Например для того, чтобы сделать отдельную кнопку для вызова избранного достаточно добавить вот такое:
Код:
if ( !this.lastChild ) {
var mc = document.getElementById("bookmarksMenuPopup");
var mcc = mc.cloneNode( true );
this.appendChild( mcc );
}
var bo = document.getBoxObjectFor( this );
this.lastChild.showPopup ( this, -1, -1, "popup", "bottomleft", "topleft" );
* This source code was highlighted with Source Code Highlighter.
Инициализация:
пусто
Здесь я обошелся без отдельного кода инициализации, засунув его и проверку в основной код.
Естественно можно разделить это как должно быть правильно — переписав все содержимое первого if в код инициализации и убрав if из кода, но зато так кароче :)
Собственно щелкнув по правой клавишей по нашей кнопке мы получим меню Custom Buttons с помощью которого можно редактировать код кнопки, копировать код кнопки — вобщем работать и изменять наше маленькое расширение, левый же клик откроет нам избранное, выполнив наш код.
Копирование кода кнопки позволит создать эту кнопку в любом другом FF, причем там сохранятся все поля вашей кнопки ( и естественно картинки, если они были dataURL ).
Вот оно!
Возвращаясь к своей персональной задачке и пока я писал для нее код, у меня родился еще небольшой функционал:
- возможность открыть определенный URL
- хотелось иметь возможность быстро запускать нужные приложения ( например хранитель паролей )
- удобно было бы иметь возможность запускать некий стандартный код JS на любой странице ( конечно можно поглядеть в FireBug-е, но всеже иногда удобней кликнуть два раза чем бегать по его закладкам )
- ну и некое подобие roboform, только с более простым функционалом — а именно — открыть нужный URL и заполнить определнные поля определнными значениями
п.3 — использую например чтобы поглядеть список iframe-ов или timer-ов на странице и много еще для чего. Очень часто мне такое бывает нужно.
п.4 — Например при отладке страниц с формами — можно конечно заполнить форму сохранить и юзать — но как-то не срослась у меня дружба с форм-филлерами для FF :) кроме того — закончил проект — все просто — удалил запись и мусора нет — все поля хранятся в самой записи. Можно тоже поспорить о целесообразности, но опять-же для меня это было бы удобно.
Почитав доки и подглядев в установленных расширения куски кода родилась вот такая штука ( над оптимизацией кода не работал — ибо не до того было — с удовольствием приму замечания :) ):
Код:
var bo = document.getBoxObjectFor( this );
this.lastChild.showPopup ( this, -1, -1, "popup", "bottomleft", "topleft" );
Инициализация:
var mitems = [
{
"label" : "localhost",
"tip" : "localhost",
"image" : "",
"type" : "url",
"val" : "localhost"
},
"separator",
{
"label" : "Google",
"tip" : "Google",
"image" : "",
"type" : "url",
"val" : "http://www.google.ru"
},
"separator",
{
"label" : "Notepad",
"tip" : "Notepad",
"image" : "",
"type" : "exec",
"val" : "c:/windows/notepad.exe"
},
"separator",
{
"label" : "other",
"tip" : "other",
"image" : "",
"type" : "submenu",
"val" :
[
{
"label" : "JS test",
"tip" : "JS test",
"image" : "",
"type" : "js",
"val" : "alert( 'js testing...' );"
},
{
"label" : "Выход из FF",
"tip" : "Выход из FF",
"image" : "",
"type" : "js",
"val" : "goQuitApplication();"
}
]
}
];
function createMenu( label, tip, image ) {
var m = document.createElement( "menu" );
m.setAttribute( "label", label );
if ( image ) {
m.setAttribute( "class", "menu-iconic" );
m.setAttribute( "image", image );
}
if ( tip ) m.tooltipText = tip;
return m;
}
function createMenuItem( label, tip, image ) {
var mi = document.createElement( "menuitem" );
mi.setAttribute( "label", label );
if ( image ) {
mi.setAttribute( "class", "menuitem-iconic" ); // "menuitem-iconic bookmark-item"
mi.setAttribute( "image", image );
}
if ( tip ) mi.tooltipText = tip;
return mi;
}
function executeFile( progPath, arg ) {
progPath = progPath.replace( /\//gi, "\\" );
try {
var argArray = arg ? arg.split(/\s+/) : [];
var nsILocalFile = Components.classes["@mozilla.org/file/local;1"].getService(Components.interfaces.nsILocalFile);
var nsIProcess = Components.classes["@mozilla.org/process/util;1"].getService(Components.interfaces.nsIProcess);
nsILocalFile.initWithPath(progPath);
nsIProcess.init(nsILocalFile);
nsIProcess.run(false, argArray, argArray.length);
// nsIProcess.close();
return nsILocalFile;
}
catch( ex ) {
alert( ex.toString() );
return null;
}
}
function buildMenu( items ) {
var menu, i, t, mi, item;
var self = this;
menu = document.createElement( "menupopup" );
menu.type = "menu";
menu.orient = "horizontal";
// menu.id = "myCustomMenu";
for ( i in items ) {
item = items[i];
if ( typeof( item ) == "string" ) {
switch( item ) {
case "separator":
mi = document.createElement( "menuseparator" );
break;
default:
mi = null;
break;
}
}
else {
switch( item.type ) {
case "url":
mi = createMenuItem( item.label, item.tip, item.image );
mi.onclick = function() {
var b = getBrowser();
b.selectedTab = b.addTab( this.onclick.addr );
b.selectedTab.onload = function() {}
};
mi.onclick.addr = item.val;
break;
case "urlForm":
mi = createMenuItem( item.label, item.tip, item.image );
mi.onclick = function() {
var item = this.onclick.item;
var b = getBrowser();
var tab = gBrowser.addTab( item.val.addr );
var newTabBrowser = b.getBrowserForTab( tab );
b.selectedTab = tab;
var lf = function( event ) {
var fields = item.val.fields;
newTabBrowser.removeEventListener( 'load', lf, true );
setTimeout(
function() {
var doc = newTabBrowser.contentDocument;
var inputs = doc.getElementsByTagName( "input" );
for ( var i in fields ) {
for ( var j = 0; j < inputs.length; j++ ) if ( inputs[j].name == i ) inputs[j].value = fields[i];
}
},
100
);
};
newTabBrowser.addEventListener( "load", lf, true );
};
mi.onclick.item = item;
break;
case "js":
mi = createMenuItem( item.label, item.tip, item.image );
mi.onclick = new Function( item.val );
break;
case "submenu":
mi = createMenu( item.label, item.tip, item.image );
mi.appendChild( buildMenu( item.val ) );
break;
case "exec":
mi = createMenuItem( item.label, item.tip, item.image );
mi.onclick = new Function( "this.onclick.executeFile('" + item.val + "')" );
mi.onclick.executeFile = executeFile;
break;
default:
mi = null;
break;
}
}
if ( mi ) menu.appendChild( mi );
}
return menu;
}
this.appendChild( buildMenu( mitems, false ) );
* This source code was highlighted with Source Code Highlighter.
mitems собственно массив наших пунктов меню и самих меню
задатеся JS объектами, поля:
- label — название пункта меню
- tip — всплывающая подсказка на пункте ( может быть пустое )
- image — либо адрес либо dataURL для картинки ( может быть пустое )
- type — тип пункта:
- url — открыть страничку, в поле val — адрес странички
- urlForm — открыть страничку и заполнить нужные поля нужными значениями, поле val:
{
"addr": "адрес страницы",
"fields": {
"поле1": "значение поля1",
"поле2": "значение поля2",
и т.д.
}
}
* This source code was highlighted with Source Code Highlighter.
- js — запустить JS код, в поле val — JS код
- exec — запустить приложение, в поле val — полный путь до приложение и его имя — типа «c:/a/b/c.exe», слэши используются обратные
- submenu — создать подменю, в поле val — массив из наших пунктов-объектов
- val — зависит от type
Ну вот собственно наше расширение и готово, не буду говорить за всех — а мне стало удобней.
И, я надеюсь, пусть на чуток я увеличу количество пользователей такого замечательного браузера ( а может быть чего доброго — и количество разработчиков расширений под него :) )
Очень надеюсь, что моя статья кому-нибудь окажется полезной…
P.S. Полный код кнопки: тут