Archiwum

Archiwum autora

Obiektowy JavaScript cz.2. – klasa sama w sobie

Marzec 2nd, 2010 reinmar Brak komentarzy

GąsienicaOpublikowane na licencji CC przez Trufflepig.

W poprzednim artykule o obiektach w JavaScriptcie poruszyłem kwestię ich tworzenia, dostępu do właściwości, kontekstu wywoływania metod oraz wspomniałem o kilku cechach. Czyste literały obiektów same w sobie są już w tym języku bardzo przydatne — przekonał się pewnie o tym każdy kto korzystał z jakiejś biblioteki typu MooTools czy Prototype. Pozwalają na przykład w dość wygodny sposób na podanie dowolnej liczby nazwanych argumentów funkcji:

new Ajax.Request('/your/url', {
	parameters: { id: 12, action: 'sth' },

	onSuccess: function (transport) {
		alert(transport.responseText);
	},

	evalJS: false
});

Pisałem ostatnio o tym, że w JavaScriptcie nie ma klas. Skąd więc w przykładzie słówko kluczowe new, które w typowym języku służy do tworzenia instancji obiektu na bazie jakiejś klasy? Otóż okazuje się, że w JavaScriptcie wprowadzono twór zwany konstruktorami obiektów.

Konstruktor obiektu

Zwyczajnie zaczyna się od definiowania klasy jakiegoś kotka, samochodu, czy figury geometrycznej. Nudy. Chciałem wymyślić coś bardziej konstruktywnego i spośród wszystkich dostępnych na moim biurku obiektów wybrałem butelkę po miodzie pitnym (pustą niestety). Myślę, że konstruktory dla napoju alkoholowego i butelki będą wystarczająco oryginalne :). Do dzieła:

var Alkohol = function (nazwa, ilosc_procentow, kolor) {

	this.nazwa = nazwa;
	this.ilosc_procentow = ilosc_procentow;

	this.kolor = kolor;
};
var ButelkaAlkoholu = function (alkohol, pojemnosc, rok_produkcji, kraj_produkcji) {

	this.alkohol = alkohol;
	this.pojemnosc = pojemnosc;

	this.rok_produkcji = rok_produkcji;
	this.kraj_produkcji = kraj_produkcji;

};

Oj tak. JavaScript to dziwoląg. Miały być konstruktory obiektów, a tu znowu pojawiły się funkcje. Z drugiej strony pojawiło się też słowo kluczowe this, więc cały przykład przypomina dwa wycięte z definicji klas napisanych w normalnym języku konstruktory. Na szczęście obiekty tworzymy już normalnie:

var miod = new Alkohol('Miód pitny', 13, '#850');

var butelka_miodu = new ButelkaAlkoholu(miod, 750, 2009, 'Polska');

 
console.dir(miod); // -> screenshot poniżej
console.dir(butelka_miodu); // -> screenshot poniżej

 
butelka_miodu.alkohol.nazwa; // -> "Miód pitny"

Obiekty miód i butelka miodu

Utworzyliśmy w ten sposób dwa obiekty, których struktura została wylistowana przy pomocy Firebuga i którą przedstawiłem na screenie.

W poprzedniej części artykułu pisałem o tym, że obiekt może mieć swoje metody (inaczej funkcje przypisane jako właściwości obiektu). Musi więc być sposób aby w konstruktorze obiektu zdefiniować takie metody. Dodajmy więc kilka linii do konstruktora ButelkaAlkohol:

var ButelkaAlkoholu = function (alkohol, pojemnosc, rok_produkcji, kraj_produkcji) {

	//...
	this.pelna = true;
	this.oproznij = function () {

		this.pelna = false;
	};
};
 
var butelka_miodu = new ButelkaAlkoholu(miod, 750, 2009, 'Polska');

butelka_miodu.pelna; // -> true
butelka_miodu.oproznij();
butelka_miodu.pelna; // -> false

Jak widać, aby utworzyć coś co możemy nazwać metodą, musimy przypisać funkcję do właściwości przyszłego obiektu. Uczulam znowu na działanie słowa this, o czym pisałem już poprzednim razem.

Alternatywna metoda tworzenia konstruktorów obiektów

Naszym celem jest utworzenie obiektu o zadanych przez nas wartościach właściwości. Pamiętając, że najprostszym sposobem na utworzenie obiektu jest skorzystanie z literału możemy dojść do następującej konstrukcji: stwórzmy funkcję zwracającją obiekt zapisany za pomocą literału:

var Alkohol2 = function (nazwa, ilosc_procentow, kolor) {

	var obj = {
		nazwa: nazwa,
		ilosc_procentow: ilosc_procentow,

		kolor: kolor,
		pelna: true,
		oproznij: function () { this.pelna = false; }

	};
 
	return obj;
};
 
var piwo = Alkohol2('piwo Tyskie', 5.6, '#FE3');

piwo; // -> Object nazwa=piwo Tyskie ilosc_procentow=5.6 kolor=#FE3

Tak więc skorzystaliśmy z funkcji w jej czysto funkcyjnym wymiarze. To jednak nie koniec. Dawno już nie było mowy o żadnym JavaScriptowym dziwactwie. Pora na następne. Otóż ten zapis również zadziała:

var piwo2 = new Alkohol2('piwo Tyskie', 5.6, '#FE3');

