marcoos.techblog

HTML 5: przesyłanie wielu plików jednocześnie

Posted by: marcoos on: 29.10.2009

Dotychczas, chcąc umożliwić użytkownikowi przesłanie wielu plików przy pomocy formularza HTML, webmasterzy musieli posiłkować się albo apletami Flasha (takimi jak SWFUpload czy Uploadify), albo wstawić wiele elementów input. To drugie rozwiązanie było niewygodne dla użytkownika, ponieważ musiał on każdy plik wybrać w osobnym oknie wyboru pliku. Tak robiło się to do tej pory w HTML 4.01:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upload plików - pojedynczy</title>
<form method="post" enctype="multipart/form-data" action="upload.php">
  <fieldset>
    <legend>Prześlij pliki</legend>
      <label>Dodaj plik 1:
        <input type="file" name="files[]">
      </label><br>
      <label>Dodaj plik 2:
        <input type="file" name="files[]">
      </label><br>
      <label>Dodaj plik 3:
        <input type="file" name="files[]">
      </label><br>
      <label>Dodaj plik 4:
        <input type="file" name="files[]">
      </label><br>
      <!-- itd... -->
      <input type="submit">
    </fieldset>
</form>

Niezbyt to wygodne, jeśli chcemy załadować np. kilka zdjęć z naszej ostatniej wycieczki w góry…

Gecko 1.9.2 (dostępne m. in. w nadchodzącym Firefoksie 3.6) oraz nowsze wersje przeglądarek opartych na silniku WebKit obsługują atrybut multiple elementu <input> z atrybutem type="file". Atrybut ten pozwala do jednego inputa wstawić wiele plików. Powyższy kod upraszcza się więc w HTML 5 do takiej postaci:

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Upload wielu plików</title>
<form method="post" enctype="multipart/form-data" action="upload.php">
  <fieldset>
    <legend>Prześlij pliki</legend>
    <label>Dodaj pliki:
      <input type="file" multiple name="files[]">
    </label><br>
    <input type="submit">
  </fieldset>
</form>

Po kliknięciu “Przeglądaj” użytkownikowi wyświetli się systemowa wybieraczka plików, pozwalająca zaznaczyć wiele plików (z wciśniętym klawiszem Ctrl, Shift, lub poprzez odpowiednie zaznaczenie myszą). Po stronie serwera nic się nie zmienia, przesłane pliki traktowane są tak samo, jakby były przesłane z kilku osobnych, a nie jednego wspólnego inputa.

Tak więc np. kod w PHP operujący na tablicy $_FILES w ogóle nie musi być modyfikowany po przejściu na input z atrybutem multiple (jeśli korzystaliśmy do tej pory z wielu inputów o tej samej “tablicowej” nazwie).

HTML 5 po raz kolejny rozwiązuje problem, który irytował webmasterów od niepamiętnych czasów. Teraz czekamy już tylko na Operę i Microsoft… :)

PS. Na razie aplety w rodzaju SWFUpload pozostają niezastąpione, jeśli chcemy użytkownikowi np. pokazywać pasek postępu podczas przesyłania plików. Wkrótce jednak flashowe protezy zupełnie stracą rację bytu – W3C pracuje nad specyfikacją FileUpload, która umożliwi przesyłanie plików poprzez XMLHttpRequest.

CSS Transitions, czyli efekty przejścia w CSS

Posted by: marcoos on: 21.10.2009

W wersji Firefoksa obecnie oznaczanej jako 3.7 pojawi się wsparcie dla efektów przejścia (ang. transitions) w CSS. Czym są efekty przejścia? W momencie zmiany stanu danego elementu (np. zmiany pseudoklasy, jak :hover, albo zmiany stylu wywołanej np. z poziomu JavaScriptu) wskazane własności CSS zmienią swe wartości w sposób płynny, a nie skokowy.

CSS Transitions pojawiły się najpierw w silniku WebKit, a od niedawna obsługuje je także silnik Gecko 1.9.3. Apple zgłosiło specyfikację CSS Transitions do konsorcjum W3C; ma ona obecnie status „Working Draft” (wersji roboczej).

Jak to wygląda?

Tutaj znajdziecie prosty przykład (potrzebny jest testowy nightly-build Firefoksa z mozilla-central lub w miarę nowe Safari lub Chrome).

Jak tego używać?

Specyfikacja definiuje własności:

transition-property
określa, jaka własność stylu ma podlegać efektowi przejścia
transition-duration
określa, jak długo ma trwać ten efekt
transition-timing-function
określa przyspieszenie lub spowolnienie animacji
transition-delay
określa opóźnienie z jakim ma zacząć się efekt
transition
skrót dla wszystkich pozostałych własności

Omówmy je pokrótce.

transition-property

Podajemy tutaj, jaka własność (lub jakie własności) mają być animowane. Jeśli nie określimy tej własności, lub podamy tu słowo kluczowe all, animowane będą wszystkie własności CSS, które tylko da się animować. Wartość none wyłącza efekty przejścia.

Przykład:

transition-property: color;

transition-duration

Tutaj podajemy czas trwania przejścia.

Przykład:

transition-duration: 5s;

Ta animacja będzie trwała 5 sekund.

transition-timing-function

Własność ta pozwala animacji zwalniać lub przyspieszać w trakcie jej trwania. Do określenia tych zmian wykorzystywane są sześcienne krzywe Béziera.

Dopuszczalne wartości to cubic-bezier(x2, y2, x3, y3), gdzie x2 i y2 oraz x3 i y3 to odpowiednio współrzędne punktów kontrolnych P2 i P3 krzywej (punkty P0 i P1są ustalone jako (0, 0) i (1, 1)).

Kilka szczególnie przydatnych wartości uzyskało także wygodne do zapamiętania aliasy:

Słowo kluczowe Odpowiednik cubic-bezier
ease cubic-bezier(0.25, 0.1, 0.25, 1.0)
linear cubic-bezier(0.0, 0.0, 1.0, 1.0)
ease-in cubic-bezier(0.42, 0, 1.0, 1.0)
ease-out cubic-bezier(0, 0, 0.58, 1.0)
ease-in-out cubic-bezier(0.42, 0, 0.58, 1.0)

