/** * TripalUploadFile Object */ (function($) { "use strict"; /** * The constructor function. */ var TripalUploadFile = function (file, options) { this.file = file; this.options = options; this.file_size = file.size; this.chunk_size = (1024 * 2000); // 2024MB this.total_chunks = ((this.file.size % this.chunk_size == 0) ? Math.floor(this.file.size / this.chunk_size) : Math.floor(this.file.size / this.chunk_size) + 1); this.curr_chunk = 0; this.status = 'pending'; this.file_id = null; if ('mozSlice' in file) { this.slice_method = 'mozSlice'; } else if ('webkitSlice' in file) { this.slice_method = 'webkitSlice'; } else { this.slice_method = 'slice'; } var self = this; this.xhr = new XMLHttpRequest(); this.xhr.onload = function() { self._onChunkComplete(); } // Respond to changes in connection if ('onLine' in navigator) { window.addEventListener('online', function () {self._onConnectionFound}); window.addEventListener('offline', function () {self._onConnectionLost}); } // ------------------------------------------------------------------------ // Internal Methods // ------------------------------------------------------------------------ /** * */ this._upload = function() { // Cacluate the range for the current chunk var range_start = this.curr_chunk * this.chunk_size; var range_end = range_start + this.chunk_size; // If we've gone beyond the number of chunks then just quit. if (this.curr_chunk > this.total_chunks) { this._onChunkComplete(); return; } // Prevent range overflow if (this.range_end > this.file_size) { this.range_end = this.file_size; } var chunk = this.file[this.slice_method](range_start, range_end); var url = this.options.url + '/' + this.file.name + '/save/' + this.curr_chunk; this.xhr.open('PUT', url, true); this.xhr.overrideMimeType('application/octet-stream'); this.xhr.setRequestHeader('Content-Range', 'bytes ' + range_start + '-' + range_end + '/' + this.file_size); this.xhr.send(chunk); }; /** * Converts a file size into a human readable value. * Borrowed function from: * http://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable */ this._getReadableSize = function(bytes, si) { var thresh = si ? 1000 : 1024; if(Math.abs(bytes) < thresh) { return bytes + ' B'; } var units = si ? ['kB','MB','GB','TB','PB','EB','ZB','YB'] : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']; var u = -1; do { bytes /= thresh; ++u; } while(Math.abs(bytes) >= thresh && u < units.length - 1); return bytes.toFixed(1) + ' ' + units[u]; }; /** * Queries server to see what chunk the loading left off at. */ this._checkUpload = function() { var url = this.options.url + '/' + this.file.name + '/check/'; var self = this; $.ajax({ url : url, data : { 'module' : this.options['module'], 'chunk_size' : this.chunk_size, 'file_size' : this.file_size, }, success : function(data, textStatus, jqXHR) { if (data['status'] == 'failed') { self.status = 'failed'; self.updateStatus(); alert(data['message']); } else { self.curr_chunk = data['curr_chunk']; self.status = 'uploading'; self._upload(); self.updateStatus(); self.updateProgressBar(); } }, error : function(jqXHR, textStatus, errorThrown) { alert(errorThrown); self.curr_chunk = 0; self._upload(); } }); } /** * */ this._mergeChunks = function() { var url = this.options.url + '/' + this.file.name + '/merge'; var self = this; $.ajax({ url : url, data : { 'module' : this.options['module'], 'file_size' : this.file_size, }, success : function(data, textStatus, jqXHR) { if (data['status'] == 'completed') { self.file_id = data['file_id']; self.status = 'completed'; self.updateStatus(); } else { self.status = 'failed'; self.updateStatus(); alert(data['message']); } }, error : function() { self.status = 'failed'; self.updateStatus(); } }); } // ------------------------------------------------------------------------ // Event Handlers // ------------------------------------------------------------------------ this._onChunkComplete = function() { // If the curr_chunk and the total_chunks is the same then // we've reached the end. if (this.curr_chunk >= this.total_chunks) { this.updateStatus(); this._onUploadComplete(); return; } // Continue as long as we aren't paused if (this.status == 'uploading') { this._upload(); this.curr_chunk++; this.updateProgressBar(); } }; /** * */ this._onUploadComplete = function() { this.status = 'merging'; this._mergeChunks(); this.updateStatus(); }; /** * When a connection has been lost but reistablished then resume uploads. */ this._onConnectionFound = function() { this.resume(); }; /** * When a cnnection has been lost then pause uploads. */ this._onConnectionLost = function() { this.pause(); }; // ------------------------------------------------------------------------ // Public Methods // ------------------------------------------------------------------------ /** * */ this.getProgressBar = function() { var progress_id = this.options['progress']; return '
0%
'; }; /** * */ this.getLinks = function() { var links_id = this.options['links']; return ''; } this.getCategory = function() { return this.options['category']; } this.getIndex = function() { return this.options['category']; } this.getTName = function() { return this.options['tname']; } this.getFileName = function() { return this.file.name; } /** * */ this.getFileSize = function(readable) { if (readable) { return this._getReadableSize(this.file.size, true); } else { return this.file.size; } }; /** * Updates the links, status text and status bar. */ this.updateStatus = function() { var progress_id = this.options['progress']; // Add the progress text. $('#' + progress_id).html(''); if (this.status == 'cancelled') { $("", { 'text' : 'Cancelled', }).appendTo('#' + progress_id) } else if (this.status == 'checking') { $("", { 'text' : 'Checking...', }).appendTo('#' + progress_id) } else if (this.status == 'merging') { $("", { 'text' : 'Processing...', }).appendTo('#' + progress_id) } else if (this.status == 'failed') { $("", { 'text' : 'Failed', }).appendTo('#' + progress_id) } else if (this.status == 'completed') { $("", { 'text' : 'Complete', }).appendTo('#' + progress_id) // Set the parent's target field. var parent = self.options['parent']; var tname = self.options['tname']; var category = self.options['category']; parent.setTarget(tname); } else if (this.status == 'paused') { $("", { 'text' : 'Paused', }).appendTo('#' + progress_id) } // Add a throbber if the status is uploading if (this.status == 'uploading' || this.status == 'checking' || this.status == 'merging') { $("", { 'src': tripal_path + '/theme/images/ajax-loader.gif', 'class' : 'tripal-uploader-chunked-file-progress-throbber', }).appendTo('#' + progress_id); } // Add the appropriate links. var links_id = this.options['links']; var category = this.options['category']; $('#' + links_id).html(''); if (this.status == 'cancelled') { $("", { 'id': links_id + '-pending', 'class': category + '-pending', 'href': 'javascript:void(0);', 'text': 'Restore', }).appendTo('#' + links_id); $('#' + links_id + '-pending').click(function() { self.pending(); }) } if (this.status == 'pending') { $("", { 'id': links_id + '-cancel', 'class': category + '-cancel', 'href': 'javascript:void(0);', 'text': 'Cancel', }).appendTo('#' + links_id); $('#' + links_id + '-cancel').click(function() { self.cancel(); }) } if (this.status == 'uploading') { $("", { 'id': links_id + '-pause', 'class': category + '-pause', 'href': 'javascript:void(0);', 'text': 'Pause', }).appendTo('#' + links_id); $('#' + links_id + '-pause').click(function() { self.pause(); }) } if (this.status == 'paused') { $("", { 'id': links_id + '-resume', 'class': category + '-resume', 'href': 'javascript:void(0);', 'text': 'Resume', }).appendTo('#' + links_id); $('#' + links_id + '-resume').click(function() { self.resume(); }) } // Add the remove link. $("", { 'id': links_id + '-remove', 'class': category + '-remove', 'href': 'javascript:void(0);', 'text': ' Remove', }).appendTo('#' + links_id); $('#' + links_id + '-remove').click(function() { var parent = self.options['parent']; var index = self.options['index']; var tname = self.options['tname']; var category = self.options['category']; parent.removeFile(tname, category, index); parent.updateTable(category); // Unset the parent's target field. parent.setTarget(tname); self.cancel(); }) } /** * Updates the status bar progress only. */ this.updateProgressBar = function() { var progress_id = this.options['progress']; var progress = (this.curr_chunk / this.total_chunks) * 100; var self = this; // Calculate the amount of the file transferred. var size_transferred = this.curr_chunk * this.chunk_size; size_transferred = this._getReadableSize(size_transferred, true); if (this.status == 'uploading') { $('#' + progress_id).html(''); $("", { 'class': 'tripal-uploader-chunked-file-progress-label', 'text': size_transferred, }).appendTo($("
", { 'id': progress_id + '-bar', 'class': 'tripal-uploader-chunked-file-progress', 'width': progress + '%' }).appendTo($("
", { 'id': progress_id + '-box', 'class': 'tripal-uploader-chunked-file-progress', }).appendTo('#' + progress_id))); } if (this.status == 'uploading' || this.status == 'checking' || this.status == 'merging') { $("", { 'src': tripal_path + '/theme/images/ajax-loader.gif', 'class' : 'tripal-uploader-chunked-file-progress-throbber', }).appendTo('#' + progress_id); } }; /** * */ this.cancel = function() { this.status = 'cancelled'; this.updateStatus(); } /** * */ this.pending = function() { this.status = 'pending'; this.updateStatus(); } /** * */ this.start = function() { if (this.status == 'pending') { // Change the status to checking. The first thing we'll // do is see what's already present on the server. this.status = 'checking'; this.curr_chunk = this._checkUpload(); } }; /** * */ this.pause = function() { this.status = 'paused'; this.updateStatus(); }; /** * */ this.resume = function() { this.status = 'uploading'; this.updateStatus(); this.updateProgressBar(); this._upload(); }; }; // Export the objects to the window for use in other JS files. window.TripalUploadFile = TripalUploadFile; })(jQuery);