środa, 31 października 2012

[HTML|JS|CSS] HTML5: Microdata

Ideą, która przyświecała twórcom mikrodanych jest zaszycie w znacznikach html informacji o pewnych encjach danych. Funkcjonalność taka, jest możliwa dzięki temu, że HTML5 ignoruje atrybuty których nie zna, co nie powoduje utrudnień w działaniu strony.  Dane  z jednej strony są przedstawiane użytkownikom odwiedzającym strony, z drugiej dostępne są do pobrania w formie klucz - wartość.  W związku z tym wymagane jest podanie adresu URL do opisu poszczególnych properties danego typu. Same atrybuty mikrodanych opisano poniżej:
  • itemscope - definicja kontenera na mikrodane
  • itemtype - adres URL z opisem poszczególnych pól danego typu, typy można zagnieżdżać
  • itemprop - klucz atrybut (wartość przeważnie pomiędzy tagiem otwierającym a zamykającym)

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Microdata</title>
</head>
<body>
  <div itemscope itemtype="http://www.data-vocabulary.org/Person/">
 <div>
   <span>Hello, my name is <strong itemprop="name">John Doe</strong></span>
 </div>
 <div>
   <span>I work as a <strong itemprop="role">Software Developer></strong> and I study at  <strong itemprop="affiliation"> AGH UST</strong>
 </div>
  </div>
</body>
</html>

[HTML|JS|CSS] HTML5: Web Workers

Web Workers to funkcjonalność, która umożliwia wykonywanie długotrwałych operacji w tle. Główną korzyścią jest więc responsywny interfejs użytkownika podczas wykonywania tych operacji. Kilka wad, jakie pojawiają się przy korzystaniu z WW, to przede wszystkim ograniczenia, a pośród nich brak dostępu do:
  • DOM
  • Window
  • Host page
Kolejnym problemem może być fakt, że kod wykonywany przez workera powinien być pisany tylko w JavaScripcie. W związku z tym nie możemy korzystać z popularnych bibliotek jak na przykład jQuery.  Pomimo tych problemów, wciąż mamy dostęp do takich mechanizmów, jak:
  • navigator
  • timeout
  • XHR
Aby skorzystać z Web Workera musimy użyć funkcji konstruktora Worker, komunikacja odbywa się poprzez funkcję postMessage, gdzie dane przekazuje się jako argumenty. Aby przerwać działanie wystarczy wywołać funkcję terminate().

Poniżej przykład obliczania w tle silni dla zadanej ilości liczb.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Web workers</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
 <h3>Factorial calculator</h3>
 <div>
  <span>
   <input type="number" id="count" />
   <button id="start">Start</button>
   <button id="terminate">Terminate</button>
  </span>
 </div>
 <div>
  <ol id="list">
  </ol>
 </div>
</body>
</html>

Plik ze skryptem:

var worker;

$(function(){
 $('#start').click(function(){
  $('#list').html("");
  var count = $('#count').val();
  var settings = {};
  settings.count = parseInt(count);
  worker = new Worker('factorial.js');
  worker.onmessage = function(event){
   var list = event.data;
   $.each(list, function(){
    printNumber(this);
   })
  };
  worker.postMessage(settings);
 });

 $('#terminate').click(function(){
  if(worker){
   worker.terminate();
  }
 });
});

function printNumber(number){
 $('#list').append('<li>'+number+'</li>');
}

Plik factorial.js

var results = [];

function messageHandler(e){
 if(e.data.count > 0){
  calculatefactorial(e.data.count);
 }
}

function factorial(n){
 if(n < 2){
  return 1;
 }
 else
  return n*factorial(n-1);
}

function calculatefactorial(length){
 for(var i = 1; i <= length; i++){
   results.push(factorial(i));
  }; 
 setTimeout(function(){
  postMessage(results);
 }, i*100);
 
}

addEventListener("message", messageHandler, true);

