http://mbranko.github.io/webkurs
Ovo je deo web kursa
Mogućnost da pozovemo više metoda prosleđujući im isti objekat:
str.replace("k", "R").toUpperCase().substr(0,4);
var userController = {
  currentUser: "",
  findUser: function (userEmail) {
    var arrayLength = usersData.length, i;
    for (i = arrayLength - 1; i >= 0; i--) {
      if (usersData[i].email === userEmail) {
        this.currentUser = usersData[i];
        break;
      }
    }
    return this;
  },
  formatName: function () {
    if (this.currentUser) {
      this.currentUser.fullName = this.currentUser.firstName + " " + this.currentUser.lastName;
    }
    return this;
  },
  createLayout: function () {
    if (this.currentUser) {
      this.currentUser.viewData = "Member: " + this.currentUser.fullName + "
"
        + "ID: " + this.currentUser.id + "
" + "Email: " + this.currentUser.email + "
";
    }
    return this;
  },
};
          Svaka funkcija vraća this.
Primer pozivanja
userController.findUser("test2@test2.com").formatName().createLayout().displayUser();
          
$("#btn_1").click(function() {
  alert("Btn 1 Clicked");
});          
          
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
  console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});         
          primer imenovane callback funkcije
var allUserData = [];
function logStuff(userData) {
  if (typeof userData === "string")
    console.log(userData);
  else if (typeof userData === "object")
    for (var item in userData)
      console.log(item + ": " + userData[item]);
}
function getInput(options, callback) {
  allUserData.push(options);
  callback(options);
}
getInput ({name:"Rich", speciality:"JavaScript"}, logStuff);
          Callback funkcija prima parametar od funkcije koja je obuhvata.
callback može pristupiti globalnim promenljivima
var defaultOptions = { ... };
function logStuff(userData) {
  if (!userData)
    userData = defaultOptions;
  ...
}
          pre poziva može se proveriti da li je u pitanju funkcija
function getInput(options, callback) {
  allUserData.push(options);
  if (typeof callback === "function")
    callback(options);
}
          Kada je callback metoda koja koristi this:
// definiši objekat sa metodom
// metodu ćemo kasnije proslediti kao callback
var clientData = {
  id: 094545,
  fullName: "Not Set",
  setUserName: function (firstName, lastName)  {
    this.fullName = firstName + " " + lastName;
  }
}
function getUserInput(firstName, lastName, callback)  {
  callback (firstName, lastName);
}
          Kada se pozove setUserName, this se ne odnosi na objekat clientData nego na window objekat u web čitaču, jer je getUserInput globalna funkcija. U globalnoj funkciji this pokazuje na window.
        
Funkcije call i apply mogu da postave this unutar funkcije i proslede parametre funkciji. Obe funkcije primaju novu vrednost za this kao prvi parametar.
// dodali smo novi parametar ovde: callbackObj
function getUserInput(firstName, lastName, callback, callbackObj)  {
  callback.apply(callbackObj, [firstName, lastName]);
}
          Drugi parametar za apply je niz koji će se proslediti funkciji kao njeni parametri.
Naredni parametri za call su parametri koji će se direktno proslediti funkciji.
Možemo proslediti više callback funkcija odjednom prilikom poziva.
function successCallback() { ... }
function completeCallback() { ... }
function errorCallback() { ... }
$.ajax({
    url: "http://fiddle.jshell.net/favicon.png",
    success: successCallback,
    complete: completeCallback,
    error: errorCallback
});
          Primer koristi jQuery ajax funkciju.
Callback poziva callback poziva callback poziva...
var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);
                        // Let's close the db
                        p_client.close();
                    });
                });
            });
        });
    });
});
          Dva pristupa rešavanju ovog problema:
Funkcija je jedina stvar u JavaScriptu koja pravi novi opseg. Pogledajmo primer...
var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
             "Thursday", "Friday", "Saturday"];
function dayName(number) {
  return names[number];
}
console.log(dayName(1)); // → Monday
          ... kada se malo preradi:
var dayName = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return function(number) {
    return names[number];
  };
}();
console.log(dayName(3)); // → Wednesday
          names je sada lokalna promenljiva. Ova funkcija se kreira i odmah poziva, a njen rezultat je funkcija koja se smešta u promenljivu dayName. Ovde može biti hiljade linija koda sa puno lokalnih promenljivih; one bi bile vidljive samo u našoj funkciji ali ne i spolja.
