| 1 |
/* |
|---|
| 2 |
* Flickr Uploadr |
|---|
| 3 |
* |
|---|
| 4 |
* Copyright (c) 2007-2008 Yahoo! Inc. All rights reserved. This library is |
|---|
| 5 |
* free software; you can redistribute it and/or modify it under the terms of |
|---|
| 6 |
* the GNU General Public License (GPL), version 2 only. This library is |
|---|
| 7 |
* distributed WITHOUT ANY WARRANTY, whether express or implied. See the GNU |
|---|
| 8 |
* GPL for more details (http://www.gnu.org/licenses/gpl.html) |
|---|
| 9 |
*/ |
|---|
| 10 |
|
|---|
| 11 |
var photos = { |
|---|
| 12 |
|
|---|
| 13 |
// Storage |
|---|
| 14 |
list: [], |
|---|
| 15 |
count: 0, |
|---|
| 16 |
errors: 0, |
|---|
| 17 |
selected: [], |
|---|
| 18 |
last: null, |
|---|
| 19 |
sort: true, |
|---|
| 20 |
batch_size: 0, |
|---|
| 21 |
thumb_cancel: false, |
|---|
| 22 |
|
|---|
| 23 |
// Upload tracking |
|---|
| 24 |
uploading: [], |
|---|
| 25 |
uploaded: [], |
|---|
| 26 |
add_to_set: [], |
|---|
| 27 |
failed: [], |
|---|
| 28 |
sets: {}, |
|---|
| 29 |
ok: 0, |
|---|
| 30 |
fail: 0, |
|---|
| 31 |
sets_fail: false, |
|---|
| 32 |
sets_out: 0, |
|---|
| 33 |
kb: { |
|---|
| 34 |
sent: 0, |
|---|
| 35 |
total: 0 |
|---|
| 36 |
}, |
|---|
| 37 |
|
|---|
| 38 |
// Queue batches of photos |
|---|
| 39 |
ready: [], |
|---|
| 40 |
ready_size: [], |
|---|
| 41 |
|
|---|
| 42 |
// Let the user select some files, thumbnail them and track them |
|---|
| 43 |
// Patch for saving our place in the directory structure from |
|---|
| 44 |
// Zoolcar9 at http://pastebin.mozilla.org/279359 |
|---|
| 45 |
add_dialog: function() { |
|---|
| 46 |
buttons.upload.disable(); |
|---|
| 47 |
|
|---|
| 48 |
// Find a good default directory for the file picker |
|---|
| 49 |
var path = nsPreferences.getLocalizedUnicharPref( |
|---|
| 50 |
'flickr.add_directory', ''); |
|---|
| 51 |
if ('' == path) { |
|---|
| 52 |
path = Cc['@mozilla.org/file/directory_service;1'] |
|---|
| 53 |
.getService(Ci.nsIProperties).get('ProfD', Ci.nsIFile).path; |
|---|
| 54 |
if (path.match(/^\//)) { |
|---|
| 55 |
path += '/../../../../../Pictures'; |
|---|
| 56 |
} else { |
|---|
| 57 |
path += '\\..\\..\\..\\..\\..\\My Documents\\My Pictures'; |
|---|
| 58 |
} |
|---|
| 59 |
} |
|---|
| 60 |
var def = Cc['@mozilla.org/file/local;1'] |
|---|
| 61 |
.createInstance(Ci.nsILocalFile); |
|---|
| 62 |
def.initWithPath(path); |
|---|
| 63 |
|
|---|
| 64 |
// Open the file picker |
|---|
| 65 |
var fp = Cc['@mozilla.org/filepicker;1'] |
|---|
| 66 |
.createInstance(Ci.nsIFilePicker); |
|---|
| 67 |
fp.init(window, locale.getString('dialog.add'), |
|---|
| 68 |
Ci.nsIFilePicker.modeOpenMultiple); |
|---|
| 69 |
var can_has_video = 'object' == typeof users.is_pro || users.is_pro; |
|---|
| 70 |
if (can_has_video) { |
|---|
| 71 |
fp.appendFilter('Photos and Videos', '*.jpeg; *.JPEG; *.jpg; ' + |
|---|
| 72 |
'*.JPG; *.gif; *.GIF; *.png; *.PNG; *.tiff; *.TIFF; *.tif; ' + |
|---|
| 73 |
'*.TIF; *.bmp; *.BMP; *.mp4; *.MP4; *.mpeg; *.MPEG; *.mpg; ' + |
|---|
| 74 |
'*.MPG; *.avi; *.AVI; *.wmv; *.WMV; *.mov; *.MOV; *.dv; ' + |
|---|
| 75 |
'*.DV; *.3gp; *.3GP; *.3g2; *.m4v; *.M4V'); |
|---|
| 76 |
} |
|---|
| 77 |
fp.appendFilter('Photos', '*.jpeg; *.JPEG; *.jpg; *.JPG; *.gif; ' + |
|---|
| 78 |
'*.GIF; *.png; *.PNG; *.tiff; *.TIFF; *.tif; *.TIF; *.bmp; *.BMP'); |
|---|
| 79 |
if (can_has_video) { |
|---|
| 80 |
fp.appendFilter('Videos', '*.mp4; *.MP4; *.mpeg; *.MPEG; ' + |
|---|
| 81 |
'*.mpg; *.MPG; *.avi; *.AVI; *.wmv; *.WMV; *.mov; *.MOV; ' + |
|---|
| 82 |
'*.dv; *.DV; *.3gp; *.3GP; *3g2; *.m4v; *.M4V'); |
|---|
| 83 |
} |
|---|
| 84 |
fp.displayDirectory = def; |
|---|
| 85 |
var res = fp.show(); |
|---|
| 86 |
if (Ci.nsIFilePicker.returnOK == res) { |
|---|
| 87 |
var files = fp.files; |
|---|
| 88 |
var paths = []; |
|---|
| 89 |
while (files.hasMoreElements()) { |
|---|
| 90 |
var arg = files.getNext().QueryInterface(Ci.nsILocalFile).path; |
|---|
| 91 |
paths.push(arg); |
|---|
| 92 |
} |
|---|
| 93 |
photos.add(paths); |
|---|
| 94 |
|
|---|
| 95 |
// Save our place in the filesystem |
|---|
| 96 |
if (arg.match(/^\//)) { |
|---|
| 97 |
path = arg.replace(/\/[^\/]+$/, '').toString(); |
|---|
| 98 |
} else { |
|---|
| 99 |
path = arg.replace(/\\[^\\]+$/, '').toString(); |
|---|
| 100 |
} |
|---|
| 101 |
nsPreferences.setUnicharPref('flickr.add_directory', path); |
|---|
| 102 |
|
|---|
| 103 |
} else if (photos.count) { |
|---|
| 104 |
buttons.upload.enable(); |
|---|
| 105 |
} |
|---|
| 106 |
}, |
|---|
| 107 |
|
|---|
| 108 |
// Add a list of photos |
|---|
| 109 |
add: function(paths, silent) { |
|---|
| 110 |
if (null == silent) { silent = false; } |
|---|
| 111 |
buttons.upload.disable(); |
|---|
| 112 |
|
|---|
| 113 |
// Tally up photos and videos and remove large videos |
|---|
| 114 |
var p_count = 0; |
|---|
| 115 |
var v_count = 0; |
|---|
| 116 |
var big_videos = []; |
|---|
| 117 |
var new_paths = []; |
|---|
| 118 |
var bad = []; |
|---|
| 119 |
for each (var p in paths) { |
|---|
| 120 |
var path = 'object' == typeof p ? p.path : p; |
|---|
| 121 |
|
|---|
| 122 |
// Photos are always allowed |
|---|
| 123 |
if (photos.is_photo(path)) { |
|---|
| 124 |
++p_count; |
|---|
| 125 |
new_paths.push(p); |
|---|
| 126 |
} |
|---|
| 127 |
|
|---|
| 128 |
// Videos are allowed for now as long as they aren't too big |
|---|
| 129 |
else if (photos.is_video(path)) { |
|---|
| 130 |
++v_count; |
|---|
| 131 |
if (file.size(path) > (null == users.videosize |
|---|
| 132 |
? conf.videosize : users.videosize)) { |
|---|
| 133 |
var filename = path.match(/([^\/\\]*)$/); |
|---|
| 134 |
big_videos.push(null == filename ? path : filename[1]); |
|---|
| 135 |
} else { |
|---|
| 136 |
new_paths.push(p); |
|---|
| 137 |
} |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
// Warn about files that are being dropped |
|---|
| 141 |
else if (path.length) { |
|---|
| 142 |
var filename = path.match(/([^\/\\]*)$/); |
|---|
| 143 |
bad.push(null == filename ? path : filename[1]); |
|---|
| 144 |
} |
|---|
| 145 |
|
|---|
| 146 |
} |
|---|
| 147 |
paths = new_paths; |
|---|
| 148 |
|
|---|
| 149 |
// Yell about anything added that wasn't a photo or video |
|---|
| 150 |
if (bad.length) { |
|---|
| 151 |
var pl = (1 == bad.length ? 's' : 'p') + |
|---|
| 152 |
(0 == paths.length ? 'z' : 'p'); |
|---|
| 153 |
var text; |
|---|
| 154 |
if (1 == bad.length) { |
|---|
| 155 |
text = locale.getFormattedString('bad.' + pl + '.text', |
|---|
| 156 |
[bad[0]]); |
|---|
| 157 |
} else { |
|---|
| 158 |
text = [locale.getFormattedString('bad.' + pl + '.text', |
|---|
| 159 |
[bad.length]), bad.join(', ')]; |
|---|
| 160 |
} |
|---|
| 161 |
alert(text, locale.getString('bad.' + pl + '.title'), |
|---|
| 162 |
locale.getString('bad.' + pl + '.ok')); |
|---|
| 163 |
} |
|---|
| 164 |
|
|---|
| 165 |
// If we're allowed to bother the user |
|---|
| 166 |
if (!silent) { |
|---|
| 167 |
|
|---|
| 168 |
// Plurality strings are decided as follows |
|---|
| 169 |
// Each dialog has identical strings but they're coded |
|---|
| 170 |
// as follows for varied pluralities: |
|---|
| 171 |
// XXX.sz.XXX: singular video, zero photos |
|---|
| 172 |
// XXX.sz.XXX: plural videos, zero photos |
|---|
| 173 |
// XXX.pp.XXX: singular video, plural photos |
|---|
| 174 |
// XXX.pp.XXX: plural videos, plural photos |
|---|
| 175 |
|
|---|
| 176 |
// Warn if a video is larger than ? MB and remove offending |
|---|
| 177 |
// videos from the list |
|---|
| 178 |
if (big_videos.length) { |
|---|
| 179 |
|
|---|
| 180 |
// Plurality, see above |
|---|
| 181 |
var pl = (1 == v_count ? 's' : 'p') + |
|---|
| 182 |
(0 == p_count ? 'z' : 'p'); |
|---|
| 183 |
|
|---|
| 184 |
window.openDialog( |
|---|
| 185 |
'chrome://uploadr/content/video_big.xul', |
|---|
| 186 |
'dialog_video_big', 'chrome,modal', |
|---|
| 187 |
locale.getString('video.add.big.' + pl + '.title'), |
|---|
| 188 |
locale.getFormattedString( |
|---|
| 189 |
'video.add.big.' + pl + '.explain', |
|---|
| 190 |
[(null == users.videosize |
|---|
| 191 |
? conf.videosize : users.videosize) >> 10] |
|---|
| 192 |
), |
|---|
| 193 |
1 == v_count ? '' : big_videos.join(', '), |
|---|
| 194 |
locale.getString('video.add.big.' + pl + '.ok')); |
|---|
| 195 |
v_count -= big_videos.length; |
|---|
| 196 |
} |
|---|
| 197 |
|
|---|
| 198 |
// If there are videos then there may be questions to ask |
|---|
| 199 |
if (v_count) { |
|---|
| 200 |
var result = {}; |
|---|
| 201 |
|
|---|
| 202 |
// Redo plurality, see above |
|---|
| 203 |
var pl = (1 == v_count ? 's' : 'p') + |
|---|
| 204 |
(0 == p_count ? 'z' : 'p'); |
|---|
| 205 |
|
|---|
| 206 |
// Offline users can have video but we should warn them that |
|---|
| 207 |
// we'll remove them without warning if they turn out to be |
|---|
| 208 |
// a free user at upload time |
|---|
| 209 |
if ('object' == typeof users.is_pro) { |
|---|
| 210 |
window.openDialog( |
|---|
| 211 |
'chrome://uploadr/content/video_offline.xul', |
|---|
| 212 |
'dialog_video_offline', 'chrome,modal', |
|---|
| 213 |
locale.getString('video.add.offline.' + pl + '.title'), |
|---|
| 214 |
locale.getString('video.add.offline.' + pl + '.explain'), |
|---|
| 215 |
locale.getString('video.add.offline.' + pl + '.ok'), |
|---|
| 216 |
locale.getString('video.add.offline.' + pl + '.cancel'), |
|---|
| 217 |
locale.getString('video.add.offline.' + pl + '.extra1'), |
|---|
| 218 |
result); |
|---|
| 219 |
} |
|---|
| 220 |
|
|---|
| 221 |
// Pro users can have videos but we still need to bother |
|---|
| 222 |
// those that have their defaults set to restricted |
|---|
| 223 |
else if (3 == settings.safety_level) { |
|---|
| 224 |
window.openDialog( |
|---|
| 225 |
'chrome://uploadr/content/video_restricted.xul', |
|---|
| 226 |
'dialog_video_restricted', 'chrome,modal', |
|---|
| 227 |
locale.getString('video.add.restricted.' + pl + '.title'), |
|---|
| 228 |
locale.getString('video.add.restricted.' + pl + '.explain'), |
|---|
| 229 |
locale.getString('video.add.restricted.' + pl + '.action'), |
|---|
| 230 |
locale.getString('video.add.restricted.' + pl + '.note'), |
|---|
| 231 |
locale.getString('video.add.restricted.' + pl + '.guidelines'), |
|---|
| 232 |
locale.getString('video.add.restricted.' + pl + '.ok'), |
|---|
| 233 |
locale.getString('video.add.restricted.' + pl + '.cancel'), |
|---|
| 234 |
locale.getString('video.add.restricted.' + pl + '.extra1'), |
|---|
| 235 |
result); |
|---|
| 236 |
} |
|---|
| 237 |
|
|---|
| 238 |
// Quit immediately if we're forgetting the whole group |
|---|
| 239 |
if ('extra1' == result.result) { |
|---|
| 240 |
return; |
|---|
| 241 |
} |
|---|
| 242 |
// Remove videos from the path list if we're keeping photos |
|---|
| 243 |
else if ('cancel' == result.result) { |
|---|
| 244 |
var new_paths = []; |
|---|
| 245 |
while (paths.length) { |
|---|
| 246 |
var p = paths.shift(); |
|---|
| 247 |
var path = 'object' == typeof p ? p.path : p; |
|---|
| 248 |
if (!photos.is_video(path)) { |
|---|
| 249 |
new_paths.push(p); |
|---|
| 250 |
} |
|---|
| 251 |
} |
|---|
| 252 |
paths = new_paths; |
|---|
| 253 |
} |
|---|
| 254 |
|
|---|
| 255 |
// If we're adding videos, remember the safety level to set |
|---|
| 256 |
else if ('ok' == result.result && result.safety_level) { |
|---|
| 257 |
var ii = paths.length; |
|---|
| 258 |
for (var i = 0; i < ii; ++i) { |
|---|
| 259 |
var p = 'object' == typeof paths[i] ? |
|---|
| 260 |
paths[i].path : paths[i]; |
|---|
| 261 |
if (photos.is_video(p)) { |
|---|
| 262 |
paths[i] = 'object' == typeof paths[i] ? |
|---|
| 263 |
paths[i] : {'path': p}; |
|---|
| 264 |
paths[i].safety_level = result.safety_level; |
|---|
| 265 |
} |
|---|
| 266 |
} |
|---|
| 267 |
} |
|---|
| 268 |
|
|---|
| 269 |
} |
|---|
| 270 |
|
|---|
| 271 |
} |
|---|
| 272 |
//videos to be rejected for non pro is beyond silence param |
|---|
| 273 |
if (v_count && users.is_pro === false) { |
|---|
| 274 |
if(confirm(locale.getString('dialog.no.video.text'), |
|---|
| 275 |
locale.getFormattedString('dialog.no.video.title', [users.username]), |
|---|
| 276 |
locale.getString('dialog.no.video.ok'), |
|---|
| 277 |
locale.getString('dialog.no.video.cancel'))) { |
|---|
| 278 |
launch_browser('http://' + SITE_HOST + '/upgrade/'); |
|---|
| 279 |
} |
|---|
| 280 |
// anyway at that point too complicate to handle video |
|---|
| 281 |
var new_paths = []; |
|---|
| 282 |
while (paths.length) { |
|---|
| 283 |
var p = paths.shift(); |
|---|
| 284 |
var path = 'object' == typeof p ? p.path : p; |
|---|
| 285 |
if (!photos.is_video(path)) { |
|---|
| 286 |
new_paths.push(p); |
|---|
| 287 |
} |
|---|
| 288 |
} |
|---|
| 289 |
paths = new_paths; |
|---|
| 290 |
} |
|---|
| 291 |
|
|---|
| 292 |
// Now add whatever's left |
|---|
| 293 |
var ii = paths.length; |
|---|
| 294 |
block_normalize(); |
|---|
| 295 |
var ext_list = []; |
|---|
| 296 |
var currentPathsLists = photos.list.map(function(x) {return (x ? x.path : "");}); |
|---|
| 297 |
|
|---|
| 298 |
for (var i = 0; i < ii; ++i) { |
|---|
| 299 |
var p = 'object' == typeof paths[i] ? paths[i].path : paths[i]; |
|---|
| 300 |
|
|---|
| 301 |
// Resolve the path and add the photo |
|---|
| 302 |
if (/^file:\/\//.test(p)) { |
|---|
| 303 |
p = Cc['@mozilla.org/network/protocol;1?name=file'] |
|---|
| 304 |
.getService(Ci.nsIFileProtocolHandler) |
|---|
| 305 |
.getFileFromURLSpec(p).path; |
|---|
| 306 |
} |
|---|
| 307 |
if(currentPathsLists.indexOf(p) === -1) { |
|---|
| 308 |
ext_list.push(photos._add(p)); |
|---|
| 309 |
|
|---|
| 310 |
// Photos can be passed as an object which already has metadata |
|---|
| 311 |
if ('object' == typeof paths[i]) { |
|---|
| 312 |
for (var k in paths[i]) { |
|---|
| 313 |
if ('id' == k) { continue; } |
|---|
| 314 |
photos.list[photos.list.length - 1][k] = paths[i][k]; |
|---|
| 315 |
} |
|---|
| 316 |
} |
|---|
| 317 |
} |
|---|
| 318 |
} |
|---|
| 319 |
|
|---|
| 320 |
// Do extension stuff after we've added all of the photos but |
|---|
| 321 |
// before the list we've saved potentially becomes invalid |
|---|
| 322 |
extension.after_add.exec(ext_list); |
|---|
| 323 |
|
|---|
| 324 |
// Update the UI |
|---|
| 325 |
photos.normalize(); |
|---|
| 326 |
if (photos.count + photos.errors) { |
|---|
| 327 |
document.getElementById('t_clear').className = 'button'; |
|---|
| 328 |
if (photos.sort) { |
|---|
| 329 |
threads.worker.dispatch(new Sort(), |
|---|
| 330 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 331 |
document.getElementById('photos_sort_default') |
|---|
| 332 |
.style.display = 'block'; |
|---|
| 333 |
document.getElementById('photos_sort_revert') |
|---|
| 334 |
.style.display = 'none'; |
|---|
| 335 |
} else { |
|---|
| 336 |
threads.worker.dispatch(new EnableUpload(), |
|---|
| 337 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 338 |
document.getElementById('photos_sort_default') |
|---|
| 339 |
.style.display = 'none'; |
|---|
| 340 |
document.getElementById('photos_sort_revert') |
|---|
| 341 |
.style.display = 'block'; |
|---|
| 342 |
} |
|---|
| 343 |
document.getElementById('photos_init') |
|---|
| 344 |
.style.display = 'none'; |
|---|
| 345 |
document.getElementById('photos_new') |
|---|
| 346 |
.style.display = 'none'; |
|---|
| 347 |
document.getElementById('no_meta_prompt') |
|---|
| 348 |
.style.visibility = 'visible'; |
|---|
| 349 |
mouse.show_photos(); |
|---|
| 350 |
} else { |
|---|
| 351 |
unblock_normalize(); |
|---|
| 352 |
document.getElementById('t_clear').className = 'disabled_button'; |
|---|
| 353 |
document.getElementById('photos_init').style.display = '-moz-box'; |
|---|
| 354 |
document.getElementById('photos_new').style.display = 'none'; |
|---|
| 355 |
} |
|---|
| 356 |
|
|---|
| 357 |
}, |
|---|
| 358 |
_add: function(path) { |
|---|
| 359 |
block_remove(); |
|---|
| 360 |
block_sort(); |
|---|
| 361 |
|
|---|
| 362 |
// Add the original image to the list and set our status |
|---|
| 363 |
var id = photos.list.length; |
|---|
| 364 |
var p = new Photo(id, path); |
|---|
| 365 |
photos.list.push(p); |
|---|
| 366 |
++photos.count; |
|---|
| 367 |
block_normalize(); |
|---|
| 368 |
|
|---|
| 369 |
// Create a spot for the image, leaving a spinning placeholder |
|---|
| 370 |
// Add images to the start of the list because this is our best |
|---|
| 371 |
// guess for ordering newest to oldest |
|---|
| 372 |
var img = document.createElementNS(NS_HTML, 'img'); |
|---|
| 373 |
img.className = 'loading'; |
|---|
| 374 |
img.setAttribute('width', 16); |
|---|
| 375 |
img.setAttribute('height', 8); |
|---|
| 376 |
img.src = 'chrome://uploadr/skin/balls-16x8-trans.gif'; |
|---|
| 377 |
var li = document.createElementNS(NS_HTML, 'li'); |
|---|
| 378 |
li.id = 'photo' + id; |
|---|
| 379 |
li.appendChild(img); |
|---|
| 380 |
var list = document.getElementById('photos_list'); |
|---|
| 381 |
list.insertBefore(li, list.firstChild); |
|---|
| 382 |
|
|---|
| 383 |
// Create and show the thumbnail |
|---|
| 384 |
photos.thumb_cancel = false; |
|---|
| 385 |
threads.worker.dispatch(new Thumb(id, conf.thumb_size, path), |
|---|
| 386 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 387 |
|
|---|
| 388 |
return p; |
|---|
| 389 |
}, |
|---|
| 390 |
|
|---|
| 391 |
// Remove selected photos |
|---|
| 392 |
remove: function() { |
|---|
| 393 |
|
|---|
| 394 |
// Respect the remove block |
|---|
| 395 |
if (0 < _block_remove) { return; } |
|---|
| 396 |
|
|---|
| 397 |
// Nothing to do if somehow there are no selected photos |
|---|
| 398 |
var ii = photos.selected.length; |
|---|
| 399 |
if (0 == ii) { return; } |
|---|
| 400 |
|
|---|
| 401 |
// Tell extensions which photos we're removing |
|---|
| 402 |
extension.before_remove.exec(photos.selected); |
|---|
| 403 |
|
|---|
| 404 |
// Remove selected photos |
|---|
| 405 |
for (var i = 0; i < ii; ++i) { |
|---|
| 406 |
var id = photos.selected[i]; |
|---|
| 407 |
var li = document.getElementById('photo' + id); |
|---|
| 408 |
if(li) { |
|---|
| 409 |
li.parentNode.removeChild(li); |
|---|
| 410 |
} |
|---|
| 411 |
|
|---|
| 412 |
// Free the size of this file |
|---|
| 413 |
photos.batch_size -= photos.list[id].size; |
|---|
| 414 |
if (users.nsid && !users.is_pro && users.bandwidth && |
|---|
| 415 |
0 < users.bandwidth.remaining - photos.batch_size) { |
|---|
| 416 |
status.clear(); |
|---|
| 417 |
} |
|---|
| 418 |
|
|---|
| 419 |
photos.list[id] = null; |
|---|
| 420 |
--photos.count; |
|---|
| 421 |
} |
|---|
| 422 |
ui.bandwidth_updated(); |
|---|
| 423 |
photos.normalize(); |
|---|
| 424 |
meta.disable(); |
|---|
| 425 |
|
|---|
| 426 |
// Clear the selection |
|---|
| 427 |
photos.selected = []; |
|---|
| 428 |
mouse.click({target: {}}); |
|---|
| 429 |
|
|---|
| 430 |
photos._remove(); |
|---|
| 431 |
}, |
|---|
| 432 |
|
|---|
| 433 |
// Allow upload only if there are photos |
|---|
| 434 |
_remove: function() { |
|---|
| 435 |
if (photos.count) { |
|---|
| 436 |
buttons.upload.enable(); |
|---|
| 437 |
} else { |
|---|
| 438 |
photos.sort = true; |
|---|
| 439 |
buttons.upload.disable(); |
|---|
| 440 |
document.getElementById('photos_sort_default') |
|---|
| 441 |
.style.display = 'none'; |
|---|
| 442 |
document.getElementById('photos_sort_revert') |
|---|
| 443 |
.style.display = 'none'; |
|---|
| 444 |
if (!photos.errors) { |
|---|
| 445 |
document.getElementById('t_clear').className = 'disabled_button'; |
|---|
| 446 |
document.getElementById('photos_init') |
|---|
| 447 |
.style.display = '-moz-box'; |
|---|
| 448 |
} |
|---|
| 449 |
document.getElementById('no_meta_prompt') |
|---|
| 450 |
.style.visibility = 'hidden'; |
|---|
| 451 |
} |
|---|
| 452 |
}, |
|---|
| 453 |
|
|---|
| 454 |
// Rotate selected files |
|---|
| 455 |
rotate: function(degrees) { |
|---|
| 456 |
|
|---|
| 457 |
// Prevent silliness |
|---|
| 458 |
var s = photos.selected; |
|---|
| 459 |
var ii = s.length; |
|---|
| 460 |
if (0 == ii) { return; } |
|---|
| 461 |
photos.selected = []; |
|---|
| 462 |
mouse.click({target: {}}); |
|---|
| 463 |
|
|---|
| 464 |
// For each selected image, show the loading spinner and dispatch |
|---|
| 465 |
// the rotate job |
|---|
| 466 |
buttons.upload.disable(); |
|---|
| 467 |
for (var i = 0; i < ii; ++i) { |
|---|
| 468 |
block_normalize(); |
|---|
| 469 |
var p = photos.list[s[i]]; |
|---|
| 470 |
if (photos.is_photo(p.path)) { |
|---|
| 471 |
block_sort(); |
|---|
| 472 |
photos.batch_size -= p.size; |
|---|
| 473 |
var img = document.getElementById('photo' + p.id) |
|---|
| 474 |
.getElementsByTagName('img')[0]; |
|---|
| 475 |
img.className = 'loading'; |
|---|
| 476 |
img.setAttribute('width', 16); |
|---|
| 477 |
img.setAttribute('height', 8); |
|---|
| 478 |
img.src = 'chrome://uploadr/skin/balls-16x8-trans.gif'; |
|---|
| 479 |
threads.worker.dispatch(new Rotate(p.id, degrees, |
|---|
| 480 |
conf.thumb_size, p.path), |
|---|
| 481 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 482 |
} |
|---|
| 483 |
} |
|---|
| 484 |
threads.worker.dispatch(new EnableUpload(), |
|---|
| 485 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 486 |
|
|---|
| 487 |
}, |
|---|
| 488 |
|
|---|
| 489 |
// Upload photos |
|---|
| 490 |
// The arguments will either both be null or both be set |
|---|
| 491 |
// If they're both set, this is an automated call to upload by the |
|---|
| 492 |
// queue |
|---|
| 493 |
upload: function(list, size) { |
|---|
| 494 |
var from_user = null == list; |
|---|
| 495 |
if (from_user) { list = photos.list; } |
|---|
| 496 |
|
|---|
| 497 |
// Don't upload if this is a user action and the button is disabled |
|---|
| 498 |
if (from_user && 'disabled_button' == document.getElementById( |
|---|
| 499 |
'button_upload').className) { |
|---|
| 500 |
return; |
|---|
| 501 |
} |
|---|
| 502 |
|
|---|
| 503 |
// Remove error indicators |
|---|
| 504 |
var li = document.getElementById('photos_list') |
|---|
| 505 |
.getElementsByTagName('li'); |
|---|
| 506 |
var ii = li.length; |
|---|
| 507 |
for (var i = 0; i < ii; ++i) { |
|---|
| 508 |
var img = li[i].getElementsByTagName('img')[0]; |
|---|
| 509 |
if ('error' == img.className) { |
|---|
| 510 |
img.onclick(); |
|---|
| 511 |
} |
|---|
| 512 |
} |
|---|
| 513 |
|
|---|
| 514 |
// Decide if we're already in the midst of an upload |
|---|
| 515 |
var not_started = 0 == photos.uploading.length; |
|---|
| 516 |
|
|---|
| 517 |
// Drop videos if we're a free user or they're over 100MB |
|---|
| 518 |
// They will have been warned that this is coming |
|---|
| 519 |
if (from_user) { |
|---|
| 520 |
var new_list = []; |
|---|
| 521 |
for each (var p in list) { |
|---|
| 522 |
if (null == p) { |
|---|
| 523 |
continue; |
|---|
| 524 |
} |
|---|
| 525 |
if (photos.is_photo(p.path)) { |
|---|
| 526 |
new_list.push(p); |
|---|
| 527 |
} else if (!users.is_pro || (null == users.videosize |
|---|
| 528 |
? conf.videosize : users.videosize) < p.size) { |
|---|
| 529 |
photos.batch_size -= p.size; |
|---|
| 530 |
} else { |
|---|
| 531 |
new_list.push(p); |
|---|
| 532 |
} |
|---|
| 533 |
} |
|---|
| 534 |
list = new_list; |
|---|
| 535 |
} |
|---|
| 536 |
|
|---|
| 537 |
// If any photos need resizing to fit in the per-photo size limits, |
|---|
| 538 |
// dispatch the jobs and wait |
|---|
| 539 |
if (from_user && !upload.processing || !from_user) { |
|---|
| 540 |
var resizing = false; |
|---|
| 541 |
var ready = []; |
|---|
| 542 |
var ready_size = 0; |
|---|
| 543 |
for each (var p in list) { |
|---|
| 544 |
if (null != p) { |
|---|
| 545 |
if (photos.is_photo(p.path)) { |
|---|
| 546 |
|
|---|
| 547 |
// Resize because of user settings |
|---|
| 548 |
if (null != settings.resize && |
|---|
| 549 |
-1 != settings.resize && |
|---|
| 550 |
(p.width > settings.resize || |
|---|
| 551 |
p.height > settings.resize)) { |
|---|
| 552 |
resizing = true; |
|---|
| 553 |
threads.worker.dispatch(new Resize( |
|---|
| 554 |
p.id, settings.resize, p.path), |
|---|
| 555 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 556 |
} |
|---|
| 557 |
|
|---|
| 558 |
// Resize because of upload limits |
|---|
| 559 |
else if (p.size > users.filesize) { |
|---|
| 560 |
resizing = true; |
|---|
| 561 |
threads.worker.dispatch(new Resize(p.id, -1, |
|---|
| 562 |
p.path), threads.worker.DISPATCH_NORMAL); |
|---|
| 563 |
} |
|---|
| 564 |
|
|---|
| 565 |
// Not resizing so record size now |
|---|
| 566 |
else { |
|---|
| 567 |
ready_size += p.size; |
|---|
| 568 |
} |
|---|
| 569 |
|
|---|
| 570 |
} |
|---|
| 571 |
|
|---|
| 572 |
// By this point there are no videos that break the rules |
|---|
| 573 |
else { |
|---|
| 574 |
ready_size += p.size; |
|---|
| 575 |
} |
|---|
| 576 |
|
|---|
| 577 |
ready.push(p); |
|---|
| 578 |
} |
|---|
| 579 |
} |
|---|
| 580 |
if (resizing) { |
|---|
| 581 |
|
|---|
| 582 |
// Setup the batch to try again after the resizing |
|---|
| 583 |
photos.ready.push(ready); |
|---|
| 584 |
photos.ready_size.push(ready_size); |
|---|
| 585 |
photos.batch_size = 0; |
|---|
| 586 |
photos.list = []; |
|---|
| 587 |
photos.count = 0; |
|---|
| 588 |
photos.selected = []; |
|---|
| 589 |
photos.last = null; |
|---|
| 590 |
var list = document.getElementById('photos_list'); |
|---|
| 591 |
while (list.hasChildNodes()) { |
|---|
| 592 |
list.removeChild(list.firstChild); |
|---|
| 593 |
} |
|---|
| 594 |
ui.bandwidth_updated(); |
|---|
| 595 |
threads.worker.dispatch(new RetryUpload(true), |
|---|
| 596 |
threads.worker.DISPATCH_NORMAL); |
|---|
| 597 |
|
|---|
| 598 |
// Give some meaningful feedback |
|---|
| 599 |
if (not_started) { |
|---|
| 600 |
document.getElementById('footer').style.display = |
|---|
| 601 |
'-moz-box'; |
|---|
| 602 |
upload.progress_bar = new ProgressBar('progress_bar'); |
|---|
| 603 |
var progress_text = document.getElementById( |
|---|
| 604 |
'progress_text'); |
|---|
| 605 |
progress_text.className = 'spinning'; |
|---|
| 606 |
progress_text.value = locale.getString( |
|---|
| 607 |
'upload.resizing.status'); |
|---|
| 608 |
status.set(locale.getString('status.uploading')); |
|---|
| 609 |
buttons.upload.disable(); |
|---|
| 610 |
document.getElementById('photos_sort_default') |
|---|
| 611 |
.style.display = 'none'; |
|---|
| 612 |
document.getElementById('photos_sort_revert') |
|---|
| 613 |
.style.display = 'none'; |
|---|
| 614 |
document.getElementById('photos_init') |
|---|
| 615 |
.style.display = 'none'; |
|---|
| 616 |
document.getElementById('photos_new') |
|---|
| 617 |
.style.display = '-moz-box'; |
|---|
| 618 |
document.getElementById('no_meta_prompt') |
|---|
| 619 |
.style.visibility = 'hidden'; |
|---|
| 620 |
meta.disable(); |
|---|
| 621 |
photos.sets[users.nsid] = meta.sets; |
|---|
| 622 |
} |
|---|
| 623 |
|
|---|
| 624 |
return; |
|---|
| 625 |
} |
|---|
| 626 |
} |
|---|
| 627 |
|
|---|
| 628 |
// Update the UI |
|---|
| 629 |
if (from_user) { |
|---|
| 630 |
status.set(locale.getString('status.uploading')); |
|---|
| 631 |
document.getElementById('t_clear').className = 'disabled_button'; |
|---|
| 632 |
buttons.upload.disable(); |
|---|
| 633 |
document.getElementById('photos_sort_default') |
|---|
| 634 |
.style.display = 'none'; |
|---|
| 635 |
document.getElementById('photos_sort_revert') |
|---|
| 636 |
.style.display = 'none'; |
|---|
| 637 |
document.getElementById('photos_init') |
|---|
| 638 |
.style.display = 'none'; |
|---|
| 639 |
document.getElementById('photos_new') |
|---|
| 640 |
.style.display = '-moz-box'; |
|---|
| 641 |
document.getElementById('no_meta_prompt') |
|---|
| 642 |
.style.visibility = 'hidden'; |
|---|
| 643 |
meta.disable(); |
|---|
| 644 |
photos.sets[users.nsid] = meta.sets; |
|---|
| 645 |
} |
|---|
| 646 |
|
|---|
| 647 |
// We're really going to start or queue a batch, so do extension stuff |
|---|
| 648 |
extension.before_upload.exec(list); |
|---|
| 649 |
|
|---|
| 650 |
// Take the list of photos into upload mode and reset the UI |
|---|
| 651 |
var ready = []; |
|---|
| 652 |
for each (var p in list) { |
|---|
| 653 |
|
|---|
| 654 |
// Keep the uploading user's NSID with each photo so to allow |
|---|
| 655 |
// people to change accounts from one batch to the next |
|---|
| 656 |
p.nsid = users.nsid; |
|---|
| 657 |
|
|---|
| 658 |
// If we have to queue this batch |
|---|
| 659 |
if (from_user && upload.processing) { |
|---|
| 660 |
ready.push(p); |
|---|
| 661 |
} |
|---|
| 662 |
|
|---|
| 663 |
// If we can upload immediately |
|---|
| 664 |
else { |
|---|
| 665 |
photos.uploading.push(p); |
|---|
| 666 |
|
|---|
| 667 |
// Setup progress bar for this photo and show it in the queue |
|---|
| 668 |
var img = document.createElementNS(NS_HTML, 'img'); |
|---|
| 669 |
img.src = 'file:///' + escape(p.thumb); |
|---|
| 670 |
img.width = p.thumb_width; |
|---|
| 671 |
img.height = p.thumb_height; |
|---|
| 672 |
var stack = document.createElement('stack'); |
|---|
| 673 |
stack.appendChild(img); |
|---|
| 674 |
p.progress_bar = new ProgressBar('queue' + |
|---|
| 675 |
(photos.uploading.length - 1), img.width); |
|---|
| 676 |
var bar = p.progress_bar.create(); |
|---|
| 677 |
bar.style.top = (img.height - 20) + 'px'; |
|---|
| 678 |
stack.appendChild(bar); |
|---|
| 679 |
var li = document.createElementNS(NS_HTML, 'li'); |
|---|
| 680 |
li.appendChild(stack); |
|---|
| 681 |
document.getElementById('queue_list').appendChild(li); |
|---|
| 682 |
|
|---|
| 683 |
} |
|---|
| 684 |
|
|---|
| 685 |
} |
|---|
| 686 |
if (from_user && upload.processing) { |
|---|
| 687 |
photos.ready.push(ready); |
|---|
| 688 |
photos.ready_size.push(photos.batch_size); |
|---|
| 689 |
} else { |
|---|
| 690 |
if (from_user) { |
|---|
| 691 |
photos.kb.total += photos.batch_size; |
|---|
| 692 |
} else { |
|---|
| 693 |
photos.kb.total += size; |
|---|
| 694 |
} |
|---|
| 695 |
} |
|---|
| 696 |
if (from_user) { |
|---|
| 697 |
photos.batch_size = 0; |
|---|
| 698 |
photos.list = []; |
|---|
| 699 |
photos.count = 0; |
|---|
| 700 |
photos.selected = []; |
|---|
| 701 |
photos.last = null; |
|---|
| 702 |
var ul = document.getElementById('photos_list'); |
|---|
| 703 |
while (ul.hasChildNodes()) { |
|---|
| 704 |
ul.removeChild(ul.firstChild); |
|---|
| 705 |
} |
|---|
| 706 |
ui.bandwidth_updated(); |
|---|
| 707 |
} |
|---|
| 708 |
|
|---|
| 709 |
upload.startTime = new Date().getTime(); |
|---|
| 710 |
|
|---|
| 711 |
// Kick off the first batch job if we haven't started |
|---|
| 712 |
if (not_started && !upload.processing) { |
|---|
| 713 |
ii = photos.uploading.length; |
|---|
| 714 |
for (var i = 0; i < ii; ++i) { |
|---|
| 715 |
if (null != photos.uploading[i]) { |
|---|
| 716 |
block_exit(); |
|---|
| 717 |
upload.start(i); |
|---|
| 718 |
break; |
|---|
| 719 |
} |
|---|
| 720 |
} |
|---|
| 721 |
} |
|---|
| 722 |
|
|---|
| 723 |
}, |
|---|
| 724 |
|
|---|
| 725 |
// Normalize the photo list and selected list with the DOM |
|---|
| 726 |
normalize: function() { |
|---|
| 727 |
|
|---|
| 728 |
// This action is blocked during loading but will always |
|---|
| 729 |
// happen at the end of loading |
|---|
| 730 |
if (_block_normalize) { return; } |
|---|
| 731 |
|
|---|
| 732 |
var list = document.getElementById('photos_list') |
|---|
| 733 |
.getElementsByTagName('li'); |
|---|
| 734 |
var old_list = photos.list; |
|---|
| 735 |
photos.list = []; |
|---|
| 736 |
photos.selected = []; |
|---|
| 737 |
for (var i = list.length - 1; i >= 0; --i) { |
|---|
| 738 |
|
|---|
| 739 |
// Move the photo info |
|---|
| 740 |
var old_id = parseInt(list[i].id.replace('photo', '')); |
|---|
| 741 |
var new_id = photos.list.length; |
|---|
| 742 |
list[i].id = 'photo' + new_id; |
|---|
| 743 |
photos.list.push(old_list[old_id]); |
|---|
| 744 |
photos.list[new_id].id = new_id; |
|---|
| 745 |
|
|---|
| 746 |
// Update selection |
|---|
| 747 |
if ('selected' == list[i].getElementsByTagName('img')[0] |
|---|
| 748 |
.className) { |
|---|
| 749 |
photos.selected.push(new_id); |
|---|
| 750 |
} |
|---|
| 751 |
|
|---|
| 752 |
} |
|---|
| 753 |
}, |
|---|
| 754 |
|
|---|
| 755 |
// Load saved metadata |
|---|
| 756 |
load: function() { |
|---|
| 757 |
var obj = file.read('photos.json'); |
|---|
| 758 |
|
|---|
| 759 |
// Don't bother if there are no photos |
|---|
| 760 |
if ('undefined' == typeof obj.list) { return; } |
|---|
| 761 |
|
|---|
| 762 |
// Add the previous batch of photos |
|---|
| 763 |
var list = obj.list; |
|---|
| 764 |
if (list.length) { |
|---|
| 765 |
photos.sort = obj.sort; |
|---|
| 766 |
document.getElementById('photos_init').style.display = 'none'; |
|---|
| 767 |
document.getElementById('photos_new').style.display = 'none'; |
|---|
| 768 |
document.getElementById('no_meta_prompt') |
|---|
| 769 |
.style.visibility = 'visible'; |
|---|
| 770 |
} |
|---|
| 771 |
photos.add(list, true); |
|---|
| 772 |
|
|---|
| 773 |
// Bring in last known sets configuration |
|---|
| 774 |
meta.sets = obj.sets; |
|---|
| 775 |
|
|---|
| 776 |
}, |
|---|
| 777 |
|
|---|
| 778 |
// clear photos.json |
|---|
| 779 |
// this is desperate move when the uploadr is in unusable state |
|---|
| 780 |
removeAll: function() { |
|---|
| 781 |
if (document.getElementById('t_clear').className == 'disabled_button') |
|---|
| 782 |
return; |
|---|
| 783 |
document.getElementById('t_clear').className = 'disabled_button'; |
|---|
| 784 |
photos.thumb_cancel = true; |
|---|
| 785 |
if (conf.console.thumb) { |
|---|
| 786 |
Cc['@mozilla.org/consoleservice;1'] |
|---|
| 787 |
.getService(Ci.nsIConsoleService) |
|---|
| 788 |
.logStringMessage('clearing'); |
|---|
| 789 |
} |
|---|
| 790 |
photos.list = []; |
|---|
| 791 |
photos.selected = []; |
|---|
| 792 |
photos.count = 0; |
|---|
| 793 |
photos.errors = 0; |
|---|
| 794 |
photos.batch_size = 0; |
|---|
| 795 |
_block_sort = _block_remove = _block_normalize = _block_exit = 0; |
|---|
| 796 |
file.remove('photos.json'); |
|---|
| 797 |
// Remove photos from UI |
|---|
| 798 |
var list = document.getElementById('photos_list'); |
|---|
| 799 |
while (list.hasChildNodes()) { |
|---|
| 800 |
list.removeChild(list.firstChild); |
|---|
| 801 |
} |
|---|
| 802 |
document.getElementById('photos_init').style.display = '-moz-box'; |
|---|
| 803 |
ui.bandwidth_updated(); |
|---|
| 804 |
meta.disable(); |
|---|
| 805 |
buttons.upload.disable(); |
|---|
| 806 |
}, |
|---|
| 807 |
|
|---|
| 808 |
// Save all metadata to disk |
|---|
| 809 |
save: function() { |
|---|
| 810 |
if (0 != _block_exit) { return; } |
|---|
| 811 |
if (1 == photos.selected.length) { |
|---|
| 812 |
meta.save(photos.selected[0]); |
|---|
| 813 |
} |
|---|
| 814 |
file.write('photos.json', { |
|---|
| 815 |
sort: photos.sort, |
|---|
| 816 |
sets: meta.sets, |
|---|
| 817 |
list: photos.list |
|---|
| 818 |
}); |
|---|
| 819 |
}, |
|---|
| 820 |
|
|---|
| 821 |
// Decide if a given path is a photo |
|---|
| 822 |
is_photo: function(path) { |
|---|
| 823 |
return /\.(jpe?g|tiff?|gif|png|bmp)$/i.test(path); |
|---|
| 824 |
}, |
|---|
| 825 |
|
|---|
| 826 |
// Similarly, is it a video |
|---|
| 827 |
is_video: function(path) { |
|---|
| 828 |
return /\.(mp4|mpe?g|avi|wmv|mov|dv|3gp|3g2|m4v)$/i.test(path); |
|---|
| 829 |
} |
|---|
| 830 |
|
|---|
| 831 |
}; |
|---|
| 832 |
|
|---|
| 833 |
// Setup auto-saving of metadata in case of crashes |
|---|
| 834 |
// See photos.save for problems related to the auto-save interval |
|---|
| 835 |
window.setInterval(function() { |
|---|
| 836 |
photos.save(); |
|---|
| 837 |
}, 1000 * conf.auto_save); |
|---|
| 838 |
|
|---|
| 839 |
// Photo properties |
|---|
| 840 |
var Photo = function(id, path) { |
|---|
| 841 |
this.id = id; |
|---|
| 842 |
this.path = path; |
|---|
| 843 |
this.date_taken = ''; |
|---|
| 844 |
this.width = 0; |
|---|
| 845 |
this.height = 0; |
|---|
| 846 |
this.thumb = ''; |
|---|
| 847 |
this.thumb_width = 0; |
|---|
| 848 |
this.thumb_height = 0; |
|---|
| 849 |
var filename = path.match(/([^\/\\]*)$/); |
|---|
| 850 |
this.filename = null == filename ? p : filename[1]; |
|---|
| 851 |
this.size = 0; // Kilobytes |
|---|
| 852 |
this.title = ''; |
|---|
| 853 |
this.description = ''; |
|---|
| 854 |
this.tags = ''; |
|---|
| 855 |
this.duration = 0; |
|---|
| 856 |
this.is_public = settings.is_public; |
|---|
| 857 |
this.is_friend = settings.is_friend; |
|---|
| 858 |
this.is_family = settings.is_family; |
|---|
| 859 |
this.content_type = settings.content_type; |
|---|
| 860 |
this.safety_level = settings.safety_level; |
|---|
| 861 |
this.hidden = settings.hidden; |
|---|
| 862 |
this.sets = []; |
|---|
| 863 |
this.progress_bar = null; |
|---|
| 864 |
this.nsid = null; |
|---|
| 865 |
this.photo_id = null; |
|---|
| 866 |
}; |
|---|