Mozilla Hacks: ECMAScript 5 Strict Mode – tryb ścisły w ECMAScripcie 5

W tym odcinku tłumaczeń artykułów z Mozilla Hacks – notka Jeffa Waldena o trybie ścisłym w JavaScripcie. Jeff zajmuje się rozwojem SpiderMonkey, silnika JS Firefoksa.

Zarówno oryginalny artykuł, jak i to tłumaczenie dostępne są na licencji Creative Commons – Attribution Share-Alike v3.0.

ECMAScript 5 strict mode – tryb ścisły ES w Firefoksie 4

Programiści ze społeczności Mozilli znacząco usprawnili silnik JavaScriptu w Firefoksie 4. Wiele wysiłku włożyliśmy w zwiększenie wydajności, ale pracowaliśmy także nad nowymi możliwościami. W szczególności skupiliśmy się na ECMAScripcie 5, najnowszej wersji standardu będącego podstawą JavaScriptu.

Tryb ścisły (ang. strict) jest najbardziej interesującą nowością ECMAScriptu 5. Programiści mają możliwość skorzystania ze ściślejszego, bardziej ograniczonego wariantu JavaScriptu. Tryb ścisły nie jest tylko podzbiorem języka: ma celowo inną semantykę w porównaniu ze zwykłym kodem. Przeglądarki, które nie obsługują trybu ścisłego, będą uruchamiać kod w nim napisany w inny sposób niż przeglądarki tryb ścisły obsługujące – nie należy więc ślepo polegać na trybie ścisłym bez testowania (wykrywania) wsparcia istotnych aspektów tego trybu.

Continue reading “Mozilla Hacks: ECMAScript 5 Strict Mode – tryb ścisły w ECMAScripcie 5”

Mozilla Hacks: Interfejs FormData wkrótce w Firefoksie

W dzisiejszym odcinku tłumaczeń z Mozilla Hacks notka Paula Rogeta, jednego z głównych europejskich „ewangelizatorów” Mozilli, poświęcona interfejsowi FormData. Jak większość treści z MozHacks, zarówno oryginalny artykuł, jak i poniższe tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Interfejs FormData wkrótce w Firefoksie

Implementacja tego interfejsu pojawiła się w Mozilla Central (trunku) i dostępna jest aktualnie tylko w conocnych kompilacjach Firefoksa.

Specyfikacja XMLHttpRequest Level 2 (obecnie w wersji roboczej) dodaje nowy interfejs – FormData. Obiekty FormData umożliwiają tworzenie par klucz-wartość, reprezentujących pola formularzy i ich wartości, które można następnie łatwo przesłać w formacie “multipart/form-data” wykorzystując metodę send() obiektu XMLHttpRequest.

Dlaczego FormData?

W celu przesłania złożonych danych ze strony WWW do serwera (pliki, treści inne niż ASCII) konieczne jest stosowanie typu zawartości multipart/form-data. Zazwyczaj aby utworzyć formularz z takim typem, stosuje się kod jak poniżej:

<form method="post" 
    enctype="multipart/form-data" action="http://foo.bar/upload.php">
<input type="file" name="media"/>
<input name="nickname"/>
<input name="website"/>
<input type="submit" value="upload"/>
</form>

W ten sposób zwykle przesyła się pliki.

Firefox 3.6 umożliwił manipulację plikami z poziomu JavaScriptu (zob. File API [w jęz. angielskim – przyp. tłum.]), ale nie było dotąd prostego sposobu na przesłanie plików przez XMLHttpRequest. Odtworzenie funkcjonalności powyższego formularza z poziomu JavaScriptu było trudne, bo wymagało zakodowania treści do postaci multipart/form-data przez kod skryptu (np. ten kod, napisany przeze mnie [przez Paula Rogeta – przyp. tłum.] jakiś czas temu, implementuje kodowanie multipart/form-data: jest powolny i niezbyt elegancki).

Tutaj właśnie przydaje się FormData: do odtworzenia funkcjonalności przesyłania formularzy z poziomu JavaScriptu.

Obiekt FormData

Obiekt FormData pozwala ułożyć zbiór par klucz-wartość do przesłania przez XMLHttpRequest. Obiekt ten posiada tylko jedną metodę:

append(key, value);

Argument key to nazwa klucza dla przesłanej wartości, a wartość ta – value – może być ciągiem znaków lub plikiem.

Można utworzyć obiekt FormData, dołączyć do niego wartości i przesłać go z wykorzystaniem XMLHttpRequest. Aby zasymulować powyższy formularz, wystarczy:

// aFile może być plikiem z inputa z atrybutem type="file"
// albo plikiem przeciągniętym metodą drag&drop
var formdata = new FormData();
formdata.append("nickname", "Foooobar"); 
formdata.append("website", "http://hacks.mozilla.org");
formdata.append("media", aFile);
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://foo.bar/upload.php");  
xhr.send(formdata);

FormData i element <form>

Firefox (czy raczej silnik Gecko – przyp. tłum.) nieco rozszerza element <form> języka HTML o metodę getFormData(), która pozwala na pobranie danych formularza w postaci obiektu FormData. Nie jest to obecnie część standardu HTML, ale spodziewamy się, że pojawi się w niej w przyszłości (możliwe, że pod inną nazwą):

var formElement = document.getElementById("myFormElement");
var xhr = new XMLHttpRequest();
xhr.open("POST", "submitform.php");
xhr.send(formElement.getFormData());

Można także dodać dane do obiektu FormData pomiędzy pobraniem go z formularza a przesłaniem, jak poniżej:

var formElement = document.getElementById("myFormElement");
formData = formElement.getFormData();
formData.append("serialnumber", serialNumber++);
xhr.send(formData);

Pozwala to na modyfikację danych formularze przed przesłaniem, na przykład rozszerzając je o dodatkowe informacje, które nie są przeznaczone do edycji przez użytkownika w formularzu.

Zasoby (w jęz. angielskim):

Mozilla Hacks: Geolokalizacja w Firefoksie 3.5

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ę http://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.

Mozilla Hacks: Przechodzenie po drzewie DOM w Firefoksie 3.5

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.

Mozilla Hacks: Synchroniczne żądania XHR w Firefoksie 3.5

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