file-upload.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * This view presents a control to upload files to the current dataset, and a recently-uploaded
  3. * log to track what has been done.
  4. */
  5. define(
  6. function( require ) {
  7. var Backbone = require( "backbone" ),
  8. _ = require( "underscore" ),
  9. sprintf = require( "sprintf" ),
  10. fui = require( "app/fui" ),
  11. fileUploadTemplate = require( "plugins/text!app/templates/file-upload.tpl" ),
  12. UploadableFileView = require( "app/views/uploadable-file" );
  13. var FileUploadView = Backbone.Marionette.CompositeView.extend( {
  14. initialize: function(){
  15. _.bindAll( this,
  16. "onUploadAdd", "onRemoveUpload", "onUploadAll",
  17. "onProgress", "onUploadDone", "onUploadFail",
  18. "onPerformUpload" );
  19. fui.vent.on( "upload.remove", this.onRemoveUpload );
  20. fui.vent.on( "upload.perform", this.onPerformUpload );
  21. },
  22. template: _.template( fileUploadTemplate ),
  23. el: "#file-upload",
  24. itemViewContainer: "ul",
  25. itemView: UploadableFileView,
  26. collection: new Backbone.Collection(),
  27. ui: {
  28. fileUpload: '#fileuploadForm',
  29. graphLabel: '.graph-label input'
  30. },
  31. events: {
  32. "click .action-upload-all": "onUploadAll"
  33. },
  34. templateHelpers: {
  35. },
  36. onRender: function() {
  37. // initialise the file upload widget
  38. this.ui.fileUpload.fileupload( {
  39. dataType: 'json',
  40. add: this.onUploadAdd,
  41. progress: this.onProgress
  42. } );
  43. },
  44. /** User has added a file */
  45. onUploadAdd: function( e, data ) {
  46. var collection = this.collection;
  47. var self = this;
  48. _.each( data.files, function( file ) {
  49. file.readableFileSize = self.readableFileSize( file );
  50. collection.add( new Backbone.Model( {file: file} ) );
  51. } );
  52. this.enableUploadAll( true );
  53. },
  54. /** Return file size in bytes in a human-readable form */
  55. readableFileSize: function( file ) {
  56. var k = 1024;
  57. var m = k * k;
  58. if (file.size >= m) {
  59. return sprintf( "%.1fmb", file.size / m );
  60. }
  61. else if (file.size >= k) {
  62. return sprintf( "%.1fkb", file.size / k );
  63. }
  64. else {
  65. return sprintf( "%d bytes", file.size );
  66. }
  67. },
  68. /** User has requested to remove a selected upload */
  69. onRemoveUpload: function( file ) {
  70. this.collection.remove( file );
  71. this.enableUploadAll( this.collection.size() > 0 )
  72. },
  73. /** User has requested to perform a selected upload */
  74. onPerformUpload: function( model ) {
  75. this.loadAll = false;
  76. this.uploadFileFromModel( model );
  77. },
  78. /** Return the list of active files waiting for upload */
  79. activeFiles: function() {
  80. var activeModels = _.filter( this.collection.models, function( m ) {
  81. return !m.completed;
  82. } );
  83. return _.map( activeModels, function( m ) {
  84. return m.get( "file" );
  85. } );
  86. },
  87. /** User action to upload all active files */
  88. onUploadAll: function( e ) {
  89. if (e) {
  90. e.preventDefault();
  91. }
  92. this.$el.find( ".file-description .action" ).attr( 'disabled', 'disabled' );
  93. this.loadNextAvailableFile( true );
  94. },
  95. /** Load the next file in the sequence */
  96. loadNextAvailableFile: function( all ) {
  97. this.loadAll = all;
  98. var files = this.activeFiles();
  99. if (files.length > 0) {
  100. this.uploadFile( files.shift() );
  101. }
  102. else {
  103. this.enableUploadAll( false );
  104. }
  105. },
  106. /** Upload the given file to the server */
  107. uploadFile: function( file ) {
  108. this.uploadFileFromModel( this.collection.findWhere( {file: file} ) );
  109. },
  110. /** Upload the file attached to a given model */
  111. uploadFileFromModel: function( model ) {
  112. this.cacheModel( model );
  113. var file = model.get( "file" );
  114. var ds = fui.models.fusekiServer.selectedDataset();
  115. var url = ds.uploadURL( this.destinationGraphName() );
  116. this.ui.fileUpload.fileupload( 'send', {
  117. files: [file],
  118. url: url
  119. })
  120. .success( this.onUploadDone )
  121. .fail( this.onUploadFail );
  122. },
  123. /** Return the selected graph name, or 'default' */
  124. destinationGraphName: function() {
  125. var gName = this.ui.graphLabel.val();
  126. return (gName && gName !== "") ? gName : 'default';
  127. },
  128. /** Callback on progress against an upload */
  129. onProgress: function( e, data ) {
  130. var complete = Math.round( 100.0 * data.loaded/ data.total);
  131. $(this.activeView.el).find( ".progress-bar" )
  132. .attr( 'aria-valuenow', complete )
  133. .css( 'width', sprintf( "%s%%", complete ));
  134. },
  135. /** Callback on successful completion */
  136. onUploadDone: function( data, response ) {
  137. var label = "Data file was empty.";
  138. if ((data.count) > 0) {
  139. var s = (data.count === 1) ? "" : "s";
  140. label = sprintf( "%d %s%s", data.count, ((data.tripleCount > 0) ? "triple" : "quad"), s );
  141. }
  142. this.displayUploadResult( sprintf( "<p><small>Result: <strong>success</strong>. %s</small></p>", label ), "" );
  143. if (this.loadAll) {
  144. this.loadNextAvailableFile( true );
  145. }
  146. },
  147. /** Callback on error */
  148. onUploadFail: function( jqxhr, error, msg ) {
  149. $(this.activeView.el).find( ".progress-bar" )
  150. .removeClass( "progress-bar-success" )
  151. .addClass( "progress-bar-warning" );
  152. this.displayUploadResult( sprintf( "<p><small>Result: <strong>failed</strong> with message &quot;%s&quot;</small></p>", msg ), "text-danger" );
  153. if (this.loadAll) {
  154. this.loadNextAvailableFile( true );
  155. }
  156. },
  157. /** Show the result of uploading a file */
  158. displayUploadResult: function( html, cls ) {
  159. var el = $(this.activeView.el);
  160. this.activeModel.completed = true;
  161. el.find( ".action" ).hide();
  162. el.find( ".result" )
  163. .addClass( cls )
  164. .append( html );
  165. },
  166. /** Cache the currently active model so that we can attach actions to the corresponding view */
  167. cacheModel: function( model ) {
  168. this.activeModel = model;
  169. this.activeView = this.children.findByModel( model );
  170. },
  171. /** Enable or disable the upload all button */
  172. enableUploadAll: function( enabled ) {
  173. if (enabled) {
  174. $(".action-upload-all").removeAttr( 'disabled' );
  175. }
  176. else {
  177. $(".action-upload-all").attr( 'disabled', 'disabled' );
  178. }
  179. }
  180. });
  181. return FileUploadView;
  182. }
  183. );