1 <!DOCTYPE html> 2 <html> 3 <head> 4 <script src="chrome://resources/js/cr.js"></script> 5 <script src="chrome://resources/js/cr/event_target.js"></script> 6 7 <script src="js/picasa_client.js"></script> 8 </head> 9 10 <body> 11 12 <script> 13 14 /** 15 * Uploader constructor. 16 * 17 * Uploader object is responsible for uploading a bunch of images to the same 18 * picasa album. It also manages the notification. 19 * 20 * @param {Array.<picasa.LocalFile>} files Files to upload. 21 * @param {picasa.Album} album Album to upload to. 22 * @param {picasa.Client} client Picasa client. 23 * @param {string} hash Hash value unique to this uploader (to differentiate 24 * multiple uploaders). 25 */ 26 function Uploader(files, album, client, hash) { 27 this.album_ = album; 28 this.client_ = client; 29 this.files_ = files; 30 31 this.filesTotal_ = this.files_.length; 32 this.filesDone_ = 0; 33 this.hash = hash; 34 35 this.request_ = null; 36 this.failed_ = false; 37 this.canceled_ = false; 38 this.finished_ = false; 39 40 this.startDate_ = null; 41 this.timeRemaining_ = null; 42 } 43 44 Uploader.prototype = { 45 /** 46 * Starts the upload process. 47 */ 48 start: function() { 49 this.startDate_ = new Date(); 50 this.failed_ = false; 51 this.createNotification_(); 52 var self = this; 53 self.uploadNextFile_(); 54 }, 55 56 /** 57 * Creates a webkit notification. 58 */ 59 createNotification_: function() { 60 // We pass unique hash to the notification dom, so it will distinct this 61 // uploader object from others. 62 this.webkitNotification_ = 63 window.webkitNotifications.createHTMLNotification( 64 chrome.extension.getURL('notification.html' + this.hash)); 65 this.webkitNotification_.onclose = this.onNotificationClose_.bind(this); 66 this.webkitNotification_.show(); 67 }, 68 69 /** 70 * Sets the notification object (see notification.html). 71 * This method is called from notification dom, so uploader can modify it. 72 * @param {Notification} notification The notification object. 73 */ 74 setNotification: function(notification) { 75 this.notification_ = notification; 76 if (this.finished_) { 77 this.showFinished_(); 78 } else { 79 this.updateNotification_(); 80 } 81 }, 82 83 /** 84 * Updates information in notification. 85 */ 86 updateNotification_: function() { 87 this.notification_.update(this.filesDone_, this.filesTotal_, 88 this.timeRemaining_); 89 }, 90 91 /** 92 * This method is called when uploading is finished (either successfully or 93 * not). 94 */ 95 done_: function() { 96 this.finished_ = true; 97 // If notification was closed by user, we should create a new one. 98 if (this.webkitNotification_) { 99 this.showFinished_(); 100 } else { 101 this.createNotification_(); 102 } 103 }, 104 105 /** 106 * Shows final information in notification. 107 */ 108 showFinished_: function() { 109 if (this.failed_) { 110 this.notification_.showFailed(this.filesDone_, this.filesTotal_); 111 } else if (this.canceled_) { 112 this.notification_.showCanceled(this.filesDone_, this.filesTotal_); 113 } else { 114 this.notification_.showCompleted(this.filesDone_, this.album_.link); 115 } 116 }, 117 118 /** 119 * Event handler for notification close. 120 */ 121 onNotificationClose_: function() { 122 if (this.finished_) { 123 // Inform background page that we are done. 124 bg.removeUploader(this.hash); 125 } else { 126 // User closed notification "in progress". We will create a new one 127 // to show final information. 128 this.webkitNotification_ = null; 129 } 130 }, 131 132 /** 133 * Uploads the next file to picasa web albums. 134 */ 135 uploadNextFile_: function() { 136 if (this.files_.length == 0 || this.failed_ || this.canceled_) { 137 this.done_(); 138 return; 139 } 140 141 var file = this.files_.shift(); 142 this.request_ = this.client_.uploadFile(this.album_, file, 143 this.uploadFileCallback_.bind(this)); 144 }, 145 146 /** 147 * Event handler for file upload. 148 * @param {?string} response The response or null if failed. 149 */ 150 uploadFileCallback_: function(response) { 151 if (this.failed_ || this.canceled_) { 152 return; 153 } 154 155 this.request_ = null; 156 if (response == null) { 157 this.failed_ = true; 158 } else { 159 this.filesDone_++; 160 var elapsed = (new Date()) - this.startDate_; 161 this.timeRemaining_ = elapsed * 162 (this.filesTotal_ - this.filesDone_) / this.filesDone_; 163 } 164 this.updateNotification_(); 165 this.uploadNextFile_(); 166 }, 167 168 /** 169 * Cancels the upload process. 170 */ 171 cancel: function() { 172 this.canceled_ = true; 173 this.done_(); 174 if (this.request_) { 175 this.request_.abort(); 176 this.request_ = null; 177 } 178 } 179 }; 180 181 182 /** 183 * BackgroundPage constructor. 184 * 185 * BackgroundPage object opens the upload window and passes upload requests 186 * to Uploader objects. It also holds the global picasa client object. 187 */ 188 function BackgroundPage() { 189 this.client = new picasa.Client(); 190 this.newFiles_ = []; 191 this.uploadPageUrl_ = chrome.extension.getURL('upload.html'); 192 this.uploaders_ = {}; 193 this.lastUploaderHash_ = 0; 194 195 var self = this; 196 chrome.fileBrowserHandler.onExecute.addListener( 197 function(id, file_entries) { 198 console.log('picasa: got task - ' + id); 199 if (id == 'upload') { 200 self.onFileUpload_(file_entries); 201 } 202 }); 203 } 204 205 BackgroundPage.prototype = { 206 /** 207 * Returns a window with specified url. 208 * @param {string} url Url of a window to find. 209 * @return {DOMWindow} Window with specified url. 210 */ 211 findWindow_: function(url) { 212 var views = chrome.extension.getViews(); 213 for (var view, i = 0; view = views[i]; i++) { 214 if (view.location.href == url) { 215 return view; 216 } 217 } 218 return null; 219 }, 220 221 /** 222 * Event handler called when user chooses "send to picasa" somewhere. 223 * @param {Array.<picasa.LocalFile>} files Files to upload. 224 */ 225 onSendImageRequest_: function(files) { 226 var win = this.findWindow_(this.uploadPageUrl_); 227 if (win) { 228 // If upload window already loaded, just add one more file. 229 win.uploadPage.addFiles(files); 230 } else { 231 // If upload window is not yet loaded, it will ask for new files via 232 // getNewFiles method. 233 this.newFiles_ = this.newFiles_.concat(files); 234 chrome.windows.create({url: this.uploadPageUrl_, width: 620, height: 465}); 235 } 236 }, 237 238 /** 239 * "Send to picasa" event handler from filebrowser. 240 * @param {*} fileEntries Entry object array. 241 */ 242 onFileUpload_: function(fileEntries) { 243 if (!fileEntries) { 244 return; 245 } 246 247 var self = this; 248 var files = []; 249 var remaining = fileEntries.length; 250 console.log('got files: ' + remaining); 251 for (var i = 0; i < fileEntries.length; i++) { 252 var entry = fileEntries[i]; 253 entry.file(function(file) { 254 files.push(new picasa.LocalFile(file)); 255 remaining--; 256 if (remaining == 0 && files.length > 0) { 257 self.onSendImageRequest_(files); 258 } 259 }); 260 } 261 262 // If not all the entries were resolved, send request for resolved ones. 263 setTimeout(function() { 264 if (remaining > 0 && files.length > 0) { 265 self.onSendImageRequest_(files); 266 } 267 }, 1000); 268 }, 269 270 /** 271 * Returns new files for upload. 272 * @return {Array.<picasa.LocalFile>} New files. 273 */ 274 getNewFiles: function() { 275 var result = this.newFiles_; 276 this.newFiles_ = []; 277 return result; 278 }, 279 280 /** 281 * Starts the uploading process. 282 * @param {Array.<picasa.LocalFile>} files Files to upload. 283 * @param {picasa.Album} album Album to upload to. 284 */ 285 uploadFiles: function(files, album) { 286 var hash = '#' + this.lastUploaderHash_++; 287 var uploader = new Uploader(files, album, this.client, hash); 288 this.uploaders_[hash] = uploader; 289 uploader.start(); 290 }, 291 292 /** 293 * Returns an Uploader object by hash. 294 * @param {string} hash Unique hash. 295 * @return {Uploader} Uploader object with given hash. 296 */ 297 getUploader: function(hash) { 298 return this.uploaders_[hash]; 299 }, 300 301 /** 302 * Removes an Uploader object by hash. 303 * @param {string} hash Unique hash. 304 */ 305 removeUploader: function(hash) { 306 this.uploaders_[hash] = null; 307 } 308 }; 309 310 /** 311 * Single BackgroundPage object. 312 * @type {BackgroundPage} 313 */ 314 var bg = new BackgroundPage(); 315 316 </script> 317 </body> 318 </html> 319