[HTML|JS|CSS] HTML5: Geolocation

Geolokacja, a więc zdolność do określania położenia geograficznego  jest kolejną funkcjonalnością dostępną w HTML5. Długość i szerokość geograficzną urządzenia klienta uzyskuje się na różne sposoby zależnie od tego, co umożliwia urządzenie, z którego wysyłamy żądanie podania położenia:
  • Adres IP
  • Sieć WiFi
  • Wbudowane urządzenie GPS
  • Bluetooth MAC Address
  • GSM/CDMA telefonu
Sposób określania położenia przekłada się na dokładność, z jaką podane nam zostaną nasze współrzędne geograficzne.

Poniżej przykład, jak w prosty sposób pobrać współrzędne:

$(function(){

 $('#getBtn').click(function(){
  navigator.geolocation.getCurrentPosition(callback,err);
 });
});

function callback(position){
 var coords = position.coords;
 $('#lat').val(coords.latitude);
 $('#lon').val(coords.longitude);
}
function err(){
 alert('An error occured');
}

Przed pierwszym pobraniem przeglądarka zapyta, czy zezwolić na pobieranie danych na temat współrzędnych.


API udostępnia kilka opcji, które możemy podać jako ostatnim argument:
  • High Accuracy (bool, default:false) - może poprawić dokładność kosztem dłuższego oczekiwania
  • Timeout (int [ms], default no value) - czas po którym strona ma zaprzestać obliczeń.
  • Maximum Age (int[ms], default: 0) - w trybie ciągłego śledzenia położenia jest to czas, po którym ma się ponownie pobrać informacja o współrzędnych.

sobota, 27 października 2012

[HTML|JS|CSS] HTML5: Offline Web Applications

Najprościej mówiąc aplikacje offline, to takie, które będą działały po utracie połączenia internetowego. Aby było to możliwe, cały dokument html wraz ze wszystkimi zależnościami musi być przechowywany na komputerze użytkownika i dodatkowo potrzebujemy mechanizmów powiadających o utracie (lub odzyskaniu) połączenia. Oczywiście nie jesteśmy w stanie przesłać wszystkiego, ale pewna ilość plików wystarczy, aby zachować funkcjonalność w trybie offline.

Do tej pory przeglądarki udostępniały tzw. Browser Cache. Jego zasada działania jest następująca : przy odwiedzeniu pewnej strony po raz pierwszy, tworzony jest dla niej folder z cache,  z pobranymi plikami (html, js, css, img). Przy kolejnych requestach o ten plik pobierany jest on z cache (jeżeli w międzyczasie nie został zmieniony), przez co czas od wysłania żądania do wyświetlenia całej zawartości strony jest znacznie krótszy. Gdy utracone zostaje połączenie, z cache pobrany zostanie tylko dokument html, bez plików js, css i plików graficznych.

Nowa funkcjonalność to Application Cache, który również jest miejscem na komputerze użytkownika, z którego strony mogą być dostarczane. Różnica polega na tym, że pliki nie są pobierane pojedynczo, a "tranzakcyjnie". Oznacza to mniej więcej tyle, że nie będziemy mogli otworzyć strony, jeżeli którykolwiek z plików nie został pobrany. Z drugiej strony, jeżeli transakcja zostanie zakończona pomyślnie, to mamy pewność, że cała zawartość strony znajduje się na naszym dysku.  

Skąd przeglądarka ma wiedzieć o tym, które pliki tworzą razem aplikację ? Mówi o tym plik manifestu, o którego strukturze nieco więcej zostanie napisane poniżej. Warto pamiętać, że zmiana dowolnego pliku aplikacji na serwerze nie spowoduje jego aktualizacji po stronie klienta. Dzieje się tak dlatego, że pliki aktualizowane są dopiero przy zmianie manifestu.