piwo2; // -> Object nazwa=piwo Tyskie ilosc_procentow=5.6 kolor=#FE3

Zonk.

Anegdota o użyciu new z funkcją

Kiedy dowiedziałem się o powyższym od razu zacząłem się zastanawiać jak w zasadzie JavaScript traktuje new w kontekście funkcji. Pierwsze co ciśnie się na palce, to modyfikacja przykładu z konstruktorem Alkohol2 tak by zwracał „nieobiekt”:

var Cons = function () {

	return 5;
};
var obj = new Cons();

obj; // -> Object

Nic ciekawego — jakiś pusty obiekt otrzymaliśmy. Dodajmy więc właściwość (pierwszy sposób tworzenia konstruktorów):

var Cons = function () {

	this.a = 'a';
	return 5;
};

var obj = new Cons();
obj; // -> Object a=a

Ciekawe, choć do wytłumaczenia — return 5 jest pomijane. Połączmy teraz dwa sposoby tworzenia konstruktorów:

var Cons = function () {

	this.a = 'a';
	return { b: 'b' };

};
var obj = new Cons();
obj; // -> Object b=b

Uups. JavaScript pomija właściwości zadeklarowane przy użyciu słowa this. W ten sposób dochodzimy do algorytmu, którym kieruje się ten język (a przynajmniej Firefox — może komuś się chce zajrzeć do specyfikacji? :):

  1. Jeśli zwracana przez funkcję wartość jest obiektem, to wyrażenie new Cons() zwraca ten obiekt.
  2. W przeciwnym wypadku wyrażenie to zwraca obiekt z właściwościami ustalonymi przy pomocy słowa this.

Porównując obydwa sposoby pisania konstruktorów można dojść do wniosku, że lepsza jest ta druga, (ze zwracaniem gotowego obiektu), ponieważ działa na obydwa sposoby: new Cons() i Cons(). Osobiście uważam jednak, że ta metoda jest gorsza — dlaczego? O tym później w tej i w następnej części artykułu.

Elementy programowania obiektowego

Umiemy już tworzyć konstruktory obiektów, które można porównać do klas w zwyczajnym języku. Obiekty tworzone za ich pomocą posiadały do tej pory jednak tylko publiczne właściwości i metody. JavaScript nie udostępnia mechanizmu modyfikatorów dostępu, można jednak skorzystać z jego cech aby oprogramować w prosty sposób podobne konstrukcje.

Właściwości prywatne

var Cons = function () {
    var private = 'jestem prywatna';

    this.getPrivate = function () { return private; };

};
 
var obj = new Cons();
obj.getPrivate(); // -> "jestem prywatna"

obj.private; // -> undefined

Dlaczego to działa? Wszystko opiera się o kolejny dziwoląg, czyli JavaScriptowego scope’a, a także o closures (BTW. fajne tematy na kolejne artykuły). W skrócie — funkcja zadeklarowana w innej funkcji ma dostęp do zmiennych dostępnych w swoim „rodzicu”. Do tego mechanizm domknięć powoduje, że funkcja getPrivate „zapamiętuje” środowisko, w którym została zadeklarowana.

Właściwości statyczne

var Cons = function () {
    this.nonstatic = 'nie jestem statyczna';

};
Cons.static = 'jestem statyczna';
 
Cons.static; // -> "jestem statyczna"

Cons.nonstatic; // -> undefined

Chyba nic nie trzeba wyjaśniać. Konstruktor Cons jak każda funkcja jest obiektem, więc można dodawać mu właściwości, co robimy w czwartej linii.

Dla jasności dodam, że w identyczny sposób tworzymy prywatne i statyczne metody obiektów i klas.

Dostęp z obiektu do konstruktora

Każdy obiekt posiada właściwość constructor, która daje dostęp do konstruktora (porównywalne do Javowego Object.getClass()):

var Cons = function () {

    this.sth = 'sth';
};
 
var obj = new Cons();

obj.constructor; // -> function()
obj.constructor.toString(); // -> "function () { this.sth = "sth"; }"

A teraz niespodzianka. Dlaczego drugi sposób tworzenia konstruktorów (przez literał obiektu) uważam za gorszy? O to jeden z powodów:

var Cons2 = function () {
    return { sth: 'sth' };

};
var obj2 = Cons2();
var obj3 = new Cons2();

 
obj2.constructor; // -> Object()
obj3.constructor; // -> Object()

obj2.constructor.toString(); // -> "function Object() { [native code] }"
obj3.constructor.toString(); // -> "function Object() { [native code] }"

Zdefiniowaliśmy konstruktor, który teoretycznie działa jak ten z poprzedniego przykładu (zwraca obiekt o tej samej właściwości). Okazuje się jednak, że kiedy spróbujemy dostać się do konstruktora tego obiektu, to nie jest nim funkcja Cons2, a zwykła, natywna Object. Dla porównania:

({}).constructor.toString(); // -> "function Object() { [native code] }"

Co dalej?

Umiemy już posługiwać się obiektami. Umiemy też tworzyć ich konstruktory. Pora więc na dziedziczenie. O tym za czas jakiś w następnej części :)

Artykuł został opublikowany także na moim blogu prywatnym.

Kategorie:Różne Tagi:

Obiektowy Javascript cz.1. – obiekt Twoim przyjacielem

Luty 6th, 2010 reinmar Brak komentarzy