Sada hoćemo da vratimo dve funkcije! Moramo ih spakovati u objekat:
var weekDay = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return {
    name: function(number) { return names[number]; },
    number: function(name) { return names.indexOf(name); }
  };
}();
console.log(weekDay.name(weekDay.number("Sunday"))); // → Sunday
          Za veće module, sakupljanje svih vrednosti u objekat na kraju funkcije može biti nečitko. Želimo da eksportovane funkcije definišemo na zgodnijem mestu.
Deklarišemo objekat i dodajemo osobine u njega kad god imamo nešto za eksport.
(function(exports) {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  exports.name = function(number) {
    return names[number];
  };
  exports.number = function(name) {
    return names.indexOf(name);
  };
})(this.weekDay = {});
console.log(weekDay.name(weekDay.number("Saturday"))); // → Saturday
          Samo jedna promenljiva u globalnom opsegu - weekDay. Ali šta ako
          dva modula koriste isto ime za globalnu promenljivu?
Deklarišemo objekat i dodajemo osobine u njega kad god imamo nešto za eksport.
(function(exports) {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  exports.name = function(number) {
    return names[number];
  };
  exports.number = function(name) {
    return names.indexOf(name);
  };
})(this.weekDay = {});
console.log(weekDay.name(weekDay.number("Saturday"))); // → Saturday
          Samo jedna promenljiva u globalnom opsegu - weekDay. Ali šta ako
          dva modula koriste isto ime za globalnu promenljivu?
require koja će, za dato ime modula, da ga učita iz fajla ili sa weba i vratiti odgovarajuću vrednostrequireProst način: upotreba eval.
function evalAndReturnX(code) {
  eval(code);
  return x;
}
console.log(evalAndReturnX("var x = 2")); // → 2
          Za nevolju, eval će rezultate izvršavanja upisati u tekući opseg.
Bolji način: pomoću Function konstruktora. On prima dva argumenta: string sa nazivima parametara funkcije razdvojenih zarezima i string sa telom funkcije.
var plusOne = new Function("n", "return n + 1;");
console.log(plusOne(4)); // → 5
          Pri kraju smo: umotaćemo kod modula u funkciju, i ta funkcija postaje opseg za naš modul.
Minimalna implementacija za require: 
function require(name) {
  var code = new Function("exports", readFile(name));
  var exports = {};
  code(exports);
  return exports;
}
console.log(require("weekDay").name(1)); // → Monday
          Pošto funkcija require umotava kod u funkciju, nema potrebe da to radimo u modulu.
Sada fajl sa modulom može ovako da izgleda:
var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
             "Thursday", "Friday", "Saturday"];
exports.name = function(number) {
  return names[number];
};
exports.number = function(name) {
  return names.indexOf(name);
};
          Tipičan modul će na vrhu učitati module koji su mu potrebni:
var weekDay = require("weekDay");
var today = require("today");
console.log(weekDay.name(today.dayNumber()));
          require će izvršiti kod modula svaki put kad se učitava.exports objekta (npr. funkciju).Moduli će dobiti promenljivu module koja je objekat sa osobinom exports. Ova osobina inicijalno pokazuje na {} koji je kreirao require.
function require(name) {
  if (name in require.cache)
    return require.cache[name];
  var code = new Function("exports, module", readFile(name));
  var exports = {}, module = {exports: exports};
  code(exports, module);
  require.cache[name] = module.exports;
  return module.exports;
}
require.cache = Object.create(null);
          
define(["weekDay", "today"], function(weekDay, today) {
  console.log(weekDay.name(today.dayNumber()));
});
          
          Funkcija define prima niz sa nazivima modula i zatim funkciju koja prima po jedan parametar za svaki modul. Učitaće module u pozadini dok stranica radi. Kada su svi moduli učitani pozvaće datu funkciju koja će obaviti inicijalizaciju.
          
Nova verzija našeg modula:
define([], function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return {
    name: function(number) { return names[number]; },
    number: function(name) { return names.indexOf(name); }
  };
});
          
          Funkcija define prima niz sa nazivima modula i zatim funkciju koja prima po jedan parametar za svaki modul. Učitaće module u pozadini dok stranica radi. Kada su svi moduli učitani pozvaće datu funkciju koja će obaviti inicijalizaciju.
          
