mirror of
https://github.com/rstudio/shiny.git
synced 2026-02-05 04:05:06 -05:00
208 lines
5.9 KiB
JavaScript
208 lines
5.9 KiB
JavaScript
var IE8FileUploader = function(shinyapp, id, fileEl) {
|
|
this.shinyapp = shinyapp;
|
|
this.id = id;
|
|
this.fileEl = fileEl;
|
|
this.beginUpload();
|
|
};
|
|
(function() {
|
|
this.beginUpload = function() {
|
|
var self = this;
|
|
// Create invisible frame
|
|
var iframeId = 'shinyupload_iframe_' + this.id;
|
|
this.iframe = document.createElement('iframe');
|
|
this.iframe.id = iframeId;
|
|
this.iframe.name = iframeId;
|
|
this.iframe.setAttribute('style', 'position: fixed; top: 0; left: 0; width: 0; height: 0; border: none');
|
|
$('body').append(this.iframe);
|
|
var iframeDestroy = function() {
|
|
// Forces Shiny to flushReact, flush outputs, etc. Without this we get
|
|
// invalidated reactives, but observers don't actually execute.
|
|
self.shinyapp.makeRequest('uploadieFinish', [], function(){}, function(){});
|
|
$(self.iframe).remove();
|
|
};
|
|
if (this.iframe.attachEvent) {
|
|
this.iframe.attachEvent('onload', iframeDestroy);
|
|
} else {
|
|
this.iframe.onload = iframeDestroy;
|
|
}
|
|
|
|
this.form = document.createElement('form');
|
|
this.form.method = 'POST';
|
|
this.form.setAttribute('enctype', 'multipart/form-data');
|
|
this.form.action = "session/" + encodeURI(this.shinyapp.config.sessionId) +
|
|
"/uploadie/" + encodeURI(this.id);
|
|
this.form.id = 'shinyupload_form_' + this.id;
|
|
this.form.target = iframeId;
|
|
$(this.form).insertAfter(this.fileEl).append(this.fileEl);
|
|
this.form.submit();
|
|
};
|
|
}).call(IE8FileUploader.prototype);
|
|
|
|
var FileUploader = function(shinyapp, id, files) {
|
|
this.shinyapp = shinyapp;
|
|
this.id = id;
|
|
FileProcessor.call(this, files);
|
|
};
|
|
$.extend(FileUploader.prototype, FileProcessor.prototype);
|
|
(function() {
|
|
this.makeRequest = function(method, args, onSuccess, onFailure, blobs) {
|
|
this.shinyapp.makeRequest(method, args, onSuccess, onFailure, blobs);
|
|
};
|
|
this.onBegin = function(files, cont) {
|
|
var self = this;
|
|
|
|
// Reset progress bar
|
|
this.$setError(null);
|
|
this.$setActive(true);
|
|
this.$setVisible(true);
|
|
this.onProgress(null, 0);
|
|
|
|
this.totalBytes = 0;
|
|
this.progressBytes = 0;
|
|
$.each(files, function(i, file) {
|
|
self.totalBytes += file.size;
|
|
});
|
|
|
|
var fileInfo = $.map(files, function(file, i) {
|
|
return {
|
|
name: file.name,
|
|
size: file.size,
|
|
type: file.type
|
|
};
|
|
});
|
|
|
|
this.makeRequest(
|
|
'uploadInit', [fileInfo],
|
|
function(response) {
|
|
self.jobId = response.jobId;
|
|
self.uploadUrl = response.uploadUrl;
|
|
cont();
|
|
},
|
|
function(error) {
|
|
self.onError(error);
|
|
});
|
|
};
|
|
this.onFile = function(file, cont) {
|
|
var self = this;
|
|
this.onProgress(file, 0);
|
|
|
|
$.ajax(this.uploadUrl, {
|
|
type: 'POST',
|
|
cache: false,
|
|
xhr: function() {
|
|
var xhrVal = $.ajaxSettings.xhr();
|
|
if (xhrVal.upload) {
|
|
xhrVal.upload.onprogress = function(e) {
|
|
if (e.lengthComputable) {
|
|
self.onProgress(
|
|
file,
|
|
(self.progressBytes + e.loaded) / self.totalBytes);
|
|
}
|
|
};
|
|
}
|
|
return xhrVal;
|
|
},
|
|
data: file,
|
|
processData: false,
|
|
success: function() {
|
|
self.progressBytes += file.size;
|
|
cont();
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
self.onError(jqXHR.responseText || textStatus);
|
|
}
|
|
});
|
|
};
|
|
this.onComplete = function() {
|
|
var self = this;
|
|
this.makeRequest(
|
|
'uploadEnd', [this.jobId, this.id],
|
|
function(response) {
|
|
self.$setActive(false);
|
|
self.onProgress(null, 1);
|
|
self.$bar().text('Upload complete');
|
|
},
|
|
function(error) {
|
|
self.onError(error);
|
|
});
|
|
this.$bar().text('Finishing upload');
|
|
};
|
|
this.onError = function(message) {
|
|
this.$setError(message || '');
|
|
this.$setActive(false);
|
|
};
|
|
this.onAbort = function() {
|
|
this.$setVisible(false);
|
|
};
|
|
this.onProgress = function(file, completed) {
|
|
this.$bar().width(Math.round(completed*100) + '%');
|
|
this.$bar().text(file ? file.name : '');
|
|
};
|
|
this.$container = function() {
|
|
return $('#' + $escape(this.id) + '_progress.shiny-file-input-progress');
|
|
};
|
|
this.$bar = function() {
|
|
return $('#' + $escape(this.id) + '_progress.shiny-file-input-progress .progress-bar');
|
|
};
|
|
this.$setVisible = function(visible) {
|
|
this.$container().css('visibility', visible ? 'visible' : 'hidden');
|
|
};
|
|
this.$setError = function(error) {
|
|
this.$bar().toggleClass('bar-danger', (error !== null));
|
|
if (error !== null) {
|
|
this.onProgress(null, 1);
|
|
this.$bar().text(error);
|
|
}
|
|
};
|
|
this.$setActive = function(active) {
|
|
this.$container().toggleClass('active', !!active);
|
|
};
|
|
}).call(FileUploader.prototype);
|
|
|
|
|
|
function uploadFiles(evt) {
|
|
// If previously selected files are uploading, abort that.
|
|
var el = $(evt.target);
|
|
var uploader = el.data('currentUploader');
|
|
if (uploader)
|
|
uploader.abort();
|
|
|
|
var files = evt.target.files;
|
|
var IE8 = (browser.isIE && browser.IEVersion === 8);
|
|
var id = fileInputBinding.getId(evt.target);
|
|
|
|
if (!IE8 && files.length === 0)
|
|
return;
|
|
|
|
// Start the new upload and put the uploader in 'currentUploader'.
|
|
if (IE8) {
|
|
/*jshint nonew:false */
|
|
new IE8FileUploader(exports.shinyapp, id, evt.target);
|
|
} else {
|
|
el.data('currentUploader', new FileUploader(exports.shinyapp, id, files));
|
|
}
|
|
}
|
|
|
|
var fileInputBinding = new InputBinding();
|
|
$.extend(fileInputBinding, {
|
|
find: function(scope) {
|
|
return $(scope).find('input[type="file"]');
|
|
},
|
|
getId: function(el) {
|
|
return InputBinding.prototype.getId.call(this, el) || el.name;
|
|
},
|
|
getValue: function(el) {
|
|
return null;
|
|
},
|
|
setValue: function(el, value) {
|
|
// Not implemented
|
|
},
|
|
subscribe: function(el, callback) {
|
|
$(el).on('change.fileInputBinding', uploadFiles);
|
|
},
|
|
unsubscribe: function(el) {
|
|
$(el).off('.fileInputBinding');
|
|
}
|
|
});
|
|
inputBindings.register(fileInputBinding, 'shiny.fileInputBinding');
|