diff --git a/cms/static/js/models/active_video_upload.js b/cms/static/js/models/active_video_upload.js index 84fa3b0c96..57e7ee993d 100644 --- a/cms/static/js/models/active_video_upload.js +++ b/cms/static/js/models/active_video_upload.js @@ -19,7 +19,8 @@ define( var ActiveVideoUpload = Backbone.Model.extend( { defaults: { - status: statusStrings.STATUS_QUEUED + status: statusStrings.STATUS_QUEUED, + progress: 0 } }, statusStrings diff --git a/cms/static/js/spec/views/active_video_upload_list_spec.js b/cms/static/js/spec/views/active_video_upload_list_spec.js index 4c36cd5bc0..1412074361 100644 --- a/cms/static/js/spec/views/active_video_upload_list_spec.js +++ b/cms/static/js/spec/views/active_video_upload_list_spec.js @@ -131,39 +131,49 @@ define( ); }); - it("should display status", function() { + it("should display upload status and progress", function() { var spec = this; expect(this.$uploadElems.length).toEqual(caseInfo.numFiles); this.$uploadElems.each(function(i, uploadElem) { var $uploadElem = $(uploadElem); + var queued = i >= concurrentUploadLimit; expect($.trim($uploadElem.find(".video-detail-name").text())).toEqual( fileNames[i] ); expect($.trim($uploadElem.find(".video-detail-status").text())).toEqual( - i >= concurrentUploadLimit ? + queued ? ActiveVideoUpload.STATUS_QUEUED : ActiveVideoUpload.STATUS_UPLOADING ); - expect($uploadElem.find(".success").length).toEqual(0); - expect($uploadElem.find(".error").length).toEqual(0); + expect($uploadElem.find(".video-detail-progress").attr("value")).toEqual(0); + expect($uploadElem).not.toHaveClass("success"); + expect($uploadElem).not.toHaveClass("error"); + expect($uploadElem.hasClass("queued")).toEqual(queued); }); }); + // TODO: test progress update; the libraries we are using to mock ajax + // do not currently support progress events. If we upgrade to Jasmine + // 2.0, the latest version of jasmine-ajax (mock-ajax.js) does have the + // necessary support. + _.each( [ { desc: "completion", responseStatus: 204, statusText: ActiveVideoUpload.STATUS_COMPLETED, - presentSelector: ".success", - absentSelector: ".error" + progressValue: 1, + presentClass: "success", + absentClass: "error" }, { desc: "failure", responseStatus: 500, statusText: ActiveVideoUpload.STATUS_FAILED, - presentSelector: ".error", - absentSelector: ".success" + progressValue: 0, + presentClass: "error", + absentClass: "success" }, ], function(subCaseInfo) { @@ -172,14 +182,17 @@ define( getSentRequests()[0].response({status: subCaseInfo.responseStatus}); }); - it("should update status", function() { + it("should update status and progress", function() { var $uploadElem = this.view.$(".active-video-upload:first"); expect($uploadElem.length).toEqual(1); expect($.trim($uploadElem.find(".video-detail-status").text())).toEqual( subCaseInfo.statusText ); - expect($uploadElem.find(subCaseInfo.presentSelector).length).toEqual(1); - expect($uploadElem.find(subCaseInfo.absentSelector).length).toEqual(0); + expect( + $uploadElem.find(".video-detail-progress").attr("value") + ).toEqual(subCaseInfo.progressValue); + expect($uploadElem).toHaveClass(subCaseInfo.presentClass); + expect($uploadElem).not.toHaveClass(subCaseInfo.absentClass); }); it("should not trigger the global AJAX error handler", function() { @@ -195,6 +208,7 @@ define( expect($.trim($uploadElem.find(".video-detail-status").text())).toEqual( ActiveVideoUpload.STATUS_UPLOADING ); + expect($uploadElem).not.toHaveClass("queued"); }); } }); diff --git a/cms/static/js/views/active_video_upload.js b/cms/static/js/views/active_video_upload.js index aed8c0a735..80db48c196 100644 --- a/cms/static/js/views/active_video_upload.js +++ b/cms/static/js/views/active_video_upload.js @@ -2,6 +2,13 @@ define( ["js/models/active_video_upload", "js/views/baseview"], function(ActiveVideoUpload, BaseView) { "use strict"; + + var STATUS_CLASSES = [ + {status: ActiveVideoUpload.STATUS_QUEUED, cls: "queued"}, + {status: ActiveVideoUpload.STATUS_COMPLETED, cls: "success"}, + {status: ActiveVideoUpload.STATUS_FAILED, cls: "error"} + ]; + var ActiveVideoUploadView = BaseView.extend({ tagName: "li", className: "active-video-upload", @@ -12,11 +19,15 @@ define( }, render: function() { - this.$el.html(this.template(this.model.attributes)); - var $statusEl = this.$el.find(".video-detail-status"); + var $el = this.$el; + $el.html(this.template(this.model.attributes)); var status = this.model.get("status"); - $statusEl.toggleClass("success", status == ActiveVideoUpload.STATUS_COMPLETED); - $statusEl.toggleClass("error", status == ActiveVideoUpload.STATUS_FAILED); + _.each( + STATUS_CLASSES, + function(statusClass) { + $el.toggleClass(statusClass.cls, status == statusClass.status); + } + ); return this; }, }); diff --git a/cms/static/js/views/active_video_upload_list.js b/cms/static/js/views/active_video_upload_list.js index 1fa5daa795..46d9fd7f05 100644 --- a/cms/static/js/views/active_video_upload_list.js +++ b/cms/static/js/views/active_video_upload_list.js @@ -36,6 +36,7 @@ define( dragover: this.dragover.bind(this), add: this.fileUploadAdd.bind(this), send: this.fileUploadSend.bind(this), + progress: this.fileUploadProgress.bind(this), done: this.fileUploadDone.bind(this), fail: this.fileUploadFail.bind(this) }); @@ -124,12 +125,22 @@ define( this.collection.get(cid).set("status", status); }, + // progress should be a number between 0 and 1 (inclusive) + setProgress: function(cid, progress) { + this.collection.get(cid).set("progress", progress); + }, + fileUploadSend: function(event, data) { this.setStatus(data.cid, ActiveVideoUpload.STATUS_UPLOADING); }, + fileUploadProgress: function(event, data) { + this.setProgress(data.cid, data.loaded / data.total); + }, + fileUploadDone: function(event, data) { this.setStatus(data.cid, ActiveVideoUpload.STATUS_COMPLETED); + this.setProgress(data.cid, 1); }, fileUploadFail: function(event, data) { diff --git a/cms/static/sass/views/_video-upload.scss b/cms/static/sass/views/_video-upload.scss index b9aa424813..509d0de47c 100644 --- a/cms/static/sass/views/_video-upload.scss +++ b/cms/static/sass/views/_video-upload.scss @@ -68,20 +68,62 @@ .video-detail-status { @include font-size(12); @include line-height(12); + } - &.error { - color: $color-error; - } + .video-detail-progress { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + margin-bottom: ($baseline/2); + border: none; + width: 100%; + height: ($baseline/4); + } - &.success { - color: $color-ready; - } + .video-detail-progress::-webkit-progress-bar { + background-color: $white; + } + + // Sadly, these rules must be separate or both Chrome and Firefox break + .video-detail-progress::-webkit-progress-value { + background-color: $color-ready; + } + + .video-detail-progress::-moz-progress-bar { + background-color: $color-ready; } &:hover { @include transition(all $tmg-f3); background: $white; } + + &.queued { + .video-detail-progress { + visibility: hidden; + } + } + + &.error { + .video-detail-status { + color: $color-error; + } + + // Sadly, these rules must be separate or both Chrome and Firefox break + .video-detail-progress::-webkit-progress-value { + background-color: $color-error; + } + + .video-detail-progress::-moz-progress-bar { + background-color: $color-error; + } + } + + &.success { + .video-detail-status { + color: $color-ready; + } + } } } } diff --git a/cms/templates/js/active-video-upload.underscore b/cms/templates/js/active-video-upload.underscore index 132de53616..d8b3f645b7 100644 --- a/cms/templates/js/active-video-upload.underscore +++ b/cms/templates/js/active-video-upload.underscore @@ -1,2 +1,3 @@
<%- gettext(status) %>