Wartość linear oznacza po prostu liniową zmianę wartości, czyli tempo animacji będzie jednostajne. Pozostałe wartości z powyższej tabeli sprawiają, że animacja zwolni lub przyspieszy na początku lub na końcu.

Przykład:

transition-timing-function: linear;

transition-delay

Możemy tu podać opóźnienie, z jakim wykona się animacja. Wartość równa 0 oznacza brak opóźnienia. Dozwolone są również wartości ujemne (-x), wówczas animacja zacznie się natychmiast (tak, jak przy wartości 0), ale początkowa wartość animowanej własności będzie taka, jakby przejście trwało już od |x| sekund.

Przykład:

transition-delay: 0.5s;

transition

Własność transition to skrótowy zapis wszystkich pozostałych, w kolejności: property, duration, timing function, delay. Na przykład:

transition-property: color;
transition-duration: 5s;
transition-timing-function: ease-in;
transition-delay: 2s;

można krótko zapisać jako:

transition: color 5s ease-in 2s;

Zarówno własności transition-* jak i skrótowa własność transition mogą przyjmować wiele wartości równolegle, tj. można
określić efekt przejścia np. dla własności color oraz dodatkowo inny dla width:

transition: color 5s ease-in 2s, width 3s ease-out 3s;

Jak zrobić div, który rozwinie się po najechaniu kursorem?

Bardzo prosto:

HTML:

<div id="slideDiv">witaj świecie!</div>

CSS:

#slideDiv {
  width: 5px;
  height: 20px;
  overflow: hidden;
  background: green;
  color: #fff;
  -moz-transition: width 1s linear;
  -webkit-transition: width 1s linear;
}

#slideDiv:hover {
  width: 20px;
}

Ograniczenia obecnych implementacji

Specyfikacja jest dopiero w stadium roboczym, tak więc wszystko tutaj może się jeszcze zmienić. Zarówno Gecko, jak i WebKit implementują powyższe własności w tzw. przedrostkiem producenta. Oznacza to, że obecnie żaden kod z powyższych przykładów nie zadziała, dopóki nie poprzedzimy odpowiednim przedrostkiem wszystkich omawianych własności. Dla Gecko jest to -moz-, dla WebKitu: -webkit-.

Aby nasze przejścia obecnie działały w obu tych silnikach musimy niestety napisać je dwukrotnie (albo i potrójnie, jeśli chcemy zachować na przyszłość składnię bez przedrostków). W poniższym przykładzie odnośniki będą płynnie zmieniać swój kolor w ciągu pół sekundy od najechania na nie myszą:

a {
  color: #f00;
  -moz-transition: color 0.5s linear;
  -webkit-transition: color 0.5s linear;
  transition: color 0.5s linear;
}

a:hover {
  color: #0f0;
}

W przypadku dostępu do tych własności z poziomu JavaScriptu należy pamiętać o tym, że minus przechodzi w powiększenie następującej po niej litery, tak więc
do własności efektów przejścia z poziomu JS na razie dobieramy się w Gecko przez element.style.MozTransition, a w WebKicie przez
element.style.WebkitTransition.

Nie wszystkie własności są obecnie animowane. Szerszy zakres ma na razie WebKit (obsługuje np. transformacje -webkit-transform; Gecko na dzień dzisiejszy nie pozwala na przejścia dla -moz-transform), ale wkrótce Mozilla powinna tutaj dogonić Apple. :)

Ważna sprawa: ani Gecko, ani WebKit nie animują przejść od wartości w rodzaju auto do wartości liczbowej. Zmiany tutaj odbywają się niestety skokowo.

Poprawna degradacja

Deklaracje własności transition w przeglądarkach nie obsługujących efektów przejścia (w tym obecne wersje Opery, IE, Firefox 3.6 i starsze, stare WebKity) nie powodują żadnych problemów, po prostu wymiary czy kolory elementów zmienią się w nich skokowo, tak jak bez transition.

Czy już to stosować?

Na poważnych witrynach najlepiej będzie się wstrzymać, przynajmniej do czasu względnego ustabilizowania się specyfikacji i jej implementacji.
Ale nikt nie broni dzisiaj poeksperymentować! :)

Linki

Mozilla Hacks: Geolokalizacja w Firefoksie 3.5

Posted by: marcoos on: 20.08.2009

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Geolocation in Firefox 3.5, którego autorem jest Doug Turner, jeden z twórców obsługi geolokalizacji w Firefoksie 3.5. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Geolokalizacja w Firefoksie 3.5

Zawsze jesteśmy w jakimś miejscu. Piszę te słowa w kawiarni w Toronto w Kanadzie. Kiedy wpiszę „google” w pasku adresu, przechodzę na stronę www.google.ca, kanadyjską wersję Google’a,, wykrywanie to działa w oparciu o mój adres IP. Zwykle kiedy chcę znaleźć najbliższe kino, po prostu wpisuję tam mój kod pocztowy. Ta informacja jest często przechowywana w witrynie, dlatego łatwiej znaleźć kino następnym razem. W takich sytuacjach jednak wygodniej jest, kiedy aplikacja webowa może automatycznie określić moje położenie. Tak naprawdę nie mam pojęcia, jaki jest kod pocztowy dla Toronto. Potrafię go znaleźć, ale to za dużo zachodu.

Firefox 3.5 zawiera proste API javascriptowe pozwalające na dodanie obsługi geolokalizacji do aplikacji webowej. Pozwala to użytkownikom – opcjonalnie – udostępnić witrynom dane o położeniu bez wprowadzania kodu pocztowego. Poniżej pokrótce przedstawiam, jak wykorzystać geolokalizację w Firefoksie, jak ona działa i jakie środki ostrożności należy mieć na uwadze podczas korzystania z geolokalizacji.

Podstawy

Pobranie danych o położeniu użytkownika jest bardzo proste:

function showPosition(position) {
    alert(position.coords.latitude + “ “ +
    position.coords.longitude);
}
 
navigator.geolocation.getCurrentPosition(showPosition);

Wywołanie metody getCurrentPosition zwraca aktualne położenie geograficzne użytkownika, które potem wyświetlamy w oknie powiadomienia. Położenie jest udostępniane w postaci współrzędnych geograficznych – długości i szerokości geograficznej (pola coords.latitude i coords.longitude – przyp. tłum.). To właśnie takie proste.