Funkcija getModule će učitati modul ili ga izvući iz keša.
var defineCache = Object.create(null);
var currentMod = null;
function getModule(name) {
  if (name in defineCache)
    return defineCache[name];
  var module = {exports: null,
                loaded: false,
                onLoad: []};
  defineCache[name] = module;
  backgroundReadFile(name, function(code) {
    currentMod = module;
    new Function("", code)();
  });
  return module;
}
          
          Funkcija backgroundReadFile nije jednostavna.
          
Funkcija define će pozvati inicijalizaciju modula kada prikupi sve potrebne module.
function define(depNames, moduleFunction) {
  var myMod = currentMod;
  var deps = depNames.map(getModule);
  deps.forEach(function(mod) {
    if (!mod.loaded)
      mod.onLoad.push(whenDepsLoaded);
  });
  function whenDepsLoaded() {
    if (!deps.every(function(m) { return m.loaded; }))
      return;
    var args = deps.map(function(m) { return m.exports; });
    var exports = moduleFunction.apply(null, args);
    if (myMod) {
      myMod.exports = exports;
      myMod.loaded = true;
      myMod.onLoad.forEach(function(f) { f(); });
    }
  }
  whenDepsLoaded();
}
          Funkcija define će učitati modul ili ga izvući iz keša pomoću getModule. Njen zadatak je da se moduleFunction (funkcija koja sadrži kod modula) pozove kada su učitani svi potrebni moduli. Zato definiše whenDepsLoaded koja se dodaje na kraj onLoad niza svih trenutno nedostajućih modula. Ova funkcija se odmah vraća ako ima još neučitanih modula. Tako će se posao obaviti samo jednom, kada se učita i poslednji modul. Poziva se i direktno iz define ako tekući modul nema potrebnih modula.
Kada su svi potrebni moduli dostupni, whenDepsLoaded poziva funkciju koja obmotava modul dajući joj sve tražene module kao parametre.
RequireJS radi na ovaj način.
Implementirani u posebnim bibliotekama od ranije, npr:
Podrška u web čitačima:
Ili ovaj polyfill za starije čitače.
Uvedeni u ECMAScript 6.
Promise je proxy za vrednost koju ne moramo znati u vreme kreiranja. Može biti u tri stanja:
var img1 = document.querySelector('.img-1');
img1.addEventListener('load', function() {
  // ...
});
img1.addEventListener('error', function() {
  // ...
});
          Događaj može da se desi pre nego što počnemo da ga osluškujemo!
Da probamo da iskoristimo osobinu complete za slike:
var img1 = document.querySelector('.img-1');
function loaded() {
  // ...
}
if (img1.complete) {
  loaded();
}
else {
  img1.addEventListener('load', loaded);
}
img1.addEventListener('error', function() {
  // ...
});
          Ne hvata slike koje su proizvele grešku pre nego što smo počeli da slušamo.
Ako treba da obradimo više slika...
Idealno nam treba nešto ovakvo:
img1.callThisIfLoadedOrWhenLoaded(function() {
  // loaded
}).orIfFailedCallThis(function() {
  // failed
});
whenAllTheseHaveLoaded([img1, img2]).callThis(function() {
  // all loaded
}).orIfSomeFailedCallThis(function() {
  // one or more failed
});
          Ovaj problem rešavaju promise objekti.
Kada bi img element imao ready metodu koja vraća promise:
img1.ready().then(function() {
  // loaded
}, function() {
  // failed
});
Promise.all([img1.ready(), img2.ready()]).then(function() {
  // all loaded
}, function() {
  // one or more failed
});
          Promise - slično osluškivanju događaja osim:
Ovako se kreira promise:
var promise = new Promise(function(resolve, reject) {
  // uradi neki posao
  if (/* sve je u redu? */) {
    resolve("Radi!");
  } else {
    reject(Error("Ne radi!"));
  }
});
          A ovako se koristi:
promise.then(function(result) {
  console.log(result); // "Radi!"
}, function(err) {
  console.log(err); // Error: "Ne radi!"
});
          then prima dva parametra
Naša stranica bi trebalo da:
U slučaju greške treba obavestiti korisnika i zaustaviti spinner.
function get(url) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.open('GET', url);
    req.onload = function() {
      if (req.status == 200)
        // uspešan ishod
        resolve(req.response);
      else
        // neuspešan ishod
        reject(Error(req.statusText));
    };
    // mrežne greške
    req.onerror = function() {
      reject(Error("Network Error"));
    };
    // pošalji zahtev
    req.send();
  });
}Korišćenje prethodno napravljenog promise:
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
})
Rezultat učitavanja će biti JSON tekst koji treba parsirati:
get('story.json').then(function(response) {
  return JSON.parse(response);
}).then(function(response) {
  console.log("Yey JSON!", response);
})
Pošto JSON.parse prima jedan parametar, to može i kraće:
get('story.json').then(JSON.parse).then(function(response) {
  console.log("Yey JSON!", response);
})
Ako callback vrati
then će je preuzetithen će je sačekati
getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  console.log("Got chapter 1!", chapter1);
})
then prima dva parametra:
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.log("Failed!", error);
})
Može i pomoću catch:
get('story.json').then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.log("Failed!", error);
})
Ali to nije baš isto!
Kada se koristi catch:
get('story.json').then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.log("Failed!", error);
})
je ekvivalentno sa:
get('story.json').then(function(response) {
  console.log("Success!", response);
}).then(undefined, function(error) {
  console.log("Failed!", error);
})
Sa then(f1, f2), biće pozvana ili f1 ili f2, nikada obe.
Sa then(f1).catch(f2), biće pozvane obe i ako f1 proizvede grešku jer su to posebni koraci u lancu.
Negativan ishod za promise dobija se
reject callback-a
var jsonPromise = new Promise(function(resolve, reject) {
  // JSON.parse baca grešku ako tekst nije pravilan JSON
  // tako da se ovo implicitno reject-uje
  resolve(JSON.parse("This ain't JSON"));
});
jsonPromise.then(function(data) {
  // nikad se neće desiti
  console.log("It worked!", data);
}).catch(function(err) {
  // ovo će se desiti
  console.log("It failed!", err);
})
          Isto važi i za greške koje nastanu u callbacku za then:
get('/').then(JSON.parse).then(function() {
  // Ovo se neće desiti, '/' je HTML strana, ne JSON
  // pa će JSON.parse baciti izuzetak
  console.log("It worked!", data);
}).catch(function(err) {
  // ovo će se desiti
  console.log("It failed!", err);
})
          U primeru sa učitavanjem knjige:
getJSON('book.json').then(function(book) {
  return getJSON(book.chapterUrls[0]);
}).then(function(chapter1) {
  addHtmlToPage(chapter1.html);
}).catch(function() {
  addTextToPage("Failed to show chapter");
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})
          Ako getJSON(book.chapterUrls[0]) vrati grešku, preskaču sve svi then callbacks, i prelazi na catch callback. Spinner će se isključiti u oba slučaja.
Prethodni primer je asinhrona varijanta sledeće ideje:
try {
  var story = getJSONSync('story.json');
  var chapter1 = getJSONSync(story.chapterUrls[0]);
  addHtmlToPage(chapter1.html);
} catch (e) {
  addTextToPage("Failed to show chapter");
}
document.querySelector('.spinner').style.display = 'none'
          Ako koristimo catch samo da zabeležimo grešku ali nastavljamo rad:
function getJSON(url) {
  return get(url).then(JSON.parse).catch(function(err) {
    console.log("getJSON failed for", url, err);
    throw err;
  });
}
          Ponovo bacimo grešku.
Počnimo od sinhrone varijante koja učitava sva poglavlja:
try {
  var story = getJSONSync('story.json');
  addHtmlToPage(story.heading);
  story.chapterUrls.forEach(function(chapterUrl) {
    var chapter = getJSONSync(chapterUrl);
    addHtmlToPage(chapter.html);
  });
  addTextToPage("All done");
}
catch (err) {
  addTextToPage("Argh, broken: " + err.message);
}
document.querySelector('.spinner').style.display = 'none'
          Sinhrono izvršavanje će blokirati web čitač dok traje download.
Počnimo od sinhrone varijante koja učitava sva poglavlja:
try {
  var story = getJSONSync('story.json');
  addHtmlToPage(story.heading);
  story.chapterUrls.forEach(function(chapterUrl) {
    var chapter = getJSONSync(chapterUrl);
    addHtmlToPage(chapter.html);
  });
  addTextToPage("All done");
}
catch (err) {
  addTextToPage("Argh, broken: " + err.message);
}
document.querySelector('.spinner').style.display = 'none'
          Sinhrono izvršavanje će blokirati web čitač dok traje download.
Asinhrona varijanta bi trebalo da izgleda ovako:
getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);
  // TODO: za svaki url u story.chapterUrls, dobavi i prikaži ga
}).then(function() {
  // završili smo
  addTextToPage("All done");
}).catch(function(err) {
  // uhvati usputne greške
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  // uvek
  document.querySelector('.spinner').style.display = 'none';
})
          Ali kako da prođemo kroz poglavlja i učitavamo ih u pravom redosledu? Ovo neće raditi:
story.chapterUrls.forEach(function(chapterUrl) {
  // dobavi poglavlje
  getJSON(chapterUrl).then(function(chapter) {
    // dodaj ga na stranicu
    addHtmlToPage(chapter.html);
  });
})
          forEach nije async-aware, tj. ne vodi računa o završetku operacije za svaki element sekvence. Poglavlja će se pojavljivati redosledu dobavljanja umesto u pravom redosledu.
Treba da pretvorimo chapterUrls u listu promisa.
// počni od promisa koji je uvek uspešan
var sequence = Promise.resolve();
// iteracija kroz chapterUrls
story.chapterUrls.forEach(function(chapterUrl) {
  // dodaj ove akcije na kraj sekvence
  sequence = sequence.then(function() {
    return getJSON(chapterUrl);
  }).then(function(chapter) {
    addHtmlToPage(chapter.html);
  });
})
          Ovo može i pomoću Array.reduce:
// iteracija kroz chapterUrls
story.chapterUrls.reduce(function(sequence, chapterUrl) {
  // dodaj ove akcije na kraj sekvence
  return sequence.then(function() {
    return getJSON(chapterUrl);
  }).then(function(chapter) {
    addHtmlToPage(chapter.html);
  });
}, Promise.resolve())
          Ne treba nam posebna promenljiva.
Rešenje:
getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);
  return story.chapterUrls.reduce(function(sequence, chapterUrl) {
    // kada je završio promise prethodnog poglavlja
    return sequence.then(function() {
      // dobavi sledeće poglavlje
      return getJSON(chapterUrl);
    }).then(function(chapter) {
      // i dodaj ga na stranicu
      addHtmlToPage(chapter.html);
    });
  }, Promise.resolve());
}).then(function() {
  // sve je gotovo
  addTextToPage("All done");
}).catch(function(err) {
  // uhvati greške usput
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  // uvek skloni spinner na kraju
  document.querySelector('.spinner').style.display = 'none';
})Zašto da radimo download sekvencijalno? Postoji API:
Promise.all(arrayOfPromises).then(function(arrayOfResults) {
  //...
})
          Promise.all prima niz promisa i kreira promise koji je ispunjen kada se svi uspešno završe. Dobija se niz rezultata u redosledu koji odgovara redosledu promisa.
Promise.all primenjeno na naš problem:
getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);
  // kreiraj niz promisa i čekaj na sve njih
  return Promise.all(
    // mapiraj chapterUrls niz na niz JSON promisa
    story.chapterUrls.map(getJSON)
  );
}).then(function(chapters) {
  // sada imamo JSON-e u pravom redosledu, iteriramo kroz njih
  chapters.forEach(function(chapter) {
    // i dodajemo u stranicu
    addHtmlToPage(chapter.html);
  });
  addTextToPage("All done");
}).catch(function(err) {
  // uhvati greške usput
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})Kada stigne poglavlje 1 možemo ga dodati na stranicu.
Kada stigne poglavlje 3 ne možemo ga dodati na stranicu jer nije još stiglo poglavlje 2.
Kada stigne poglavlje 2 možemo dodati poglavlja 2 i 3 na stranicu.
Dobavićemo sva poglavlja paralelno, ali ćemo kreirati sekvencu za dodavanje u stranicu.
Paralelni download, sekvencijalno dodavanje u stranicu:
getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);
  // Mapiraj niz chapterUrls na niz JSON promisa. Oni će se dobavljati paralelno.
  return story.chapterUrls.map(getJSON)
    .reduce(function(sequence, chapterPromise) {
      // pomoću reduce ćemo ulančati promise, dodajući sadržaj na stranicu za svako poglavlje
      return sequence.then(function() {
        // sačekaj na sve u sekvenci, onda sačekaj na poglavlje
        return chapterPromise;
      }).then(function(chapter) {
        addHtmlToPage(chapter.html);
      });
    }, Promise.resolve());
}).then(function() {
  addTextToPage("All done");
}).catch(function(err) {
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})Forma za unos podataka
<section>
  <form onsubmit="javascript:setSettings()">
    <label>Select your BG color: </label>
    <input id="favcolor" type="color" value="#ffffff" />
    <label>Select Font Size: </label>
    <input id="fontwt" type="number" max="14" min="10" value="13" />
    <input type="submit" value="Save" />
    <input onclick="clearSettings()" type="reset" value="Clear" />
  </form>
</section> 
          Test podrške za local storage:
function setSettings() {
  if ('localStorage' in window && window['localStorage'] !== null) {
    // postoji podrška za local storage
  } else {
    alert('Nije moguće sačuvati podešavanja jer Vaš čitač nema local storage');
  }
}
          Čitkije pomoću Modernizr:
<script type="text/javascript" src="modernizr.min.js"></script>
          
if (Modernizr.localstorage) {
  // postoji podrška za local storage
} else {
  alert('Nije moguće sačuvati podešavanja jer Vaš čitač nema local storage');
}
          Dodat kod za čuvanje podataka:
function setSettings() {
  if (Modernizr.localstorage) {
    try {
      var favcolor = document.getElementById('favcolor').value;
      var fontwt = document.getElementById('fontwt').value;
      localStorage.setItem('bgcolor', favcolor);
      localStorage.fontweight = fontwt;
    } catch (e) {
      if (e == QUOTA_EXCEEDED_ERR) { // premašio 10 MB
        alert('Quota exceeded!');
      }
    }
  } else {
    alert('Nije moguće sačuvati podešavanja jer Vaš čitač nema local storage');
  }
}
          Čitanje iz local storage:
function applySettings() {
  if (localStorage.length != 0) {
    document.body.style.backgroundColor = localStorage.getItem('bgcolor');
    document.body.style.fontSize = localStorage.fontweight + 'px';
    document.getElementById('favcolor').value = localStorage.bgcolor;
    document.getElementById('fontwt').value = localStorage.fontweight;
  } else {
    document.body.style.backgroundColor = '#FFFFFF';
    document.body.style.fontSize = '13px'
    document.getElementById('favcolor').value = '#FFFFFF';
    document.getElementById('fontwt').value = '13';
  }
}
          length vraća broj sačuvanih elemenata.
Uklanjanje iz local storage:
function clearSettings() {
  localStorage.removeItem("bgcolor");
  localStorage.removeItem("fontweight");
  document.body.style.backgroundColor = '#FFFFFF';
  document.body.style.fontSize = '13px'
  document.getElementById('favcolor').value = '#FFFFFF';
  document.getElementById('fontwt').value = '13';
}
          Prilikom upisa ili brisanja, poseban događaj će se desiti za window objekat.  Možemo dodati osluškivanje za taj događaj.
window.addEventListener('storage', storageEventHandler, false);
function storageEventHandler(event) {
  applySettings();
}
          Event ima sledeće atribute:
localStorage ili sessionStorage objekatDogađaj će se desiti samo u drugim prozorima - ne i u prozoru koji ga je izazvao. Desiće se samo ako je došlo do promene u podacima.
Kako web čitači učitavaju sadržaj:
Skript je zgodno staviti na sam kraj HTML fajla, neposredno ispred </body> taga. To garantuje da će čitač parsirati ceo fajl i da je DOM spreman pre nego što mu skript pristupi.
<!DOCTYPE html>
<html>
  <body>
    <p id="myParagraph">This is my paragraph! 
      <span class="hideme">Lorem ipsum</span> 
      dolor sit amet.
    </p>
    <p class="hideme">Another paragraph!</p>
    <script>
      var myPar = document.getElementById("myParagraph");
      myPar.innerText = "I have changed the content!";
    </script>
  </body>
</html>
          Ako stavimo skript na početak body počeće da se izvršava pre nego što je dokument u celosti parsiran što može dovesti do greške.
<!DOCTYPE html>
<html>
  <body>
    <script>
      var myPar = document.getElementById("myParagraph");
      myPar.innerText = "I have changed the content!";
    </script>
    <p id="myParagraph">This is my paragraph! 
      <span class="hideme">Lorem ipsum</span> 
      dolor sit amet.
    </p>
    </p>
    <p class="hideme">Another paragraph!</p>
  </body>
</html>
          Šta ako imamo jednostavan dokument koga puno menja skript?
var defaultManifest = [
  "scripts/js-lib.js",
  "scripts/js-objects.js",
  "scripts/third-party/omniture.js",
  "http://big.cdn.com/useful-library.js"
]
function loadManifest(arrManifest) {
  var i, arrManifestLength = arrManifest.length;
  for (i = 0; i < arrManifestLength; i++) {
    var newScript = document.createElement("script");
    newScript.src = arrManifest[i];
    document.getElementsByTagName("head")[0].appendChild(newScript);
  }
}
          Programski kreiramo script tag unutar head.
<!DOCTYPE html>
<html>
  <head>
    <script src="scripts/js-loader.js"></script>
    <script>
      loadManifest(defaultManifest);
    </script>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>
          Sada se skriptovi učitavaju nakon što se programski kreiraju script čvorovi ali to ne blokira učitavanje stranice i drugih elemenata.
Šta ako imamo jednostavan dokument koga puno menja skript?
Iz bezbednosnih razloga skriptovi su ograničeni samo na pristup podacima koji imaju isto poreklo (origin). Poreklo je definisano u RFC 6454 kao kombinacija
Primer: http://www.example.com/dir/page.html
| URL | status | razlog | 
|---|---|---|
| http://www.example.com/dir/page2.html | OK | isti protokol, host i port | 
| http://www.example.com/dir2/other.html | OK | isti protokol, host i port | 
| http://username:password@www.example.com/dir2/other.html | OK | isti protokol, host i port | 
| http://www.example.com:81/dir/other.html | NOK | različit port | 
| https://www.example.com/dir/other.html | NOK | različit protokol | 
| http://en.example.com/dir/other.html | NOK | različit host | 
| http://example.com/dir/other.html | NOK | različit host | 
| http://v2.www.example.com/dir/other.html | NOK | različit host | 
| http://www.example.com:80/dir/other.html | ??? | zavisi od browsera | 
CORS (Cross-Origin Resource Sharing) pruža mogućnost da sajt A dopusti pristup svojim podacima skriptovima sa sajta B.
Server A treba da doda CORS zaglavlja u svoj HTTP odgovor.
Podrška za CORS u web čitačima:
Tekuće stanje je na http://caniuse.com/#search=cors.
function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) { // da li je XMLHttpRequest2 objekat
    xhr.open(method, url, true);
  } else if (typeof XDomainRequest != "undefined") {
    // XDomainRequest: specijalan slučaj za MSIE
    xhr = new XDomainRequest();
    xhr.open(method, url);
  } else {
    xhr = null; // CORS nije podržan
  }
  return xhr;
}
var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}
          Treba definisati callback za onload i onerror.
xhr.onload = function() {
  var responseText = xhr.responseText;
  console.log(responseText);
  // obradi odgovor
};
xhr.onerror = function() {
  console.log('Greška!');
};
          Standardni CORS zahtevi ne podrazumevaju cookies. Za uključivanje cookies treba:
xhr.withCredentials = true;
          Server sa svoje strane mora da omogući credentials odgovarajućim zaglavljem:
Access-Control-Allow-Credentials: true
          Ovi cookies neće biti dostupni u JavaScriptu zbog SOP.
Kada je sve pripremljeno:
xhr.send();
          Ako zahtev ima telo, ono se prosleđuje kao parametar send.
Najveći posao će obaviti web čitač i server. Čitač će dodati zaglavlja i po potrebi slati dodatne zahteve.
 
        Prosti CORS zahtevi imaju:
var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('GET', url);
xhr.send();
          
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
          Origin zaglavlje je dodao čitač i ne može se uticati na njega!
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
...
          * (za sve domene)Složeni zahtevi su potrebni ako šaljemo druge metode kao PUT i DELETE ili ako prenosimo JSON podatke.
var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
          Čitač prvo šalje probni zahtev (OPTIONS) da proveri da li ima dozvolu da šalje pravi zahtev. Posle pozitivnog odgovora šalje pravi zahtev. Ovo se odvija transparentno za JavaScript program. Probni zahtev se može keširati da se ne bi slao više puta.
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
          
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
          
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
          
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
          Ako se izostave CORS zaglavlja, signalizira se neispravan zahtev.