GąsienicaOpublikowane na licencji CC przez Trufflepig.

Do tego czteroczęściowego (jak zapowiem, to może napiszę :) artykułu natchnęło mnie szkolenie z zaawansowanego JavaScriptu które, w miniony weekend, zorganizował we Wrocławiu Damian Ferrante Wielgosik. Już na początku chciałbym mu podziękować za wiedzę, którą się podzielił, ponieważ przed szkoleniem byłbym wstanie napisać tylko część tego artykułu.

JavaScript to dziwoląg

Tego nie da się ukryć. JavaScript należy do wąskiej grupy języków z rodziny ECMAScriptu (JS, ActionScript, E4X), którą cechuje to, że ich obiektowość oparta jest na prototypach. Oznacza to, że w JavaScriptcie nie istnieje pojęcie klasy. Nie oznacza to jednak tego, że w JavaScriptcie nie da się programować obiektowo z wykorzystaniem np. konkretyzacji, dziedziczenia, czy właściwości prywatnych. Da się. Co więcej — da się uzyskać dużo więcej funkcjonalności klasycznego języka obiektowego, choć, o czym na sam koniec, niekoniecznie jest po co.

Obiekt

W języku z obiektowością klasową wypada zacząć od definicji klasy. Tak przynajmniej zaczynali autorzy książek, które czytałem. W przypadku JavaScriptu mielibyśmy jednak pewien problem, bo klas tutaj nie ma. Trzeba więc zacząć od utworzenia obiektu. Oto najprostszy:

var obj = {};
typeof obj; // -> "object"

Tak, to w JavaScriptcie jest najprostszy obiekt. Zadeklarowany w przykładzie jest pusty i dziedziczy po obiekcie Object. Możemy sprawdzić to za pomocą poniższego kodu (jak działa i co to znaczy, o tym później):

var obj = {};
obj.__count__; // -> 0, działa tylko w Firefoksie

obj.__proto__.constructor; // -> Object()

Obiekt możemy również utworzyć za pomocą konstruktora Object(). Nie jest to jednak sposób polecany, ponieważ… zmienną Object można nadpisać. Lepiej pozostać przy literałach obiektów, które są bezpieczniejsze i ładniejsze.

var obj = new Object(); // -> Object

Object = 5;
var obj2 = Object(); // ->  TypeError: Object is not a constructor

Właściwości obiektu

Nic nam jednak z pustego obiektu. Pora dodać do niego dane i metody. Możemy skorzystać ze składni literału obiektu:

var obj = {
	text: 'Jestem obiektem',

	saySth: function () {
		alert(this.text);
	}

};

Bądź stworzyć pusty obiekt i dodać mu właściwości:

var obj = {};
obj.text = 'Jestem obiektem';

obj.saySth = function () {
	alert(this.text);
};

W obu przypadkach efekt będzie ten sam. Kiedy wywołamy obj.saySth(); dostaniemy alerta z tekstem Jestem obiektem.

Jeszcze słowo wtrącenia o literałach. Trzeba uważać na przecinek po ostatniej właściwości. Firefox i chyba wszystkie normalne przeglądarki trzymają się specyfikacji ECMAScript 5 pkt 11.1.5 (bądź to specyfikacja trzyma się tych przeglądarek ;), a kochany IE6, który zawsze musi być inny („inny nie znaczy gorszy” w tym wypadku nie działa ;) trzyma się specyfikacji ECMAScript 3 pkt 11.1.5. Tak więc przecinek po ostatniej właściwości zostanie zaakceptowany przez przeglądarki z rodziny normalnych, zaś IE6 wywali cichaczem błąd. Nie wiem co prawda jak sprawa wygląda w IE7 i IE8, ale radzę po prostu tego przecinka nie stawiać. Koniec dygresji.

W Javie, czy PHP metoda obiektu jest czymś zupełnie innym niż jego właściwość. W JavaScriptcie metoda jest po prostu funkcją przypisaną do właściwości obiektu. Widać to najlepiej w trzeciej linii ostatniego przykładu: obj.saySth = function () {};. Wynika to z tego, że w JavaScriptcie funkcja też jest obiektem. Dzięki temu możemy ją przypisywać do zmiennych, zwracać w innych funkcjach, czy… wywoływać na niej metody. Ta właściwość JavaScriptu jest naprawdę potężna i myślę, że język ten sporo teraz zyskał w oczach osób lubiących Rubiego, Pythona, czy jakiś język funkcyjny :).

Uwaga na this

typeof obj.saySth; // -> "function"
var sayNothing = obj.saySth;

typeof sayNothing; // -> "function"
sayNothing(); // -> alert undefined

Z pierwszych trzech linii widać, że możemy sobie dowolnie operować funkcją. Czwarta może być zaskoczeniem. Dlaczego this.text === undefined? Zauważcie, że funkcja sayNothing nie została wywołana w kontekście obiektu obj, tylko w kontekście… no właśnie — czego? Żeby to sprawdzić dodajmy dwie linie kodu do poprzednich przykładów:

window.text = 'Jestem globalnym scopem';
sayNothing(); // -> alert "Jestem globalnym scopem"

Teraz widać, że funkcja sayNothing(); wywołana jest w globalnym kontekście. Możemy ją także wywołać w odpowiednim kontekście używając metody call() działającej na funkcji (tak jak pisałem wcześniej — funkcja też jest obiektem, więc ma też swoje metody):

