A Web Konferenciás, Netvibes UWA témakörben tartott előadásom rövidesen elérhető lesz a rendezvény honlapján, de úgy döntöttem hogy egy blogbejegyzés formájában itt is részletesen közzéteszem az ott bemutatottakat. A cél egy az UWA filozófiáját, megközelítését bemutató widget elkészítése volt, melynek során mind AJAX kommunikációt (RSS tartalom lehívás), mind az előre elkészített építőelemek (fülek, lapozó), mind pedig általában egy widget fejlesztését be tudom mutatni. A végeredmény itt érhető el: Indafotó UWA widget.

A Netvibest remélhetőleg nem kell bemutatnom ezen sorokat olvasóimnak, és az előadásom során sem tette fel senki a kezét amikor megkérdeztem, hogy be kell-e mutatnom a szolgáltatást. Annyit szeretnék elmondani mégis, hogy a közhiedelemmel ellentétben a Netvibes nem feedolvasó alkalmazás, nem a hírek, hanem a felhasználót érintő összes webes szolgáltatás egy helyre való összegyűjtésére szolgál, mintegy információs központként. És ennek csak egy része a hírek, blogbejegyzések olvasása. Ugyanígy jellemzően a többi hasonló szolgáltatás célja is az összes információ egy helyre sűrítése.
UWA widgeteket pont ezért kell készítenünk, hiszen míg a nemzetközi oldalak, alkalmazások jól le vannak fedve, addig a hazai és saját fejlesztésű szolgáltatások nyilván kevésbé. A másik fele a dolognak – amiért megéri egy widgetet leprogramozni -, az a “reklámfelület”, avagy annak lehetősége, hogy brandünkkel a felhasználó folyamatosan találkozzon saját oldalán. A widgetek RSS feedekhez történő hasonlítása több ponton is helytálló amikor valaki felteszi a “miért” kérdést.
Térjünk a tárgyra: a Netvibes UWA egy olyan widget fejlesztést megkönnyítő keretrendszer, mely ebbe a folyamatba illeszkedik. Használjon valaki Netvibes-t. iGoogle-t, Live.com-ot, Apple Dashboard-ot, Windows Vista Sidebar-t. Yahoo! Widgeteket (anno Konfabulator), Opera Widgeteket, iPhone-t, vagy akár JBoss-t, egyszeri, egy widget lefejlesztésével, jellemzően a platformok sajátosságainak ismerete nélkül célozhatjuk meg őt, ha ezt a keretrendszert használjuk.
Az UWA azért született meg, mert nem volt de-facto standard szintű megoldás erre a feladatra, jellemzően minden widget szolgáltatás, widget motor, a saját formátumát használta, a W3C ezirányú standardizálási törekvései pedig eléggé le vannak maradva. Az UWA mögött levő rendszer feladata az, hogy az elkészített widgetünket az összes több platform számára “on-the-fly” átkonvertálja, és azokon működővé tegye. Bár mindegyik támogatott platform HTML+CSS+JavaScript megoldást használ, mindegyik például más és más függvénnyel/formátummal oldja meg a(z AJAX) kommunikációt, a beállítások letárolását és hasonlókat.
Egy UWA widget kifejlesztése jellemzően könnyű, mivel jól dokumentált standardokra épít, avagy XHTML, CSS és JavaScript ismerete szükséges a fejlesztéshez. Ezen kívül az UWA környezet számos segítséget is kínál feed feldolgozás, kontrollok, előre létrehozott CSS osztályok segítségével. Egy UWA widget a következőképpen épül fel:
- Egy statikus, UTF-8 kódolású XHTML fájl, ami egyben valid XML is. Ez adja a widget vázát.
- Az XHTML alap egy specifikus Netvibes névtérrel egészül ki: xmlns:widget=“http://www.netvibes.com/ns/”
- Meta leíró elemeket használ a szerző, widget és UWA verzió, az esetleges automatikus frissítések és egyebek leírására.
- Speciális widget:preferences elem segítségével írhatóak le a widget beállításai.
- A widget megjelenése CSS segítségével írható le.
- A widget működését JavaScriptben kell leprogramozni.
- A widget indulásakor aktuális megjelenést (jellemzően Loading… felirat) pedig a HTML body-jában kell leírni.
A cél
A célunk egy Indafotó albumokat megjelenítő widget létrehozása lesz. A beállítások között felsorolhatunk felhasználóneveket vesszővel elválasztva, s az ezekhez a felhasználókhoz tartozó legfrissebb képeket jelenítjük meg füleken. A fülek neve az adott felhasználó azonosítója, a fül tartalma pedig x darab kép lesz, mely lapozható. Ehhez az Indafotó RSS interfészét fogjuk felhasználni.
Konkrét tippeket az UWA fejlesztéssel kapcsolatosan ennek az írásnak a végén fogok megosztani, például azzal kapcsolatosan is, hogy pontosan milyen fejlesztői környezetben érdemes elkezdeni kifejleszteni egy widgetet, most viszont szeretném bemutatni azt, hogy hogyan épülhet fel, és hogyan működhet az előbb felvázolt widget.
Az első lépések
Első lépésként hozzuk létre a widget vázául szolgáló XHTML dokumentumot, és indulásként jelenítsünk megy egy fotót, alatta pedig egy tájékoztató szöveget.
A váz így fog kinézni:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://www.netvibes.com/ns/">
<head>
<title>Indafotó</title>
<meta name="author" content="András Bártházi" />
<meta name="version" content="1.0" />
<meta name="apiVersion" content="1.0" />
<meta name="autoRefresh" content="20" />
<link rel="icon" type="image/x-icon" href="http://indafoto.hu/favicon.ico" />
<link rel="stylesheet" type="text/css" href="http://www.netvibes.com/themes/uwa/style.css" />
<script type="text/javascript" src="http://www.netvibes.com/js/UWA/load.js.php?env=Standalone"></script>
<style type="text/css">/*<![CDATA[*/
/*]]>*/</style>
<script type="text/javascript">//<![CDATA[
// ]]></script>
</head>
<body>
<p>Loading...</p>
</body>
</html>
Remélhetőleg egy a HTML-t ismerő személy számára semmilyen újdonságot nem mutattam itt most be ezzel a forráskóddal. Ami UWA specifiku, az a szerzőt, a widget verziószámát, az UWA (API) verziószámát) és az automatikus frissítési időt leíró meta elemek. A belinkelt favicon a widget ikonja lesz, a belinkelt CSS és JavaScript pedig az úgynevezett Standalone módban történő működtetésért és megjelenítésért felelős kódokat tölti be. Egy UWA widget többféle körülmények között megjelenhet, az egyik ilyen “körülmény” a Standalone mód, amikor egy web böngészőben nyitjuk meg minden direktben az UWA forrást. Ezt a két sort nem kötelező betenni, de csak nyerünk vele, ha benne lesz az oldalban.
Van egy-egy üres CSS és JavaScript kódrész is – ezekbe fogjuk írni a konkrét kódunkat. Végül, de nem utolsó sorban egy HTML BODY rész jön, amiben egyelőre egy Loading… üzenetet helyeztünk el. Ide a widget funkcióinak függvényében komolyabb HTML-t is lehet rakni, ez az a HTML rész, mely egyből megjelenik. Adott esetben olyan widgetet is készíthetünk, mely nem tartalmaz semmilyen JavaScript-et sem, hanem csak egy HTML-ből áll, például ha egy képet, Flash fájlt szeretnénk csak megjeleníteni.
Egy widget jellemzően különböző eseményeket kezel le. A legfontosabb esemény az onLoad, ez a widget betöltődésekor történik, és ennek az eseménynek a hatására kell beindítanunk a widget működését. Egy esemény lekezelése a megfelelő függvény létrehozásával történik. Indulásként a widget betöltődésekor hívjunk meg egy olyan függvényt, mely megjeleníti a kép és üzenet párost:
widget.onLoad = function() {
Indafoto.showMessage();
}
És készítsük is el ezt a függvényt:
var Indafoto = {};
Indafoto.showMessage = function() {
widget.body.innerHTML =
'<p><a href="http://indafoto.hu/boogie/image/85596-84fb74c7" target="_blank">' +
'<img src="http://img2.indafoto.hu/4/9/759_2b3bf3eee2475e03885a110e9acaab61/85596_84fb74c738fba9b5ccd963e4d9da79db_m.jpg" width="100%"/>' +
'</a></p>' +
'<p>A widget tartalmát a Beállítások között tudod beállítani. Az "Elemek" értékhez Indafotó felhasználók azonosítóját írhatod be, vesszővel elválasztva.</p>' +
'<p>Például: <strong>boogie,eszpee,blumi,darthwalk,tschoppy,dtamas</strong></p>' +
'<p>Fedezd fel az <a href="http://indafoto.hu/">Indafotót</a> további azonosítókat illetően!</p>';
}
Az első sor egy Indafoto nevű objektumot hoz létre. Célszerű az összes függvényünket egy a widget nevével megegyező objektumba pakolni, átláthatóbb, rendezettebb lesz a kód.
Ezután definiáljuk a függvényt, mely a widget tartalmát fogja beállítani. A kulcs a widget.body nevű változó, mely mindig azt a “div”-et tartalmazza, ami a fenti XHTML forrásban a <body> résznek felel meg, a document.body analógiát fedezhetjük itt fel. Ennek a div-ek a tartalmát esetünkben az innerHTML segítségével volt a legegyszerűbb beállítani.
Itt tartunk:

Neki is állhatunk
A következő feladat, és innen már meg sem állunk a végleges widgetig, a widget beállításainak leírása. Ehhez a fejlécbe kell tennünk egy <widget:preferences> részt (én a stíluslap elé szoktam tenni), mely ezt írja le:
<widget:preferences>
<preference name="title" type="text" label="Cím" defaultValue="Indafotó" />
<preference name="tabs" type="text" label="Elemek" defaultValue="" />
<preference name="nbTitles" type="range" label="Db" defaultValue="10"
step="1" min="1" max="30" />
</widget:preferences>
A különböző beállítási lehetőségeket itt most nem mutatom be, itt most két fajtát használtunk: a text és a range típust. Előbbi egy sima input elemet, utóbbi egy select mezőt fog megjeleníteni, esetünkben 1 és 30 közötti számokkal. Kb. így fog megjelenni – persze ez függ attól is, hogy milyen környezetben, milyen témát választottunk éppen:

Figyeljünk arra, hogy túl hosszú címet ne adjunk, hiszen lehet hogy olyan vékony lesz a widget, hogy nem fog tudni megjelenni. Ugyanez igaz a select elemek választási lehetőségeinek szövegére is.
Alakítsuk át az onLoad eseményt lekezelő részt, hogy ha már kitöltésre került az Elemek rész a beállításoknál, akkor ne a tájékoztató üzenetünk jelenjen már meg:
widget.onLoad = function() {
if (!widget.getValue('tabs')) {
Indafoto.showMessage();
} else {
Indafoto.ids = widget.getValue('tabs').split(/\s*,\s*/);
Indafoto.initTabs();
}
}
A beállításokat mint láthatjuk, a widget.getValue függvény segítségével tudjuk lekérdezni. Ha be van állítva már valami, akkor szétvágjuk a szöveget a vesszők mentén (lesz egy tömbünk a felhasználó azonosítókról), és ezt letároljuk egy változóba. Ezután pedig meghívjuk a fülek megjelenítéséért felelős kódrészletet.
Ami itt is van:
Indafoto.initTabs = function() {
widget.body.innerHTML = '';
Indafoto.tabs = new UWA.Controls.TabView({
});
for (var i=0; i<Indafoto.ids.length; i++) {
Indafoto.tabs.addTab(i, {text: Indafoto.ids[i]});
}
Indafoto.tabs.observe("activeTabChange",
Indafoto.onTabChange);
Indafoto.tabs.selectTab(0);
Indafoto.tabs.appendTo(widget.body);
}
A widget.body.innerHTML rész már ismerős lehet. A fülek létrehozása már érdekesebb, egy előre elkészített UWA kontrolt, a TabView-t fogjuk ugyanis ehhez felhasználni. Miután létrehoztunk ezt a konrolt, hozzáadunk annyi fület, amennyi azonosítót találtunk a beállítások között (a fül felirata a felhasználó azonosítója lesz), beállítjuk, hogy mi történjen ha másik fülre vált a felhasználó, kiválasztjuk az első fület, majd az egész kontrolt hozzáfűzzük a widgetünk tartalmához. A pontos paraméterezésről nem ejtenék szót, az előbb belinkelt oldalon részletes doksi olvasható róla.
Már csak az a kérdés, hogy mi történik ha fület váltunk (illetve amikor megjelenik az első fül induláskor). Erre szolgál a váltáskor meghívott onTabChange függvényünk:
Indafoto.onTabChange = function(tabId, data) {
Indafoto.data.currentId = tabId;
Indafoto.data.offset = 0;
widget.setTitle(widget.getValue('title') + ": " +
Indafoto.ids[tabId]);
if (!widget.data[tabId]) {
UWA.Data.getFeed("http://feed.indafoto.hu/"+Indafoto.ids[tabId]+
"/feed", function(data) {
widget.data[tabId] = data;
Indafoto.display(data);
})
} else {
Indafoto.display(widget.data[tabId]);
}
}
A függvény két paramétert kap, az első az éppen újként kiválasztott fül azonosítója, a másik pedig a fülhöz tartozó adatok. Azt kapjuk a gyakorlatban vissza, amit a fülek létrehozásakor, az addTab függvénynek átadtunk. Az offset beállítása a lapozáshoz kell, azt fogja tartalmazni, hogy hányadik elemtől kell megjelenítenünk a képeket.
Ezután a widget.setTitle segítségével beállítjuk a widget fejlécében látható címet, mely a beállításoknál megadott címből, és az aktuális fül címéből fog állni. Ha még nem töltöttük le az adatot, akkor letöltjük, majd ha megérkezett, akkor letároljuk és megjelenítjük, ha pedig már korábbi fülváltás során már letöltöttük, akkor megjelenítjük.
Az AJAX kommunikációra különböző lehetőségeink vannak, ezek közül most az előfeldolgozást is tartalmazó feed lekérést használtuk. Az UWA keretrendszer tartalmaz egy feed előfeldolgozót, ami egységes formátumra hozza az összes ismert formátumot, legyen az RSS valamelyik verziója vagy Atom formátum. Az UWA.Data.getFeed “szokásos” aszinkron módon működik, vagyis a kérést elindítja, és majd ha megjött a válasz, akkor meghív egy függvényt.
A megjelenítésért a display nevű függvényünk fog felelni, melynek egyetlen paramétereként a beérkezett feed tartalmat adjuk át akkor is ha éppen most érkezett, akkor is ha már a cache-nek nevezhető tömbünkben már benne volt.
Ez a függvény így fog kinézni:
Indafoto.display = function(feed) {
var tabContent = Indafoto.tabs.getTabContent(Indafoto.data.currentId);
if (!feed) {
tabContent.innerHTML = '<p>Nem létező azonosító, vagy egyéb hiba.</p>'
} else {
tabContent.innerHTML = '';
var pager = new UWA.Controls.Pager({
module: widget,
limit: widget.getValue('nbTitles'),
offset: Indafoto.data.offset,
dataArray: feed.items
});
pager.onChange = function(newOffset) {
Indafoto.data.offset = newOffset;
Indafoto.display(widget.data[Indafoto.data.currentId]);
}
var feedList = widget.createElement('ul');
var j = 0;
for (var i = Indafoto.data.offset; i < feed.items.length; i++) {
if (j++>=widget.getValue('nbTitles')) break;
var item = feed.items[i];
var li = widget.createElement('li');
var a = widget.createElement('a');
a.href = item.link;
a.target = '_blank';
a.desc = item.content.stripTags().truncate(255);
a.onmouseover = function() {
UWA.Utils.setTooltip(this, this.desc, 250);
}
var img = widget.createElement('img');
img.src = item.enclosures[0].url;
a.appendChild(img);
li.appendChild(a);
feedList.appendChild(li);
}
tabContent.appendChild(pager.getContent());
tabContent.appendChild(feedList);
tabContent.appendChild(pager.getContent());
}
}
Ez az eddigi leghosszabb függvényünk, de ez sem igazán bonyolult. Az első sorában a TabView kontrol megfelelő metódusának meghívásával lekérdezzük azt a “div”-et, amibe pakolhatjuk a tartalmat. Ezután megnézzük, hogy sikerült-e megkapnunk a feed tartalmat, ha nem, akkor egy hibaüzenetet jelenítünk meg.
Az ezutáni kód három fő részből áll. Elsőként létrehozunk egy lapozásért felelős Pager kontrolt, majd felépítjük a képekből álló listát, s végül kipakoljuk ezeket a fülbe.
A Pager kontrolt szintén nem fogom most részleteiben bemutatni, jó a doksija. Beállítjuk a fő paramétereit, melyek azt mondják meg, hogy mennyi elem között kell lapozni, egy oldalra mennyi elem jut, stb., utána pedig egy függvény létrehozásával megmondjuk, hogy mi történjen mikor lapozunk.
A képek megjelenítéséhez létrehozunk egy felsorolást (ul) a widget.createElement-tel – ez a document.createElement-hez hasonló függvény -, majd végigmegyünk a feed elemein, és létrehozunk listaelemeket, azokba linkeket, és azokba képeket.
Végül a fül tartalmának elejére és végére betesszük a lapozót, közé pedig a képekből álló felsorolást.
Ezzel készen is lennénk ami a JavaScript kódot illeti, de hogy jól is nézzen ki a képekből álló felsorolás, még adjuk hozzá a következő CSS kódot a widget fejlécében:
.nv-tabContent ul,
.nv-tabContent ul li {
display: block;
margin: 5px;
padding: 0;
background: none;
}
.nv-tabContent ul li {
background: #000;
padding: 1px;
float: left;
}
És már működik is a widgetünk:

Tippek és trükkök
Összegyűjtöttem pár tippet és trükköt is, melyet nem árt, ha ismerünk ha UWA widget fejlesztésére adjuk a fejünket. Íme:
- Mindenekelőtt olvasd végig a teljes doksit, és nézzed át a fejlesztői blog bejegyzéseit, különös tekintettel az archívumra. A doksi nem hosszú, de számos további tippet és trükköt sorol fel, kitérve esetleges platform sajátosságokra is. Mindenképpen javaslom ezt a lépést, mert sokkal jobban képbe lehet jönni a lehetőségeket illetően, nem beszélve a példakódokról.
- Az UWA widgeted különböző böngészőkben, különböző platformokon fog futni. A Netvibes oldalán “bármilyen” böngészővel használhatják, az Apple Dashboardon WebKit, Opera Widgetként Opera megjelenítő motor fogja megjeleníteni. Használj szabványos megoldásokat (XHTML, CSS, JavaScript), melyek mindegyik böngészőben, motorral működni fognak!
- Az UWA működéséhez főként az szükséges, hogy a feldolgozóscript hozzáférjen a kódunkhoz. Ez akkor lehetséges csak, ha a kódunk valamilyen publikusan elérhető címen, egy webszerveren érhető el. Kivétel a Standalone mód, de azt nem ajánlom fejlesztésre.
- Az UWA fejlesztést legegyszerűbb élesben végezni. Ehhez látogasd meg a Netvibes oldalát, válaszd ki a tartalom hozzáadását a felső sávból, az Esential widgeteket, majd onnan (lapozás után) az UWA widgetet. A beállítások között add meg a widgeted címét, majd kattintsd be mind a két checkbox-ot: legyen inline is a widget, és tiltsad is le a cache-t. Így a widget refresh gombjának megnyomásával mindig a legfrissebb kódod lesz meg.
- A widget refresh gombja mindaddig újratölti az egész widgetet számodra, amíg le nem kezeled az onRefresh eseményt. Ezután a teljes oldalt kell újratöltened, ha a widget kódját újra szeretnéd tölteni. Ha már van Netvibes oldalad, hozz létre egy üres, csak a fejlesztett widgetet tartalmazó fület a fejlesztéshez.
- Minél több dolog kerüljön bele a widgetbe, minél kevesebb információt tölts be. Így lesz gyors a widgeted.
- Ne használj egyedi id-kat a widgeteden belül, mivel előfordulhat, hogy a widgeted több példányban jelenik megy egy ugyanazon oldalon belül, ekkor pedig ütközés léphet fel az id-kat illetően. Használj class-okat, és a widget.getElementsByClassName(‘class’)[0] formát.
- A függvényeidet tegyed egy a widget neve alapján elnevezett objektumba, pl. var Widgetem = {}; Widgetem.display = function() {}; Widgetem.hide = function() {}; stb. Így jóval áttekinthetőbb, szebb kódot kapsz, és nem fog semmivel sem ütközni a függvény elnevezésed (ki tudja melyik platform milyen függvényeket használ…)
- Kódolj angolul, ha segítséget kell kérned, jóval könnyebb lesz kommunikálnod, nem kell még a fordítással is foglalkozni. Jelenleg több külföldi, angolul beszélő fejlesztő van a világon, mint magyar.
Linkgyűjtemény
Pár link, melyek segíthetnek a fejlesztés során:
Ginger újdonságok
A Ginger még regeteg mindent fog hozni az UWA kapcsán, de már most elérhető két újdonság:
- widget.readOnly – ha publikus oldalon jelenik meg a widget, akkor igaz az értéke, különben hamis – publikus oldalon másképp viselkedő widget készíthető segítségével
- widget.addStar – bármilyen link + szöveg pár hozzáadható a Netvibes hírfolyamhoz is – a Ginger szociális újdonságai kapcsán
Ezeken kívül is készülünk az OpenSocial-lal, és egyéb meglepetésekkel.
Összefoglalás
Nem lett rövid olvasnivaló amit a fentiekben összeszedtem, illetve az előadásomban előadtam, főként akkor, ha még utána is olvasol a lehetőségeknek. Remélem hasznos információkkal bírtam szolgálni, és több magyar UWA widget fog készülni a jövőben.