Files
shiny/srcjs/input_binding_fileinput.js
2015-03-30 16:17:04 -05:00

208 lines
6.3 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');