Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/core/DragAndDropFileUpload.js
Show First 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | start : function() { | ||||
if (!spec.name) { | if (!spec.name) { | ||||
spec.name = 'pasted_file'; | spec.name = 'pasted_file'; | ||||
} | } | ||||
this._sendRequest(spec); | this._sendRequest(spec); | ||||
} | } | ||||
})); | })); | ||||
} | } | ||||
}, | }, | ||||
_sendRequest : function(spec) { | _sendRequest : function(spec) { | ||||
var file = new JX.PhabricatorFileUpload() | var file = new JX.PhabricatorFileUpload() | ||||
.setRawFileObject(spec) | |||||
.setName(spec.name) | .setName(spec.name) | ||||
.setTotalBytes(spec.size) | .setTotalBytes(spec.size); | ||||
.setStatus('uploading') | |||||
var threshold = this.getChunkThreshold(); | |||||
if (threshold && (file.getTotalBytes() > threshold)) { | |||||
// This is a large file, so we'll go through allocation so we can | |||||
// pick up support for resume and chunking. | |||||
this._allocateFile(file); | |||||
} else { | |||||
// If this file is smaller than the chunk threshold, skip the round | |||||
// trip for allocation and just upload it directly. | |||||
this._sendDataRequest(file); | |||||
} | |||||
}, | |||||
_allocateFile: function(file) { | |||||
file | |||||
.setStatus('allocate') | |||||
.update(); | .update(); | ||||
this.invoke('willUpload', file); | var alloc_uri = this._getUploadURI(file) | ||||
.setQueryParam('allocate', 1); | |||||
new JX.Workflow(alloc_uri) | |||||
.setHandler(JX.bind(this, this._didAllocateFile, file)) | |||||
.start(); | |||||
}, | |||||
var up_uri = JX.$U(this.getURI()) | _getUploadURI: function(file) { | ||||
var uri = JX.$U(this.getURI()) | |||||
.setQueryParam('name', file.getName()) | .setQueryParam('name', file.getName()) | ||||
.setQueryParam('__upload__', 1); | .setQueryParam('length', file.getTotalBytes()); | ||||
if (this.getViewPolicy()) { | if (this.getViewPolicy()) { | ||||
up_uri.setQueryParam('viewPolicy', this.getViewPolicy()); | uri.setQueryParam('viewPolicy', this.getViewPolicy()); | ||||
} | |||||
if (file.getAllocatedPHID()) { | |||||
uri.setQueryParam('phid', file.getAllocatedPHID()); | |||||
} | |||||
return uri; | |||||
}, | |||||
_didAllocateFile: function(file, r) { | |||||
var phid = r.phid; | |||||
var upload = r.upload; | |||||
if (!upload) { | |||||
if (phid) { | |||||
this._completeUpload(file, r); | |||||
} else { | |||||
this._failUpload(file, r); | |||||
} | |||||
return; | |||||
} else { | |||||
if (phid) { | |||||
// Start or resume a chunked upload. | |||||
file.setAllocatedPHID(phid); | |||||
this._loadChunks(file); | |||||
} else { | |||||
// Proceed with non-chunked upload. | |||||
this._sendDataRequest(file); | |||||
} | |||||
} | |||||
}, | |||||
_loadChunks: function(file) { | |||||
file | |||||
.setStatus('chunks') | |||||
.update(); | |||||
var chunks_uri = this._getUploadURI(file) | |||||
.setQueryParam('querychunks', 1); | |||||
new JX.Workflow(chunks_uri) | |||||
.setHandler(JX.bind(this, this._didLoadChunks, file)) | |||||
.start(); | |||||
}, | |||||
_didLoadChunks: function(file, r) { | |||||
file.setChunks(r); | |||||
this._uploadNextChunk(file); | |||||
}, | |||||
_uploadNextChunk: function(file) { | |||||
var chunks = file.getChunks(); | |||||
var chunk; | |||||
for (var ii = 0; ii < chunks.length; ii++) { | |||||
chunk = chunks[ii]; | |||||
if (!chunk.complete) { | |||||
this._readChunk( | |||||
file, | |||||
chunk, | |||||
JX.bind(this, this._didReadChunk, file, chunk)); | |||||
break; | |||||
} | |||||
} | |||||
}, | |||||
_readChunk: function(file, chunk, callback) { | |||||
var reader = new FileReader(); | |||||
var blob = file.getRawFileObject().slice(chunk.byteStart, chunk.byteEnd); | |||||
reader.onload = function() { | |||||
callback(reader.result); | |||||
}; | |||||
reader.onerror = function() { | |||||
this._failUpload(file, {error: reader.error.message}); | |||||
}; | |||||
reader.readAsBinaryString(blob); | |||||
}, | |||||
_didReadChunk: function(file, chunk, data) { | |||||
file | |||||
.setStatus('upload') | |||||
.update(); | |||||
var chunkup_uri = this._getUploadURI(file) | |||||
.setQueryParam('uploadchunk', 1) | |||||
.setQueryParam('__upload__', 1) | |||||
.setQueryParam('byteStart', chunk.byteStart) | |||||
.toString(); | |||||
var callback = JX.bind(this, this._didUploadChunk, file, chunk); | |||||
var req = new JX.Request(chunkup_uri, callback); | |||||
var seen_bytes = 0; | |||||
var onprogress = JX.bind(this, function(progress) { | |||||
file | |||||
.addUploadedBytes(progress.loaded - seen_bytes) | |||||
.update(); | |||||
seen_bytes = progress.loaded; | |||||
this.invoke('progress', file); | |||||
}); | |||||
req.listen('error', JX.bind(this, this._onUploadError, req, file)); | |||||
req.listen('uploadprogress', onprogress); | |||||
req | |||||
.setRawData(data) | |||||
.send(); | |||||
}, | |||||
_didUploadChunk: function(file, chunk, r) { | |||||
file.didCompleteChunk(chunk); | |||||
if (r.complete) { | |||||
this._completeUpload(file, r); | |||||
} else { | |||||
this._uploadNextChunk(file); | |||||
} | } | ||||
}, | |||||
up_uri = up_uri.toString(); | _sendDataRequest: function(file) { | ||||
file | |||||
.setStatus('uploading') | |||||
.update(); | |||||
this.invoke('willUpload', file); | |||||
var up_uri = this._getUploadURI(file) | |||||
.setQueryParam('__upload__', 1) | |||||
.toString(); | |||||
var onupload = JX.bind(this, function(r) { | var onupload = JX.bind(this, function(r) { | ||||
if (r.error) { | if (r.error) { | ||||
this._failUpload(file, r); | |||||
} else { | |||||
this._completeUpload(file, r); | |||||
} | |||||
}); | |||||
var req = new JX.Request(up_uri, onupload); | |||||
var onprogress = JX.bind(this, function(progress) { | |||||
file | file | ||||
.setStatus('error') | .setTotalBytes(progress.total) | ||||
.setError(r.error) | .setUploadedBytes(progress.loaded) | ||||
.update(); | .update(); | ||||
this.invoke('didError', file); | this.invoke('progress', file); | ||||
} else { | }); | ||||
req.listen('error', JX.bind(this, this._onUploadError, req, file)); | |||||
req.listen('uploadprogress', onprogress); | |||||
req | |||||
.setRawData(file.getRawFileObject()) | |||||
.send(); | |||||
}, | |||||
_completeUpload: function(file, r) { | |||||
file | file | ||||
.setID(r.id) | .setID(r.id) | ||||
.setPHID(r.phid) | .setPHID(r.phid) | ||||
.setURI(r.uri) | .setURI(r.uri) | ||||
.setMarkup(r.html) | .setMarkup(r.html) | ||||
.setStatus('done') | .setStatus('done') | ||||
.update(); | .update(); | ||||
this.invoke('didUpload', file); | this.invoke('didUpload', file); | ||||
} | }, | ||||
}); | |||||
var req = new JX.Request(up_uri, onupload); | _failUpload: function(file, r) { | ||||
file | |||||
.setStatus('error') | |||||
.setError(r.error) | |||||
.update(); | |||||
var onerror = JX.bind(this, function(error) { | this.invoke('didError', file); | ||||
}, | |||||
_onUploadError: function(file, req, error) { | |||||
file.setStatus('error'); | file.setStatus('error'); | ||||
if (error) { | if (error) { | ||||
file.setError(error.code + ': ' + error.info); | file.setError(error.code + ': ' + error.info); | ||||
} else { | } else { | ||||
var xhr = req.getTransport(); | var xhr = req.getTransport(); | ||||
if (xhr.responseText) { | if (xhr.responseText) { | ||||
file.setError('Server responded: ' + xhr.responseText); | file.setError('Server responded: ' + xhr.responseText); | ||||
} | } | ||||
} | } | ||||
file.update(); | file.update(); | ||||
this.invoke('didError', file); | this.invoke('didError', file); | ||||
}); | |||||
var onprogress = JX.bind(this, function(progress) { | |||||
file | |||||
.setTotalBytes(progress.total) | |||||
.setUploadedBytes(progress.loaded) | |||||
.update(); | |||||
this.invoke('progress', file); | |||||
}); | |||||
req.listen('error', onerror); | |||||
req.listen('uploadprogress', onprogress); | |||||
req | |||||
.setRawData(spec) | |||||
.send(); | |||||
} | } | ||||
}, | }, | ||||
properties: { | properties: { | ||||
URI : null, | URI: null, | ||||
activatedClass : null, | activatedClass: null, | ||||
viewPolicy : null | viewPolicy: null, | ||||
chunkThreshold: null | |||||
} | } | ||||
}); | }); |