Kiedy witryna prosi o udostępnienie informacji o położeniu, użytkownikowi wyświetli się następujący pasek powiadomienia:

Zrzut ekranu - geolokalizacja w Firefoksie

Dostępne opcje pozwalają udostępnić położenie lub nie i zapamiętać tę decyzję.

Obsługa błędów

Istotne jest obsłużenie w kodzie dwóch przypadków błędów:

Po pierwsze, użytkownik może odmówić udostępniania położenia lub zignorować tę prośbę. API pozwala na określenie opcjonalnej procedury obsługi błędu (errorCallback w przykładzie poniżej – przyp. tłum.). Jeśli użytkownik odmówi udzielenia zgody, procedura to zostanie wywołana z odpowiednim kodem błędu. Jeśli użytkownik nie odpowie, procedura nie zostanie wywołana. Aby obsłużyć i ten przypadek, należy dodać parametr timeout do wywołania metody getCurrentPosition, określający czas, po jakim upłynie czas oczekiwania na odpowiedź użytkownika.

navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {timeout:30000});

Ten kod sprawi, że funkcja errorCallback zostanie wywołana zarówno, jeśli użytkownik odmówi zgody, jak też po 30 sekundach od wyświetlenia monitu w przypadku, gdy użytkownik na niego nie odpowie.

Po drugie, dokładność określenia położenia użytkownika może być różna. Przyczyn tego jest kilka:

  • Różne metody określenia położenia mają różne stopnie dokładności
  • Użytkownik może nie zechcieć udostępnić witrynie dokładnego położenia.
  • Wiele urządzeń GPS ma ograniczoną dokładność w zależności od widoczności nieba. Jeśli widoczność się pogarsza, pogarsza się dokładność.
  • Wielu urządzeniom GPS zajmuje kilka minut przejście od zgrubnie określonego położenia do dokładnego, nawet przy dobrej widoczności nieba.

(Informacje o dokładności – w metrach – wyznaczonego położenia zawiera pole accuracy obiektu coords – przyp. tłum.)

Przypadki takie będą się zdarzać, dlatego ważne jest wsparcie dla zmian w dokładności.

Aby monitorować zmieniające się położenie, należy zarejestrować procedurę przy użyciu metody watchPosition:

navigator.geolocation.watchPosition(showPosition);

Funkcja showPosition zostanie wywołana przy każdej zmianie położenia.

Można także obserwować zmiany położenia odpytując getCurrentPosition co jakiś czas. Jednakże dla zaoszczędzenia energii i lepszej wydajności zaleca się korzystanie z watchPosition. API typu callback zwykle oszczędzają energię i wywoływane są tylko wtedy, gdy są potrzebne. Dzięki temu przeglądarka jest bardziej responsywna, co jest szczególnie istotne na urządzeniach przenośnych.

Więcej informacji znaleźć można w roboczej specyfikacji API, zawierającej inne przydatne przykłady.

Od kuchni

Istnieje kilka sposobów na określenie położenia. Najpopularniejsze opierają się na informacjach o sieciach WiFi w okolicy, o adresie IP i urządzeniach GPS podłączonych do komputera. W Firefoksie 3.5 do wyznaczenia położenia wykorzystujemy dane o lokalnych sieciach WiFi i adres IP.

Kilka firm objeżdża miasta samochodami z zamontowanymi antenami WiFi, zapisując adresy punktów dostępowych i ich siłę sygnału w danym miejscu. Dane te umieszczane są w wielkiej bazie danych. Następnie, przy pomocy odpowiednich algorytmów można znaleźć odpowiedź na pytanie: „Jeśli widzę takie i takie punkty dostępowe WiFi, to gdzie jestem?”. To jest główny sposób odnajdywania położenia wykorzystywany przez Firefoksa 3.5.

Nie wszyscy jednakże mają karty WiFi, a ponadto nie każde miejsce na świecie zostało przeskanowane pod kątem punktów dostępowych WiFi. W takiej sytuacji Firefox spróbuje użyć adresu IP, korzystając z bazy danych, która mapuje adresy IP do przybliżonego położenia geograficznego. Tak określone położenie jest niestety znacznie mniej dokładne niż to określone przy użyciu WiFi. Na przykład, tutaj w Toronto położenie określone na bazie WiFi ma dokładność ok. 150 metrów. To samo miejsce wyznaczone na podstawie adresu IP ma dokładność około 25 km.

Prywatność

Ochrona prywatności użytkownika jest bardzo ważna dla Mozilli – to część naszej misji. W zakresie danych zbieranych online, informacja o położeniu jest szczególnie delikatna. Unia Europejska uznaje dane o położeniu za dane osobowe (PII, personally identifiable information), które muszą być odpowiednio traktowane (zgodnie z dyrektywą 95/46/EC). Wierzymy, że użytkownicy powinni mieć ścisłą kontrolę nad danymi udostępnianymi witrynom. Dlatego też Firefox pyta przed udostępnieniem witrynie informacji o położeniu, pozwala też użytkownikowi na usunięcie witryn z listy tych, którym udostępniane jest położenie, i udostępnia ustawienia udostępniania w oknie informacji o stronie

Firefox robi, co tylko może, dla ochrony prywatności. Ponadto, grupa robocza geolokalizacji W3C proponuje następujące zasady ochrony prywatności użytkownika dla twórców i operatorów witryn:

  • Odbiorcy danych o położeniu muszą żądać danych o położeniu tylko wtedy, gdy jest to niezbędne.
  • Odbiorcy muszą używać danych o położeniu tylko do celu, dla którego zostały one im przekazane.
  • Odbiorcy muszą pozbyć się informacji o położeniu natychmiast po ukończeniu tego zadania, chyba że użytkownik wprost wyraził zgodę na ich przechowywanie.
  • Odbiorcy muszą również podjąć odpowiednie środki ochrony tych informacji przed niepowołanym dostępem.
  • Jeśli informacje o położeniu są przechowywane, użytkownicy powinni mieć możliwość ich aktualizacji i usunięcia.
  • Odbiorca informacji o położeniu nie może retransmitować tych danych bez zgody użytkownika. Należy zachować ostrożność podczas retransmisji, zaleca się też korzystanie z protokołu HTTPS.
  • Odbiorcy muszą też otwarcie i wyraźnie ujawnić fakt gromadzenia danych o położeniu, przyczynę ich gromadzenia i okres czasu, w jakim są przechowywane, sposób, w jaki są zabezpieczane, sposób, w jaki są współdzielone (jeśli są), jak wielu użytkowników może korzystać, aktualizować i usuwać te dane, jak również wszelkie inne decyzje, jakie są podejmowane w związku z tymi danymi. Ujawnienie to musi zawierać wyjaśnienie wszelkich odstępstw od powyższych wytycznych.