Aby móc skorzystać z funkcjonalności aplikacji offline, nie wolno korzystać z browser cache, a także należy pliki manifestu przesyłać ze specjalnym Content-Type w nagłówku. Przykładowa konfiguracja serwera w Node.js poniżej.

var express = require('express');
var http = require('http');
var app = express();

app.use(express.static(__dirname));

app.get("/cache.manifest", function(req, res){
  res.header("Content-Type", "text/cache-manifest");
  res.end("CACHE MANIFEST");
});

app.listen('1027');

Manifest

Dołącza się go w znaczniku html, może być plikiem o dowolnym rozszerzeniu, np:
<html manifest="cache.manifest">
Przykładowy plik manifestu:

CACHE MANIFEST
# version 1.2

CACHE:
script.js
HTML5_Logo.png
http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js


NETWORK:
jquery.jpg

Mamy dostępne kilka sekcji. Wersja może być przydatna, ponieważ zmiana wersji spowoduje pobranie na nowo plików przez przeglądarkę.  
CACHE - lista plików do pobrania
NETWORK - pliki, które z jakiegoś powodu muszą być pobierane zawsze
FALLBACK - zamienniki dla plików, których nie da się pobrać gdy aplikacja przejdzie w tryb offline.

W javascripcie poprzez obiekt applicationCache, możemy obsłużyć zdarzenia, jakie występują w przypadku update'u manifestu oraz pobierania plików z Application Cache.

Pełny kod z przykładem obsługi wszystkich dostępnych zdarzeń poniżej:


<!doctype html>
<html manifest="cache.manifest">
<head>
<meta charset="utf-8">
<title>Offline Web Applications</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
 $(function(){
 window.applicationCache.onupdateready = function(e){
  console.log('onupdateready');
  applicationCache.swapCache();
 }
});
</script>
</head>
<body>
 <h1>hello world</h1>
</body>
</html>



$(function(){
 if(window.applicationCache){
  console.log('supporting app cache');

  window.applicationCache.onchecking = function(e){
   console.log('onchecking');
  }

  window.applicationCache.oncached = function(e){
   console.log('oncached');
  }

  window.applicationCache.onupdate = function(e){
   console.log('onupdate');
  }

  window.applicationCache.onobsolete = function(e){
   console.log('onobsolete');
  }

  window.applicationCache.ondownloading = function(e){
   console.log('ondownloading');
  }
 }
});

piątek, 26 października 2012

[HTML|JS|CSS] HTML5: Web storage

Do tej pory popularną metodą przechowywania danych po stronie klienckiej były tzw, cookies. Serwer wysyłał pewne informacje, zapisywane na komputerze klienta i odczytywał je przy ponownym uruchomieniu strony. Mechanizm ten jest nadal szeroko stosowany przy projektowaniu "spersonalizowanych" stron www. Wraz z nadejściem standardu HTML5 dostajemy alternatywę w postaci Web Storage. Funkcjonalność ta umożliwia składowanie danych po stronie klienckiej bez ingerencji serwera. Dodatkowo otrzymujemy znacznie więcej dostępnej pamięci. Wprowadzono dwa rodzaje Web storage, które udostępniają identyczne API dla programistów JS:
  • Local storage
  • Session storage
Oba obiekty to properties obiektu window, więc mamy do nich łatwy dostęp po stronie skryptowej. Różnica polega na tym, że o ile pamięć local storage jest trwała (tzn. musimy ją opróżniać konkretnym poleceniem), o tyle dane zapisane w session storage zostają utracone wraz z utratą sesji (15 minut bezczynności użytkownika, zamknięcie przeglądarki, zamknięcie karty itd).
Aby uruchomić funkcjonalność Web storage należy stronę wystawiać na serwerze (nie wystarczy otworzyć w przeglądarce dokumentu).

