Vyuºití JavaScriptových knihoven - ExtBrain
Transkript
České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů Bakalářská práce Využití JavaScriptových knihoven Lukáš Kukačka Vedoucí práce: Ing. Tomáš Novotný Studijní program: Softwarové technologie a management, Bakalářský Obor: Softwarové inženýrství 26. května 2010 iv v Poděkování Zde můžete napsat své poděkování, pokud chcete a máte komu děkovat. vi vii Prohlášení Prohlašuji, že jsem práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon). V Dobříši dne 15. 5. 2008 ............................................................. viii Abstract Translation of Czech abstract into English. Abstrakt Abstrakt práce by měl velmi stručně vystihovat její podstatu. Tedy čím se práce zabývá a co je jejím výsledkem/přínosem. Očekávají se cca 1 – 2 odstavce, maximálně půl stránky. ix x Obsah 1 Úvod 2 Zhodnocení knihoven 2.1 jQuery . . . . . . . 2.2 Dojo . . . . . . . . 2.3 Ext . . . . . . . . . 2.4 MochiKit . . . . . 2.5 MooTools . . . . . 2.6 YUI . . . . . . . . 1 pro JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Vektorová grafika v JavaScriptu 3.1 Vektorové formáty pro web . . . . . . . . . . . 3.1.1 SVG . . . . . . . . . . . . . . . . . . . . 3.1.2 VML . . . . . . . . . . . . . . . . . . . . 3.1.3 Silverlight . . . . . . . . . . . . . . . . . 3.2 Implementace v různých prohlížečích . . . . . . 3.3 Knihovny pro cross-browser vektorovou grafiku 3.3.1 dojox.gfx . . . . . . . . . . . . . . . . . 3.3.2 Raphaël . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 . 3 . 7 . 9 . 11 . 12 . 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 15 17 17 18 18 18 20 4 Popis problému a specifikace cíle ukázkové aplikace 4.1 Popis řešeného problému . . . . . . . . . . . . . . . . 4.2 Existující implementace . . . . . . . . . . . . . . . . 4.2.1 Raphaël Graffle . . . . . . . . . . . . . . . . . 4.2.2 Litha–Paint . . . . . . . . . . . . . . . . . . . 4.2.3 RichDraw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 21 22 22 22 22 5 Analýza a návrh řešení ukázkové aplikace 5.1 Analýza implementovatelnosti a volba knihoven 5.2 Funkční prototypy . . . . . . . . . . . . . . . . 5.3 Návrh . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Struktura kódu aplikace . . . . . . . . . 5.3.2 Node . . . . . . . . . . . . . . . . . . . . 5.3.3 Skupina . . . . . . . . . . . . . . . . . . 5.3.4 Spojení mezi nody . . . . . . . . . . . . 5.3.5 Pohyb nodů a překreslování cest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 24 25 25 27 28 29 31 xi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii 6 Realizace ukázkové aplikace 6.1 Inicializace aplikace . . . . . . . . . . . . . . 6.2 Rozhraní zpráv . . . . . . . . . . . . . . . . 6.2.1 Zpráva uživateli . . . . . . . . . . . . 6.2.2 Debugovací informace . . . . . . . . 6.3 Inicializace uživatelského rozhraní . . . . . . 6.4 Vlastní selektory . . . . . . . . . . . . . . . 6.5 Ovládací prvky . . . . . . . . . . . . . . . . 6.5.1 Funkce pro práci s ovládacími prvky 6.6 Vybírání elementů . . . . . . . . . . . . . . 6.6.1 Metoda topGroups . . . . . . . . . . 6.6.2 Metoda mmSelectable . . . . . . . . 6.7 Vkládání nodů . . . . . . . . . . . . . . . . 6.8 Pohyblivost nodů a skupin . . . . . . . . . . 6.8.1 Metoda mmDraggable . . . . . . . . 6.9 Odstranění nodů . . . . . . . . . . . . . . . 6.10 Vytvoření skupiny . . . . . . . . . . . . . . 6.11 Zrušení skupiny . . . . . . . . . . . . . . . . 6.12 Cesty . . . . . . . . . . . . . . . . . . . . . 6.12.1 Vytvoření cesty . . . . . . . . . . . . 6.12.2 Spojení více nodů . . . . . . . . . . . 6.12.3 Hledání vnitřních a vnějších cest . . 6.12.4 Přepočet cest . . . . . . . . . . . . . 6.13 Problémy funkčnosti . . . . . . . . . . . . . OBSAH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 33 35 35 36 36 37 37 37 38 38 38 39 40 40 42 42 43 44 44 44 45 46 47 7 Závěr 49 Literatura 51 A Editace základního vzhledu 53 B Specifické vlastnosti kódu 55 B.1 jQuery length vs. size() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 C Seznam použitých zkratek 57 D Obsah přiloženého CD 59 Seznam obrázků 2.1 2.2 2.3 2.4 Dojo namespacy v DOM (screenshot z nástroje FireBug) . . . . . . . . . . . . 7 Ukázkový příklad aplikace v ExtJS. V prohlížeči napodobuje plochu a chování oken jako v desktopovém operačním systému . . . . . . . . . . . . . . . . . . 9 Strom vytvořený ukázkovým příkladem. . . . . . . . . . . . . . . . . . . . . . 10 Ukázkový příklad rozhraní aplikace v YUI . . . . . . . . . . . . . . . . . . . . 13 3.1 3.2 Obraz vzniklý z ukázkového SVG dokumentu . . . . . . . . . . . . . . . . . . 16 Obraz vzniklý z ukázkového VML dokumentu . . . . . . . . . . . . . . . . . . 17 5.1 Ilustrace hledání vnitřních spojení ve skupině . . . . . . . . . . . . . . . . . . 29 6.1 6.2 6.3 Screenshot aplikace s diagramem . . . . . . . . . . . . . . . . . . . . . . . . . 34 Screenshot zpráv uživateli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Ukázka znaménka příslušnosti skupině . . . . . . . . . . . . . . . . . . . . . . 43 D.1 Seznam přiloženého CD — příklad . . . . . . . . . . . . . . . . . . . . . . . . 59 xiii xiv SEZNAM OBRÁZKŮ Seznam tabulek 6.1 6.2 Přehled vlastních jQuery selektorů . . . . . . . . . . . . . . . . . . . . . . . . 37 Přehled funkcí pro aktualizaci cesty . . . . . . . . . . . . . . . . . . . . . . . . 47 A.1 Přehled CSS stylů editoru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 xv xvi SEZNAM TABULEK Kapitola 1 Úvod Tohle je includovanej uvod +ěšččřřžžýýáí Úvod charakterizující kontext zadání, případně motivace. Výsledná struktura vaší práce a názvy a rozsahy jednotlivých kapitol se samozřejmě budou lišit podle typu práce a podle konkrétní povahy zpracovávaného tématu. Níže uvedená struktura práce odpovídá práci implementační, viz [?] respektive [?]. 1 2 KAPITOLA 1. ÚVOD Kapitola 2 Zhodnocení knihoven pro JavaScript JavaScriptových knihoven je velké množství a liší se jak ve způsobu vývoje, tak ve svém původním účelu. V této kapitole představím některé z nejrozšířenějších knihoven. Každá knihovna je stručně představena a uvádím její základní či specifické prvky. 2.1 jQuery jQuery je dnes zřejmě všeobecně nejznámější JavaScriptová knihovna. Původní autor John Resig ji poprvé představil v polovině roku 2006 a v současné době se o její správu stará jQuery Project. jQuery je využíváno například společnostmi Google, DELL a mozilla.org nebo oblíbenými a rozšířenými CMS (content management systémy) Wordpress a Drupal. Tato skutečnost jQuery poskytuje široké fanouškovské a vývojářské zázemí. jQuery zjednodušuje procházení HTML dokumentu, zacházení s eventy (uživatelskými akcemi), animacemi a práci s AJAXem. jQuery odděluje chování a od HTML dokumentu. To znamená, že objekty v DOMu nemusí obsahovat akce "on-click"apod., ale pomocí jQuery najdeme objekt a tomu přiřadíme akce. Selektorový engine Sizzle Jedním ze základních prvků jQuery je další počin autora jQuery a sice CSS selektorový engine Sizzle. Ten byl plně integrován do jádra jQuery od verze 1.3, tedy od ledna 2009. Sizzle je napsaný v čistém JavaScriptu (tudíž není závislý na žádné specifické knihovně) a díky tomu umožňuje snadnou integraci do knihoven. Nabízí možnost vyhledávat elementy v DOM ne pouze na základě atributů id, class apod., ale i složitějších požadavků jako určení pořadí výskytu, porovnání atributů, procházení ve stromové struktuře DOM, druhu objektu a dalších. 3 4 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT Základní prvky Jednou z prvních vlastností, se kterou se vývojář setká hned po nainstalování jQuery, je nahrazení window.onload... vlastní metodou $(document).ready(function().... jQuery metoda .ready() (jednoduše vysvětlená zde [2]) obsluhuje událost domready. Podle jQuery dokumentace [14] bude metoda spuštěna, když je DOM plně načten. To znamená, že je sestaven DOM, nicméně nemusí být nutně načteny všechny obrázky. Event má několik možných způsobů zápisu, vždy je ale ekvivalentní se zápisem $(document).ready(handler) tedy "jakmile je plně připraven DOM dokumentu, zavolej handler". V praxi se jako handler nejčastěji používá anonymní funkce $(document).ready(function(){ kód, který má být vykonán }); jQuery objekt je vytvořen jednoduchým způsobem. Kód vypadá následovně: $("selektor").metoda("parametr"); Kde $ označuje jQuery objekt a selektor je výraz kompatibilní se selektorovým enginem Sizzle. Ten určuje, s jakým elementem se bude pracovat. Je vytvořena kolekce objektů, nad kterou jsou volány jQuery metody. Většina jQuery metoda vrací jQuery objekt, což umožňuje zřetězení metod (výjimkou je např. metoda size() vracející integer počet elementů v kolekci). Zřetězení je volání metod za sebou v rámci jednoho příkazu, jak je ukázáno na následujícím příkladě: $("#nejakyDiv") .children("a") .addClass("vybranyOdkaz"); Uvedený kód najde element s atributem id odpovídajícím řetězci nejakyDiv a vytvoří objekt. Funkce .children("a") najde potomky, které jsou hypertextové odkazy a všem přidá HTML třídu vybranyOdkaz V jádře jQuery jsou zakomponovány funkce pro správu událostí (event handling). Pokud je daná událost na elementu vyvolána, je spuštěna akce, která se k dané události váže. Základem připojování událostí k elementům je metoda .bind(). Základní využití vypadá takto: $(’selektor’).bind(’akce’, function() { // zde bude kód, který je vykonán }); 2.1. JQUERY 5 Na vybrané elementy bude připojena událost akce. Pokud zvolená událost nastane, bude vykonána tzv. callback funkce. Mezi použitelné události patří všechny akce myši (od stisku tlačítka, přes kliknutí až po pohyb) a klávesnice (stisknutá klávesa) a akce specifické pro druh elementu, jako například focus pro textové pole nebo submit pro odeslání formuláře. Pro zjednodušení je možné všechny události nastavit zkráceným voláním $(’selektor’).click(function() { // zde bude kód, který je vykonán }); kde click je název události. Za zmínku jistě stojí metoda jQuery.noConflict() umožňující kompatibilitu s některými jinými knihovnami. jQuery jako svůj klíčový znak užívá $, nicméně ten je používán například knihovnou Prototype. Volání var jq = jQuery.noConflict(); umožní používat vlastní značení jq namísto $. Zdrojový kód jQuery kód se skládá z několika hlavních částí: • Definice objektu jQuery • Definice zahrnutého selektorového enginu Sizzle (od verze 1.3) • Pomocné metody Definice jQuery objektu sama o sobě obsahuje několik základních metod jádra vytvořených pomocí jQuery.prototype, převážně pro práci s poli a kolekcemi jQuery objektů. Na tyto metody lze přistoupit i zkráceným zápisem jQuery.fn. Jednou z nejzajímavějších je metoda jQuery.extend (přesněji jQuery.fn.extend). Ta umožňuje rozšiřování objektu jQuery o téměř jakoukoli část. Umožňuje nejen vkládat nové metody: jQuery.fn.extend({ novaMetoda1 novaMetoda2 }); : function() { ... }, : function() { ... } ale například i rozšiřovat si sadu předdefinovaných selektorů o své vlastní například takto: $.extend($.expr[’:’],{ block: function(a) { return $(a).css(’display’) === ’block’; } }); 6 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT Zápis objektu pomocí výrazu $(’:block’) poté vybere všechny elementy, jejichž CSS hodnota display je block. Podobně jde filtrovat podrobněji např. $(’div:block’) vybere všechny elementy div, které mají CSS hodnotu display rovnu block. Vlastní selektor se dá použít pro složitější hledání a může kombinovat více podmínek. Můžeme pomocí něho vybrat například jen ty elementy, které mají šířku menší než 100px a zároveň mají nějakou specifickou třídu class. jQuery UI Součástí projektu jQuery je knihovna jQuery UI [15] nabízející snadné použití vizuálních efektů, widgetů pro uživatelské rozhraní a skinovací framework. jQuery UI využívá knihovnu jQuery a je na ní přímo závislý. Stránka pro stažení nabízí možnost vybrat si komponenty, které chceme mít v knihovně zahrnuty. Ve standardní distribuci jsou všechny komponenty zahrnuty. Skinů je na výběr poměrně velké množství a pro možnost přizpůsobení vzhledu je na stránkách projektu webová aplikace ThemeRoller umožňující sestavit si vlastní téma. jQuery UI se skládá z těchto částí: • JavaScriptový kód rozšiřující možnosti standardního jQuery objektu o efekty a widgety • CSS styl upravující vzhled widgetů • Grafika (obrázky na pozadí a ikonky) jQuery UI si stejně jako jQuery zakládá na jednoduchosti použití. Díky integraci metod efektů do jQuery objektu je lze snadnou použít a zřetězovat, jak je ukázáno výše v kapitole Základní prvky. Widget je použit stejně. Vizuální manipulace s daty Jak jsem již uvedl, pro vizuální manipulaci s daty slouží převážně jQuery UI, konkrétně jeho widgety a část pro interakci. Ta řeší například řazení HTML seznamu metodou Sortable. Základní použití interakční části kódu jakož i widgetů je velice snadné a vypadá následovně: $("#sortable").sortable({ placeholder: ’ui-state-highlight’ }); Vytvořím jQuery objekt pomocí selektoru a zavolám na něj metodu (v ukázkovém příkladě Sortable) a té případně vložím objekt jako parametry. Všechny fungují s výchozím nastavením i bez parametrů. 2.2. DOJO 2.2 7 Dojo Dojo Toolkit je open source JavaScriptová knihovna. Toolkit v názvu přesněji říká "sada nástrojů". Je navržena pro zjednodušení rychlého vývoje cross-browser aplikací založených na JavaScriptu a AJAXu. Dle [8] její vývoj započali v roce 2004 Alex Russell, Dylan Schiemann, David Schontzler a další pod duální licencí BSD a AFL. O správu knihovny se stará nezisková organizace Dojo Foundation. Stejně jako ostatní nejčastěji používané knihovny, i Dojo používají některé světově známé firmy jako AOL či Cisco. Dojo podporuje CSS3 selektory a funguje ve všech nejpoužívanějších prohlížečích (IE6+, Firefox, Safari, Opera, Chrome) Pěkný seriál o Dojo v češtině je na webu zdroják.cz [3]. Základní prvky Základní funkce Dojo jsou obsaženy v jádře nazvaném Base Dojo v souboru dojo.js (dokumentace [7]. Další funkcionalita je nahrávána z modulů určených namespacem (jmenným prostorem) [3]. Dojo striktně dodržuje zápis pomocí namespaců, aby nezaplňoval hlavní namespace velkým množstvím funkcí. Pro ukázku: Obrázek 2.1: Dojo namespacy v DOM (screenshot z nástroje FireBug) Vyznačené objekty vytvořilo Dojo jako svůj namespace. Metody v namespacu Dojo nezasahují mezi ostatní funkce a nemohou proto být v konfliktu například s jinými knihovnami. Zápis namespaců je oddělen . (tečkou) takto: dojo.fx Pokud je potřeba další funkcionalita mimo jádro (což je velice často), je nahrán namespace obsahující danou funcionalitu následovně: dojo.require("dojo.fx.easing"); Metoda dojo.require() nahrává další pluginy, v tomto případě easing (styl průběhu animace - lineární, "elastický"apod). 8 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT Dojo definuje vlastní metodu, která je spuštěna, když je připraven DOM. Je to metoda dojo.addOnLoad(). Používá se podobně jako u jQuery v kombinaci s anonymní funkcí takto: dojo.addOnLoad(function() { kód pro vykonání }); Dojo má vlastní selektorový engine podporující CSS3. Hledání elementu pomocí CSS zápisu provádí metoda dojo.query("selektor") Základní zacházení s událostmi provádí metoda dojo.connect(). Jak je zvykem, na jakýkoli element umí připojit události DOM (click, mousedown apod.). Dojo connect ovšem umí propojit akci na jakýkoli objekt. Umožňuje tedy například připojit funkci na další funkci. var handle = dojo.connect(dojo, "require", function(arg) { console.log("Bylo voláno require() s argumenty: ", arg) dojo.disconnect(handle); }); dojo.require("dojo.io.iframe"); Ukázkový kód z [3] připojí akci na metodu dojo.require(). Jakmile je tedy zavolána, je zavolána i připojená funkce. V jádře Dojo jsou i metody pro AJAX. Velké množství další funkcionality je možné načíst pomocí pluginů. K projektu Dojo patří Dijit a DojoX. • Dijit - skinovatelná knihovna uživatelského rozhraní pro Dojo, obsahuje velké množství widgetů a ovládacích prvků, které běžně HTML nepodporuje (slider, paletu barev a další) • DojoX (Dojo eXtensions) - rozšíření a podprojekty (např. dojox.Drawing pro vektorovou grafiku) 2.3. EXT 2.3 9 Ext Ext je JavaScriptová knihovna vhodná především pro vytváření interaktivních webových aplikací. Ext se skládá hlavně z prvků uživatelského rozhraní (různé panely, editory, pole pro tabulkové a stromové strukury atd).) a proto si zakládá na vyladěném a jednotném vzhledu svých komponent. Nejlépe jsou možnosti vidět na ukázkových příkladech [11] Obrázek 2.2: Ukázkový příklad aplikace v ExtJS. V prohlížeči napodobuje plochu a chování oken jako v desktopovém operačním systému V době psaní práce je nejnovější verzí Ext 3.2. Na knihovně Ext je zajímavé licencování. Zatímco ostatní knihovny jsou většinou open source a zdarma a placená je případně až podpora, Ext má jiný přístup. Je zdarma pro osobní, výukové a nekomerční využití, pro komerční využití je potřeba licence v ceně řádově stovek amerických dolarů. Dle [10] v roce 2006 začal Jack Slocum vyvíjet množinu rozšíření pro knihovnu YUI 2.6. Ta byla rychle zorganizována do nezávislé knihovny pod názvem "yui-ext". Knihovna získávala rychle popularitu a následně byla přejmenována na Ext. Oficiální verze 1.0 byla uvedena 1.4.2007. 10 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT Základní prvky Objekty Ext jsou vytvářeny v nějakém HTML elementu. Vytváření objektu demonstruji na ukázkovém příkladu, který jsem vytvořil pro řazení stromové struktury. Ext.onReady(function(){ var json = [ {"text" : "pes", "id" : 100, "leaf" : false, "cls" : "folder", "children" :[ {"text" : "maly;", "id" : "100000", "leaf" : true, "cls" : "file"}, ... ]}]; var Tree = Ext.tree; var tree = new Tree.TreePanel({ useArrows: true, autoScroll: true, animate: true, enableDD: true, containerScroll: true, root: { text: ’strom’, draggable: false, id: ’src’, children: json } }); tree.render(’tree-div’); tree.getRootNode().expand(); }); Do proměnné json jsou vložena data, která se zobrazí ve stromové struktuře. V ukázce kódu jsou data nekompletní. Do proměnné Tree se vytvoří nový objekt Ext.tree, tedy stromová struktura. V proměnné tree je vytvořen ze stromové struktury panel s určitými vlastnostmi. tree.render(’tree-div’) vykreslí stromovou strukturu do elementu s id tree-div. Poslední řádek rozbalí kořen stromu. Výsledek vypadá takto: Obrázek 2.3: Strom vytvořený ukázkovým příkladem. 2.4. MOCHIKIT 2.4 11 MochiKit MochiKit je malá JavaScriptová knihovna, kterou vyvíjí a udržuje Bob Ippolito. Kód je inspirovaný jazykem Python. Motem MochiKit je "makes JavaScript suck a bit less". MochiKit je rozdělen do několika souborů, každý z nich je balíčkem funkcionality. Jednotlivá funkcionalita je nahrávána načtením daného souboru. Výhodou MochiKitu je přehledná dokumentace [18], kde je pro každou funkcionalitu ukázkový kód, a její jednoduché využítí. Nevýhodou je, že na internetu existuje velice málo tutoriálů. Základní prvky V hlavičce HTML souboru je nutno načíst soubory s potřebnou funkcionalitou. Hledání elementů podle CSS selektoru je podporováno v balíčku MochiKit.Selector. Metody jsou vždy volány s celým jmenným prostorem podobně jako u Dojo. 12 2.5 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT MooTools MooTools začal vyvíjet Valerio Proietti a v září 2006 vydal první verzi. Knihovna je inspirována knihovnou Prototype a vychází z pluginu Proietti pro Prototype. Knihovna je založená na možnosti rozšiřování existujících objektů, tedy přidávání k existujícím objektům vlastní metody. MooTools se drží objektově orientovaného přístupu k JavaScriptu. Dokumentace MooTools [19] je přehledná a obsahuje velké množství ukázek kódu. Základní prvky MooTools poskytuje několik balíků, z nichž každý rozšiřuje jinou část základního JavaScriptu. Základem je balík Core , který přidává metody pro základní práci s JavaSciptem (kontrola existence proměnné, zjištění typu objektu, spojení objektů, iterace objekty a další). MooTools obsahuje vlastní systém vytváření tříd a dědičnosti. Příklad (převzatý z dokumentace): var Animal = new Class({ initialize: function(age){ this.age = age; } }); var Cat = new Class({ Extends: Animal, initialize: function(name, age){ this.parent(age); //will call initalize of Animal this.name = name; } }); var myCat = new Cat(’Micia’, 20); alert(myCat.name); //Alerts ’Micia’. alert(myCat.age); //Alerts 20. Jako Animal je vytvořena třída. Metoda initialize je konstruktor třídy, její argumenty jsou argumenty při vytváření třídy. Třída Cat rozšiřuje třídu Animal a nastavuje vlastní konstruktor, který akceptuje 2 argumenty. Do proměnné myCat je vytvořen nový objekt Cat. Balíky Native rozšiřují funkcionalitu základních objektů Array, Function, Number, String, Hash a Event. Hledání elementů pomocí CSS selektorů umožňuje balík Utilities/Selectors. Hledat lze relativně k elementu $(’#el’).getElements(’div’); nebo absolutně ve stránce $$(’#el div’); Kód nalezne všechny elementy div uvnitř elementu s id el. 2.6. YUI 2.6 13 YUI YUI je zkratka z Yahoo! User Interface Library. YUI je knihovna vyvíjená společností Yahoo představená v únoru 2006. V době psaní práce je nejnovější verze YUI 3.1.1 uvedená v 5.5.2010. Jak je uvedeno uvedeno v 2.3, knihovna Ext vychází z YUI. Obě knihovny jsou si proto velice podobné. V komentářích na [22] je řešeno srovnání ExtJS, YUI a jQuery. Uživatel karf (komentář č. 5) vystihuje rozdíly: YUI a ExtJS simulují objektový přístup, jQuery je spíše funkcionální. Obrázek 2.4: Ukázkový příklad rozhraní aplikace v YUI Knihovna YUI je stejně jako ExtJS vhodná pro tvorbu aplikací založených na JavaScriptu. Má vlastní vzhled uživatelského rozhraní a poskytuje velké množství ovládacích prvků a widgetů, které HTML neposkytuje (např. slider). Pro svojí rozsáhlost není příliš vhodná na jednoduché manipulace ve stránce. YUI má kvalitní dokumentaci [21]. Je přehledná a každá vlastnost je rozsáhle zdokumentována i s příklady kódu. 14 KAPITOLA 2. ZHODNOCENÍ KNIHOVEN PRO JAVASCRIPT Základní prvky YUI má funkcionalitu rozdělenu do modulů. Pro načtení modulů lze použít načtení souboru v hlavičce dokumentu nebo metodu use() YUI().use(’node’, function(Y) { var node = Y.one(’#demo’); Y.log(’Found node.. Setting style’); node.setStyle(’backgroundColor’, ’#D00050’); node.set(’innerHTML’, ’<strong>Changed!</strong>’); }); Ukázka kódu je z dokumentace. Metoda use načítá modul pro práci s nodem a function(Y) je nepovinná callback funkce, která bude vykonána. Y.one() najde element podle CSS selektoru a vloží ho do proměnné node. Nad tímto elementem jsou pak provedeny další změny. YUI má vlastní systém vytváření tříd a dědění. Příklad kódu z dokumentace: Bird = function (name) { this.name = name; }; Bird.prototype.flighted = true; Bird.prototype.isFlighted = function () { return this.flighted }; Bird.prototype.getName = function () { return this.name }; Chicken = function (name) { Chicken.superclass.constructor.call(this, name); }; Y.extend(Chicken, Bird); Chicken.prototype.flighted = false; Je vytvořena třída Bird a její prototype rozšířen o metody a proměnné. Dále je vytvořena třída Chicken. Ta je voláním Y.extend(Chicken, Bird) nastavena jako podtřída třídy Bird. Kapitola 3 Vektorová grafika v JavaScriptu Vektorová grafika je způsob reprezentace obrazových dat pomocí geometrických tvarů určených vzorci. Oproti rastrové grafice má tu výhodu, že změna rozměrů se neprojevuje negativně na její kvalitě. 3.1 3.1.1 Vektorové formáty pro web SVG Scalable Vector Graphic [4] je formát pro popis dvojrozměrné vektorové grafiky. SVG byl navržen a vytvořen konsorciem W3C tak, aby splňoval požadavky vektorové grafiky pro web. Má dvě části: • formát souboru založený na XML • API pro grafické aplikace SVG formát umožňuje vytvářet tvary, text a vloženou rastrovou grafiku. Vše je popsáno XML formátem. Pro příklad dokumentu: <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="400" height="300" version="1.1" xmlns="http://www.w3.org/2000/svg"> <rect x="10" y="10" rx="20" width="300" height="200" style="fill:red;stroke:black;stroke-width:5"/> <circle cx="250" cy="200" r="120" style="fill:blue;stroke:black;stroke-width:3;opacity:0.7"/> </svg> 15 16 KAPITOLA 3. VEKTOROVÁ GRAFIKA V JAVASCRIPTU Ukázkový dokument vytvoří SVG plochu o velikosti 400x300px. V něm na souřadnicích [10,10] vykreslí obdélník ("x="10" y="10") o velikosti 300x200 (width="300" height="200"). Bude mít kulaté rohy s poloměrem s rohů 20 (rx="20") a styl daný kódem style="...". Podobně druhý objekt bude kruh na pozici [250,200] s poloměrem 120 (r="120"). Ten navíc bude částečně průhledný. Ukázkový dokument popisuje následující obrázek. Kruh je oříznutý, protože přesahuje z dokumentu. Obrázek 3.1: Obraz vzniklý z ukázkového SVG dokumentu 3.1. VEKTOROVÉ FORMÁTY PRO WEB 3.1.2 17 VML Vector Markup Language [5] je formát pro popis vektorové grafiky psaný syntaxí XML stejně jako HTML. VML používá CSS stejným způsobem jako HTML pro určení vzhledu prvků. Formát VML je vyvinut a prosazován firmou Microsoft. Podpora v produktech Microsoftu je od IE5 a od Office 2000. Následující VML kód vykreslí podobný obrázek jako u příkladu k SVG <html xmlns:v> <style>v\:*{behavior:url(#default#VML);position:absolute}</style> <body> <v:roundrect style="left:10px;top:10px;width:400px;height:300px" arcsize="0.1" fillcolor="red" strokecolor="black" strokeweight="5px" /> <v:oval style="left:250px;top:200px;width:240;height:240;" fill="true" fillcolor="blue" strokecolor="black" strokeweight="3px" /> </body> </html> Výsledkem bude následující obrázek Obrázek 3.2: Obraz vzniklý z ukázkového VML dokumentu 3.1.3 TODO Silverlight 18 KAPITOLA 3. VEKTOROVÁ GRAFIKA V JAVASCRIPTU 3.2 Implementace v různých prohlížečích Problémem implementace vektorové grafiky na webu je podpora prohlížečů. Microsoft ve všech dosavadních verzích prohlížeče podporuje VML a nikoli SVG, které je navrženo přímo pro web. Podle [13] má Internet Exploreru verze 9 už SVG podporovat. Všechny ostatní nejčastěji rozšířené prohlížeče (Mozilla Firefox, Safari, Google Chrome, Opera) podporují SVG. 3.3 Knihovny pro cross-browser vektorovou grafiku Cross-browser implementace knihovny musí řešit rozdílnou podporu prohlížečů. To znamená, že pro práci s grafikou knihovna poskytuje jednotné rozhraní, ale vnitřně pracuje podle druhu prohlížeče. Pokud je grafika vykreslována v IE, vykresluje ji pomocí VML, v ostatních prohlížečích pomocí SVG. V obou prohlížečích musí ovšem grafika vypadat stejně. 3.3.1 dojox.gfx dojox.gfx [9] je rozšíření knihovny Dojo pro vytváření cross-browser vektorové grafiky. Vybírá nejvhodnější engine (SVG, VML, Silverlight, Canvas) v daném prohlížeči a za pomocí toho vykresluje grafiku. Každý obraz vykreslený pomocí dojox.gfx je vykreslován na Surface (povrch). Surface je obdélníkový kontejner pro grafiku. Stránka může obsahovat více Surfaců, každý má vlastní poziční systém od souřadnic [0,0]. Na Surface jsou vytvářeny objekty Shape (tvar). dojox.gfx podporuje následující tvary: • Rectangle - obdélník (možno se zakulacenými okraji) • Circle - kruh • Ellipse - elipsa • Line - čára • Polyline / polygon - lomená čára / mnohoúhelník • Path - cesta/křivka (nejpřizpůsobivější tvar) • Image - obrázek • Text • TextPath - křivka, jejíž tvar bude kopírovat text Každý tvar má vlastnosti geometrické (popis tvaru) a vizuální (vytažení tvaru, vyplnění tvaru). Detaily v dokumentaci [9]. V dokumentaci jsou rovněž informace o odlišném chování v různých prohlížečích a jaké volby nejsou podporovány v jednotlivých technologiích. 3.3. KNIHOVNY PRO CROSS-BROWSER VEKTOROVOU GRAFIKU 19 V dojox.gfx jsem vytvořil příklad, který vytvoří obrázek jako ukázkové SVG 3.1.1 a VML 3.1.2 dokumenty. JavaScriptový kód vypadá následovně: dojo.require("dojox.gfx"); dojo.require("dojo.colors"); dojo.addOnLoad(function(){ var surface = dojox.gfx.createSurface("canvas", 400, 300); var rect = surface.createRect({ x: 10, y: 10, width: 300, height: 200, r: 20 }).setFill("red").setStroke({ color: "black", width: 5 }); var transparentBlue = new dojo.Color([0, 0, 255, 0.7]); var circ = surface.createCircle({ cx: 250, cy: 200, r: 120 }).setFill(transparentBlue).setStroke({ color: "black", width: 3 }); }); Metody dojo.require() načtou balíčky pro práci s dojox.gfx a barvami. V metodě addOnLoad je vytvořen Surface (povrch na vykreslování) v elementu s id canvas. Metody surface.createRect() a surface.createCircle() vytvoří tvary v Surfacu a dalšími metodami jim jsou nastaveny atributy. Zajímavé je volání new dojo.Color([0, 0, 255, 0.7]), to vytváří objekt barvy s průhledností. Předpis je tvaru (R, G, B, opacity). Průhlednost nelze nastavit běžným voláním metody. 20 KAPITOLA 3. VEKTOROVÁ GRAFIKA V JAVASCRIPTU 3.3.2 Raphaël Raphaël je JavaScriptová knihovna zjednodušující práci s vektorovou grafikou na webu. Knihovnu vyvíjí Dmitry Baranovskiy. Autor ji představuje na [1]. Raphaël používá SVG a VML pro vykreslení grafiky v prohlížeči. Raphaël umožňuje vytvářet stejné základní tvary jako dojo.gfx (kromě TextPath) a každému nastavovat vlastnosti. Raphaël vytváří v elementu obdélníkový Canvas s danými rozměry. V canvasu jsou vykreslovány všechny tvary vytvářené Raphaëlem. V Raphaëlu jsem vytvořil stejný příklad jako v dojox.gfx, aby bylo možné srovnat zdrojový kód pro stejný obrázek. window.onload = function(){ var paper = Raphael("canvas", 400, 300); var rect = paper.rect(10, 10, 300, 200, 20).attr({ fill: "red", stroke: "black", "stroke-width": 5 }); var circ = paper.circle(250, 200, 120).attr({ fill: "blue", "fill-opacity": 0.7, stroke: "black", "stroke-width": 3 }); }; Do proměnné paper je vytvořen Raphaël Paper objekt, ve kterém jsou vykreslovány tvary. Metody paper.rect a paper.circle vytvoří obdélník a kruh o dané velikosti. Veškerý vzhled (atributy) je nastaven jediným volání metody attr(). Metoda attr umožňuje přímo nastavit průhlednost. Kapitola 4 Popis problému a specifikace cíle ukázkové aplikace Vzorová aplikace má za cíl demonstrovat využití některé z popisovaných JavaScriptových knihoven v kombinaci s vektorovou grafikou pro JavaScript. 4.1 Popis řešeného problému Při konzultacích s vedoucím práce jsme usoudili, že vhodnou úlohou pro demonstraci výše zmíněného bude implementovat základní funkcionalitu editoru diagramů. Jednotlivé objekty je možné vkládat, odstraňovat, vybírat, propojovat a přesouvat. 21 22KAPITOLA 4. POPIS PROBLÉMU A SPECIFIKACE CÍLE UKÁZKOVÉ APLIKACE 4.2 4.2.1 Existující implementace Raphaël Graffle Graffle [12] je ukázkovým příkladem knihovny RaphaëlJS. Umožňuje přesouvat vektorové tvary a při přesouvání jsou přepočítávány cesty mezi tvary. Je tedy svým chováním blízko aplikaci z této práce. Graffle nevyužívá žádnou další JavaScriptovou knihovnou, pracuje pouze pomocí knihovny RaphaëlJS a eventů prohlížeče. 4.2.2 Litha–Paint Litha–Paint [17] je v současné době ve stádiu beta verze. Litha–Paint je plnohodnotný online vektorový editor, takže má jiný účel než aplikace popisovaná v této práci. Nicméně je zajímavé ji zhodnotit technologicky. Přestože tvůrci na úvodní stránce uvádějí tučným písmem "free HTML+Javascript vector graphic editor", čili "HTML+Javascript editor vektorové grafiky zdarma", editor není čistě HTML+JavaScript editorem. Po analýze nástrojem FireBug je vidět, že na HTML je založeno uživatelské rozhraní a editační prvky grafiky a na JavaScriptu odezva uživateli, nicméně po provedení akce je provedena komunikace se serverem a obraz v prohlížeči je vytvořen vyskládáním obrázků 200x200px přes celý editační prostor a tedy nevyužívá VML/SVG grafiku v prohlížeči. 4.2.3 RichDraw RichDraw [20] a je oproti předchozímu jednoduchý editor založený na JavaScriptu a VML/SVG, ovšem je také pouze vektorovým editorem a tedy není relevantní ho srovnávat s prací. Nicméně je při jeho analýze FireBugem dobře vidět kombinace HTML a SVG. Kapitola 5 Analýza a návrh řešení ukázkové aplikace V této kapitole je popsán návrh aplikace a specifika realizace. Je třeba vysvětlit základní pojmy, které využívám: • node - objekt (uzel) v diagramu, je možné ho spojovat spojeními • spojení, cesta - vizuální spojení nodů • drag, draggování - pohyb nodem tažením myši • id - pokud mluvím o id, je téměř vždy myšlena hodnota HTML atributu id nebo identifikační řetězec cesty 5.1 Analýza implementovatelnosti a volba knihoven Pro splnění požadavků jsem potřeboval následující funkcionalitu: • vkládání a odstraňování elementů - základní funkce, splní každá knihovna • pohyblivost elementů ("drag & drop"chování) - podporují jQuery UI, Dojo/dnd, YUI • vybírání elementů - hotové řešení nabízí jQuery UI pomocí Selectable • spojení elementů - zajistí knihovny pro vektorou grafiku, RaphaëlJS nebo Dojox.gfx Rozhodl jsem se použít jQuery. Poskytuje "drag & drop", mám s ním předchozí zkušenosti a pracuje se mi s ním nejsnáze. Dále jsem se seznámil s cross-browser implementacemi vektorové grafiky pro JavaScript. Existují dvě vhodná řešení. V obou jsem si zkusil vykreslit několik objektů a zvolil jsem RaphaëlJS. Lépe se mi s ním pracuje. Zajímavým řešením by mohlo být spojení knihovny Dojo Toolkit a DojoX.gfx. To by poskytlo jednotné rozhraní používání knihoven. 23 24 5.2 KAPITOLA 5. ANALÝZA A NÁVRH ŘEŠENÍ UKÁZKOVÉ APLIKACE Funkční prototypy Po zvolení knihoven pro implementaci jsem začal vytvářet technologický prototyp, na kterém jsem vyzkoušel propojení jQuery s RaphaëlJS. 1. prototyp Prototyp uměl vkládal nody. Pohyblivost nodů byla řešena metodou Selectable z jQuery UI. Vybírání jsem řešil vlastním způsobem na základě stavových proměnných. Po kliknutí na node byla volána funkce, která zkontrolovala, jaká akce je vykonávána. Pokud při kliknutí byla stisknuta klávesa CTRL, byl node přidán do výběru, jinak byl vybrán samostatně. Výběr pomocí přetažení obdélníku myši neuměl. Propojování nodů fungovalo na základě stavových proměnných. Po stisknutí tlačítka byla nastavena proměnná. Při kliknutí byla zkontrolována proměnná spojování a pokud byla nastavena, bylo vykresleno spojení mezi nody. Překreslování nodů probíhalo pomalu. Při každém pohybu nodem byly překresleny všechny cesty spojené s nodem. To bylo neefektivní, protože rychlost překreslování byla přímo závislá na počtu spojení. Zdrojový kód jsem psal třídně. Node a cesta měli vlastní třídu. Kód měl následující strukturu: function Node (nodeId) { this.id = nodeId; this.jqObj = $(’#’ + this.id); var text = ’prvni text’; this.create = function() {...}; } Metody a proměnné začínající this. jsou veřejné, ostatní privátní. To umožňuje zapouzdření. V prototypu jsem si ověřil, že danou funkcionalitu je možno pomocí jQuery a RaphaëlJS implementovat. Seznámil jsem se s metodou Draggable, kterou jsem poté využil i ve finální aplikaci. 2. prototyp Aplikace podporovala vkládání nodů. Pohyblivost zajišťovalo jQuery Draggable, vybírání nodů jQuery Selectable. Během vývoje byla přidávána funkcionalita jako editace uvnitř skupiny. Experimentoval jsem se stylem zobrazení skupiny. Řešil jsem, zda ji zobrazit jako obdélník okolo všech vnitřních elementů nebo ji skrýt. Po dohodě s cvičícím jsem se rozhodl, že nejlepším řešení bude element skupiny úplně skrýt nastavením velikosti na 0x0px. Postupné zásahy do aplikace zanesly do kódu nepřehlednost a aplikace se začala stávat neudržovatelnou. Proto jsem se rozhodl na základě chyb vytvořit nový prototyp od začátku. 3. prototyp Třetí prototyp zůstal aplikací popisovanou v této práci. 5.3. NÁVRH 5.3 25 Návrh Na základě zkušeností z prototypů jsem vypracoval návrh chování a struktury aplikace 5.3.1 Struktura kódu aplikace Po dohodě s cvičícím jsem se rozhodl formátovat zdrojový kód podobně, jako to dělá třeba jQuery. jQuery.fn = jQuery.prototype = { init: function( selector, context ) {...}, jquery: "1.4.2", length: 0, size: function() { return this.length; } } Zdrojový kód je strukturovaný podobně jako JSON do stromové struktury. Každý klíč je buď proměnná (v příkladě jquery a length), metoda (size) nebo další objekt ({} za jQuery.fn). Tento formát se dá používat tak, že si kód rozdělím do modulů podle účelu. Pro tento formát kódu je používán název "jmenný prostor ". V aplikaci používám následující strukturu mm = { settings: {...}, canvas: null, paper: null, vars: {}, init: function() {...}, ctrl: { node: {...}, group: {...}, con: {...}, deselect: function() {...} }, dom: {...}, gui: {...}, fn: {...}, msg: {...} }; $.fn.mmSelectable = function(){...}; Array.prototype.unique = function(){...}; (pozn. struktura je zjednodušena a zkrácena, slouží pro ilustraci) 26 KAPITOLA 5. ANALÝZA A NÁVRH ŘEŠENÍ UKÁZKOVÉ APLIKACE Celý kód má vlastní namespace mm. V každém modulu jsou metody a proměnné, které se vztahují ke stejné oblasti funkcionality. Například settings obsahuje nastavení aplikace, fn pomocné funkce, msg funkce pro zobrazení zpráv atd. Proměnné a metody jsou volány pomocí tečkové notace. Každá úroveň ve struktuře kódu značí jednu tečku v zápisu názvu funkce. Pro ilustraci tedy funkce deselect() v modulu ctrl bude volána zápisem mm.ctrl.deselect(); Zdrojový kód je komentován pomocí JSDoc [16] v angličtině. JSDoc je formát dokumentování JavaScriptového kódu přímo inspirovaný formátem JavaDoc. Pro příklad, komentář funkce vypadá následovně: /** * function returns center coordinations of given element * in format like * { * hor: Integer, * ver: Integer * } * hor and ver means "horizontal" (x) and "vertical" (y) * * @param {Object} el jQuery object * @return {Object} center coordinations of element */ getCenter: function(el){... Hodnoty bez @ jsou popis metody, @param značí typ a popis parametru, @return typ a popis návratové hodnoty. Kód je formátován automatickým formátováním vývojového prostředí Aptana Studio 2 [6]. 5.3. NÁVRH 5.3.2 27 Node HTML nodu má následující formát <div id="UUIDElementu" class="nodeDiv"> <div class="nodeText">obsah nodu</div> <div class="groupSigns"></div> </div> • UUIDElementu - vygenerované globálně unikátní id podle RFC 4122. Při vývoji jsem používal id ve tvaru nodeX, kde X bylo číslo nodu podle pořadí, ve které byl vložen. To mi umožňovalo snadněji se vyznat v dokumentu a hledat chyby. • nodeDiv - třída značící, že div element je node Do prvního elementu div uvnitř nodu je vkládán textový obsah nodu. Ve 2. prototypu jsem pro textový obsah nodu neměl vlastní element, což dělalo problémy při editaci. Element s textem a ve skupině vypadal následovně: <div id="node1" class="nodeDiv"> <p>zkusebni text</p> <p>dalsi text<br></p> <div class="nodeGrMembDiv"> <div class="membsign-group1"> </div> </div> </div> Editor, který pracuje nad elementem nodu nodeDiv, se bude snažit pracovat se všemi potomky. Tedy i s elemtem pro značení skupin, což je problematické. Pro každý element jsou ukládána pole paths a parents • paths - id všech spojení, které začínají nebo končí v nodu. Tyto je potřeba překreslit při pohybu nodem • parents - id všech skupin, ve kterých se node nachází. Po vložení nodu do skupiny je na konec pole přidáno id skupiny. Nižší index prvku tedy znamená bližšího rodiče. Slouží pro rychlé hledání specificky vzdálených rodičů (skupiny, která je pohybována při pohybu nodem). 28 KAPITOLA 5. ANALÝZA A NÁVRH ŘEŠENÍ UKÁZKOVÉ APLIKACE Pro ilustraci, při struktuře <div id="group2"> <div id="group1"> <div id="node1" /> </div> </div> bude pole parents elementu node1 vypadat: ["group1", "group2"]. 5.3.3 Skupina HTML kód skupiny má následující strukturu <div id="UUIDSkupiny" class="groupDiv"> 2 až n nodů nebo skupin </div> • UUIDSkupiny - je řešeno stejně jako u nodu • groupDiv - třída značící, že div element je skupina Pro každou skupinu jsou ukládána pole innerPaths, childrens a objekt pathSet • innerPaths - id všech spojení, které jsou uvnitř skupiny (spojení, která začínají a končí v nodech uvnitř skupiny) • childrens - id všech přímých potomků skupiny • pathSet - Raphaël objekt set (množina objektů), obsahuje všechny vnitřní cesty s id podle pole innerPaths Množina pathSet je uchovávána pro možnosti posouvání. Vnitřní cesty nemusí být jednotlivě přepočítávány, když je skupina posouvána. Místo toho je posunuta celá množina spojení jako celek. Dokumentace Raphaël nicméně varuje, že při vytváření množiny pro ní není vytvořený žádný samostatný element. To znamená, že při posouvání Raphaël sám posouvá všechny jednotlivé prvky v množině pomocí vypočtených souřadnic. Pokud by byl pro množinu vytvořen element, výpočty by zařídílo na jádro prohlížeče, což by bylo rychlejší. 5.3. NÁVRH 29 Obrázek 5.1: Ilustrace hledání vnitřních spojení ve skupině Pokud na obrázku jsou node1 a node2 ve stejné skupině (ohraničení jen pro názornost), v poli innerPaths bude pouze id jejich spojení, nikoli ostatních cest. 5.3.4 Spojení mezi nody Spojení mezi nody, jinak také cesta (objekt čáry se v Raphaëlu jmenuje path) je Raphaël objekt path. S cestou není pracováno v DOM. Změny jsou prováděny pouze nad objektem path, případně množinou set cest path. Objekty path tedy musím uchovávat v proměnných. Struktura uchovávající spojení je následující: mm.vars.path = { map: { "nodeA-nodeB": "pathUUID", "nodeB-nodeA": "pathUUID" }, obj: { "pathUUID": { from: "nodeAId", to: "nodeBId", path: RaphaelObjektPath } } } 30 KAPITOLA 5. ANALÝZA A NÁVRH ŘEŠENÍ UKÁZKOVÉ APLIKACE Klíč mm.vars.path uchovává informace o spojeních. Pod klíčem obj jsou uchovávány informace o cestě. Klíč jednotlivých objektů cest je UUID cesty. • from - id elementu, ve kterém spojení začíná • to - id elementu, ve kterém spojení končí • path - Raphaël objekt path reprezentující cestu Informace o počátečním a koncovém nodu spojení je až uvnitř objektu. Při hledání spojení mezi dvěma specifickými nody by bylo nutné projít všechny objekty pod klíčem obj a následně porovnat jejich hodnoty from a to. Proto jsou pod klíčem map uloženy ještě mapovací klíče. nodeA-nodeB jsou id obou elementů oddělená pomlčkou. Mapování z počátečního nodu do koncového i naopak ukazují na stejné UUID, protože nelze zaručit, v jakém pořadí budou nody, jejichž spojení hledám. Například id koncového elementu cesty zjistím zápisem: mm.vars.path.obj["nejakeUUIDcesty"].to Pro ilustraci hledání spojení mezi dvěma nody: 1. zjistím id prvního elementu, např. node4 2. zjistím id druhého elementu, např. node2 3. vytvořím řetězec z jejich id oddělený pomlčkou, tedy node4-node2 4. voláním mm.vars.path.map["node4-node2"] zjistím UUID cesty 5. voláním mm.vars.path.obj["UUIDcesty"].to přistoupím na objekt cesty a zjistím id koncového elementu 5.3. NÁVRH 5.3.5 31 Pohyb nodů a překreslování cest Pohyblivý (draggable) je každý node. Při začátku pohybu nodem je potřeba nastavit collection, pathSet a alonePaths. • collection - jQuery kolekce objektů, které budou přemístěny podle pohybu myši. Může obsahovat nody i skupiny • pathSet - množina spojení, která bude posouvána podle pohybu myši • alonePaths - pole id spojení, která budou přepočítávána Všechny proměnné závisí na stavu aktuálního výběru a druhu dragovaného elementu. • není vybrán žádný element – posouván samostatný node ∗ collection - samotný node ∗ pathSet - prázdný ∗ alonePaths - všechny cesty, které začínají nebo končí v dragovaném nodu – posouván node ve skupině ∗ collection - nejvyšší rodičovská skupina ∗ pathSet - pathSet rodičovské skupiny (obsahuje cesty, které začínají a končí v některém z nodů ve skupině) ∗ alonePaths - všechny cesty, které začínají nebo končí v některém z nodů ve skupině • vybrán alespoň 1 element – collection - mm-selected elementy (vysvětleno dále zde 6.6) – pathSet - všechny vnitřní cesty mezi mm-selected elementy (obsahuje cesty, které začínají a končí některém z nodů ve výběru) – alonePaths - všechny cesty, které začínají nebo končí v nodu ve výběru Při dragování elementu jsou elementy v collection a množina pathSet posunuty o pohyb myši. Akce drag, ve které je volán posun collection a pathSet, může být aktivována až několikrát za vteřinu. Počet volání funkce závisí na rychlosti pohybu myši. Množství posunů (resp. přepočtů souřadnic) je tedy lineárně závislé na rychlosti pohybu myši. Přepočet všech cest alonePaths nemůže být volán při každém pohybu. Rychlost odezvy pro uživatele by potom byla silně závislá na počtu cest v alonePaths. Pro kompenzaci byla navržena metoda překreslování popsaná v sekci 6.12.4 Zkoušel jsem i možnost, kdy byl dragovatelný samostatný node a celá skupina. Problém nastal, když byla dragována skupina, ale ve výběru bylo jen několik nodů z ní. Protože byly pohybovány nody v kolekci a zároveň skupina, chycený node se pohyboval 2x rychleji než ostatní. 32 KAPITOLA 5. ANALÝZA A NÁVRH ŘEŠENÍ UKÁZKOVÉ APLIKACE Kapitola 6 Realizace ukázkové aplikace Kapitola popisuje realizaci ukázkové aplikace. V popisu se zaměřuji na nestandardní části řešení a popis implementace základní funkcionality. 6.1 Inicializace aplikace Po načtení stránky stránky je třeba zavolat inicializační funkci mm.init v jQuery eventu .ready(). $(document).ready(function(){ mm.init(); }); mm.init = function(){ // init canvas and paper mm.canvas = mm.vars.currentContext = $(’#canvas’); mm.paper = Raphael(’canvas’, mm.canvas.width(), mm.canvas.height()); $(mm.canvas).mmSelectable(); // init GUI mm.gui.init(); }; Inicializační funkce provádí inicializaci a přiřazení základních proměnných • mm.canvas ukazuje na element s id canvas, který je rodičovským elementem celého editoru. Jsou v něm prováděny všechny změny DOMu při editaci • mm.paper ukazuje na objekt RaphaëlJS Paper, ve kterém je vykreslována vektorová grafika mm.paper je vytvořen voláním metody Raphael(). Ta vytvoří Raphaël paper v elementu s id canvas o rozměrech tohoto elementu. 33 34 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE Další částí inicializační funkce je vytvoření selectable (možnosti vybírání elementů) pomocí volání $(mm.canvas).mmSelectable(); v elementu mm.canvas. Popis metody se nachází dále v textu 6.6.2. Při inicializaci aplikace je třeba inicializovat uživatelské rozhraní voláním 6.3 mm.gui.init(); Obrázek 6.1: Screenshot aplikace s diagramem 6.2. ROZHRANÍ ZPRÁV 6.2 35 Rozhraní zpráv Vytvořil jsem si vlastní rozhraní pro vytváření zpráv v objektu mm.msg. To umožňuje jednodušší a přehlednější spravování cíle a účelu zprávy. 6.2.1 Zpráva uživateli Tato funkce vytvoří informační okno se zprávou ve stylu MAC OS X, jak je vidět na obrázku níže Obrázek 6.2: Screenshot zpráv uživateli Zpráva je vytvořena voláním funkce mm.msg.notice(str); kde str je obsah sdělení, který může obsahovat HTML formátování. Zdrojový kód funkce vypadá následovně notice: function(str){ $("<div/>", { "class": ’noticeMsg’, "html": str, "css": { opacity: 0.7 } }).hide().appendTo(’#noticePanel’) .fadeIn(2000).delay(5000) .fadeOut(2000, function(){ $(this).css({ display: ’block’, visibility: ’hidden’ }).addClass(’noticeHidden’); if ($(’#noticePanel>div:not(.noticeHidden)’).length == 0) { $(’#noticePanel>div’).remove(); }; }); } 36 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE Pro každou zprávu je vytvořen element div s třídou noticeMsg a HTML obsahem str. Element je ještě před připojením do DOM skryt voláním metody .hide(), aby se element hned neobjevil, ale bylo ho možno animovat. Následně je element připojen do rodičovského elementu. Element bude postupně zobrazen během 2s (2000ms), zůstane 5s vidět a následně bude během 2s skryt. Metoda fadeOut() má vloženu callback funkci, která opravuje standarní chování metody. Po doběhnutí animace by byla elementu nastavena CSS vlastnost display: ’none’. To by způsobilo nežádoucí "poskočení"elementu úplně nahoru v rodiči, které nevypádá dobře pro uživatele. Callback funkce nastaví elementu zpět display:’block’ a skrytí zprávy provede vlastností visibility: ’hidden’, takže element zůstane na místě, jen nebude vidět. Následně elementu nastaví třídu noticeHidden. Ta je dále využita pro kontrolu, zda je ještě nějaká zpráva zobrazena a pokud ne, odstraní všechny zprávy z DOM. 6.2.2 Debugovací informace Tato zpráva při vývoji vypisovala debugovací informace. Debugovací zprávy jsou vkládány do elementu s id debug jako nové řádky. Funkce je volána takto: mm.msg.debug(str); Debugovací informace jsou v ukázkové aplikaci skryty. 6.3 Inicializace uživatelského rozhraní Funkce mm.gui.init() inicializuje uživatelské rozhraní. Provede zakázání tlačítek a nastaví tlačítkům a panelu nastavení vzhled jQuery UI. Canvas div element a Raphael Paper objekt jsou vytvořeny s pevnými rozměry. Proto při inicializaci aplikaci jsou zjištěny rozměry okna a nastaveny canvasu i paperu. Problém by nastal, pokud by byla změněna velikost okna. Canvas i paper by měli pořád stejný rozměr. Proto je oknu prohlížeče nastaven event při změně velikosti resize, který oběma nastaví nové rozměry. $(window).resize(function(){ var w = $(window).width(); var h = $(window).height(); $(mm.canvas).width(w).height(h - $(’#controls’).height()); mm.paper.setSize(w, h); }); 6.4. VLASTNÍ SELEKTORY 6.4 37 Vlastní selektory Pro zjednodušení vybírání a filtrování elementů pomocí jQuery jsem si vytvořil některé vlastní selektory. Jejich přehled a popis je v následující tabulce selektor :nodes :groups popis selektoru vybere pouze elementy, které jsou nodem vybere pouze elementy, které jsou skupinou Tabulka 6.1: Přehled vlastních jQuery selektorů 6.5 Ovládací prvky Ovládací prvky se nachází v horní části editoru ve vlastním panelu. 6.5.1 Funkce pro práci s ovládacími prvky Specifické akce editoru lze provést pouze za určitého stavu (například skupina jde vytvořit pouze pokud je vybráno 2 a více nodů nebo skupin). Proto nemohou být všechna tlačítka aktivní neustále. Pro povolení, zakázání a aktualizaci zobrazení tlačítek jsem vytvořil funkce. Zakázání tlačítek Tlačítko je výchozím stavu povoleno. Jeho zakázání je provedeno voláním mm.gui.btn.disable(el); kde el je jQuery objekt tlačítka. Uvnitř funkce je voláno $(el).button(’disable’); pro zakázání tlačítka vytvořeného pomocí jQuery UI. Povolení tlačítek Tlačítko je povoleno funkcí mm.gui.btn.enable(el) stejně jako u zakázání. Povolení je provedeno podobně jako zakázání nastavením $(el).button(’enable’); Aktualizace tlačítek Funkce mm.gui.btn.refreshAll() provádí aktualizaci stavů jednotlivých tlačítek v závislosti na stavu editoru. Aktualizace probíhá po změně výběru. Jednotlivé stavy a jim náležící povolená tlačítka jsou komentovány ve zdrojovém kódu funkce. 38 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE 6.6 Vybírání elementů Aplikace umožňuje vybírání elementů tažením obdélníku myší tak, jak je uživatel zvyklý z desktopových operačních systémů. Tuto funkcionalitu nabízí jQuery Selectable. jQuery Selectable je jednou z interakčních metod jQuery UI. Jak bylo zmíněno v sekci 6.1, aktivaci Selectable provádí metoda mmSelectable. V souvislosti s vybíráním existují třídy ui-selected a mm-selected • ui-selected - nody vybrané myší pomocí jQuery Selectable. Nody s touto třídou jsou zvýrazněny v uživatelském rozhraní jinou barvou. • mm-selected - node vybraný vzhledem k editoru. Může to být node nebo skupina. Dle této třídy jsou vybírány elementy, se kterými se bude pracovat, a určují aktivní ovládací prvky. Pokud je mm-selected skupina, všechny nody uvnitř ní jsou ui-selected, tedy zvýrazněny v uživatelském rozhraní. 6.6.1 Metoda topGroups Metoda topGroups(index) je vlastní jQuery metoda, která hledá nejvyšší rodiče elementů pro účely vybírání. Pokud je vybrán node a má s ním být vybrána celá skupina, metoda je zavolána na element nodu a ta vrátí element celé skupiny. Pokud node nemá žádného rodiče, je vrácen samotný node. 6.6.2 Metoda mmSelectable Metoda mmSelectable je mým vlastním rozšířením funkcionality jQuery. Vlastní metodu jsem vytvořil pro zpřehlednění inicializační funkce. Metoda je volána nad elementem, uvnitř kterého bude možnost vybírat. Vytváří běžný jQuery Selectable s mými požadovanými vlastnostmi a callback funkcemi. Následně vrací objekt, nad kterým je volána, v souladu s konvencemi psaní vlastních jQuery metod. Použití metody je již ukázáno v sekci 6.1. filter Parametr filter určuje, které elementy bude možné vybrat. Jeho hodnota je vlastní selektor :nodes. To znamená, že bude možno vybrat pouze nody. stop K eventu stop lze připojit callback funkci. Ta je provedena, když je skončeno vybírání, přesněji když je uvolněno tlačítko myši. V callback funkci tohoto eventu je provedeno několik důležitých akcí. Předně, pokud není nic vybráno (počet elementů s třídou ui-selected je roven 0), jsou pouze odstraněna označení mm-selected, aktualizována tlačítka a příkazem return je ukončen běh funkce. 6.7. VKLÁDÁNÍ NODŮ 39 Do proměnné aloneNodes jsou vybrány samostatné nody, které jsou přímými samostatnými potomky canvasu. Při výběru je zkontrolován checkbox, zda mají vybrány celé skupiny • nezaškrtnutý - jako mm-selected jsou označeny nody, které jsou vybrány uživatelem (nody ui-selected pomocí Selectable) • zaškrtnutý - metodou topGroups jsou nalezeny nejvyšší elementy, ve kterých se node nachází (skupina nebo sám node), a jsou označeny jako mm-selected. Když je checkbox zaškrtnutý, jsou elementy nalezené metodou topGroups uloženy do proměnné topGroups. Pokud se v kolekci nalezených elementů vyskytuje skupina if (topGroups.not(’:nodes’).length != 0 && $(’#selectionMethod’).is(’:checked’)) ... je jim přidána třída mm-selected, všechny vnitřní nody jsou označeny jako ui-selected a je vypsáno oznámení uživateli, že byly vybrány celé skupiny. Dále jsou aktualizována tlačítka. Pokud je nastavena kolekce mm.vars.connectingTo, probíhá akce "connect to", tedy propojení více nodů najednou. Nodům, které jsou v této kolekci, je odstraněna třída mm-selected a je volána funkce mm.ctrl.con.connectToStop(). Třída mm-selected je odstraněna, aby se funkce nemusela pokoušet propojit node sám se sebou. 6.7 Vkládání nodů Základní uživatelskou akcí je vložení nodu. Po kliknutí na příslušné tlačítko je vložen node do editoru, tedy do mm.canvas. Všechny nové nody jsou vkládány na stejnou pozici podle nastavení v CSS dokumentu. Zdrojový kód funkce pro vkládání nodu do DOM create: function(nodeId){ $("<div/>", { "id": nodeId, "class": mm.settings.nodeDivClassName, "html": ’<div class="nodeText">’ + mm.settings.defaultValues.node.html + ’<br/>’ + nodeId + ’</div>’ + ’<div class="groupSigns"></div>’ }).data({ ’paths’: [], // array with IDs of all path connecting node ’parents’: [] // array of parent IDs }).appendTo(mm.canvas).mmDraggable(); } Je vytvořen element ve tvaru uvedeném v sekci 5.3.2, který je následně připojen do canvasu. Dále je nad nodem zavolána metoda, která umožní jeho pohyblivost. 40 6.8 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE Pohyblivost nodů a skupin Pro pohyblivost (dragování) nodů jsem si vytvořil vlastní metodu mmDraggable. 6.8.1 Metoda mmDraggable Metoda mmDraggable je stejně jako mmSelectable mým vlastním rozšířením funkcionality jQuery. Metoda vytváří nad objektem, nad kterým je volána, běžný jQuery Draggable se zadanými vlastnostmi a callback funkcemi dle mé potřeby. Metoda vrací objekt, nad kterým je zavolána, aby splnila konvence vlastních metod jQuery. Použití metody je uvedeno v sekci 6.7. Zdrojový kód metody je velice rozsáhlý, proto není v práci uveden. K nalezení je ve zdrojových kódech aplikace cursor Parametr cursor je nastaven na move. Toto je z čistě estetického hlediska, kdy se uživateli při začátku dragu změní kurzor myši na kříž pohybu, jak je zvykem v desktopových prostředích. grid Parametr grid nastavuje elementu mřížku na přichytávání. Hodnota je pole ve tvaru [x,y]. Lze nastavit jednoduše v mm.settings.canvas.grid. helper Parametr helper umožňuje nahradit pohybovaný node vlastním kódem. Funkce vrací prázdný div element. Toto nastavení zakáže pohyb dragovaného elementu. Ten je pak posunut stejně jako ostatní s kolekcí mm.vars.drag.collection. start Parametr start určuje callback funkci vykonanou při začátku dragování. V této funkci jsou nastaveny v klíči mm.vars.drag proměnné pathSet, alonePaths a collection podle návrhu v sekci 5.3.5. Když je splněna podmínka if ($(’div.ui-selected’).length == 0) { ... výběr je prázdný a proměnné jsou nastaveny podle nodu, který spustil akci. if ($(this).data(’parents’).length != 0) Když element nemá prázdné pole parents, má rodiče (je ve skupině). Potom jsou kódem 6.8. POHYBLIVOST NODŮ A SKUPIN 41 var parents = $(this).data(’parents’); var parent = $(’#’ + parents[parents.length - 1]); mm.vars.drag.pathSet = parent.data(’pathSet’); var nodes = parent.find(’:nodes’); var alonePaths = mm.dom.con.getOuterConnectionIds(nodes); if (alonePaths.length != 0) mm.vars.drag.alonePaths = alonePaths; nastaveny proměnné • mm.vars.drag.pathSet je pathSet nalezené rodičovské skupiny • mm.vars.drag.alonePaths jsou vnější spojení všech nodů ve skupině • mm.vars.drag.collection je rodičovská skupina Pokud dragovaný node nemá rodiče, všechny proměnné jsou nastaveny podlě něho mm.vars.drag.pathSet = false; mm.vars.drag.alonePaths = $(this).data(’paths’); mm.vars.drag.collection = $(this); • mm.vars.drag.pathSet je false, protože žádné cesty nebudou pouze přesouvány • mm.vars.drag.alonePaths jsou všechny cesty, které má node v poli paths • mm.vars.drag.collection je sám node Pokud výběr není prázdný, všechny proměnné jsou nastaveny podle vybraných elementů, tedy mm-selected. • mm.vars.drag.collection jsou všechny mm-selected elementy • mm.vars.drag.pathSet je vytvořen z vnitřních spojení spojení všech nodů ve výběru • mm.vars.drag.alonePaths jsou vnější spojení všech nodů ve výběru Všechny nody, které se budou přepočítávat, jsou zesvětleny. Následně je spustěna metoda mm.dom.con.refreshCycle() 6.12.4 zajišťující překreslování cest. drag Metoda vypočte změnu pozice od předchozího volání metody drag. Pokud je nastaven mm.vars.drag.pathSet, je posunut o tuto změnu. Pokud je nastavena kolekce, pro všechny její členy jsou vypočteny a nastaveny nové souřadnice. mm.vars.initPos = { x: x, y: y }; nastaví proměnné, které udržují pozici, na které byl naposledy proveden posun elementů. 42 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE stop Metoda je volána, když skončí dragování (myš na dragovaném elementu je uvolněna). Pokud pole alonePaths není prázdné, jsou všechny cesty přepočteny a je jim nastaven výchozí styl. Všechny nastavené proměnné vzhledem k dragovaní (initPos, collection, dragging, pathSet a alonePaths) jsou nastaveny na false. 6.9 Odstranění nodů Vybrané nody lze odstranit, pokud ve výběru není skupina. Funkce dostane jako parametr kolekci nodů mm-selected. Pro všechny nody v kolekci jsou odstraněny cesty z paths (začínající nebo končící v nodu). Id cest je odstraněno i z nodů, které nejsou odstraňovány, ale cesta v nich začíná nebo končí. Pro každou jednotlivou cestu jsou odstraněny položky v mapovacích proměnných a objekt cesty. Dále jsou odstraněny elementy v kolekci. 6.10 Vytvoření skupiny Ve funkci mm.ctrl.group.create() je vygenerováno id nové skupiny a vygenerována barva značení skupiny. Barvy skupin jsou uloženy v proměnné mm.vars.group[groupId] = { color: barvaSkupiny }; Poté je zavolána funkce mm.dom.group.create(groupId, parent, childrens); • groupId - id nově vytvářené skupiny • parent - rodičovský prvek, ve kterém bude vytvořena skupina • childrens - potomci nové skupiny Funkce mm.dom.group.create(groupId, parent, childrens) vytvoří element skupiny podle návrhu 5.3.3. 6.11. ZRUŠENÍ SKUPINY 43 Kód var groupSign = $("<div/>", { "class": ’groupMembSign grMembSign-’ + groupId, "css": { "background-color": mm.vars.group[groupId].color } }); vytvoří čtverec značící příslušnost skupině. Ten má dříve vygenerovanou barvu. Obrázek 6.3: Ukázka znaménka příslušnosti skupině Následně je vytvořeno pole childrensArr s id potomků. Všem nodům ve skupině je na konec pole parents vloženo id nové skupiny. Funkce mm.dom.con.refreshGroup(objektCesty) aktualizuje pole innerPaths a pathSet skupiny. 6.11 Zrušení skupiny Funkce odstraní znaménka příslušnosti ke skupině všem vnitřním nodům. Dále odstraní id skupiny z pole parents všech nodů. Všichni přímí potomci skupiny mají přepočteny pozice (pozice elementu + pozice skupiny). Dále jsou přepojeny do rodiče a element skupiny odstraněn. 44 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE 6.12 6.12.1 Cesty Vytvoření cesty Cesta může být vytvořena mezi dvěma nody. Jedno spojení vytvoří funkce mm.dom.con.connect(nodes) Funkce vytvoří obě možné kombinace pro mapování. Pokud už existuje spojení cest, funkce končí. Pokud ne, jsou nastaveny mapovací proměnné a vytvořen objekt cesty mm.vars.path.map[nameA] = pathUUID; mm.vars.path.map[nameB] = pathUUID; mm.vars.path.obj[pathUUID] = { from: nodeA.attr(’id’), to: nodeB.attr(’id’) }; Do pole paths obou nodů je přidáno UUID cesty. Následně jsou zjištěny počáteční a koncové souřadnice cesty a mezi těmito body vytvořena cesta Raphaël path. mm.vars.path.obj[pathUUID].path = mm.paper.path(...) Pokud je některý z nodů ve skupině, je aktualizován obsah proměnných cesty. 6.12.2 Spojení více nodů Tlačítko connect to uživatelského rozhraní umožňuje propojit více nodů najednou. Při stisku tlačítka je volána funkce mm.ctrl.con.connectToStart(). Do proměnné mm.vars.connectingTo jsou vloženy elementy mm-selected a je zrušen výběr. Callback funkce mmSelectable stop zjistí, že probíhá spojení více nodů a volá funkci mm.ctrl.con.connectToStop() (více v 6.6.2). Funkce mm.dom.con.connectTo() Funkce mm.dom.con.connectTo() vytváří samotné vícenásobné spojení. connectTo: function(where){ var from = mm.vars.connectingTo; var to = $(where); for (i = 0; i < from.length; i++) { var fromId = $(from).eq(i).attr(’id’); for (j = 0; j < to.length; j++) { var toId = $(to).eq(j).attr(’id’); var toConnect = $(’#’ + fromId + ’, #’ + toId); mm.dom.con.connect(toConnect); } }; } 6.12. CESTY 45 Funkce dostává jako parametr cílové elementy v atributu where. Dva for cykly v sobě projdou kombinaci všech nodů, které byly vybrány jako první (from, zdroje spojení), a nodů které byly výbrány jako druhé (to, cíle spojení). Na každou kombinaci nodů poté volá funkci pro běžné spojení dvou nodů. 6.12.3 Hledání vnitřních a vnějších cest Pro hledání vnitřních a vnějších cest daných nodů jsem si vytvořil dvě funkce. • vnitřní cesty nodů - cesty, které začínají a končí v některém z daných nodů • vnější cesty nodů - cesty, které začínají nebo končí v některém z daných nodů Hledání vnitřních cest getInnerConnectionIds: function(elements){ var paths = []; $(elements).each(function(key, value){ var elId = $(this).attr(’id’); for (i = 1; i < $(elements).length; i++) { var thisId = $(elements).eq(i).attr(’id’); if (elId == thisId || i <= key) continue; var mapName = elId + ’-’ + thisId; if (mm.dom.con.exists(mapName)) paths.push(mm.dom.con.getId(mapName)); } }); return paths; } Funkce hledá všechny možné kombinace vnitřních cest. Konkrétně vytváří všechny možné kombinace id a pomocí mapování hledá, zda cesta existuje. Pokud ano, do pole paths přidá její id. Pole id je nakonec vráceno. 46 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE Hledání vnějších cest getOuterConnectionIds: function(elements){ var innerConnections = mm.dom.con.getInnerConnectionIds(elements); var allConnections = []; var outerConnections = []; var pathsArr = []; $(elements).each(function(key, value){ pathsArr = $(this).data(’paths’); allConnections = allConnections.concat(pathsArr); }); outerConnections = allConnections.unique().diff(innerConnections); return outerConnections; } Funkce dostává jako parametr elementy, jejichž vnitřní spojení má vrátit. Funkce nejprve do pole allConnections najde všechna spojení všech nodů. Do pole outerConnections najde rozdíl polí všech a vnitřních spojení. To jsou vnější cesty a ty jsou vráceny. 6.12.4 Přepočet cest Přepočet cesty je její překreslení. To probíhá na základě aktuálních souřadnic počátečního a koncového nodu cesty. Základní funkcí je přepočet jedné cesty určené podle UUID cesty mm.dom.con.refreshSingle(UUID). Přepočet jedné cesty Funkce mm.dom.con.refreshSingle(UUID) najde počáteční a koncové elementy cesty a do proměnných start a end najde jejich středy. Pokud je některý z nodů ve skupině, jsou přičteny souřadnice skupiny. newPath = { path: "M" + start.hor + " " + start.ver + "L" + end.hor + " " + end.ver }; pathObj.attr(newPath); newPath obsahuje klíč path. To je řetězec určující souřadnice cesty. Tento tvar má, protože Raphaël metoda attr vyžaduje objekt s jednotlivými atributy. Metoda attr() nastavuje nové hodnoty atributů jednotlivému Raphaël objektu nebo množině. Překreslování cest při draggování Funkce mm.dom.con.refreshCycle(index) provádí "lazy"("líné") překreslování. To znamená, že nepřekresluje cesty při dragu, ale na pozadí nezávisle na pohybu myši. Když je spuštěna, najde si cesty z mm.vars.drag.alonePaths, které mají být přepočteny a podle indexu přepočte jednu z nich. Jakmile je přepočtena cesta určená indexem, funkce volá sebe samu s indexem o jedna větším. To znamená, že přepočte další cestu. 6.13. PROBLÉMY FUNKČNOSTI 47 Další přepočty cest Další metody přepočtu v klíči mm.dom.con využívají přepočet jednotlivé cesty. Liší se typem parametru, který určuje, jaké cesty přepočítat. funkce refresh(node) refreshByArray(paths) popis překreslí cesty podle pole paths daného nodu překreslí cesty dané polem paths v parametru Tabulka 6.2: Přehled funkcí pro aktualizaci cesty 6.13 Problémy funkčnosti Nefunkčnost v IE V průběhu vývoje přestala aplikace správně pracovat v prohlížeči Internet Explorer. Problém nastane, když se uživatel snaží pohybovat nodem. V debugeru IE je hlášena chyba "Neplatný argument"v souboru jquery-1.4.2.min.js s knihovnou jQuery. Problém se projevil po spojení jQuery Draggable a Selectable. Při dragu nodu nebyl správně zachycen event a zároveň začala akce select, která při dragu nodu nemá být spuštěna. Spojení nodu vektorově vykreslenou cestou funguje v pořádku. Pokud by aplikace měla přejít do produkční verze, toto by byl první problém, který by bylo potřeba vyřešit. Výběr pomocí klávesy CTRL jQuery Selectable podporuje výběr pomocí klikání s klávesou CTRL, jak je zvykem z desktopových prostředí. Po kombinaci Draggable a Selectable tato možnost přestala fungovat. 48 KAPITOLA 6. REALIZACE UKÁZKOVÉ APLIKACE Kapitola 7 Závěr • Zhodnocení splnění cílů DP/BP a vlastního přínosu práce (při formulaci je třeba vzít v potaz zadání práce). • Diskuse dalšího možného pokračování práce. 49 50 KAPITOLA 7. ZÁVĚR Literatura [1] Dmitry Baranovskiy. Raphaël: a JavaScript API for SVG. http://dev.opera.com/articles/view/raphael-a-javascript-api-for-svg/, stav z 22.5.2010. [2] R. Šerý. JavaScript s jQuery - lehký úvod. http://interval.cz/clanky/javascript-s-jquery-lehky-uvod/, stav z 8.5.2010. [3] M. Hassman. Seriál Seznamte se s Dojo Toolkitem. http://zdrojak.root.cz/serialy/seznamte-se-s-dojo-toolkit/, stav z 18.5.2010. [4] W3C. Scalable Vector Graphics (SVG). http://www.w3.org/Graphics/SVG/, stav z 21.5.2010. [5] W3C. Vector Markup Language (VML). http://www.w3.org/TR/NOTE-VML, stav z 21.5.2010. [6] Aptana Studio 2 - oficiální stránky. http://www.aptana.org/, stav z 23.5.2010. [7] Dojo dokumentace - dojocampus. http://www.dojotoolkit.org/documentation/, stav z 19.5.2010. [8] Book of Dojo. http://o.dojotoolkit.org/book/dojo-book-0-9/introduction/history, 18.5.2010. stav z [9] dojox/gfx - DojoCampus - Docs. http://docs.dojocampus.org/dojox/gfx/, stav z 22.5.2010. [10] Ext JS learn overview. http://www.extjs.com/learn/Overview, stav z 19.5.2010. [11] Ext JS 3.2 Samples. http://www.extjs.com/deploy/dev/examples/, stav z 19.5.2010. [12] Graffle - příklad knihovny Raphaël. http://raphaeljs.com/graffle.html, stav z 2.5.2010. [13] IEBlog: SVG in IE9 Roadmap. http://blogs.msdn.com/ie/archive/2010/03/18/svg-in-ie9-roadmap.aspx, stav z 21.5.2010. 51 52 LITERATURA [14] jQuery dokumentace. http://docs.jquery.com/, stav z 25.5.2010. [15] jQuery UI domácí stránka. http://jqueryui.com/, stav z 8.5.2010. [16] JSDoc - JavaScript Documentation Tool. http://jsdoc.sourceforge.net/, stav z 23.5.2010. [17] Litha–Paint - web drawing service. http://www.litha-paint.com/, stav z 25.5.2010. [18] Mochikit documentation. http://www.mochikit.com/doc/html/MochiKit/index.html, stav z 19.5.2010. [19] Mootools dokumentace. http://mootools.net/docs/, stav z 19.5.2010. [20] RichDraw Demo. http://starkravingfinkle.org/projects/richdraw/richdraw_demo.htm, 25.5.2010. stav z [21] YUI 3 dokumentace. http://developer.yahoo.com/yui/3/, stav z 20.5.2010. [22] Komentáře článku Naučte se YUI - Dan Wellman. http://www.misantrop.info/679958-naucte-se-yui-dan-wellman.php, 20.5.2010. stav z Příloha A Editace základního vzhledu Veškeré nastavení vzhledu je prováděno pomocí CSS stylů v souboru mm-style.css. V následující tabulce je uveden seznam používaných specifických CSS stylů s jejich popisem selektor #canvas #controls #noticePanel #noticePanel .noticeMsg .nodeDiv .groupDiv .ui-selecting, .ui-selecting .nodeDiv .ui-selected, .ui-selected .nodeDiv .groupSigns .groupMembSign popis stylu styly pro element canvas umístění a styl panelu s kontrolními prvky element pro umísťování zpráv uživateli styl jednotlivých zpráv pro uživatele inspirován hlášeními MAC OS X třída specifikující styl vloženého nodu třída specifikující styl skupiny styl vybraných elementů během vybírání styl vybraných elementů po skončení vybírání styl elementu, do kterého jsou vkládány znaménka příslušnosti ke skupinám styl znaménka příslušnosti ke skupině Tabulka A.1: Přehled CSS stylů editoru 53 54 PŘÍLOHA A. EDITACE ZÁKLADNÍHO VZHLEDU Příloha B Specifické vlastnosti kódu Ve zdrojovém kódu se vyskytují specifické zápisy, které bych rád vysvětlil B.1 jQuery length vs. size() jQuery metoda size() vrací délku kolekce (počet elementů v kolekci). Metoda však pouze vrací hodnotu proměnné length. Proto nepoužívám metodu size(), ale na počet elementů se dotazuji přímo v proměnné length. Tím je ušetřeno jedno zbytečné volání funkce. Zjišťování délky kolekce díky tomu vypadá stejně jako například zjištění délky pole. 55 56 PŘÍLOHA B. SPECIFICKÉ VLASTNOSTI KÓDU Příloha C Seznam použitých zkratek AJAX Asynchronous JavaScript and XML API Application Programming Interface CSS Cascading Style Sheets DOM Document Object Model IE Internet Explorer JS JavaScript JSON JavaScript Object Notation HTML HyperText Markup Language SVG Scalable Vector Graphics UUID Universally Unique Identifier VML Vector Markup Language XML Extensible Markup Language YUI Yahoo! User Interface 57 58 PŘÍLOHA C. SEZNAM POUŽITÝCH ZKRATEK Příloha D Obsah přiloženého CD Tato příloha je povinná pro každou práci. Každá práce musí totiž obsahovat přiložené CD. Viz dále. Může vypadat například takto. Váš seznam samozřejmě bude odpovídat typu vaší práce. (viz [?]): Obrázek D.1: Seznam přiloženého CD — příklad Na GNU/Linuxu si strukturu přiloženého CD můžete snadno vyrobit příkazem: $ tree . >tree.txt Ve vzniklém souboru pak stačí pouze doplnit komentáře. 59 60 PŘÍLOHA D. OBSAH PŘILOŽENÉHO CD Z README.TXT (případne index.html apod.) musí být rovněž zřejmé, jak programy instalovat, spouštět a jaké požadavky mají tyto programy na hardware. Adresář text musí obsahovat soubor s vlastním textem práce v PDF nebo PS formátu, který bude později použit pro prezentaci diplomové práce na WWW.
Podobné dokumenty
Obsah - CPress
Technické triky
Trik 12.01: Upravte si sluchátka, aby fungovala s původním iPhonem
Trik 12.02: Přinuťte kolébku původního iPhonu fungovat i s iPhonem 3G
Trik 12.03: Vyrobte si pro iPhone headset z ...
Přednáška v PDF.
– Konvence má přednost před konfigurací = Nastavujeme
pouze to, co se liší od výchozí konfigurace
– http://blog.karmi.cz/2007/6/16/co-je-ruby-on-rails-cast-2
prezentace
Srovnání JS frameworků
Jiří Kunčar & Jiří Martišek
Technologie vývoje webových aplikací
Konspekt-Speciální technické prostředky
nástroje a pracovních tlaků, které se pohybují
od 35 do 72 MPa! Střižná síla je největší
u čepu hydraulických nůžek viz obr. Stříhání
pružinových a kalených ocelí je nepřípustné.
Nůžky existují s r...
Co je Dojo?
Dojo je javascriptový toolkit určený k usnadnění tvorby webových aplikací. Nabízí
nástroje pro manipulaci s DOM, animaci, AJAX a internacionalizaci webových projektů.
Dojo Core, který obsahuje nejz...
Operační systéme II
Návod : Vytvořte kopii shellu bash a nastavte mu všechna práva a s-bit uživatele. Potom zkuste předat jeho vlastnictví
superuživateli. Vytvořili jste kopii shellu s nastaveným s-bitem ve vlastnictv...
Dokumentace
že se jedná o velmi kvalitní kreslící a prezentační nástroj, ve kterém lze dokonce tvořit
aplikace a hry, je uživatelsky přívětivý, intuitivní a velmi dobře se v něm pracuje.
ActionScript samotný j...
zablokování olivkou
• Signál Memory Read (MR), zabezpečuje časování čtení z pamětí či
jiných bloků do mikroprocesoru nebo počítače,
• Signál Memory Write (MW), zabezpečuje časování zápisu do pamětí či jiných bloků z m...