Rzecz jasna, są to dobrowolne sugestie, ale mamy nadzieję, że tworzą one podstawę do dobrego zachowania operatorów witryn, którą użytkownicy pomogą na nich wymusić.

Zastrzeżenia

Zaimplementowaliśmy pierwszą publiczną wersję roboczą specyfikacji geolokalizacji W3C. Mogą w niej zajść drobne zmiany, ale będziemy zachęcali grupę roboczą do zachowania wstecznej kompatybilności.

Jedyny znany nam problem, który może dotyczyć użytkowników tego API, to możliwa zmiana nazwy metody enableHighAccuracy na inną, np. useLowPower. Firefox 3.5 zawiera metodę enableHighAccuracy z przyczyn zgodności, jednakże obecnie nic ona nie robi. Jeśli nazwa zostanie zmieniona, dla zgodności zachowamy obie wersje.

Podsumowanie

Firefox 3.5 to pierwszy krok na drodze do szerszej obsługi geolokalizacji i wielu innych standardów obecnie rozwijanych w różnych grupach roboczych. Spodziewamy się, że ludzie polubią tę funkcjonalność w aplikacjach mapowych, witrynach ze zdjęciami oraz w serwisach takich jak Twitter i Facebook. Najbardziej interesująca dla nas jest jednak wiedza, że ludzie znajdą nowe sposoby wykorzystania tego API, o których my nawet nie pomyśleliśmy. Sieć WWW się zmienia, a informacje o położeniu zaczynają odgrywać w niej istotną rolę. Cieszymy się, że możemy tu pomóc.

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Elliptical Borders in Firefox 3.5, którego autorem jest Lim Chee Aun (web developer z Malezji, autor ikon i motywu Phoenity). Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Własność border-radius jest prawdopodobnie jedną z bardziej interesujących części specyfikacji CSS 3, umożliwiającą tworzenie zaokrąglonych rogów elementów dokumentu. Na przykład:

div {

  border-radius: 10px;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;

}

W Firefoksie 3.5 własność -moz-border-radius jest obecnie zgodna z najnowszą wersją (roboczą – przyp. tłum.) specyfikacji CSS 3. Dzięki temu można tworzyć także eliptyczne obramowania.

Co to więc znaczy? Według specyfikacji składnia jest następująca:

-moz-border-radius: <border-radius>{1,4} [ / <border-radius>{1,4}]?

Mamy tutaj zbiór wartości tej własności rozdzielonych ukośnikiem. Tutaj leży cała magia. Jeśli dwa zbiory wartości rozdzielone są ukośnikiem, wartości z lewej strony określają półoś poziomą, a wartości z prawej – półoś pionową.

Oferuje to ciekawe możliwości. Poniższe demo pokazuje kilka eksperymentów z różnymi kształtami, które można w ten sposób uzyskać.

W przykładzie tym eksperymentujemy nie tylko z grubością i promieniem/półosiami obramowania, ale także i jego stylem – o wartościach ridge, double i groove. Obecnie wartości dotted i dashed nie działają, wyświetlane są jak solid. Więcej informacji na ten temat znaleźć można w zgłoszeniu błędu Mozilli nr 431176 .

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu DOM Traversal in Firefox 3.5 autorstwa Johna Resiga (znanego m. in. jako twórca biblioteki jQuery). Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Przechodzenie po drzewie DOM w Firefoksie 3.5

Firefox 3.5 obsługuje dwie nowe specyfikacje W3C dotyczące przechodzenia po drzewie DOM. Pierwsza z nich – Element Traversal API – ułatwia przechodzenie pomiędzy kolejnymi elementami, a druga – interfejs NodeIterator – sprawia, że odnajdywanie węzłów wszystkich typów staje się prostsze.

Element Traversal API

Zadaniem Element Traversal API (API przechodzenia po elementach) jest ułatwienie przechodzenia po elementach w drzewie DOM z pominięciem pośrednich węzłów tekstowych, węzłów komentarzy itp. Rozwiązuje to istniejący od dawna problem irytujący programistów; w szczególności problematyczne były przypadki, w których document.documentElement.firstChild miało różną wartość w zależności od występowania białych spacji w dokumencie.

Element Traversal API wprowadza kilka nowych własności węzłów DOM, znacznie ułatwiających przechodzenie drzewa.

Poniżej zestawienie istniejących dotychczas własności DOM i ich nowych odpowiedników:

Cel Wszystkie węzły DOM Tylko elementy DOM
Pierwszy .firstChild .firstElementChild
Ostatni .lastChild .lastElementChild
Poprzedni .previousSibling .previousElementSibling
Następny .nextSibling .nextElementSibling
Długość .childNodes.length .childElementCount

Własności te są dość prostym rozszerzeniem specyfikacji DOM (szczerze mówiąc, powinny się w niej znajdować od początku).

Jednej własności jednakże brakuje: .childElements (jako odpowiednika .childNodes). Własność ta (zawierająca dynamiczną kolekcję typu NodeSet elementów potomnych danego elementu DOM) znajdowała się w poprzednich iteracjach tej specyfikacji, ale w międzyczasie została z niej usunięta.

Ale nie wszystko stracone. Obecnie Internet Explorer, Opera i Safari obsługują własność .children, oferującą nadzbiór funkcjonalności, która miała być udostępniana przez .childElements. Kiedy obsługa Element Traversal API została włączona do Firefoksa 3.5, zaimplementowana wraz z nią została także kolekcja .children. Oznacza to, że obecnie każda z głównych przeglądarek obsługuje tę własność (wyprzedza ona pod tym względem właściwą specyfikację Element Traversal).