Poniżej prosty przykład wykorzystujący API Web storage. Zapis i odczyt można wykonywać na trzy sposoby:
  • localStorage[key] = value
  • localStorage.setItem(key,value)
  • localStorage.key = value


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Web storage</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
 <div>
  <h1>Local storage</h1>
  <span>Key: 
   <input type="text" id="lskey" />
  </span>
  <span>Value: 
   <input type="text" id="lsvalue" />
  </span>
  <span>
   <button id="lswrite">Write</button>
   <button id="lsread">Read</button>
   <button id="lsclear">Clear</button>
  </span>
 </div>
 <div>
  <h1>Session storage</h1>
  <span>Key: 
   <input type="text" id="sskey" />
  </span>
  <span>Value: 
   <input type="text" id="ssvalue" />
  </span>
  <span>
   <button id="sswrite">Write</button>
   <button id="ssread">Read</button>
   <button id="ssclear">Clear</button>
  </span>
 </div>
</body>
</html>

Obsługa API Web Storage:

$(function(){

 //local storage
 $('#lswrite').click(function(){
  var key = $('#lskey').val();
  var val = $('#lsvalue').val();
  localStorage.setItem(key,val);
 });

 $('#lsread').click(function(){
  var key = $('#lskey').val();
  $('#lsvalue').val(localStorage.getItem(key));
 });

 $('#lsclear').click(function(){
  var key = $('#lskey').val();
  var val = $('#lsvalue').val();
  if(!localStorage[key]){
   localStorage.clear();
  }
  else{
   localStorage[key] = "";
  }
 });

 //session storage
 $('#sswrite').click(function(){
  var key = $('#sskey').val();
  var val = $('#ssvalue').val();
  sessionStorage.setItem(key,val);
 });

 $('#ssread').click(function(){
  var key = $('#sskey').val();
  $('#ssvalue').val(sessionStorage.getItem(key));
 });

 $('#ssclear').click(function(){
  var key = $('#sskey').val();
  var val = $('#ssvalue').val();
  if(!sessionStorage[key]){
   sessionStorage.clear();
  }
  else{
   sessionStorage[key] = "";
  }
 });
});

wtorek, 23 października 2012

[HTML|JS|CSS] HTML5: Audio and video

Standard HTML5 wprowadza dwa nowe tagi: <audio> i <video>. Umożliwiają one osadzanie plików multimedialnych w stronach www, zapewniając jednocześnie podstawową funkcjonalność do ich odtwarzania. Jedną z głównych zalet w porównaniu z dotychczasowym sposobem odtwarzania multimediów jest brak zapotrzebowania na technologię Flash, z którą spore problemy występują np. na iPadach.

1. Tag <video>

Aby odtwarzać filmy na naszej stronie, wystarczy dostarczyć plik oraz osadzić poniższy fragment kodu na stronie. 

<video width="320" height="240" controls="controls">
 <source src="Two Steps from Hell - Heart of Courage.mp4" type="video/mp4">   
  Your browser does not support video tag
</video>

Tekst o nie wspieraniu tagu zostanie przykryty w momencie gdy przeglądarka rozpozna tag source. Dodatkowo mamy do dyspozycji kilka użytecznych atrybutów.
  • autoplay - true lub false w zależności od tego, czy film ma startować od razu przy przeładowaniu strony.
  • controls - w zależności od tego, czy występuje pojawia się pasek z kontrolkami obsługującymi film
  • loop  gdy ustawimy na true, film będzie się zaczynał od nowa po zakończeniu odtwarzania
  • poster - obrazek, który będzie wyświetlany, gdy nie zostanie odnaleziony film
  • onerror - zdarzenie błędu, które może być obsłużone w JavaScripcie.

2. Tag <audio>

W bardzo podobny sposób działa tag do odtwarzania plików dźwiękowych. W tym przypadku warto pamiętać o tym, która przeglądarka wspiera które formaty (choć oczywiście sytuacja jest dynamiczna i już wkrótce ten problem może się nie pojawiać). Spis kompatybilności przeglądarek z formatami audio tutaj. Jeżeli chcemy wspierać wiele przeglądarek, można zawsze umieścić źródła do plików w wielu formatach. Przeglądarka wybierze pierwszy wspierany.

<audio controls="controls">
 <source src="Wilderness.ogg" type="audio/ogg">
 <source src="Wilderness.mp3" type="audio/mp3">
 Your browser does not support audio tag
</audio>

Z atrybutów opisanych dla video mamy dostępne wszystkie poza poster.

poniedziałek, 22 października 2012

[HTML|JS|CSS] HTML5: Drag and drop

Jedną z nowości, jakie wprowadza standard HTML5 jest API do drag and drop. Dzięki niemu użytkownik może w obrębie okna przeglądarki przesuwać elementy. Programista dostaje z kolei kilka atrybutów, które może wykorzystać jako callbacki oprogramowane w JavaScript. Wspomniane atrybuty to:
  • draggable
  • ondragenter
  • ondragover
  • ondrop
  • ondragstart
  • ondragend
Pierwszy z nich ustawia się na true na pewnym elemencie DOM, jeśli chcemy zezwolić użytkownikowi na przesuwanie tego elementu. Pozostałe służą do powiązania z nimi odpowiednich funkcji JS. Atrybuty ondragenter oraz ondragover oraz ondrop służą do przechwytywania zdarzeń elementu, nad którym ma mieć miejsce przeciąganie, dwa ostatnie dotyczą samego elementu przeciąganego.

Ostatnim ważnym obiektem jest dataTransfer. Dostęp do niego mamy poprzez zdarzenia związane z drag and drop. Możemy w nim przechowywać dane za pomocą funkcji setData, getData, clearData. W property o nazwie effectAllowed programista może ustawić na jaki efekt zezwala użytkownikowi:
  • move - przeniesienie elementu
  • copy - skopiowanie elementu w miejscu, w którym nastąpi drop
  • link - podlinkowanie elementu poprzez data transfer 
Poniżej obszerny przykład, jak napisać prosty mechanizm drag and drop w HTML5.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Drag and drop</title>
<script type="text/javascript" src="script.js"></script>
<style>
 #target1, #target2, #target3{
  float: left; 
  width: 250px; 
  height: 250px;
  padding: 10px;
  margin: 10px;
  background-color: cyan;
 }
 #draggable1, #draggable2, #draggable3{
  width: 75px;
  height: 70px;
  padding: 5px;
  margin: 5px;
  background-color: orange;  
 } 
</style>
</head>
<body>
 <h1>Drag and drop example</h1>
 <div id="target1"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">1</div>
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">2</div>
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">3</div> 
 </div>
 <div id="target2"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
 </div>
 <div id="target3"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
 </div>
</body>
</html>


Część skryptowa:
1. start przy rozpoczęciu przemieszczania elementu, określany jest dozwolony rodzaj ruchu oraz przechowywane jest jego id.

function start(e){
 e.dataTransfer.effectAllowed = 'move';
 e.dataTransfer.setData("Data", e.target.getAttribute('id'));
 return true;
}

2. enter- pozwala draggowalnym obiektom przemieszczać się na targetami

function enter(e){
 return true;
}

3. over - funkcja ta decyduje o tym, czy dany element może zostać opuszczony nad konkretnym kontenerem

function over(e){
 var dragid = e.dataTransfer.getData("Data");
 var id = e.target.getAttribute('id');
 if(id == "target3" && dragid == "draggable3"){
  return true;
 }
 if(id == "target2" && dragid == "draggable2"){
  return true;
 }
 return false;
}

4. drop - co ma się stać z elementem po opuszczeniu go przez użytkownika

function drop(e){
 var id = e.dataTransfer.getData("Data");
 e.target.appendChild(document.getElementById(id));
 e.stopPropagation();
 return false;
}

5. end - zakończenie drag and drop, czyszczenie dataTransfer

function end(e){
 e.dataTransfer.clearData("Data");
 return false;
}