Piszemy plugin do FireFoxa [tutorial]
Napiszmy plugin sprawdzający, czy nie ma nowych komentarzy na tym blogu. Będzie wyglądał tak (to "A:" to jest skrót od "Autor ostatniego komentarza"):
Zasada działania jest dość prosta, ciekawe w tym jest to, jakich narzędzi się przy tym używa. Jeszcze z 2-3 lata temu nie do pomyślenia było dla mnie takie coś, żeby 'dopisać' kawałek kodu do przeglądarki :) Wracając do algorytmu:
- W pasku na dole FireFoxa wyświetlać się będzie nick osoby, która ostatnio dodała komentarz
- Po najechaniu na to pole będzie widać treść jej wypowiedzi oraz czas utworzenia.
- Całość będzie się odświeżała w tle, co zadany czas, tak by nie musieć sprawdzać ręcznie klikając gdzieś etc.
Post opiera się w dużej mierze na kodzie z temat tego artykułu i stąd. Właściwie to jest ich syntezą :) Anyway chyba rozwinę bliżej ten plugin, więc niedługo będzie bardziej 'mój własny'. Jeżeli czytelnik po zapoznaniu się z postem uważa iż blisko mu do plagiatu, czy też nie powinienem się na ten temat wypowiadać zachęcam do zostawienia komentarza!
Wtyczka do FireFoxa ma rozszerzenie xpi ale tak na prawdę to zwykłe archiwum zip. Możemy otworzyć czyjąś wtyczkę i ją przeanalizować. Plugin zbudowany jest tak, że jego wygląd określają pliki XML, natomiast zachowanie pliki JavaScript.
Do rzeczy, do utworzenia pustego plugina, jego spakowania (jak już mówiliśmy są tam zip'y z różnymi końcówkami) posłużyłem sie makefile'm:
PROJECT_NAME=mojpasek.xpi new: mkdir chrome mkdir chrome/skin mkdir chrome/content touch install.rdf build: cd chrome && zip -r engine.jar content/ skin/ zip $(PROJECT_NAME) install.rdf chrome/engine.jar clear: rm chrome/engine.jar rm $(PROJECT_NAME)
Tworząc nowy plugin będziemy musieli mieć katalog chrome z dwoma w/w podkatalogami, oraz install.rdf opisujący plugin. 'Kompilując' plugin (czyli pakując go do pojedyńczego pliku xpi) musimy zacząć od spakowania plików z katalogu chrome, dopiero potem możemy spakować całość.
Idąc dalej, plik install.rdf, przechowuje on główne dane o naszym pluginie. To, co jest zaznaczone boldem to identyfikator pluginu, powinniśmy wygenerować swój (w necie jest skrypt online, nie pamiętam linka).
Gałąź targetApplication służy opisowi, dla jakich programów i dla jakich wersji plugin działa, identyfikator, który tam jest należy do aplikacji (ten jest FireFoxa), taki ma być. Jeden plugin może działać dla wielu aplikacji. W gałęzi file mamy opis plików jar (czyli zip'ów), w których ukryty jest kod naszej aplikacji.
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>{30bad1a0-2721-4bf9-8c3a-ebf960308f64}</em:id>
<em:name>Robert Gawron - sprawdzanie komentarzy</em:name>
<em:version>1.0</em:version>
<em:description>Megalomański plugin do sprawdzania nowych komentarzy u siebie na blogu</em:description>
<em:file>
<Description about="urn:mozilla:extension:file:engine.jar">
<em:package>content/</em:package>
<em:skin>skin/</em:skin>
</Description>
</em:file>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>1.0</em:minVersion>
<em:maxVersion>2.5</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>
Trzeba nam też chrome/skins/contents.rdf, on jest tak samo zrobiony jak plik w linku który podałem, z którego się wzorowałem. Nie wiem czemu tak a nie inaczej, ale działa:
<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Seq about="urn:mozilla:skin:root">
<RDF:li resource="urn:mozilla:skin:classic/1.0" />
</RDF:Seq>
<RDF:Description about="urn:mozilla:skin:classic/1.0">
<chrome:packages>
<RDF:Seq about="urn:mozilla:skin:classic/1.0:packages">
<RDF:li resource="urn:mozilla:skin:classic/1.0:statusbar" />
</RDF:Seq>
</chrome:packages>
</RDF:Description>
</RDF:RDF>
Plik chrome/content/contents.rdf, oba źródła mówią, że takie coś musi być:
<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Description about="urn:mozilla:package:statusbar"
chrome:extension="true" chrome:name="statusbar"/>
<RDF:Seq RDF:about="urn:mozilla:package:root">
<RDF:li RDF:resource="urn:mozilla:package:statusbar"/>
</RDF:Seq>
<RDF:Seq RDF:about="urn:mozilla:overlays">
<RDF:li RDF:resource="chrome://browser/content/browser.xul"/>
<RDF:li RDF:resource="chrome://navigator/content/navigator.xul"/>
</RDF:Seq>
<RDF:Seq RDF:about="chrome://browser/content/browser.xul">
<RDF:li>chrome://statusbar/content/statusbar.xul</RDF:li>
</RDF:Seq>
<RDF:Seq about="chrome://navigator/content/navigator.xul">
<RDF:li>chrome://statusbar/content/statusbar.xul</RDF:li>
</RDF:Seq>
</RDF:RDF>
Dobra, teraz będzie już coś o czym wiem, plik javascript'a, czyli serce naszego kodu. działa to tak, iż sprawdzamy czy na serwerze plik rss z komentarzami uległ zmianie. Zmiana oznacza tyle, że ktoś napisał nowego posta, więc odwołujemy się do miejsca w pliku rss, gdzie ten komentarz jest, pobieramy dane i wyświetlamy. Odwołujemy sie do miejsca gdzie możemy pisać na dolnej belce poprzez jego id (jak w dhtml). Kod:
function makeRequest(url) {
http_request = false
http_request = new XMLHttpRequest()
if (http_request.overrideMimeType)
http_request.overrideMimeType('text/xml');
if (!http_request)
return false
http_request.onreadystatechange = newItem
http_request.open('GET', url, true)
http_request.send(null)
}
function newItem() {
if (http_request.readyState == 4) {
if (http_request.status == 200) {
var xmlobject = http_request.responseXML;
// we need this..
var root = xmlobject.getElementsByTagName('rss')[0];
var channels = root.getElementsByTagName("channel");
var items = channels[0].getElementsByTagName("item");
// to get this..
var descriptions = items[0].getElementsByTagName("description");
var authors = items[0].getElementsByTagName("author");
var postdate = items[0].getElementsByTagName("pubDate");
var desc = descriptions[0].firstChild.nodeValue
var desc1 = desc.split("<p>");// we have content of mesage
var tooltipstring = "(" + postdate[0].firstChild.nodeValue + ") - " + desc1[0]
document.getElementById('mainpanel').label = "A: "+authors[0].firstChild.nodeValue
document.getElementById('mainpanel').tooltipText = tooltipstring
}
}
}
function checkComments() {
var url = 'http://rgawron.megiteam.pl/comments/rss'
makeRequest(url)
self.setTimeout('checkComments()', 900000)
}
window.addEventListener("load", checkComments, false);
Plik statusbar.xui odpowiedzialny jest za wygląd, a konkretnie za GUI, mamy w nim mały panel, gdzie wyświetla się nasze info a który jezeli się nań kliknie pokazuje menu. Menu na razie nie działa :(
<?xml version="1.0"?>
<overlay id="sample"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<statusbar id="status-bar">
<popupset>
<popup id="clipmenu">
<menuitem label="Exit"/>
<menuitem label="About"/>
</popup>
</popupset>
<box context="clipmenu">
<statusbarpanel id="mainpanel" label="are you connected?"/>
</box>
</statusbar>
<script src="update.js"/>
</overlay>
To by było na tyle, Tutaj można ściągnąć/zainstalować wersje tego pluginu. Jak już wspomniano, jest to plik zip, więc po rozpakowaniu można go przeanalizować/poprawić.