sayNothing.call(obj); // -> alert "Jestem obiektem"

Więcej na ten temat u Ferrante w „this” w JavaScript.

Jeszcze trochę o obiektach

Do właściwości obiektu możemy się również dostać za pomocą operatora [], czyli w taki sposób jak do elementów tablicy. Jest to przydatne kiedy chcemy uzyskać właściwość, której nazwa trzymana jest w zmiennej:

var obj = { a: 'A', b: 'B', m: function () { return 'M'; } };

 
obj['a']; // -> "A"
var name = 'b';

obj[b]; // -> "B"
obj['m'](); // -> "M"

Po właściwościach obiektu można też iterować za pomocą konstrukcji for (i in obj). Trzeba jednak uważać, bo, w zależności od tego jakim obiektem dysponujemy, możemy dostać niespodziewane (przynajmniej na razie) rezultaty. Póki co jednak wystarczy nam:

var obj = { a: 'A', b: 'B', c: 'C', d: 'D' };

for (i in obj) {
	alert('obj[' + i + '] = ' + obj[i]);

}

Obiekty można też zagłębiać. Jest to własność oczywista, ale wypada dla jasności o niej wspomnieć.

var obj = {
	obj: {
		obj: {

			hidden: 'Zonk'
		}
	}
};
obj.obj.obj.hidden; // -> "Zonk"

Co dalej?

Samym obiektem programista żyć nie może. Klas jednak w JavaScripcie nie uświadczymy, trzeba więc wymyślić coś innego. Z pomocą przyjdą nam konstruktory obiektów, ale zrobią to dopiero w następnej części :).

Poza tym w trzeciej części mam zamiar napisać o prototypach, czyli JavaScriptowym dziedziczeniu. W czwartej chciałbym zebrać wszystko do kupy, pokazać może jakieś wzorce i napisać kilka uwag o programowaniu w JavaScriptcie. W zasadzie to uwagi mam już napisane, bo zacząłem od końca :D

Artykuł został także opublikowany na moim prywatnym blogu.

Dlaczego frameworki CSS ssą?

Styczeń 29th, 2010 reinmar 3 comments

Sesja się już dla mnie skończyła, można więc coś napisać. Dzisiaj będzie jednak krótko, bo i temat prosty.

Cóż to takiego framework CSS?

No właśnie. Każdy wie co to są frameworki programistyczne, ale CSSowy? Według wiki:

A CSS framework, also known as a web design framework is a pre-prepared library that is meant to allow for easier, more standards-compliant styling of a webpage using the Cascading Style Sheets language. Just like programming and scripting language libraries, CSS frameworks (usually packaged as external .css sheets inserted into the header) package a number of ready-made options for designing and outlaying a webpage.

Czyli, tak jak w językach programowania, framework jest biblioteką (w tym wypadku grupą reguł CSS) która ułatwia implementowanie jakichś standardowych, często powtarzających się funkcjonalności (w tym wypadku pewnych schematów w layoutcie).

Co, według mnie, frameworkiem CSS nie jest? Pisałem w listopadzie o parserze/kompilatorze/procesorze CSS – LESS, który rozszerza składnię CSS o kilka świetnych rzeczy. Między innymi – zmienne, zagnieżdżone reguły, obliczenia, wielokrotne wykorzystywanie reguł. Jakby nie było – nie podchodzi to pod definicję z Wikipedii, ale niestety znajduje się w linkach do przykładowych frameworków. Dla ustalenia uwagi – LESS nie jest frameworkiem.

Czemuż ssie?

Pod mą lupę wziąłem pierwsze linki zwrócone przez Google. Przyjrzałem się chwilkę frameworkom: Blueprint, Elastic CSS, YUI 2 Grids. Prawdopodobnie są lepsze rozwiązania od tych i z chęcią się im przyjrzę jeśli linki pojawią się w komciach :).

Słówko o nieudanym porodzie

Wypada jeszcze wtrącić, dla niezorientowanych, co to za poroniony pomysł ten Grid system. Otóż wpadł ktoś na pomysł, że wygodnie będzie wszystkim (grafikom, programistom) jak ustalimy sobie, że strona składa się z X kolumn po Ypx szerokości każda, między którymi są marginesy po Zpx. Przyznam, że obaj z grafikiem byliśmy zachwyceni, aż do… pierwszego projektu. Życzę np. sporo zabawy z cieniami wychodzącymi poza granice kolumn, w przypadku kiedy nie możemy użyć box-shadow. Szlag trafia wszystkie piękne, okrągłe wartości.

Koniec wtrącenia

Najpierw kawałek kodu z YUI 2 Grids:

<div id="yui-main">
   <div class="yui-b">
      <div class="yui-g">

         <div class="yui-g first">
            <div class="yui-u first"></div>
            <div class="yui-u"></div>
         </div>

         <div class="yui-g">
            <div class="yui-u first"></div>
            <div class="yui-u"></div>
         </div>

      </div>
   </div>
</div>

