<!DOCTYPE html>
<html>
<head>
<script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/cr/event_target.js"></script>
<script src="js/picasa_client.js"></script>
</head>
<body>
<script>
/**
* Uploader constructor.
*
* Uploader object is responsible for uploading a bunch of images to the same
* picasa album. It also manages the notification.
*
* @param {Array.<picasa.LocalFile>} files Files to upload.
* @param {picasa.Album} album Album to upload to.
* @param {picasa.Client} client Picasa client.
* @param {string} hash Hash value unique to this uploader (to differentiate
* multiple uploaders).
*/
function Uploader(files, album, client, hash) {
this.album_ = album;
this.client_ = client;
this.files_ = files;
this.filesTotal_ = this.files_.length;
this.filesDone_ = 0;
this.hash = hash;
this.request_ = null;
this.failed_ = false;
this.canceled_ = false;
this.finished_ = false;
this.startDate_ = null;
this.timeRemaining_ = null;
}
Uploader.prototype = {
/**
* Starts the upload process.
*/
start: function() {
this.startDate_ = new Date();
this.failed_ = false;
this.createNotification_();
var self = this;
self.uploadNextFile_();
},
/**
* Creates a webkit notification.
*/
createNotification_: function() {
// We pass unique hash to the notification dom, so it will distinct this
// uploader object from others.
this.webkitNotification_ =
window.webkitNotifications.createHTMLNotification(
chrome.extension.getURL('notification.html' + this.hash));
this.webkitNotification_.onclose = this.onNotificationClose_.bind(this);
this.webkitNotification_.show();
},
/**
* Sets the notification object (see notification.html).
* This method is called from notification dom, so uploader can modify it.
* @param {Notification} notification The notification object.
*/
setNotification: function(notification) {
this.notification_ = notification;
if (this.finished_) {
this.showFinished_();
} else {
this.updateNotification_();
}
},
/**
* Updates information in notification.
*/
updateNotification_: function() {
this.notification_.update(this.filesDone_, this.filesTotal_,
this.timeRemaining_);
},
/**
* This method is called when uploading is finished (either successfully or
* not).
*/
done_: function() {
this.finished_ = true;
// If notification was closed by user, we should create a new one.
if (this.webkitNotification_) {
this.showFinished_();
} else {
this.createNotification_();
}
},
/**
* Shows final information in notification.
*/
showFinished_: function() {
if (this.failed_) {
this.notification_.showFailed(this.filesDone_, this.filesTotal_);
} else if (this.canceled_) {
this.notification_.showCanceled(this.filesDone_, this.filesTotal_);
} else {
this.notification_.showCompleted(this.filesDone_, this.album_.link);
}
},
/**
* Event handler for notification close.
*/
onNotificationClose_: function() {
if (this.finished_) {
// Inform background page that we are done.
bg.removeUploader(this.hash);
} else {
// User closed notification "in progress". We will create a new one
// to show final information.
this.webkitNotification_ = null;
}
},
/**
* Uploads the next file to picasa web albums.
*/
uploadNextFile_: function() {
if (this.files_.length == 0 || this.failed_ || this.canceled_) {
this.done_();
return;
}
var file = this.files_.shift();
this.request_ = this.client_.uploadFile(this.album_, file,
this.uploadFileCallback_.bind(this));
},
/**
* Event handler for file upload.
* @param {?string} response The response or null if failed.
*/
uploadFileCallback_: function(response) {
if (this.failed_ || this.canceled_) {
return;
}
this.request_ = null;
if (response == null) {
this.failed_ = true;
} else {
this.filesDone_++;
var elapsed = (new Date()) - this.startDate_;
this.timeRemaining_ = elapsed *
(this.filesTotal_ - this.filesDone_) / this.filesDone_;
}
this.updateNotification_();
this.uploadNextFile_();
},
/**
* Cancels the upload process.
*/
cancel: function() {
this.canceled_ = true;
this.done_();
if (this.request_) {
this.request_.abort();
this.request_ = null;
}
}
};
/**
* BackgroundPage constructor.
*
* BackgroundPage object opens the upload window and passes upload requests
* to Uploader objects. It also holds the global picasa client object.
*/
function BackgroundPage() {
this.client = new picasa.Client();
this.newFiles_ = [];
this.uploadPageUrl_ = chrome.extension.getURL('upload.html');
this.uploaders_ = {};
this.lastUploaderHash_ = 0;
var self = this;
chrome.fileBrowserHandler.onExecute.addListener(
function(id, file_entries) {
console.log('picasa: got task - ' + id);
if (id == 'upload') {
self.onFileUpload_(file_entries);
}
});
}
BackgroundPage.prototype = {
/**
* Returns a window with specified url.
* @param {string} url Url of a window to find.
* @return {DOMWindow} Window with specified url.
*/
findWindow_: function(url) {
var views = chrome.extension.getViews();
for (var view, i = 0; view = views[i]; i++) {
if (view.location.href == url) {
return view;
}
}
return null;
},
/**
* Event handler called when user chooses "send to picasa" somewhere.
* @param {Array.<picasa.LocalFile>} files Files to upload.
*/
onSendImageRequest_: function(files) {
var win = this.findWindow_(this.uploadPageUrl_);
if (win) {
// If upload window already loaded, just add one more file.
win.uploadPage.addFiles(files);
} else {
// If upload window is not yet loaded, it will ask for new files via
// getNewFiles method.
this.newFiles_ = this.newFiles_.concat(files);
chrome.windows.create({url: this.uploadPageUrl_, width: 620, height: 465});
}
},
/**
* "Send to picasa" event handler from filebrowser.
* @param {*} fileEntries Entry object array.
*/
onFileUpload_: function(fileEntries) {
if (!fileEntries) {
return;
}
var self = this;
var files = [];
var remaining = fileEntries.length;
console.log('got files: ' + remaining);
for (var i = 0; i < fileEntries.length; i++) {
var entry = fileEntries[i];
entry.file(function(file) {
files.push(new picasa.LocalFile(file));
remaining--;
if (remaining == 0 && files.length > 0) {
self.onSendImageRequest_(files);
}
});
}
// If not all the entries were resolved, send request for resolved ones.
setTimeout(function() {
if (remaining > 0 && files.length > 0) {
self.onSendImageRequest_(files);
}
}, 1000);
},
/**
* Returns new files for upload.
* @return {Array.<picasa.LocalFile>} New files.
*/
getNewFiles: function() {
var result = this.newFiles_;
this.newFiles_ = [];
return result;
},
/**
* Starts the uploading process.
* @param {Array.<picasa.LocalFile>} files Files to upload.
* @param {picasa.Album} album Album to upload to.
*/
uploadFiles: function(files, album) {
var hash = '#' + this.lastUploaderHash_++;
var uploader = new Uploader(files, album, this.client, hash);
this.uploaders_[hash] = uploader;
uploader.start();
},
/**
* Returns an Uploader object by hash.
* @param {string} hash Unique hash.
* @return {Uploader} Uploader object with given hash.
*/
getUploader: function(hash) {
return this.uploaders_[hash];
},
/**
* Removes an Uploader object by hash.
* @param {string} hash Unique hash.
*/
removeUploader: function(hash) {
this.uploaders_[hash] = null;
}
};
/**
* Single BackgroundPage object.
* @type {BackgroundPage}
*/
var bg = new BackgroundPage();
</script>
</body>
</html>