diff --git a/cms/static/cms/js/spec/main.js b/cms/static/cms/js/spec/main.js
index 076c47376c..db82630e76 100644
--- a/cms/static/cms/js/spec/main.js
+++ b/cms/static/cms/js/spec/main.js
@@ -228,7 +228,6 @@
'coffee/spec/models/upload_spec',
'coffee/spec/views/course_info_spec',
'coffee/spec/views/metadata_edit_spec',
- 'coffee/spec/views/module_edit_spec',
'coffee/spec/views/textbook_spec',
'coffee/spec/views/upload_spec',
'js/spec/video/transcripts/utils_spec',
@@ -250,6 +249,7 @@
'js/spec/views/assets_spec',
'js/spec/views/baseview_spec',
'js/spec/views/container_spec',
+ 'js/spec/views/module_edit_spec',
'js/spec/views/paged_container_spec',
'js/spec/views/group_configuration_spec',
'js/spec/views/unit_outline_spec',
diff --git a/cms/static/coffee/spec/views/module_edit_spec.coffee b/cms/static/coffee/spec/views/module_edit_spec.coffee
deleted file mode 100644
index a631c4ddde..0000000000
--- a/cms/static/coffee/spec/views/module_edit_spec.coffee
+++ /dev/null
@@ -1,180 +0,0 @@
-define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit_helpers",
- "coffee/src/views/module_edit", "js/models/module_info", "xmodule"],
- ($, ViewUtils, edit_helpers, ModuleEdit, ModuleModel) ->
-
- describe "ModuleEdit", ->
- beforeEach ->
- @stubModule = new ModuleModel
- id: "stub-id"
-
- setFixtures """
-
- """
- edit_helpers.installEditTemplates(true);
- spyOn($, 'ajax').and.returnValue(@moduleData)
-
- @moduleEdit = new ModuleEdit(
- el: $(".component")
- model: @stubModule
- onDelete: jasmine.createSpy()
- )
-
- describe "class definition", ->
- it "sets the correct tagName", ->
- expect(@moduleEdit.tagName).toEqual("li")
-
- it "sets the correct className", ->
- expect(@moduleEdit.className).toEqual("component")
-
- describe "methods", ->
- describe "initialize", ->
- beforeEach ->
- spyOn(ModuleEdit.prototype, 'render')
- @moduleEdit = new ModuleEdit(
- el: $(".component")
- model: @stubModule
- onDelete: jasmine.createSpy()
- )
-
- it "renders the module editor", ->
- expect(ModuleEdit.prototype.render).toHaveBeenCalled()
-
- describe "render", ->
- beforeEach ->
- spyOn(@moduleEdit, 'loadDisplay')
- spyOn(@moduleEdit, 'delegateEvents')
- spyOn($.fn, 'append')
- spyOn(ViewUtils, 'loadJavaScript').and.returnValue($.Deferred().resolve().promise());
-
- window.MockXBlock = (runtime, element) ->
- return { }
-
- window.loadedXBlockResources = undefined
-
- @moduleEdit.render()
- $.ajax.calls.mostRecent().args[0].success(
- html: 'Response html
'
- resources: [
- ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
- ['hash2', {kind: 'url', mimetype: 'text/css', data: 'css-url'}],
- ['hash3', {kind: 'text', mimetype: 'application/javascript', data: 'inline-js'}],
- ['hash4', {kind: 'url', mimetype: 'application/javascript', data: 'js-url'}],
- ['hash5', {placement: 'head', mimetype: 'text/html', data: 'head-html'}],
- ['hash6', {placement: 'not-head', mimetype: 'text/html', data: 'not-head-html'}],
- ]
- )
-
- afterEach ->
- window.MockXBlock = null
-
- it "loads the module preview via ajax on the view element", ->
- expect($.ajax).toHaveBeenCalledWith(
- url: "/xblock/#{@moduleEdit.model.id}/student_view"
- type: "GET"
- cache: false
- headers:
- Accept: 'application/json'
- success: jasmine.any(Function)
- )
-
- expect($.ajax).not.toHaveBeenCalledWith(
- url: "/xblock/#{@moduleEdit.model.id}/studio_view"
- type: "GET"
- headers:
- Accept: 'application/json'
- success: jasmine.any(Function)
- )
- expect(@moduleEdit.loadDisplay).toHaveBeenCalled()
- expect(@moduleEdit.delegateEvents).toHaveBeenCalled()
-
- it "loads the editing view via ajax on demand", ->
- edit_helpers.installEditTemplates(true);
- expect($.ajax).not.toHaveBeenCalledWith(
- url: "/xblock/#{@moduleEdit.model.id}/studio_view"
- type: "GET"
- cache : false
- headers:
- Accept: 'application/json'
- success: jasmine.any(Function)
- )
-
- @moduleEdit.clickEditButton({'preventDefault': jasmine.createSpy('event.preventDefault')})
-
- mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore')
-
- $.ajax.calls.mostRecent().args[0].success(
- html: mockXBlockEditorHtml
- resources: [
- ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
- ['hash2', {kind: 'url', mimetype: 'text/css', data: 'css-url'}],
- ['hash3', {kind: 'text', mimetype: 'application/javascript', data: 'inline-js'}],
- ['hash4', {kind: 'url', mimetype: 'application/javascript', data: 'js-url'}],
- ['hash5', {placement: 'head', mimetype: 'text/html', data: 'head-html'}],
- ['hash6', {placement: 'not-head', mimetype: 'text/html', data: 'not-head-html'}],
- ]
- )
-
- expect($.ajax).toHaveBeenCalledWith(
- url: "/xblock/#{@moduleEdit.model.id}/studio_view"
- type: "GET"
- cache: false
- headers:
- Accept: 'application/json'
- success: jasmine.any(Function)
- )
- expect(@moduleEdit.delegateEvents).toHaveBeenCalled()
-
- it "loads inline css from fragments", ->
- expect($('head').append).toHaveBeenCalledWith("")
-
- it "loads css urls from fragments", ->
- expect($('head').append).toHaveBeenCalledWith("")
-
- it "loads inline js from fragments", ->
- expect($('head').append).toHaveBeenCalledWith("")
-
- it "loads js urls from fragments", ->
- expect(ViewUtils.loadJavaScript).toHaveBeenCalledWith("js-url")
-
- it "loads head html", ->
- expect($('head').append).toHaveBeenCalledWith("head-html")
-
- it "doesn't load body html", ->
- expect($.fn.append).not.toHaveBeenCalledWith('not-head-html')
-
- it "doesn't reload resources", ->
- count = $('head').append.calls.count()
- $.ajax.calls.mostRecent().args[0].success(
- html: 'Response html 2
'
- resources: [
- ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
- ]
- )
- expect($('head').append.calls.count()).toBe(count)
-
- describe "loadDisplay", ->
- beforeEach ->
- spyOn(XBlock, 'initializeBlock')
- @moduleEdit.loadDisplay()
-
- it "loads the .xmodule-display inside the module editor", ->
- expect(XBlock.initializeBlock).toHaveBeenCalled()
- expect(XBlock.initializeBlock.calls.mostRecent().args[0].get(0)).toBe($('.xblock-student_view').get(0))
diff --git a/cms/static/coffee/src/views/module_edit.coffee b/cms/static/coffee/src/views/module_edit.coffee
deleted file mode 100644
index b04495f8c5..0000000000
--- a/cms/static/coffee/src/views/module_edit.coffee
+++ /dev/null
@@ -1,56 +0,0 @@
-define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
- "js/views/xblock", "js/views/modals/edit_xblock"],
-($, _, gettext, XBlock, XBlockView, EditXBlockModal) ->
- class ModuleEdit extends XBlockView
- tagName: 'li'
- className: 'component'
- editorMode: 'editor-mode'
-
- events:
- "click .edit-button": 'clickEditButton'
- "click .delete-button": 'onDelete'
-
- initialize: ->
- @onDelete = @options.onDelete
- @render()
-
- loadDisplay: ->
- # Not all components render an inline student view, e.g. child containers which
- # instead render a link to a separate container page.
- xblockElement = @$el.find('.xblock-student_view')
- if xblockElement.length > 0
- XBlock.initializeBlock(xblockElement)
-
- createItem: (parent, payload, callback=->) ->
- payload.parent_locator = parent
- $.postJSON(
- @model.urlRoot + '/'
- payload
- (data) =>
- @model.set(id: data.locator)
- @$el.data('locator', data.locator)
- @$el.data('courseKey', data.courseKey)
- @render()
- ).success(callback)
-
- loadView: (viewName, target, callback) ->
- if @model.id
- $.ajax(
- url: "#{decodeURIComponent(@model.url())}/#{viewName}"
- type: 'GET'
- cache: false
- headers:
- Accept: 'application/json'
- success: (fragment) =>
- @renderXBlockFragment(fragment, target).done(callback)
- )
-
- render: -> @loadView('student_view', @$el, =>
- @loadDisplay()
- @delegateEvents()
- )
-
- clickEditButton: (event) ->
- event.preventDefault()
- modal = new EditXBlockModal();
- modal.edit(this.$el, self.model, { refresh: _.bind(@render, this) })
diff --git a/cms/static/js/spec/views/module_edit_spec.js b/cms/static/js/spec/views/module_edit_spec.js
new file mode 100644
index 0000000000..ebd54396d4
--- /dev/null
+++ b/cms/static/js/spec/views/module_edit_spec.js
@@ -0,0 +1,265 @@
+(function() {
+ 'use strict';
+ define([
+ 'jquery', 'common/js/components/utils/view_utils', 'js/spec_helpers/edit_helpers',
+ 'js/views/module_edit', 'js/models/module_info', 'xmodule'],
+ function($, ViewUtils, edit_helpers, ModuleEdit, ModuleModel) {
+ describe('ModuleEdit', function() {
+ beforeEach(function() {
+ this.stubModule = new ModuleModel({
+ id: 'stub-id'
+ });
+ setFixtures('\n' +
+ '- \n' +
+ '
\n' +
+ '
\n' +
+ ' ${editor}\n' +
+ '
\n' +
+ '
Save\n' +
+ '
Cancel\n' +
+ '
\n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ '
');
+ edit_helpers.installEditTemplates(true);
+ spyOn($, 'ajax').and.returnValue(this.moduleData);
+ this.moduleEdit = new ModuleEdit({
+ el: $('.component'),
+ model: this.stubModule,
+ onDelete: jasmine.createSpy()
+ });
+ return this.moduleEdit;
+ });
+ describe('class definition', function() {
+ it('sets the correct tagName', function() {
+ return expect(this.moduleEdit.tagName).toEqual('li');
+ });
+ it('sets the correct className', function() {
+ return expect(this.moduleEdit.className).toEqual('component');
+ });
+ });
+ describe('methods', function() {
+ describe('initialize', function() {
+ beforeEach(function() {
+ spyOn(ModuleEdit.prototype, 'render');
+ this.moduleEdit = new ModuleEdit({
+ el: $('.component'),
+ model: this.stubModule,
+ onDelete: jasmine.createSpy()
+ });
+ return this.moduleEdit;
+ });
+ it('renders the module editor', function() {
+ return expect(ModuleEdit.prototype.render).toHaveBeenCalled();
+ });
+ });
+ describe('render', function() {
+ beforeEach(function() {
+ spyOn(this.moduleEdit, 'loadDisplay');
+ spyOn(this.moduleEdit, 'delegateEvents');
+ spyOn($.fn, 'append');
+ spyOn(ViewUtils, 'loadJavaScript').and.returnValue($.Deferred().resolve().promise());
+ window.MockXBlock = function() {
+ return {};
+ };
+ window.loadedXBlockResources = void 0;
+ this.moduleEdit.render();
+ return $.ajax.calls.mostRecent().args[0].success({
+ html: 'Response html
',
+ resources: [
+ [
+ 'hash1', {
+ kind: 'text',
+ mimetype: 'text/css',
+ data: 'inline-css'
+ }
+ ], [
+ 'hash2', {
+ kind: 'url',
+ mimetype: 'text/css',
+ data: 'css-url'
+ }
+ ], [
+ 'hash3', {
+ kind: 'text',
+ mimetype: 'application/javascript',
+ data: 'inline-js'
+ }
+ ], [
+ 'hash4', {
+ kind: 'url',
+ mimetype: 'application/javascript',
+ data: 'js-url'
+ }
+ ], [
+ 'hash5', {
+ placement: 'head',
+ mimetype: 'text/html',
+ data: 'head-html'
+ }
+ ], [
+ 'hash6', {
+ placement: 'not-head',
+ mimetype: 'text/html',
+ data: 'not-head-html'
+ }
+ ]
+ ]
+ });
+ });
+ afterEach(function() {
+ window.MockXBlock = null;
+ return window.MockXBlock;
+ });
+ it('loads the module preview via ajax on the view element', function() {
+ expect($.ajax).toHaveBeenCalledWith({
+ url: '/xblock/' + this.moduleEdit.model.id + '/student_view',
+ type: 'GET',
+ cache: false,
+ headers: {
+ Accept: 'application/json'
+ },
+ success: jasmine.any(Function)
+ });
+ expect($.ajax).not.toHaveBeenCalledWith({
+ url: '/xblock/' + this.moduleEdit.model.id + '/studio_view',
+ type: 'GET',
+ headers: {
+ Accept: 'application/json'
+ },
+ success: jasmine.any(Function)
+ });
+ expect(this.moduleEdit.loadDisplay).toHaveBeenCalled();
+ return expect(this.moduleEdit.delegateEvents).toHaveBeenCalled();
+ });
+ it('loads the editing view via ajax on demand', function() {
+ var mockXBlockEditorHtml;
+ edit_helpers.installEditTemplates(true);
+ expect($.ajax).not.toHaveBeenCalledWith({
+ url: '/xblock/' + this.moduleEdit.model.id + '/studio_view',
+ type: 'GET',
+ cache: false,
+ headers: {
+ Accept: 'application/json'
+ },
+ success: jasmine.any(Function)
+ });
+ this.moduleEdit.clickEditButton({
+ 'preventDefault': jasmine.createSpy('event.preventDefault')
+ });
+ mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore');
+ $.ajax.calls.mostRecent().args[0].success({
+ html: mockXBlockEditorHtml,
+ resources: [
+ [
+ 'hash1', {
+ kind: 'text',
+ mimetype: 'text/css',
+ data: 'inline-css'
+ }
+ ], [
+ 'hash2', {
+ kind: 'url',
+ mimetype: 'text/css',
+ data: 'css-url'
+ }
+ ], [
+ 'hash3', {
+ kind: 'text',
+ mimetype: 'application/javascript',
+ data: 'inline-js'
+ }
+ ], [
+ 'hash4', {
+ kind: 'url',
+ mimetype: 'application/javascript',
+ data: 'js-url'
+ }
+ ], [
+ 'hash5', {
+ placement: 'head',
+ mimetype: 'text/html',
+ data: 'head-html'
+ }
+ ], [
+ 'hash6', {
+ placement: 'not-head',
+ mimetype: 'text/html',
+ data: 'not-head-html'
+ }
+ ]
+ ]
+ });
+ expect($.ajax).toHaveBeenCalledWith({
+ url: '/xblock/' + this.moduleEdit.model.id + '/studio_view',
+ type: 'GET',
+ cache: false,
+ headers: {
+ Accept: 'application/json'
+ },
+ success: jasmine.any(Function)
+ });
+ return expect(this.moduleEdit.delegateEvents).toHaveBeenCalled();
+ });
+ it('loads inline css from fragments', function() {
+ var args = "";
+ return expect($('head').append).toHaveBeenCalledWith(args);
+ });
+ it('loads css urls from fragments', function() {
+ var args = "";
+ return expect($('head').append).toHaveBeenCalledWith(args);
+ });
+ it('loads inline js from fragments', function() {
+ return expect($('head').append).toHaveBeenCalledWith('');
+ });
+ it('loads js urls from fragments', function() {
+ return expect(ViewUtils.loadJavaScript).toHaveBeenCalledWith('js-url');
+ });
+ it('loads head html', function() {
+ return expect($('head').append).toHaveBeenCalledWith('head-html');
+ });
+ it("doesn't load body html", function() {
+ return expect($.fn.append).not.toHaveBeenCalledWith("not-head-html");
+ });
+ it("doesn't reload resources", function() {
+ var count;
+ count = $('head').append.calls.count();
+ $.ajax.calls.mostRecent().args[0].success({
+ html: 'Response html 2
',
+ resources: [
+ [
+ 'hash1', {
+ kind: 'text',
+ mimetype: 'text/css',
+ data: 'inline-css'
+ }
+ ]
+ ]
+ });
+ return expect($('head').append.calls.count()).toBe(count);
+ });
+ });
+ describe('loadDisplay', function() {
+ beforeEach(function() {
+ spyOn(XBlock, 'initializeBlock');
+ return this.moduleEdit.loadDisplay();
+ });
+ it('loads the .xmodule-display inside the module editor', function() {
+ expect(XBlock.initializeBlock).toHaveBeenCalled();
+ var sel = '.xblock-student_view';
+ return expect(XBlock.initializeBlock.calls.mostRecent().args[0].get(0)).toBe($(sel).get(0));
+ });
+ });
+ });
+ });
+ });
+}).call(this);
diff --git a/cms/static/js/views/module_edit.js b/cms/static/js/views/module_edit.js
new file mode 100644
index 0000000000..64aa7bafff
--- /dev/null
+++ b/cms/static/js/views/module_edit.js
@@ -0,0 +1,111 @@
+(function() {
+ 'use strict';
+
+ var __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) {
+ var key;
+ for (key in parent) {
+ if (__hasProp.call(parent, key)) {
+ child[key] = parent[key];
+ }
+ }
+ function Ctor() {
+ this.constructor = child;
+ }
+ Ctor.prototype = parent.prototype;
+ child.prototype = new Ctor();
+ child.__super__ = parent.prototype;
+ return child;
+ };
+
+ define(['jquery', 'underscore', 'gettext', 'xblock/runtime.v1', 'js/views/xblock', 'js/views/modals/edit_xblock'],
+ function($, _, gettext, XBlock, XBlockView, EditXBlockModal) {
+ var ModuleEdit = (function(_super) {
+
+ __extends(ModuleEdit, _super);
+
+ function ModuleEdit() {
+ return ModuleEdit.__super__.constructor.apply(this, arguments);
+ }
+
+ ModuleEdit.prototype.tagName = 'li';
+
+ ModuleEdit.prototype.className = 'component';
+
+ ModuleEdit.prototype.editorMode = 'editor-mode';
+
+ ModuleEdit.prototype.events = {
+ 'click .edit-button': 'clickEditButton',
+ 'click .delete-button': 'onDelete'
+ };
+
+ ModuleEdit.prototype.initialize = function() {
+ this.onDelete = this.options.onDelete;
+ return this.render();
+ };
+
+ ModuleEdit.prototype.loadDisplay = function() {
+ var xblockElement;
+ xblockElement = this.$el.find('.xblock-student_view');
+ if (xblockElement.length > 0) {
+ return XBlock.initializeBlock(xblockElement);
+ }
+ };
+
+ ModuleEdit.prototype.createItem = function(parent, payload, callback) {
+ var _this = this;
+ if (_.isNull(callback)) {
+ callback = function() {};
+ }
+ payload.parent_locator = parent;
+ return $.postJSON(this.model.urlRoot + '/', payload, function(data) {
+ _this.model.set({
+ id: data.locator
+ });
+ _this.$el.data('locator', data.locator);
+ _this.$el.data('courseKey', data.courseKey);
+ return _this.render();
+ }).success(callback);
+ };
+
+ ModuleEdit.prototype.loadView = function(viewName, target, callback) {
+ var _this = this;
+ if (this.model.id) {
+ return $.ajax({
+ url: '' + (decodeURIComponent(this.model.url())) + '/' + viewName,
+ type: 'GET',
+ cache: false,
+ headers: {
+ Accept: 'application/json'
+ },
+ success: function(fragment) {
+ return _this.renderXBlockFragment(fragment, target).done(callback);
+ }
+ });
+ }
+ };
+
+ ModuleEdit.prototype.render = function() {
+ var _this = this;
+ return this.loadView('student_view', this.$el, function() {
+ _this.loadDisplay();
+ return _this.delegateEvents();
+ });
+ };
+
+ ModuleEdit.prototype.clickEditButton = function(event) {
+ var modal;
+ event.preventDefault();
+ modal = new EditXBlockModal();
+ return modal.edit(this.$el, this.model, {
+ refresh: _.bind(this.render, this)
+ });
+ };
+
+ return ModuleEdit;
+
+ })(XBlockView);
+ return ModuleEdit;
+ });
+
+}).call(this);
diff --git a/cms/static/js/views/tabs.js b/cms/static/js/views/tabs.js
index 9f83edeb19..04ace25d7e 100644
--- a/cms/static/js/views/tabs.js
+++ b/cms/static/js/views/tabs.js
@@ -19,7 +19,7 @@
};
define(['underscore', 'jquery', 'jquery.ui', 'backbone', 'common/js/components/views/feedback_prompt',
- 'common/js/components/views/feedback_notification', 'coffee/src/views/module_edit',
+ 'common/js/components/views/feedback_notification', 'js/views/module_edit',
'js/models/module_info', 'js/utils/module'],
function(_, $, ui, Backbone, PromptView, NotificationView, ModuleEditView, ModuleModel, ModuleUtils) {
var TabsEdit;