Oto kilka przykładów wykorzystania Element Traversal API (i kolekcji .children):

Wyświetl następny element po kliknięciu:

someElement.addEventListener("click", function(){
    this.nextSiblingElement.style.display = "block";
}, false);

Ustaw klasę dla wszystkich elementów bezpośrednio podrzędnych:

for ( var i = 0; i < someElement.children.length; i++ ) {
    someElement.children[ i ].className = "active";
}

NodeIterator API

NodeIterator to dość stare API, które nie było dotąd szeroko stosowane, a obecnie zostało zaimplementowane w Firefoksie 3.5. NodeIterator API ma na celu ułatwienie przechodzenie po wszystkich węzłach dokumentu DOM (w tym węzłów tekstowych, komentarzy itd.).

Samo API jest dość zawiłe (zawiera sporo funkcji, które nie są specjalnie istotne dla większości programistów), ale w prostych zastosowaniach jest dość łatwe w użyciu.

Zasada działania jest następująca: tworzymy instancję NodeIterator (za pomocą document.createNodeIterator) i przekazujemy jej zbiór filtrów. NodeIterator potrafi zwrócić wszystkie węzły w dokumencie (czy w kontekście danego węzła), dlatego zwykle chcemy odfiltrować wyniki, by dostać tylko pożądane węzły. Oto prosty przykład:

Utwórzmy instancję NodeIterator, by przeiterować po wszystkich węzłach komentarzy w dokumencie.

var nodeIterator = document.createNodeIterator(
    document,
    NodeFilter.SHOW_COMMENT,
    null,
    false
);

var node;

while ( (node = nodeIterator.nextNode()) ) {
    node.parentNode.removeChild( node );
}

Po utworzeniu NodeIterator jest dwukierunkowy (można przechodzić w dowolnym kierunku, przy pomocy własności previousNode i nextNode).

Prawdopodobnie najsensowniejszym zastosowaniem tego API jest przechodzenie po często używanych (ale w inny sposób trudnych do przejścia) węzłach, takich jak komentarze i węzły tekstowe. Jako że istnieje już kilka innych API do przechodzenia po elementach DOM (jak np. getElementsByTagName), omówione tu API jest kolejną przydatną alternatywą dla innych sposobów na przechodzenie węzłów dokumentu.

W uzupełnieniu powyższego artykułu Johna – dwa zdania ode mnie na ten temat. W swoim blogu autor powyższego artykułu jest bardziej krytyczny co do NodeIteratora. Zgadzam się z nim tutaj w pełni – osobiście mam wrażenie, że gdyby nie test ACID 3, którego NodeIterator jest częścią, nie zobaczylibyśmy implementacji tego API w Gecko. Omówione w pierwszej części artykułu Element Traversal API jest natomiast zdecydowanie bardziej przydatne w codziennej pracy web developera.

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Synchronous XHR requests in Firefox 3.5 autorstwa Douga Turnera. Doug pracuje w Mozilli nad projektem Mobile. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Synchroniczne żądania XHR w Firefoksie 3.5

Żądania XMLHttpRequest (XHR) mogą być zarówno synchroniczne, jak i asynchroniczne. Chociaż w większości przypadków korzysta się z żądań asynchronicznych, zdarza się, że możemy potrzebować żądania synchronicznego (np. w wątkach roboczych – przyp. tłum.), czyli wstrzymać dalsze wykonywanie kodu JavaScript aż do ukończenia obsługi żądania XMLHttpRequest. W Firefoksie 3 i starszych przeglądarka odpalała zdarzenia czasowe i reagowała na wprowadzanie danych podczas synchronicznego żądania XHR. W Firefoksie 3.5 i nowszych zdarzenia wprowadzania danych, takie jak ruchy myszą, oraz zdarzenia czasowe są wstrzymywane do chwili ukończenia żądania synchronicznego. Dzięki temu żądania synchroniczne są żądaniami blokującymi.

Na przykład:

function hello() {
     alert(“hello”);
}

 
setTimeout(hello, 20);
 
var req = new XMLHttpRequest();

req.open('GET', 'http://www.mozilla.org/', false);
req.send(null);

Przed Firefoksem 3.5 nie można było określić, czy funkcja „hello” wywołana zostanie przed czy po żądaniu XHR. Powodowało to różne problemy czasowe w aplikacjach WWW korzystających z synchronicznych żądań XHR.

Rozwiązaniem tego problemu jest wstrzymanie zdarzeń wprowadzania danych i zdarzeń czasowych do momentu powrotu z req.send.

Więcej informacji na ten temat można znaleźć w zgłoszeniach błędów nr 340345 i 333198 dotyczących tego problemu

Mozilla Hacks: API selektorów w DOM w Firefoksie 3.5

Posted by: marcoos on: 28.07.2009

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu DOM Selectors API in Firefox 3.5 autorstwa Johna Resiga. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

API selektorów w DOM w Firefoksie 3.5

Specyfikacja Selectors API, rekomendacja W3C, to nowe rozwiązanie pozwalające programistom JavaScriptu odnajdywać elementy drzewa DOM strony przy użyciu selektorów CSS. To pojedyncze API pozwala na przeszukiwanie drzewa DOM i odnajdywanie jego elementów przy użyciu prostego, ujednoliconego interfejsu.

API selektorów jest jednym z lepiej obsługiwanych przez wszystkie przeglądarki nowych standardów: można z niego korzystać już dziś w Internet Explorerze 8, Chrome, Safari, Firefoksie 3.5 i wkrótce w Operze 10.

Korzystanie z querySelectorAll

Selectors API zawiera dwie metody dostępne na wszystkich dokumentach, elementach i fragmentach DOM: querySelector i querySelectorAll. Metody te działają prawie identycznie – obie przyjmują jako argument selektor CSS i zwracają pasujące do niego elementy DOM (z tą różnica, że querySelector zwraca tylko pierwszy pasujący element).

Na przykład, mając dany poniższy fragment kodu HTML:

<div id="id" class="class">
    <p>Pierwszy akapit.</p>

    <p>Drugi akapit.</p>
</div>

