Uma (re)introdução ao JavaScript




TDC Florianópolis
                                         Rodrigo Vieira
25/08/2012                                     @rodbv
                                                          1
Saturday, August 25, 2012   2
 Um pouco de história
 A linguagem
 Dicas e erros comuns
Mocha!
           LiveScript!




Brendan Eich       Scheme




                            4
    Linguagem dinâmica, fracamente tipada, funcional
     e OO

    Objetos e vetores são “dicionários melhorados”

    Herança por prototipação




Saturday, August 25, 2012                               5
Java + script


DOM


AJAX


jQuery
Saturday, August 25, 2012   6
 Valor
       String, Number, Boolean, null, undefined
 Referência
         Objetos
         Funções
         Arrays
         RegEx
         Date
         Math
Saturday, August 25, 2012                          7
    Sequência de caracteres unicode

    Strings com mesmo valor são consideradas
     idênticas

    Não existe tipo char

    Podemos usar aspas simples e duplas

    Possui métodos
Saturday, August 25, 2012                       8
 Ponto flutuante de 64 bits
 Não existe tipo inteiro
 NaN
 Infinity
 Hexa (0xa12)
 Notação científica (1.23e-8)
 Octal: primeiro dígito zero, cuidado!
       parseInt(“08”) //0
       parseInt(“08”, 10) //8


Saturday, August 25, 2012                 9
 undefined: valor padrão para variáveis,
     parâmetros e atributos sem valor atribuído

 null: objeto nulo, atribuído explicitamente




Saturday, August 25, 2012                         10
 true
 false




Saturday, August 25, 2012   11
    Os seguintes valores são avaliados como false quando fazem
     partes de expressões booleanas (falsy):
       0
       “”
       null
       undefined
       NaN

    Todo o resto é avaliado como true (truthy)
       inclusive as strings ”false” e ”0”!



Saturday, August 25, 2012                                         12
 d = new Date(); //data atual


 d = new Date (88500); //ms desde 1.1.1970


 d = new Date(2012, 25, 7);




Saturday, August 25, 2012                     13
 Não são arrays de verdade, mas um
     dicionário com chaves numéricas
       Não dá erro de limites
       Aceita diferentes tipos de dados

    var a = new Array(); //oldskool
    var a = []; //cool
    var a = [1, “a”, obj]; //inline

    a.push(“floripa”);
    a.length; //4

Saturday, August 25, 2012                  14
Saturday, August 25, 2012   15
    var r = new RegExp(“w{1,3}d{2}”, “gim”);

    var r = /w{1,3}d{2}/gim;

    r.test(“ab12”); //true

    “abc12xyz”.replace(/w{1,3}/gim, “”); //12xyz




Saturday, August 25, 2012                            16
 O coração de programação decente em JS
       Cidadãs de primeira classe
       Uma função pode ser:
        ▪ Variável
        ▪ Parâmetro
        ▪ Propriedade de objeto
        ▪ Anônima
        ▪ Retorno de outra função
        ▪ Interna a outra função

Saturday, August 25, 2012                  17
 Declaração comum


             function fala(texto) {
                  alert(“Oi,” + texto + “!”);
             }

             fala(“amigo”); //Oi,amigo!



Saturday, August 25, 2012                       18
 Variável com função anônima

var minhaFuncao = function(texto) {
  alert(“Fala,” + texto + “!”);
};

minhaFuncao(“amigo”); //Fala,amigo!




Saturday, August 25, 2012             19
function geraSoma (x) {
     return function(num) {
           return x + num;
     }
 }

 var soma5 = geraSoma(5);

 soma5(3); //8



Saturday, August 25, 2012     20
function geraSoma (x) {
     return function(num) {
           return x + num;
     }
 }
                              Closure
 var somador = geraSoma(5);

 somador(3); //8


Saturday, August 25, 2012               21
function geraSoma (x) {
     return function(num) {
           return x + num;
     }
 }
 var x = 5;                   Closure
 var somador = geraSoma(x);

 somador(3); //8
 x = 9;
 somador(3); //ainda 8
Saturday, August 25, 2012               22
Uma função cria cópias dos
 valores disponíveis durante
 sua criação, para serem usados
 em tempo de execução


                            Closure


                                Função
Saturday, August 25, 2012                23
function executa(func) {
     func();
 }

 function dizOi() {
     alert(“oi!”);
 }

 executa(dizOi); //oi!



Saturday, August 25, 2012   24
executa(function() {
      alert(‘oi’);
}); //oi!


Saturday, August 25, 2012   25
function soma(x,y) {
    return x + y;
}

soma(2,3); //5

soma(2,”5”) //”25”


Saturday, August 25, 2012   26
function soma(x,y) {
    return x + y;
}

soma(2,3,5,”hello”); //5

soma(2); //NaN


Saturday, August 25, 2012   27
    Coleção de argumentos passado para a função

function soma() {
  var total=0;
  for (var i=0; i<arguments.length; i++) {
     total += arguments[i];
  }
  return total;
}

soma(1,2,3,4,5); //15
Saturday, August 25, 2012                          28
    JavaScript possui apenas 2 blocos de escopo
       Global
       Função


    Variável declarada dentro de função, com “var”, é local

    Função declarada dentro de função é local

    Variáveis declaradas dentro de blocos if, while, for etc
     não são locais ao bloco, e sim à função.



Saturday, August 25, 2012                                       29
 Esse código é feio, mas é válido

         function calculaMedia(x,y){
             soma = fazSoma();
             return soma/2;
             function fazSoma() {
                 return x + y;
             }
             var soma;
         }
Saturday, August 25, 2012              30
function calculaMedia(x,y){
           soma = x + y;
           return soma/2;
         }                             Variável global

         calculaMedia(2,3); //5
         alert(soma); //5




Saturday, August 25, 2012                                31
 Um dicionário enfeitado




Saturday, August 25, 2012   32
    Clássica
          var ator = new Object();
          ator.nome = “Jim”;
          ator.sobrenome = “Parsons”;

    Simplificada
          var ator = {};
          ator.nome = “Jim”;
          ator.sobrenome = “Parsons”;

    Inline
             var ator = {
                    nome: “Jim”,
                    sobrenome: “Parsons”
             };
Saturday, August 25, 2012                  33
var ator = {
   nome: “Jim”,
   sobrenome: “Parsons”,
   nomeCompleto: function() {
      return this.nome + “ ” + this.sobrenome;
   }
};

ator.nomeCompleto(); //Jim Parsons




Saturday, August 25, 2012                        34
function Ator(nome, sobrenome){
  this.nome = nome;
  this.sobrenome = sobrenome;
  this.nomeCompleto = function() {
     return this.nome + “ ” + this.sobrenome;
  };
}

var jim = new Ator(“Jim”, “Parsons”);
jim.nomeCompleto(); //Jim Parsons

Convenciona-se usar inicial maiúscula

Saturday, August 25, 2012                       35
function Ator(nome, sobrenome){
  var ator = {} ;
  ator.sobrenome = sobrenome;
  ator.nomeCompleto = function() {
     return ator.nome + “ ” + ator.sobrenome;
  };
  return ator;
}

var jim = new Ator(“Jim”, “Parsons”);
jim.nomeCompleto(); //Jim Parsons


Saturday, August 25, 2012                       36
carro.marca = “Citroen”;
      carro[“placa”] = “MHJ8832”;

    A segunda forma aceita palavras reservadas, símbolos etc

      carro[“#”] = “dummy”;
      carro[“for”] = “yammy”;




Saturday, August 25, 2012                                       37
 Permitem executar uma função
     especificando qual objeto será o “this”




Saturday, August 25, 2012                      38
function setColors(color, border){
     this.style.backgroundColor = color;
     this.style.borderColor = border;
}

var botao1 = document.getElementById(“botao1”);
var botao2 = document.getElementById(“botao2”);

setColors.call(botao1, “red”, “blue”);
setColors.call(botao2, “blue”, “green”);

                      “ this”
Saturday, August 25, 2012                         39
function setStyle(color, border){
     this.style.backgroundColor = color;
     this.style.borderColor = border;
}

var botao1 = document.getElementById(“botao1”);
var botao2 = document.getElementById(“botao2”);

setColors.apply(botao1, [“red”, “blue”]);
setColors.apply(botao2, arguments);

                      “ this”
Saturday, August 25, 2012                         40
 Como funções são objetos, elas podem ter
     atributos – isso evita variáveis globais




Saturday, August 25, 2012                       41
function executaUmaVez() {
      if (executaUmaVez.jaExecutou) {
             return;
      }
      //executa a funcao
      alert(‘oeeee’);
      executaUmaVez.jaExecutou = true;
}

executaUmaVez(); //oeeee
executaUmaVez(); //Não executa




Saturday, August 25, 2012                42
function ehPrimo(x) {
      if (!ehPrimo.cache) {
             ehPrimo.cache = {};
      }

             if (!ehPrimo.cache[x]) {
                    ehPrimo.cache[x] = … //calcula a parada
             }

             return ehPrimo.cache[x];
}




Saturday, August 25, 2012                                     43
function ehPrimo(x) {
      if (!ehPrimo.cache) {
             ehPrimo.cache = {};
      }

             if (!ehPrimo.cache[x]) {
                    ehPrimo.cache[x] = … //calcula a parada
             }

             return ehPrimo.cache[x];
}

Quiz: por que eu usei um objeto e não um array no memo?



Saturday, August 25, 2012                                     44
function MeuModulo(p1,p2) {
    //privado
    var x = p1, y = p2;
    function funcaoPrivada() {
           //…
    }
    //público
    this.valor1 = x + y;
    this.funcaoPublica = function() {
        return funcaoPrivada(x,y);
    };
}
var m = new MeuModulo(1,2);
alert(m.valor1); //3                    45
    Usando função

         function meuModulo(p1,p2) {
             //privado
             var x = p1, y = p2;
             function fazAlgumaCoisa() {
                   //…
             }
             //público
             return {
                   valor1: x + y,
                   funcaoPublica: function() {
                         return fazAlgumaCoisa(x,y);
                      }
             };
         }
         var m = meuModulo(1,2);
         alert(m.valor1); //3


Saturday, August 25, 2012                              46
Saturday, August 25, 2012   47
function soma(x,y) {
                 return
                   x + y;
              }
              soma(2,3); //undefined


Saturday, August 25, 2012              48
 Ponto-e-vírgula é opcional, mas o js coloca
     pra você na hora de rodar
            function soma(x,y) {
              return;
                x + y;
            }
            soma(2,3); //undefined



Saturday, August 25, 2012                       49
 Pra quebrar linha, use “pontuação”


              function soma(x,y) {
                 return x +
                           y;
              }
              soma(2,3); //5


Saturday, August 25, 2012              50
 Caso você queira usar uma variável global,
     use MAIÚSCULAS (assim todos sabem que
     foi por querer)

 E, melhor ainda, crie “namespaces” para suas
     variáveis e funções globais

      var MINHALIB = {};
      MINHALIB.usuario = “rodbv”;
      MINHALIB.quebraTudo = function() {…};
Saturday, August 25, 2012                        51
 Executando código descartável

         (function() {
          /* todas variáveis e funções declaradas
          aqui têm acesso ao escopo global,
          mas sairão de escopo quando a
          função terminar de ser executada */
         })();




Saturday, August 25, 2012                           52
 Use === e !==



        1 == “1” //true     1 === “1” //false
        0 == false //true   0 === false //false
        “” == 0 //true      “” === 0 //false
        1 != true //false   1!== true //true




Saturday, August 25, 2012                         53
 Lembre-se do “var” no for loop

     function minhaFuncao() {
             for (i = 0; i < 10; i++){
                   //…
             }
     }
     alert(i) //10;




Saturday, August 25, 2012                54
 Cuidado com o seguinte padrão

     function minhaFuncao() {
             var a = b = 10;

     }
     minhaFuncao();
        alert(a); //undefined, beleza
        alert(b); //10 ?!?



Saturday, August 25, 2012               55
 Cuidado com o seguinte padrão

     function minhaFuncao() {
             var a = b = 10;

     }
     minhaFuncao();
        alert(a); //undefined, beleza
        alert(b); //10 ?!?



Saturday, August 25, 2012               56
 Evite eval


      setTimeout(“alert(‘ola’);”, 10);
      setTimeout(function() { alert(‘ola’);}, 10);

      var p = eval(“ator.” + propr);

      var p = ator[propr];



Saturday, August 25, 2012                            57
 Coloque o seu javascript sempre no fundo da
  página
 Minifique e combine os arquivos

<script type="text/javascript" src="http://yui.yahooapis.com/co
  mbo?
  2.9.0/build/utilities/utilities.js&2.9.0/build/datasource/dataso
  urce-min.js&2.9.0/build/autocomplete/autocomplete-
  min.js&2.9.0/build/calendar/calendar-
  min.js&2.9.0/build/tabview/tabview-min.js"></script>

Saturday, August 25, 2012                                            58
Douglas Crockford é o cara
http://javascript.crockford.com/




Saturday, August 25, 2012          59
@rodbv
rodrigo.vieira@gmail.com



Saturday, August 25, 2012   60

Javascript não é Java+Script (TDC Floripa 2012)

  • 1.
    Uma (re)introdução aoJavaScript TDC Florianópolis Rodrigo Vieira 25/08/2012 @rodbv 1
  • 2.
  • 3.
     Um poucode história  A linguagem  Dicas e erros comuns
  • 4.
    Mocha! LiveScript! Brendan Eich Scheme 4
  • 5.
    Linguagem dinâmica, fracamente tipada, funcional e OO  Objetos e vetores são “dicionários melhorados”  Herança por prototipação Saturday, August 25, 2012 5
  • 6.
  • 7.
     Valor  String, Number, Boolean, null, undefined  Referência  Objetos  Funções  Arrays  RegEx  Date  Math Saturday, August 25, 2012 7
  • 8.
    Sequência de caracteres unicode  Strings com mesmo valor são consideradas idênticas  Não existe tipo char  Podemos usar aspas simples e duplas  Possui métodos Saturday, August 25, 2012 8
  • 9.
     Ponto flutuantede 64 bits  Não existe tipo inteiro  NaN  Infinity  Hexa (0xa12)  Notação científica (1.23e-8)  Octal: primeiro dígito zero, cuidado!  parseInt(“08”) //0  parseInt(“08”, 10) //8 Saturday, August 25, 2012 9
  • 10.
     undefined: valorpadrão para variáveis, parâmetros e atributos sem valor atribuído  null: objeto nulo, atribuído explicitamente Saturday, August 25, 2012 10
  • 11.
     true  false Saturday,August 25, 2012 11
  • 12.
    Os seguintes valores são avaliados como false quando fazem partes de expressões booleanas (falsy):  0  “”  null  undefined  NaN  Todo o resto é avaliado como true (truthy)  inclusive as strings ”false” e ”0”! Saturday, August 25, 2012 12
  • 13.
     d =new Date(); //data atual  d = new Date (88500); //ms desde 1.1.1970  d = new Date(2012, 25, 7); Saturday, August 25, 2012 13
  • 14.
     Não sãoarrays de verdade, mas um dicionário com chaves numéricas  Não dá erro de limites  Aceita diferentes tipos de dados  var a = new Array(); //oldskool  var a = []; //cool  var a = [1, “a”, obj]; //inline  a.push(“floripa”);  a.length; //4 Saturday, August 25, 2012 14
  • 15.
  • 16.
    var r = new RegExp(“w{1,3}d{2}”, “gim”);  var r = /w{1,3}d{2}/gim;  r.test(“ab12”); //true  “abc12xyz”.replace(/w{1,3}/gim, “”); //12xyz Saturday, August 25, 2012 16
  • 17.
     O coraçãode programação decente em JS  Cidadãs de primeira classe  Uma função pode ser: ▪ Variável ▪ Parâmetro ▪ Propriedade de objeto ▪ Anônima ▪ Retorno de outra função ▪ Interna a outra função Saturday, August 25, 2012 17
  • 18.
     Declaração comum function fala(texto) { alert(“Oi,” + texto + “!”); } fala(“amigo”); //Oi,amigo! Saturday, August 25, 2012 18
  • 19.
     Variável comfunção anônima var minhaFuncao = function(texto) { alert(“Fala,” + texto + “!”); }; minhaFuncao(“amigo”); //Fala,amigo! Saturday, August 25, 2012 19
  • 20.
    function geraSoma (x){ return function(num) { return x + num; } } var soma5 = geraSoma(5); soma5(3); //8 Saturday, August 25, 2012 20
  • 21.
    function geraSoma (x){ return function(num) { return x + num; } } Closure var somador = geraSoma(5); somador(3); //8 Saturday, August 25, 2012 21
  • 22.
    function geraSoma (x){ return function(num) { return x + num; } } var x = 5; Closure var somador = geraSoma(x); somador(3); //8 x = 9; somador(3); //ainda 8 Saturday, August 25, 2012 22
  • 23.
    Uma função criacópias dos valores disponíveis durante sua criação, para serem usados em tempo de execução Closure Função Saturday, August 25, 2012 23
  • 24.
    function executa(func) { func(); } function dizOi() { alert(“oi!”); } executa(dizOi); //oi! Saturday, August 25, 2012 24
  • 25.
    executa(function() { alert(‘oi’); }); //oi! Saturday, August 25, 2012 25
  • 26.
    function soma(x,y) { return x + y; } soma(2,3); //5 soma(2,”5”) //”25” Saturday, August 25, 2012 26
  • 27.
    function soma(x,y) { return x + y; } soma(2,3,5,”hello”); //5 soma(2); //NaN Saturday, August 25, 2012 27
  • 28.
    Coleção de argumentos passado para a função function soma() { var total=0; for (var i=0; i<arguments.length; i++) { total += arguments[i]; } return total; } soma(1,2,3,4,5); //15 Saturday, August 25, 2012 28
  • 29.
    JavaScript possui apenas 2 blocos de escopo  Global  Função  Variável declarada dentro de função, com “var”, é local  Função declarada dentro de função é local  Variáveis declaradas dentro de blocos if, while, for etc não são locais ao bloco, e sim à função. Saturday, August 25, 2012 29
  • 30.
     Esse códigoé feio, mas é válido function calculaMedia(x,y){ soma = fazSoma(); return soma/2; function fazSoma() { return x + y; } var soma; } Saturday, August 25, 2012 30
  • 31.
    function calculaMedia(x,y){ soma = x + y; return soma/2; } Variável global calculaMedia(2,3); //5 alert(soma); //5 Saturday, August 25, 2012 31
  • 32.
     Um dicionárioenfeitado Saturday, August 25, 2012 32
  • 33.
    Clássica var ator = new Object(); ator.nome = “Jim”; ator.sobrenome = “Parsons”;  Simplificada var ator = {}; ator.nome = “Jim”; ator.sobrenome = “Parsons”;  Inline var ator = { nome: “Jim”, sobrenome: “Parsons” }; Saturday, August 25, 2012 33
  • 34.
    var ator ={ nome: “Jim”, sobrenome: “Parsons”, nomeCompleto: function() { return this.nome + “ ” + this.sobrenome; } }; ator.nomeCompleto(); //Jim Parsons Saturday, August 25, 2012 34
  • 35.
    function Ator(nome, sobrenome){ this.nome = nome; this.sobrenome = sobrenome; this.nomeCompleto = function() { return this.nome + “ ” + this.sobrenome; }; } var jim = new Ator(“Jim”, “Parsons”); jim.nomeCompleto(); //Jim Parsons Convenciona-se usar inicial maiúscula Saturday, August 25, 2012 35
  • 36.
    function Ator(nome, sobrenome){ var ator = {} ; ator.sobrenome = sobrenome; ator.nomeCompleto = function() { return ator.nome + “ ” + ator.sobrenome; }; return ator; } var jim = new Ator(“Jim”, “Parsons”); jim.nomeCompleto(); //Jim Parsons Saturday, August 25, 2012 36
  • 37.
    carro.marca = “Citroen”; carro[“placa”] = “MHJ8832”;  A segunda forma aceita palavras reservadas, símbolos etc carro[“#”] = “dummy”; carro[“for”] = “yammy”; Saturday, August 25, 2012 37
  • 38.
     Permitem executaruma função especificando qual objeto será o “this” Saturday, August 25, 2012 38
  • 39.
    function setColors(color, border){ this.style.backgroundColor = color; this.style.borderColor = border; } var botao1 = document.getElementById(“botao1”); var botao2 = document.getElementById(“botao2”); setColors.call(botao1, “red”, “blue”); setColors.call(botao2, “blue”, “green”); “ this” Saturday, August 25, 2012 39
  • 40.
    function setStyle(color, border){ this.style.backgroundColor = color; this.style.borderColor = border; } var botao1 = document.getElementById(“botao1”); var botao2 = document.getElementById(“botao2”); setColors.apply(botao1, [“red”, “blue”]); setColors.apply(botao2, arguments); “ this” Saturday, August 25, 2012 40
  • 41.
     Como funçõessão objetos, elas podem ter atributos – isso evita variáveis globais Saturday, August 25, 2012 41
  • 42.
    function executaUmaVez() { if (executaUmaVez.jaExecutou) { return; } //executa a funcao alert(‘oeeee’); executaUmaVez.jaExecutou = true; } executaUmaVez(); //oeeee executaUmaVez(); //Não executa Saturday, August 25, 2012 42
  • 43.
    function ehPrimo(x) { if (!ehPrimo.cache) { ehPrimo.cache = {}; } if (!ehPrimo.cache[x]) { ehPrimo.cache[x] = … //calcula a parada } return ehPrimo.cache[x]; } Saturday, August 25, 2012 43
  • 44.
    function ehPrimo(x) { if (!ehPrimo.cache) { ehPrimo.cache = {}; } if (!ehPrimo.cache[x]) { ehPrimo.cache[x] = … //calcula a parada } return ehPrimo.cache[x]; } Quiz: por que eu usei um objeto e não um array no memo? Saturday, August 25, 2012 44
  • 45.
    function MeuModulo(p1,p2) { //privado var x = p1, y = p2; function funcaoPrivada() { //… } //público this.valor1 = x + y; this.funcaoPublica = function() { return funcaoPrivada(x,y); }; } var m = new MeuModulo(1,2); alert(m.valor1); //3 45
  • 46.
    Usando função function meuModulo(p1,p2) { //privado var x = p1, y = p2; function fazAlgumaCoisa() { //… } //público return { valor1: x + y, funcaoPublica: function() { return fazAlgumaCoisa(x,y); } }; } var m = meuModulo(1,2); alert(m.valor1); //3 Saturday, August 25, 2012 46
  • 47.
  • 48.
    function soma(x,y) { return x + y; } soma(2,3); //undefined Saturday, August 25, 2012 48
  • 49.
     Ponto-e-vírgula éopcional, mas o js coloca pra você na hora de rodar function soma(x,y) { return; x + y; } soma(2,3); //undefined Saturday, August 25, 2012 49
  • 50.
     Pra quebrarlinha, use “pontuação” function soma(x,y) { return x + y; } soma(2,3); //5 Saturday, August 25, 2012 50
  • 51.
     Caso vocêqueira usar uma variável global, use MAIÚSCULAS (assim todos sabem que foi por querer)  E, melhor ainda, crie “namespaces” para suas variáveis e funções globais var MINHALIB = {}; MINHALIB.usuario = “rodbv”; MINHALIB.quebraTudo = function() {…}; Saturday, August 25, 2012 51
  • 52.
     Executando códigodescartável (function() { /* todas variáveis e funções declaradas aqui têm acesso ao escopo global, mas sairão de escopo quando a função terminar de ser executada */ })(); Saturday, August 25, 2012 52
  • 53.
     Use ===e !== 1 == “1” //true 1 === “1” //false 0 == false //true 0 === false //false “” == 0 //true “” === 0 //false 1 != true //false 1!== true //true Saturday, August 25, 2012 53
  • 54.
     Lembre-se do“var” no for loop function minhaFuncao() { for (i = 0; i < 10; i++){ //… } } alert(i) //10; Saturday, August 25, 2012 54
  • 55.
     Cuidado como seguinte padrão function minhaFuncao() { var a = b = 10; } minhaFuncao(); alert(a); //undefined, beleza alert(b); //10 ?!? Saturday, August 25, 2012 55
  • 56.
     Cuidado como seguinte padrão function minhaFuncao() { var a = b = 10; } minhaFuncao(); alert(a); //undefined, beleza alert(b); //10 ?!? Saturday, August 25, 2012 56
  • 57.
     Evite eval setTimeout(“alert(‘ola’);”, 10); setTimeout(function() { alert(‘ola’);}, 10); var p = eval(“ator.” + propr); var p = ator[propr]; Saturday, August 25, 2012 57
  • 58.
     Coloque oseu javascript sempre no fundo da página  Minifique e combine os arquivos <script type="text/javascript" src="http://yui.yahooapis.com/co mbo? 2.9.0/build/utilities/utilities.js&2.9.0/build/datasource/dataso urce-min.js&2.9.0/build/autocomplete/autocomplete- min.js&2.9.0/build/calendar/calendar- min.js&2.9.0/build/tabview/tabview-min.js"></script> Saturday, August 25, 2012 58
  • 59.
    Douglas Crockford éo cara http://javascript.crockford.com/ Saturday, August 25, 2012 59
  • 60.