Tyle się kiedyś mówiło o semantyce kodu i oddzieleniu wyglądu od treści. Ponad pięć lat temu, kiedy zaczynałem naukę HTMLa i CSSa, trwała walka o to aby programiści zaczęli pisać porządny kod. Wydawało mi się, że teraz już nie powinno być z tym problemów, a tu zonk. Patrzę na ten przykład i co widzę?

  • Prezentacja zbita razem z HTMLem – żeby zmienić układ strony zmieniamy HTMLa, a nie CSS,
  • Kompletnie bezznaczeniowe nazwy klas i identyfikatorów – jedno wielkie semantyczne szambo. Wybrałem akurat framework od YUI, bo jest w tym najgorszy (choć Blueprint mu nie ustępuje). Elastic CSS wygląda trochę lepiej,
  • Divitis. W przypadku YUI można chyba akurat dowolnie zmieniać użyte tagi, bo framework resetuje marginesy i paddingi dla wszystkich elementów (co też, tak na marginesie, uważam za kiepską praktykę). Kiedy jednak używając Blueprinta postanowimy zawrzeć którąś kolumnę w listę, to wszystko szlag trafia, bo uwaga… niektóre selektory zawierają nazwę tagu :O
  • Narzucona struktura i kolejność elementów w kodzie HTML. Ja akurat poświęcam sporo uwagi temu aby każdy element nawigacyjny był w sensownym miejscu, aby nie używać niepotrzebnych tagów, a wykorzystując framework nie mam tej elastyczności,
  • I wreszcie – przecież to wszystko co oferuje framework można w czasie, który jest bez znaczenia w stosunku do całego projektu, napisać ręcznie. To jest kilka reguł, które doświadczona osoba pisze na raz i to od razu z ewentualnym hackiem dla IE6. Tak, wiem, że nie wszyscy mają taką wiedzę, ale wykorzystując framework nigdy jej nie pogłębią.

Tak więc w żadnym wypadku nie widzę sensu w używaniu frameworków do budowy układu strony. Żeby jednak nie było, że w ogóle nie umiem wykorzystywać zewnętrznego kodu – uważam, że przydatne są frameworki poprawiające typografię, bądź też pod niektórymi względami formularze. Muszą jednak bazować na selektorach używających tagów, a nie klas, czy nie daj Boże identyfikatorów.

Kategorie:HTML + CSS Tagi:, ,

Zen Coding – snippety do kosza

Listopad 23rd, 2009 reinmar 3 comments

Snippety do htmla? Nieeee. Zobaczcie Zen Coding. W skrócie działa to tak. Wpisuję w edytorze CSSową składnią:

div#content>h1+p

Wciskam jakąś kombinację klawiszy i dostaję:

<div id="content">
<h1></h1>
<p></p>
</div>

Sprytne, nie? :) Ale to jeszcze mało. Spróbujcie tego:

div#top>h1>a[title=Do strony głównej]{Moja strona}<ul#menu>li.pos$*3>a
<div id="top">
    <h1>
        <a href="" title="Do strony głównej">Moja strona</a>
    </h1>

    <ul id="menu">
        <li class="pos1">
            <a href=""></a>
        </li>
        <li class="pos2">

            <a href=""></a>
        </li>
        <li class="pos3">
            <a href=""></a>
        </li>

    </ul>
</div>

Chyba nie muszę mówić jak bardzo taka pomoc przyspiesza pracę. Napisanie z palca kodu z drugiego przykładu zajęłoby mi przypuszczalnie więcej niż 2 minuty. Gdybym użył snippetów (do których przy HTMLu nie mogę się przyzwyczaić) może skróciłbym ten czas dwukrotnie. Zaś używając wynalazku Zen Coding całość naklepałem w 20s (i to nie mając wprawy). Tak więc gorąco polecam.

Gdyby ktoś szukał wtyczki do VIMa, to powstał plugin, którego twórca zainspirował się Zen Codingiem.

Wpis ten opublikowałem też na swoim prywatnym blogu.

Kategorie:HTML + CSS, Vim Tagi:, , , ,

Adobe AIR podstaw podstawy

Kwiecień 25th, 2009 reinmar Brak komentarzy

Generalnie, jako bardziej linuksowe wydanie geeka, nie patrzyłem na newsy związane z Adobe AIR zbyt przychylnym okiem. Jakieś zamknięte rozwiązanie, pewnie trzeba mieć adobowskie środowisko za 1k$ od stanowiska i na bank zrobią obsługę na Linuksie za dwa lata, jak zacznie im się tam nudzić. Tak więc skłaniałem się bardziej ku rozwiązaniom pokroju Mozilli Prism, czy Google Gears. Byłem jednak przez Darka Juszczuka namiętnie atakowany linkami o AIRze i kiedyś jeden otworzyłem. Na stronie znalazłem przycisk „install”, po kliknięciu którego, jak zrozumiałem, miała zainstalować mi się jakaś aplikacja.Z uśmiechem politowania na twarzy, bo w końcu nie mam AIRa na swoim Linuksie, więc się musi wysypać, kliknąłem w ów przycisk. No i zonk. Z poziomu wtyczki Flash zainstalował mi się Adobe AIR, po czym zaczął się instalować program, którego stronę przed chwilą oglądałem. Z pewną dozą nieufności podałem swoje hasło „administratora” (jedyny minus – nie wystarczą uprawnienia szarego usera do instalacji softu – na szczęście sam instalator jest zaufany) i gotowe. Aplikacja się odpala, śmiga jak marzenie, ładnie wygląda i w ogóle jakbym się na Windowsa przeniósł. No cóż. To skoro panowie z Adobe się tak postarali pora dać im szansę :).

