I am new to JavaScript, especially the wonders of async and closures and would like some help on the attached code. It is in a Chrome extension and retrieves a list of users albums with the Picasa Web API then gets the items in each album so I can filter out unwanted media and directories. It appears to be working as intended, but I am a little concerned that there are some unintended side effects lurking. I would also appreciate advice on how it may be improved.
// callback = function (albumList, error);
function loadAlbumList(callback) {
var albumListQuery = '?v2&alt=json';
var request = picasaPath + 'default/' + albumListQuery;
authenticatedXhr('GET',request, function (error, httpStatus, responseText) {
var root = JSON.parse(responseText);
var feed = root.feed;
var entries = feed.entry || [], entry;
var albumList = [], album;
var ct = 0;
for (var i = 0; i < entries.length; ++i) {
(function(index) {
entry = entries[index];
loadAlbum(entries[index].gphoto$id.$t, false, function (photos) {
if(!entries[index].gphoto$albumType && photos.length) {
album = new Object();
album.index = index;
album.name = entries[index].title.$t;
album.id = entries[index].gphoto$id.$t;
album.ct = photo.length;
album.thumb = entries[index].media$group.media$thumbnail[0].url;
album.checked = false;
album.photos = photos;
albumList.push(album);
}
if(ct === (entries.length - 1)) {
albumList.sort(function(a, b) {
return a.index - b.index;
});
callback(albumList, error);
}
ct++;
});
})(i);
}
});
}
// callback = function (albumList, error);
function loadAlbum(id, preload, callback) {
var request = picasaPath + 'default/albumid/' + id + '/' + photosQuery;
authenticatedXhr('GET',request, function (error, httpStatus, responseText) {
var root = JSON.parse(responseText);
var feed = root.feed;
var entries = feed.entry || [], entry;
var imgs = [], img;
var photos = [],photo;
var url,author,width,height,aspectRatio;
for (var i = 0; i < entries.length; i++) {
entry = entries[i];
if(isImage(entry)) {
url = entry.media$group.media$content[0].url;
width = entry.media$group.media$content[0].width;
height = entry.media$group.media$content[0].height;
aspectRatio = width / height;
author = entry.media$group.media$credit[0].$t;
if(preload) {
img = new Image();
// img.addEventListener('error', _imgError);
img.src = url;
imgs.push(img);
}
photo = new Object();
photo.url = url;
photo.author = author;
photo.asp = aspectRatio.toPrecision(3);
photos.push(photo);
}
}
callback(photos, error);
});
}
// callback = function (error, httpStatus, responseText);
function authenticatedXhr(method, url, callback) {
var retry = true;
(function getTokenAndXhr() {
chrome.identity.getAuthToken({ 'interactive': true },
function (access_token) {
if (chrome.runtime.lastError) {
callback(chrome.runtime.lastError);
return;
}
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader('Authorization',
'Bearer ' + access_token);
xhr.send();
xhr.onload = function () {
if (this.status === 401 && retry) {
// This status may indicate that the cached
// access token was invalid. Retry once with
// a fresh token.
retry = false;
chrome.identity.removeCachedAuthToken(
{ 'token': access_token },
getTokenAndXhr);
return;
}
callback(null, this.status, this.responseText);
}
});
})();
}