możemy wykorzystać querySelectorAll do ustawienia czerwonego koloru tła wszystkich akapitów leżących wewnątrz diva o ID równym „id”:

var p = document.querySelectorAll("#id p");

for ( var i = 0; i < p.length; i++ ) {

    p[i].style.backgroundColor = "red";
}

Możemy też odnaleźć pierwszy akapit diva o klasie „class” i ustawić jego nazwę klasy na „first”.

document.querySelector("div.class > p:first-child")
    .className = "first";

Dotychczas, bez użycia Selectors API, tego rodzaju przeszukiwanie wymagałoby napisania długiego kodu JavaScript/DOM, złożonego z wielu linii i wielu zapytań.

O ile metody Selectors API są relatywnie proste w użyciu (pobierają tylko jeden argument), o tyle bardziej problematyczną sprawą jest dobór selektorów CSS. API selektorów korzysta z selektorów CSS natywnie obsługiwanych przez przeglądarkę do stylowania elementów przy użyciu CSS. W większości przeglądarek (Firefoksa, Safari, Chrome i Opery) oznacza to, że możemy w pełni korzystać z selektorów CSS 3. Internet Explorer 8 obsługuje bardziej ograniczony podzbiór selektorów, włączając w to selektory CSS 2 (które i tak są bardzo przydatne).

Największym wyzwaniem dla nowych użytkowników API selektorów jest więc określenie, które selektory CSS są odpowiednie do odnalezienia pożądanych elementów – szczególnie, że większość programistów piszących kod działający w wielu przeglądarkach ma doświadczenie głównie z ograniczonym podzbiorem w pełni działających selektorów CSS 1.

Dobrze jest zacząć od specyfikacji selektorów CSS 2 i CSS 3, ale warto też przejrzeć poniższe artykuły, by dowiedzieć się więcej (w języku angielskim – przyp. tłum.):

Implementacje w bibliotekach

Najciekawszy przypadek użycia API selektorów to nie tyle ich bezpośrednie stosowanie przez programistów WWW, ale ich wykorzystanie w bibliotekach, które już teraz dostarczają funkcjonalność selektorów CSS w DOM. Istotnym dziś problemem w szerszym stosowaniu API selektorów jest fakt, że nie są one dostępne we wszystkich przeglądarkach, dla których tworzy się obecnie strony (w tym IE 6, IE 7 i Firefox 3). Dlatego też, dopóki starsze przeglądarki są w użyciu, musimy korzystać z narzędzia pośredniczącego, aby odtworzyć zachowanie selektorów CSS w DOM.

Na szczęście w wielu istniejących bibliotekach jest dostępne API zgodne z Selectors API (w rzeczywistości Selectors API zostało w dużej mierze zainspirowane przez istniejące biblioteki). Co więcej, wiele z tych implementacji używa wewnętrznie właśnie Selectors API. Oznacza to, że można stosować selektory CSS w DOM w wielu przeglądarkach już teraz, zapewniając większą wydajność w nowych przeglądarkach obsługujących Selectors API, bez nadmiernego wysiłku.

Oto niektóre z istniejących implementacji, które korzystają z Selectors API, o ile to możliwe:

Warto jeszcze raz podkreślić duży skok w wydajności, jaki powoduje korzystanie z nowego API (w porównaniu z tradycyną mieszanką DOM i JabaScriptu, stosowaną do tej pory). Różnicę tę widać na przykład we wzroście wydajności bibliotek javascriptowych, odkąd zaimplementowały one Selectors API.

Ostatnio wykonane testy przyniosły następujące wyniki:

Widać tu dramatyczny wzrost wydajności po zaimplementowaniu natywnego API selektorów w bibliotekach – całkiem możliwe, że podobny wzrost wydajności zobaczysz także w swoich aplikacjach.

Zestaw testów

Razem ze specyfikacją Selectors API przygotowany został zbiór testów Selectors API, jego autorem jest John Resig z Mozilli. Ten zbiór testów pozwala określić jakość implementacji API selektorów w głównych przeglądarkach.

Aktualne wyniki (w momencie pisania oryginalnego artykułu – przyp. tłum.) dla przeglądarek obsługujących nowe API:

  • Firefox 3.5: 99.3%
  • Safari 4: 99.3%
  • Chrome 2: 99.3%
  • Opera 10b1: 97.5%
  • Internet Explorer 8: 47.4%

Internet Explorer 8, jak wspomniano wcześniej, nie obsługuje większości selektorów CSS 3, dlatego nie przechodzi większości powiązanych testów.

Selectors API pozwala na proste i szybkie odnajdywanie elementów DOM strony. Już teraz przynosi ono pożytek osobom korzystającym z bibliotek javascriptowych zapewniających podobną funkcjonalność, dlatego też zachęcamy do wypróbowania nowego API już teraz.

Mozilla Hacks: Nieprzezroczystość w Firefoksie 3.5

Posted by: marcoos on: 23.07.2009

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie (króciutkiego) artykułu Opacity in Firefox 3.5, autorstwa Chrisa Blizzarda. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Nieprzezroczystość w Firefoksie 3.5

To będzie bardzo krótka notka, ale warta opublikowania, bo pokazuje, jak możliwości przeglądarek rozwijają się, począwszy od własnej implementacji producenta po w pełni wspierany standard.

Firefox 3.5 nie obsługuje już specyficznej dla Mozilli własności CSS -moz-opacity. Programiści chcący określić stopień nieprzezroczystości elementu powinni stosować standardową własność opacity.

Własność opacity pojawiła się już w Firefoksie 0.9, a -moz-opacity została oznaczona jako przestarzała. W Firefoksie 3.5 została ostatecznie usunięta.

Trwało to długo, jak na prostą własność, ale warto o tym wspomnieć, by pokazać perspektywę czasową dla tego rodzaju funkcji i ich związku ze standardami.

Mozilla Hacks: Korzystanie z Web Workers

Posted by: marcoos on: 19.07.2009

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Using Web Workers – Working Smarter, Not Harder. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.


Korzystanie z Web Workers – jak pracować mądrze, a nie ciężko

Autorem tego artykułu jest Malte Ubl, który wykonał sporo dobrej roboty przy użyciu technologii Web Workers w ramach projektu bespin.

W ostatnim czasie aplikacje webowe stały się znacznie bogatsze. Programy działające w przeglądarce, takie jak GMail, Meebo i Bespin pokazują nam, jak WWW będzie wyglądać i zachowywać się w przyszłości. Kluczowym aspektem tworzenia przyjaznej dla użytkownika aplikacji jest wysoka responsywność. Użytkownicy nie lubią czekać, a szczególnie nie lubią sytuacji, w których wydaje się, że program działa, po czym przestaje reagować.

W sercu współczesnych aplikacji WWW po stronie klienta leży język programowania JavaScript. JavaScript wraz z obiektowym modelem dokumentu DOM jest całkowicie jednowątkowy. Oznacza to, że w JavaScripcie tylko jedna rzecz może się zdarzyć w danej chwili. Nawet jeśli komputer ma 32-rdzeniowy procesor, tylko jeden rdzeń będzie zajęty podczas długich obliczeń. Na przykład, obliczając idealną trajektorię lotu na Księżyc nie można jednocześnie renderować animacji, która pokazuje trajektorię w tym samym czasie i nie ma możliwości reagowania na zdarzenia użytkownika, takie jak kliknięcia myszą czy pisanie na klawiaturze podczas obliczeń.

Współbieżność

Dla zachowania responsywności podczas intensywnych obliczeń większość współczesnych języków programowania wykorzystuje współbieżność. W przeszłości do uzyskania współbieżności często wykorzystywano wątki. Klasyczne wątki utrudniają jednakże programiście zrozumienie przebiegu programu, co często prowadzi do trudnych do zrozumienia błędów i chaotycznego zachowania w chwili, gdy różne wątki próbują równocześnie operować na tych samych danych.

Technologia wątków roboczych – Web Workers – zalecana przez WHATWG, pojawiła się w Firefoksie 3.5, pozwalając na wzbogacenie programów w JavaScripcie o współbieżność, unikając problemów związanych z typowymi programami wielowątkowymi. Rozpoczęcie wątku roboczego jest bardzo proste – wystarczy użyć konstruktora new Worker.

W tym przykładzie plik worker.js zostanie wczytany i uruchomiony w nowym wątku, dla niego utworzonym.

// Utwórz wątek roboczy na bazie pliku "worker.js"
var worker = new Worker("worker.js");

Komunikacja między głównym wątkiem interfejsu użytkownika a wątkami roboczymi opiera się na przesyłaniu komunikatów przy użyciu metody postMessage. Metoda ta została dodana do Firefoksa 3 by zapewnić komunikację między oknami. Aby przesłać komunikat z wątku roboczego do strony, wystarczy użyć tej metody:

// Wyślij komunikat do głównego wątku UI
postMessage("Witaj, strono!");

Aby odebrać wiadomość od wątku roboczego, na obiekcie typu Worker należy określić procedurę obsługi zdarzenia “onmessage”. Tutaj po prostu wyświetlimy dane zdarzenia, przekazane do tej procedury. W naszym przypadku własność “event.data” zawiera tekst “Witaj, strono!”, przesłany powyżej.

worker.onmessage = function (event) {

  alert(event.data);
  // Wyślij wiadomość do wątku roboczego
  worker.postMessage("Witaj, wątku!");

}

Do wysłania komunikatów do wątku roboczego wywołujemy metodę postMessage na obiekcie wątku. Aby odebrać te komunikaty w wątku, należy zdefiniować funkcję onmessage, która będzie wywoływana po każdym przesłaniu do niego komunikatu.

Obsługa błędów

Istnieją dwa sposoby obsługi błędów czasu wykonania w wątku. Po pierwsze, wewnątrz wątku można zdefiniować funkcję onerror. Po drugie, można obsłużyć błędy z zewnątrz wątku, przypisując procedurę onerror obiektowi typu Worker:

worker.onerror = function (event) {

  alert(event.message);
  event.preventDefault();
}

Metoda event.preventDefault() zapobiega wykonaniu domyślnej operacji, którą byłoby tutaj wyświetlenie błędu użytkownikowi lub wypisanie jej w konsoli błędów. Zamiast tego, w tym miejscu wyświetlmy treść błędu w oknie powiadomienia.

Brak współdzielenia

Wątki robocze nie dzielą żadnych stanów ze stroną, z którą są powiązane, ani z żadnymi innymi wątkami roboczymi; jedyną możliwą interakcją jest przesyłanie komunikatów przy użyciu metody postMessage. Wątki robocze nie mają dostępu do DOM, nie mogą więc manipulować stroną WWW. W ten sposób nie ma żadnego ryzyka dla integralności danych w sytuacji, gdy wiele wątków chce równocześnie operować na tych samych danych.

Typowe wykorzystanie wątków roboczych składa się ze strony-komponentu JavaScript, oczekującej na zdarzenia użytkownika. Kiedy nastąpi zdarzenie wywołujące intensywne obliczenia, do wątku roboczego przesyłany jest komunikat, który powoduje rozpoczęcie obliczeń. Skrypt na stronie może jednak natychmiast powrócić do oczekiwania na dalsze zdarzenia. W momencie, gdy wątek roboczy skończy pracę, przesyła wiadomość do strony, która może wówczas np. wyświetlić wyniki.

nigdy-wiecej
Ostrzeżenie przed nieresponsywnym skryptem, wyświetlane przez przeglądarki, gdy skrypt wykonuje się zbyt długo, odchodzi w przeszłość dzięki wątkom roboczym.

Przykład – ciąg Fibonacciego

W kolejnym przykładzie wątek roboczy oblicza w tle liczby Fibonacciego od 0 do 99. W rzeczywistości, ponieważ obliczanie liczb Fibonacciego tą nieefektywną
metodą może trwać długo przy większych liczbach (np. większych niż 30), skrypt może nawet nigdy nie zakończyć się na komputerze (albo „wyłożyć sie” z powodu przepełnienia stosu), ale ponieważ dzieje się to w wątku roboczym, nie ma to żadnego skutku co do responsywności strony. Można więc nadal wyświetlać skomplikowaną animację, by uprzyjemnić oczekiwanie na kolejną liczbę.

Poniższa strona HTML zawiera skrypt uruchamiający wątek roboczy z pliku „fib-worker.js”. Komunikaty z wątku roboczego wyświetlane są w konsoli przeglądarki (czy raczej Firebuga – przyp. tłum.) przy użyciu console.log.

<!DOCTYPE html>
<html>
    <head>
      <title>Web Worker API Demo</title>
      <script type="text/javascript">

        var worker = new Worker("fib-worker.js");
        worker.onmessage = function (event) {
          console.log(event.data.index +" -> " + event.data.value)
        }
      </script>
    </head>
    <body>
    </body>

</html>

Plik JavaScript z implementacją wątku roboczego zawiera pętlę, która wylicza liczby Fibonacciego i przesyła wyniki do strony.

// Plik fib-worker.js
function fib(n) {

   return n < 2 ? n : fib(n-1) + fib(n-2);

}
 
for(var i = 0; i < 100; ++i) {

   postMessage({
      index: i,
      value: fib(i)

   })
}

W powyższym przykładzie widać też, że przy użyciu postMessage można przesyłać złożone obiekty. Obiekty takie mogą zawierać wszystko to, co można przesłać w formacie JSON. Oznacza to, że nie można przesłać funkcji, a obiekty przekazywane są przez wartość, a nie przez referencję.

API wątków roboczych

Wątki robocze obsługują funkcję o nazwie importScripts. Dzięki niej można wczytać do wątku roboczego więcej plików źródłowych.

importScripts("file.js");

importScripts("foo.js", "bar.js");

Jeśli funkcja zostanie wywołana z wieloma argumentami, skrypty zostaną pobrane równolegle, ale wykonane w kolejności zdefiniowania. Funkcja ta jest blokująca – bieg wątku zostanie wstrzymany do momentu pobrania i wykonania wszystkich skryptów.

W kolejnym przykładzie wczytujemy zewnętrzny plik JavaScript, który oblicza wartość funkcji SHA-1 z ciągów znaków, a następnie wykorzystujemy go do wyliczania wartości SHA-1 dla zawartości odczytanej poprzez żądanie AJAX. Używamy tu standardowego obiektu XMLHttpRequest do pobrania zawartości z adresu URL przekazanego do zdarzenia onmessage. Co ciekawe, nie musimy budować tutaj asynchronicznego żądania AJAX, jako że wątek sam w sobie jest asynchroniczny względem strony, tak więc oczekiwanie na żądanie HTTP nie jest tu żadnym problemem.

importScripts("sha1.js")
 
function onmessage(event) {
    var xhr = new XMLHttpRequest();

    xhr.open('GET', event.data, false);
    xhr.send();

    postMessage(sha1(xhr.responseText));
}

Inne API dostępne dla wątków roboczych

Wątki mogą używać XMLHttpRequest do żądań AJAX, jak w powyższym przykładzie, jak również mają dostęp do bazy danych po stronie klienta dzięki API Web Storage. API te w wątkach roboczych są takie same jak w „zwykłym” JavaScripcie.

Funkcje setTimeout i setInterval (oraz clearTimeout i clearInterval) są dostępne, co pozwala na wykonywanie kodu po upływie danego czasu lub co pewien interwał. Dostępny jest też obiekt navigator object, udostępniający informacje o przeglądarce.

Dodatkowe API mogą zostać dodane w przyszłości.

Zgodność z przeglądarkami

W momencie pisania tego artykułu, o ile wiadomo jego autorowi, tylko Firefox 3.5 obsługuje możliwość przesyłania złożonych obiektów poprzez postMessage i implementuje rozszerzone API opisane powyżej. Safari 4 zawiera prostą implementację API Worker. W innych przeglądarkach można korzystać z Workers poprzez wtyczkę Google Gears, w której technologia ta pojawiła się na początku.

Przykład wykorzystania na prawdziwej stronie

W ramach projektu Bespin, opartego na przeglądarce edytora kodu źródłowego, z powodzeniem wykorzystujemy wątki robocze do implementacji intensywnych dla procesora funkcji, takich jak sprawdzanie poprawności kodu na bieżąco czy podpowiadanie kodu. Stworzyliśmy także nakładkę, implementującą API Worker w ramach Google Gears, która także dodaje brakującą funkcjonalność w Safari 4, a także umożliwiliśmy korzystanie ze transparentnych zdarzeń własnych, na bazie interfejsu postMessage. Komponenty te zostaną upublicznione jako odrębna biblioteka, do wykorzystania przez inne projekty.

Technologia Web Workers odgrywa istotną rolę w próbie uczynienia z Otwartej Sieci WWW jeszcze potężniejszej platformy dla złożonych programów. Jako że wszystko, co robią wątki robocze, to wykonywanie JavaScriptu, łatwo jest napisać skrypty, które działają także w przeglądarkach nie zapewniających luksusu wątków roboczych. Polecamy dodanie Web Workers do Twoich programów, aby stały się bardziej responsywne i przyjemniejsze w użyciu.

Mozilla Firefox 3.5 już jest!

Posted by: marcoos on: 30.06.2009

Przed paroma minutami, po roku wytężonych prac, Mozilla oficjalnie wydała wersję 3.5 przeglądarki internetowej Firefox.

Jak dla mnie najfajniejsze są dwie rzeczy: nowy, lepszy silnik JavaScriptu (w tym wątki “web workers” i natywny JSON) oraz obsługa znaczników <audio> i <video> i formatu Ogg. Obsługa filtrów w SVG i transformacje w CSS także ucieszą wielu web developerów. A dla zwykłego użytkownika ważne jest to, że w 3.5 aplikacje WWW działają szybciej i otworem stoi droga do zwiększenia ich możliwości.

Więcej informacji na stronie Możliwości Firefoksa 3.5 i w informacjach o wydaniu.

Zapraszamy do pobierania i życzymy miłego użytkowania! :)

PS. Na bieżąco aktualizowane statystyki pobierania Firefoksa 3.5 można podglądać na stronie downloadstats.mozilla.com.

Ważne akcje

Rusz tyłek - pomóż Paulinie

Pomóż Paulinie!
Więcej: paulapruska.blogspot.com
Pomóż przez siepomaga.pl

twitter

Kategorie

Archiwa

Statystyka

  • 374,212 wejść