AIR – was ist das?

Za wikipedią:

Adobe Integrated Runtime, w skrócie AIR (nazwa kodowa Apollo) – wieloplatformowe środowisko wykonawcze dla RIA (ang. Rich Internet Application) zbudowanych za pomocą Flash, Flex, lub HTML i AJAX.

Programy AIR mogą być aplikacjami pulpitu.

Czyli? Środowisko dla efektownych i bogatych aplikacji internetowych. Aplikacje te mogą być odpalane jak normalne desktopowe programy. A co to w praktyce? Jeszcze nie do końca wiem, bo w zasadzie, to na bieżąco się uczę pisząc ten artykuł (dla tego proszę o poprawianie jak bym jakoś naginał czasoprzestrzeń), ale na pewno integracja z systemem plików, lokalna baza danych, kontrolki jakie oferuje Flex (o nim pewnie w przyszłości), itd.

Co ważne dla geeków ortodoksów. AIR != Flash. Aplikacje Flashowe mogą używać AIRa by powiększać swe możliwości. Ale Flasha w aplikacji AIRowej może w ogóle nie być. Zamiast niego można z powodzeniem użyć XHTMLa+JS, czy Fleksa+Actionscripta (który to kompiluje się już do obiektu SWF, ale za to ma wolne SDK i nie potrzeba do niego żadnych płatnych narzędzi).

Co trochę znamienne dla strony Adobe – miałem przed chwilą spory problem z podlinkowaniem SDK Fleksa. Niestety nie potrafię się poruszać płynnie po ich stronie i w ogóle nie znalazłem podstrony samego SDK. Ale spokojnie – może trochę dziwnie i niespójnie połączone, ale na stronie(nach) znajduje się cała potrzebna dokumentacja, którą do tej pory oceniam raczej dobrze.

Aha – Flex, jeśli dobrze rozumiem, jest open source’owy :O

Moja pierwsza aplikacja AIR oparta o HTML

Czego potrzebujemy? Na pewno przyda nam się AIR SDK. Co prawda można skorzystać także z darmowej Aptany, albo płatnego Adobe Dreamweaver CS3/CS4 z Adobe AIR Extension for Dreamweaver, ale my skupimy się na najniższym poziomowo rozwiązaniu, czyli AIR SDK (który rzecz jasna należy pobrać). Do tego na pewno będzie nam potrzebna zainstalowana biblioteka uruchomieniowa (czyt. plugin) Adobe AIR. No i na koniec zwykły edytor tekstu (najlepiej gvim :).

Mało odkrywczo zaczniemy od przywitania się ze światem. Nie pokazuje to ani trochę możliwości AIRa, ale w tym wpisie już nic więcej nie zmieszczę – za długi się robi :). Stwórzmy więc sobie katalog HelloWorld, w którym będziemy potrzebowali kilku plików.

HelloWorld.html
plik obowiązkowy – główna strona naszej aplikacji,
HelloWorld-app.xml
również obowiązkowy – konfiguracja aplikacji (z ang. descriptor) w którym między innymi można ustawić ścieżkę instalacji, nazwę, ikony, ustawienia okna, czy ścieżkę do głównego pliku HTML,
AIRAliases.js
plik przydatny – tworzy proste aliasy do JSowych obiektów dostępnych dzięki AIR. Np. zamiast window.runtime.flash.filesystem.File otrzymujemy air.File. Plik ten przekopiowujemy z sciezka_do_AdobeAIRSDK/frameworks/libs/air/.

Na początek HelloWorld.html. Zakładam, że składnię HTMLa znasz i nie muszę nic tłumaczyć. Jakby ktoś chciał więcej szczegółów odnośnie zawartości plików, to odsyłam do źródła skąd cały ten przykład zaczerpnąłem.

<html>
<head>
    <title>Hello World</title>
    <script type="text/javascript" src="AIRAliases.js"></script>
    <script type="text/javascript">
        function appLoad(){
            air.trace("Hello World");
        }
    </script> 

</head>
<body onLoad="appLoad()">
    <h1>Hello World</h1>
</body>
</html>

Jak widać – żadnych rewolucji. Pora na HelloWorld-app.xml:

<?xml version="1.0" encoding="UTF-8"?> 

<application xmlns="http://ns.adobe.com/air/application/1.5">
    <id>examples.html.HelloWorld</id>
    <version>0.1</version>
    <filename>HelloWorld</filename>
    <initialWindow>
        <content>HelloWorld.html</content>
        <visible>true</visible>
        <width>400</width>
        <height>200</height>
    </initialWindow> 

</application>

Wydaje mi się, że wszystko powinno być jasne. Jedynie wartość elementu <filename> była dla mnie niejasna, więc tłumaczę – jest to nazwa aplikacji w systemie (czyli po zainstalowaniu). Wchodzi w to: katalog instalacyjny, plik wykonywalny oraz nazwa w menu systemowym (każdą z tych rzeczy można jeszcze ustawić osobno).

Testowanie aplikacji

Do testowego uruchamiania (i debugowania) stworzonej aplikacji służy odpalany z wiersza poleceń AIR Debug Launcher (ADL). Znajduje się on w sciezka_do_AdobeAIRSDK/bin/. Włączamy więc konsolę, przechodzimy do katalogu HelloWorld i startujemy:

p@d:/air/HelloWorld$ /air/AdobeAIRSDK/bin/adl HelloWorld-app.xml
Jakis trace
p@d:/air/HelloWorld$

Powinniśmy obejrzeć takie o to, piękne okienko:

Wynik działania programu HelloWorld

Jak widać program wydrukował nam też na konsoli „Jakis trace” co jest wynikiem działania air.trace("Jakis trace").

Tworzenie pliku instalacyjnego .air

Pora teraz spakować do jednego pliku instalacyjnego (paczka zip) naszą aplikację dzięki której zapanujemy nad światem. Służy do tego narzędzie ADT.

Pierwsze co trzeba wiedzieć, to że wszystkie AIRowskie aplikacje są cyfrowo podpisane. Temat ten jest mi kompletnie obcy i za pierwszym razem męczyłem się 3h żeby wygenerować jakiś swój klucz. Otóż poszedłem w stronę javowego keytoola, co było dla mnie ślepą uliczką ;). Co prawda nauczyłem się generować wszystkie typy kluczy, ale żaden nie działał :D. Na szczęście jest o wiele prostsza (i przede wszystkim działająca) ścieżka, którą przegapiłem. O niej za chwilę, a teraz cytat ze strony Adobe o bezpieczeństwie:

To ensure application security, all AIR installation files must be digitally signed. For development purposes, you can generate a basic, self-signed certificate with ADT or another certificate generation tool. You can also buy a commercial code-signing certificate from a commercial certificate authority such as VeriSign or Thawte. When users install a self-signed AIR file, the publisher is displayed as “unknown” during the installation process. This is because a self-signed certificate only guarantees that the AIR file has not been changed since it was created. There is nothing to prevent someone from self-signing a masquerade AIR file and presenting it as your application. For publicly released AIR files, a verifiable, commercial certificate is strongly recommended. For an overview of AIR security issues, see AIR security.

Oczywiście przegapiłem zaznaczony fragment i cały akapit poniżej :) A w zasadzie to mam wrażenie, że go tam wcześniej nie było. Ale koniec płaczu nad straconym czasem. Generujemy własny klucz:

p@d:/air/HelloWorld$ /air/AdobeAIRSDK/bin/adt -certificate -cn AIRBegginer 1024-RSA my_key_file dupa.8

W ten o to sposób wygenerowaliśmy klucz na nazwę podmiotu (company name) AIRBegginer, zabezpieczony hasłem dupa.8, który został zapisany do pliku my_key_file. Można go podejrzeć javowym narzędziem keytool:

p@d:/air/HelloWorld$ keytool -list -v -keystore my_key_file -storetype pkcs12
Enter keystore password:  

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: 1
Creation date: 2009-04-25
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=AIRBegginer
Issuer: CN=AIRBegginer
Serial number: 36373061626165333a31323064646264383133663a2d38303030
Valid from: Sat Apr 25 16:46:05 CEST 2009 until: Fri Apr 25 16:46:05 CEST 2014
(...)

Tak utworzonym kluczem możemy teraz podpisać naszą aplikację:

p@d:/air/HelloWorld$ /air/AdobeAIRSDK/bin/adt -package -storetype pkcs12 -keystore my_key_file HelloWorld.air HelloWorld-app.xml HelloWorld.html AIRAliases.js

Zostaniemy poproszeni o hasło i jeśli nic nie skopaliśmy, to ADT stworzy nam plik HelloWorld.air, który jest naszą pierwszą spakowaną i gotową do instalacji aplikacją AIR.

Instalacja

Najprostszy sposób jeśli mamy w systemie zmapowane rozszerzenie .air z instalatorem Adobe AIR (ja standardowo tak miałem) to instalacja jak na Windowsie – przez podwójnego klika w HelloWorld.air. Jeśli nie to wpisujemy:

p@d:/air/HelloWorld$ /opt/Adobe AIR/Versions/1.0/airappinstaller ./HelloWorld.air
unexpected error: ArgumentError: Error #2004
p@d:/air/HelloWorld$ /opt/Adobe AIR/Versions/1.0/airappinstaller /air/HelloWorld/HelloWorld.air

Jak widać skrypt jest zbyt „głupi” żeby sobie poradzić ze ścieżką względną i trzeba podać absolutną. Chwila czekania i zium:

Instalowanie aplikacji AIR

Znów hasło roota i wszystko działa.

Zarządzanie zainstalowanymi aplikacjami na Linuksie

Programy niestety na moim Ubuntu pojawiają się w menu systemowym w sekcji „Akcesoria”. Zainstalowałem już kilka na próbę i zaczął mi się tam robić bałagan. Ustawienie w pliku konfiguracyjnym aplikacji pola <programMenuFolder> zdaje się działać tylko na Windowsach.

Przed chwilą znalazłem także informację o tym jak można zarządzać już zainstalowanymi programami. Otóż są one instalowane jak paczki odpowiednio deb albo rpm. Dzięki temu np. w moim Ubuntu mogłem odinstalować programy z poziomu Synaptica. Nazwa programu składa się z z pola applicationId (np. examples.html.helloworld) oraz publisherID.

Program AIR w Synapticu

Uwaga dla każdego, kto nie znalazł swojej aplikacji – ja musiałem zrestartować system żeby Synaptic się odświeżył.

Koniec

