|
function Dropbox(dropbox, url) { |
|
this.box = dropbox; |
|
this.url = url; |
|
this.queue = []; |
|
this.num_current_uploads = 0; |
|
|
|
// Add clickable file picker |
|
this.picker = document.createElement('input'); |
|
this.picker.setAttribute('type', 'file'); |
|
this.box.appendChild(this.picker); |
|
// Add progress bar container |
|
this.uploads = document.createElement('div'); |
|
this.box.parentNode.insertBefore(this.uploads, this.box.nextSibling); |
|
|
|
// Optional defaults |
|
this.max_concurrent = 2; // Queue uploads after n concurrent |
|
this.max_size = null; // Max size in MB |
|
this.success = null; // Callback on successful upload, passed the response body |
|
this.error = null; // Callback on upload failure, passed the error text |
|
this.mime_types = null; // A regex to match file mime types |
|
|
|
var _this = this; |
|
// Init drag and drop handlers |
|
this.box.addEventListener("dragenter", function(e) { _this.drag(e, true) }, false); |
|
this.box.addEventListener("dragleave", function(e) { _this.drag(e, false) }, false); |
|
this.box.addEventListener("dragover", noop, false); |
|
this.box.addEventListener("drop", function(e) { _this.drop(e) }, false); |
|
this.picker.addEventListener("change", function(e) { _this.drop(e) }) |
|
} |
|
|
|
Dropbox.prototype.drop = function(e) { |
|
this.drag(e, false); |
|
var files = (e.dataTransfer || e.target).files; |
|
for ( var i=0; i<files.length; i++ ) |
|
this.handle_file(files[i]); |
|
}; |
|
|
|
Dropbox.prototype.drag = function(e, active) { |
|
noop(e); |
|
this.box.className = active ? this.box.className += ' active' : this.box.className.replace(/ ?active/, ''); |
|
}; |
|
|
|
Dropbox.prototype.handle_file = function(file) { |
|
// Bad file type |
|
if ( this.mime_types && !file.type.match(this.mime_types) ) { |
|
alert(file.name + ' is not an allowed file type'); |
|
// Too large |
|
} else if ( this.max_size && (file.size / 1024 / 1024) > this.max_size ) { |
|
alert(file.name + ' is too large; maximum is ' + this.max_size.toFixed(2) + ' MB'); |
|
} else { |
|
file.label = this.add_label(file); |
|
// Enqueue it |
|
if ( this.max_concurrent > -1 && this.num_current_uploads >= this.max_concurrent ) |
|
this.queue.push(file); |
|
// Upload it |
|
else this.process_file(file); |
|
} |
|
}; |
|
|
|
Dropbox.prototype.process_file = function(file) { |
|
this.num_current_uploads += 1; |
|
var _this = this; |
|
var reader = new FileReader(); |
|
reader.onload = function(e) { |
|
var buffer = e.target.result; |
|
var buffer_view = new Uint8Array(buffer); |
|
_this.upload_file(file, buffer_view); |
|
} |
|
reader.readAsArrayBuffer(file); |
|
}; |
|
|
|
Dropbox.prototype.upload_file = function(file, file_contents) { |
|
var query_params = [['filename', file.name], ['mimetype', file.type], ['size', file.size]]; |
|
query_params.push(['preventCache', rand(30)]); // Chrome fix - it caches regardless of Cache-Control and Pragma headers |
|
var query_string = query_params.map(function(p) { return p[0] + '=' + encodeURIComponent(p[1]) }).join('&'); |
|
var upload_url = this.url + '?' + query_string; |
|
|
|
var _this = this; |
|
var xhr = new XMLHttpRequest(); |
|
if ( xhr.upload ) xhr.upload.addEventListener('progress', function(e) { _this.handle_upload_progress(e, file.label) }, false) |
|
xhr.open('POST', upload_url); |
|
xhr.setRequestHeader('Content-Type', 'text/plain'); // Safari fix - it always sends application/x-www-form-urlencoded |
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); |
|
xhr.setRequestHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); |
|
xhr.setRequestHeader('Pragma', 'no-cache'); |
|
xhr.onreadystatechange = function(e) { |
|
if ( xhr.readyState === 4 ) { |
|
// Success |
|
if (xhr.status === 200) { |
|
file.label.getElementsByClassName('percent')[0].innerHTML = '100%'; |
|
if ( _this.success ) _this.success(file, xhr.responseText); |
|
// Error |
|
} else { |
|
file.label.getElementsByClassName('percent')[0].innerHTML = 'Error'; |
|
if ( _this.error ) _this.error(file, xhr.statusText, xhr.responseText); |
|
} |
|
// Pop next upload off the queue |
|
_this.num_current_uploads -= 1; |
|
if ( _this.queue.length > 0 ) _this.process_file(_this.queue.shift()) |
|
// Remove the label in 1 second |
|
setTimeout(function() { _this.uploads.removeChild(file.label) }, 1000); |
|
} |
|
} |
|
xhr.send(file_contents) |
|
}; |
|
|
|
Dropbox.prototype.handle_upload_progress = function(e, label) { |
|
if ( e.lengthComputable ) { |
|
var progress = label.getElementsByTagName('progress')[0]; |
|
progress.setAttribute('value', e.loaded); |
|
progress.setAttribute('max', e.total); |
|
label.getElementsByClassName('percent')[0].innerHTML = ((e.loaded / e.total) * 100).toFixed(0) + '%'; |
|
} |
|
}; |
|
|
|
Dropbox.prototype.add_label = function(file) { |
|
var size = (file.size / 1024 / 1024).toFixed(2); |
|
var label = document.createElement('div'); |
|
label.setAttribute('class', 'upload-progress'); |
|
label.innerHTML = '<progress value="0" max="100"></progress> <span class="desc">' + file.name + ' - <span class="percent">0%</span> (' + size + ' MB)</span>'; |
|
this.uploads.insertBefore(label, null); |
|
return label; |
|
}; |
|
|
|
function rand(n) { |
|
var str = '', possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
|
for ( var i = 0; i < n; i++ ) { |
|
str += possible.charAt(Math.floor(Math.random() * possible.length)); |
|
} |
|
return str; |
|
} |
|
|
|
function noop(e) { |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
} |