To by było na tyle. Miałem jeszcze w tym artykule (ależ to ładnie brzmi ;P) pokazać jakiś ciekawszy przykład aplikacji, ale wydaje mi się, że byłby to już zbyt długi wpis. Postaram się więc dość szybko zmajstrować następną notkę. I będzie ona albo przedstawiała możliwości jakie daje AIR, albo skrobnę coś o Fleksie. A najpewniej i jedno, i drugie w krótkim czasie :).

Jeśli chodzi o ten wpis, to wszelkie sugestie, uwagi, czy pytania mile widziane. Jak zaznaczyłem gdzieś po drodze – moja wiedza na temat Adobe AIR jest nikła i mogłem popełnić sporo błędów.

Źródło i inne przydatne linki

Artykuł ten ukazał się równolegle na moim prywatnym blogu.

Kategorie:Adobe AIR, HTML + CSS Tagi:

Walidacja jednego pola formularza w symfony

Marzec 31st, 2009 reinmar 2 comments

Natknąłem się ostatnio na problem. Otóż wszystkie formularze walidujemy przy wykorzystaniu symfoniowych Formsów. Standardowe użycie podczas aktualizacji obiektu Place wygląda następująco:

$this->form = new PlaceForm ($place);
$this->form->bind ($request->getParameter ('place'));

if ($this->form->isValid())
{
	$place = $this->form->updateObject();
	$place->save();
}

Co jednak kiedy nie chcemy walidować całego formularza? Np. w przypadku kiedy używając InPlaceEditora zmieniamy jedno pole. Podpowiem, że próba wysłania tylko tego jednego pola i wykorzystaniu tej samej akcji skończy się wyzerowaniem pozostałych pól.

Dokumentacja symfony niestety milczy na ten temat (choć przyznam się, że nie szukałem jakoś strasznie dokładnie), a wiemy chyba wszyscy co jest jej największą bolączką – opisanie tylko kilku funkcjonalności poszczególnych modułów. Jak się chce czegoś więcej, to droga wolna – szukajcie sobie sami w kodzie.

Tak też postąpiłem. I po koszmarnie długim czasie odkryłem, że do walidowania każdego z pól służy metoda o intuicyjnej nazwie… clean(). Przykład walidacji jednego pola:

$form = new PlaceForm($place);
try
{
	$cleaned = $form->getValidator($field_name)->clean($value); #1

	$place->setByName($field_name, $cleaned, BasePeer::TYPE_FIELDNAME); #2
	$place->save();
}
catch (sfValidatorError $e) #3
{
	#Obsługa błędu
}
  1. Pobranie odpowiedniego walidatora oraz wywołanie na nim metody clean(), która w przypadku, gdy pole ma poprawną wartość zwraca je. A w przypadku błędu wyrzuca wyjątek sfValidatorError,
  2. Zapisanie wyczyszczonej wartości tego pola do obiektu,
  3. Kod obsługujący przypadek niewalidującego się pola.

Mam nadzieję, że ten krótki artykuł pozwoli komuś zaoszczędzić trochę czasu.

Wpis opublikowałem także na prywatnym blogu reinmar.jogger.pl.

Konsola Firebuga – „echowanie” Javascriptu

Marzec 18th, 2009 reinmar 1 komentarz

„Echowanie” jest zapewne znane wszystkim koderom PHP :) Pomaga w prostym debugowaniu skryptów. Javascript podobnie jak PHP żadnego sensownego debuggera nie posiada, a dodatkowo nie ma żadnego wyjścia. Korzystanie z metody alert() jest o tyle niewygodne, że zatrzymuje wykonywanie skryptu. Z ratunkiem przychodzi Konsola Firebuga. Udostępnia ona kilka ciekawych funkcjonalności. Pierwszą z nich jest console.log(), która pozwala formatować wyjście na kilka sposobów:

console.log("Ten kod powinien się wykonać");

var foo = "bar", x = 12.34, i = 122;

console.log("foo: %s, (int) i: %d, (float) x: %f", foo, i, x);

function Kopytko() {
	this.palec1 = 11.12;
	this.palec2 = "qń";
};
kopyto = new Kopytko();
console.log(kopyto);
console.log(kopyto.brak);

Przykład działania console.log w Firebugu

Jednak to nie wszystko. Możemy także pokolorować i nadać znaczenia naszej radosnej twórczości. I tak mamy dostępne metody console.debug(), console.info(), console.warn(), console.error().

Przykład formatowania informacji na konsoli Firebuga

Dodatkowo, przydatne (np. przy pracy z Ajaksem, czy manipulacjach w drzewie DOM) może się okazać również drukowanie HTMLa oraz XMLa. Służy do tego metoda console.dirxml(DOMElement). Wyświetla ona drzewo znane nam z zakładki „HTML” Firebuga.

Przykład działania console.dirxml() w Firebugu

To chyba na tyle o „echowaniu” na konsolę Firebuga. Więcej informacji możecie znaleźć w Console API oraz Rejestrowaniu Javascriptu (damn – nie mogę lepszego tłumaczenia znaleźć :). I tak z poziomu kodu można mierzyć czas wykonania bloku instrukcji console.time("name"); {..} console.timeEnd("name");, czy tworzyć asercje (a jak to się tłumaczy?).

Artykuł ten ukazał się też w zbliżonej formie na moim osobistym blogasku.

Kategorie:HTML + CSS, JavaScript Tagi: