Implement editing from the Studio container page.
STUD-1306
This commit is contained in:
@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
|
||||
in roughly chronological order, most recent first. Add your entries at or near
|
||||
the top. Include a label indicating the component affected.
|
||||
|
||||
Studio: Add edit button to leaf xblocks on the container page. STUD-1306.
|
||||
|
||||
Blades: Add LTI context_id parameter. BLD-584.
|
||||
|
||||
Blades: Update LTI resource_link_id parameter. BLD-768.
|
||||
|
||||
@@ -107,7 +107,7 @@ def click_component_from_menu(category, component_type, is_advanced):
|
||||
def edit_component_and_select_settings():
|
||||
world.wait_for(lambda _driver: world.css_visible('a.edit-button'))
|
||||
world.css_click('a.edit-button')
|
||||
world.css_click('#settings-mode a')
|
||||
world.css_click('.settings-button')
|
||||
|
||||
|
||||
@world.absorb
|
||||
@@ -174,7 +174,7 @@ def verify_all_setting_entries(expected_entries):
|
||||
|
||||
@world.absorb
|
||||
def save_component(step):
|
||||
world.css_click("a.save-button")
|
||||
world.css_click("a.action-save")
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ def save_component_and_reopen(step):
|
||||
|
||||
@world.absorb
|
||||
def cancel_component(step):
|
||||
world.css_click("a.cancel-button")
|
||||
world.css_click("a.action-cancel")
|
||||
# We have a known issue that modifications are still shown within the edit window after cancel (though)
|
||||
# they are not persisted. Refresh the browser to make sure the changes were not persisted.
|
||||
reload_the_page(step)
|
||||
|
||||
@@ -44,13 +44,13 @@ def click_edit_or_delete(step, edit_or_delete):
|
||||
|
||||
@step(u'I change the name to "([^"]*)"$')
|
||||
def change_name(step, new_name):
|
||||
settings_css = '#settings-mode a'
|
||||
settings_css = '.settings-button'
|
||||
world.css_click(settings_css)
|
||||
input_css = 'input.setting-input'
|
||||
world.css_fill(input_css, new_name)
|
||||
if world.is_firefox():
|
||||
world.trigger_event(input_css)
|
||||
save_button = 'a.save-button'
|
||||
save_button = 'a.action-save'
|
||||
world.css_click(save_button)
|
||||
|
||||
|
||||
|
||||
@@ -209,23 +209,31 @@ def check_text_in_the_captions(_step, text):
|
||||
|
||||
@step('I see value "([^"]*)" in the field "([^"]*)"$')
|
||||
def check_transcripts_field(_step, values, field_name):
|
||||
world.click_link_by_text('Advanced')
|
||||
editor_tabs = world.browser.find_by_css('.editor-tabs a')
|
||||
basic_tab = editor_tabs[0]
|
||||
advanced_tab = editor_tabs[1]
|
||||
advanced_tab.click()
|
||||
field_id = '#' + world.browser.find_by_xpath('//label[text()="%s"]' % field_name.strip())[0]['for']
|
||||
values_list = [i.strip() == world.css_value(field_id) for i in values.split('|')]
|
||||
assert any(values_list)
|
||||
world.click_link_by_text('Basic')
|
||||
basic_tab.click()
|
||||
|
||||
|
||||
@step('I save changes$')
|
||||
def save_changes(_step):
|
||||
save_css = 'a.save-button'
|
||||
save_css = 'a.action-save'
|
||||
world.css_click(save_css)
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
|
||||
@step('I open tab "([^"]*)"$')
|
||||
def open_tab(_step, tab_name):
|
||||
world.click_link_by_text(tab_name.strip())
|
||||
editor_tabs = world.browser.find_by_css('.editor-tabs a')
|
||||
expected_tab_text = tab_name.strip().upper()
|
||||
matching_tabs = [tab for tab in editor_tabs if tab.text == expected_tab_text]
|
||||
assert len(matching_tabs) == 1
|
||||
tab = matching_tabs[0]
|
||||
tab.click()
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ def set_show_captions(step, setting):
|
||||
world.browser.cookies.delete('hide_captions')
|
||||
|
||||
world.css_click('a.edit-button')
|
||||
world.wait_for(lambda _driver: world.css_visible('a.save-button'))
|
||||
world.click_link_by_text('Advanced')
|
||||
world.wait_for(lambda _driver: world.css_visible('a.action-save'))
|
||||
world.click_link_by_text('ADVANCED')
|
||||
world.browser.select('Transcript Display', setting)
|
||||
world.css_click('a.save-button')
|
||||
world.css_click('a.action-save')
|
||||
|
||||
|
||||
@step('when I view the video it (.*) show the captions$')
|
||||
@@ -161,7 +161,7 @@ def correct_video_settings(_step):
|
||||
|
||||
@step('my video display name change is persisted on save$')
|
||||
def video_name_persisted(step):
|
||||
world.css_click('a.save-button')
|
||||
world.css_click('a.action-save')
|
||||
reload_the_page(step)
|
||||
world.wait_for_xmodule()
|
||||
world.edit_component()
|
||||
|
||||
@@ -273,6 +273,7 @@ def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=N
|
||||
'context_course': course,
|
||||
'unit': item,
|
||||
'unit_locator': locator,
|
||||
'xblocks': [xblock for xblock in xblocks],
|
||||
'locators': locators,
|
||||
'component_templates': component_templates,
|
||||
'draft_preview_link': preview_lms_link,
|
||||
@@ -317,14 +318,17 @@ def container_handler(request, tag=None, package_id=None, branch=None, version_g
|
||||
while parent and parent.category != 'sequential':
|
||||
ancestor_xblocks.append(parent)
|
||||
parent = get_parent_xblock(parent)
|
||||
|
||||
ancestor_xblocks.reverse()
|
||||
|
||||
unit = None if not ancestor_xblocks else ancestor_xblocks[0]
|
||||
unit_publish_state = None if not unit else compute_publish_state(unit)
|
||||
|
||||
return render_to_response('container.html', {
|
||||
'context_course': course,
|
||||
'xblock': xblock,
|
||||
'xblock_locator': locator,
|
||||
'unit': None if not ancestor_xblocks else ancestor_xblocks[0],
|
||||
'unit': unit,
|
||||
'unit_publish_state': unit_publish_state,
|
||||
'ancestor_xblocks': ancestor_xblocks,
|
||||
})
|
||||
else:
|
||||
|
||||
@@ -34,6 +34,7 @@ from ..utils import get_modulestore
|
||||
|
||||
from .access import has_course_access
|
||||
from .helpers import _xmodule_recurse
|
||||
from contentstore.utils import compute_publish_state, PublishState
|
||||
from contentstore.views.preview import get_preview_fragment
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from models.settings.course_grading import CourseGradingModel
|
||||
@@ -224,11 +225,12 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
})
|
||||
elif view_name in ('student_view', 'container_preview'):
|
||||
is_container_view = (view_name == 'container_preview')
|
||||
component_publish_state = compute_publish_state(component)
|
||||
is_read_only_view = component_publish_state == PublishState.public
|
||||
|
||||
# Only show the new style HTML for the container view, i.e. for non-verticals
|
||||
# Note: this special case logic can be removed once the unit page is replaced
|
||||
# with the new container view.
|
||||
is_read_only_view = is_container_view
|
||||
context = {
|
||||
'runtime_type': 'studio',
|
||||
'container_view': is_container_view,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import hashlib
|
||||
from functools import partial
|
||||
|
||||
from django.conf import settings
|
||||
@@ -170,7 +169,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
|
||||
"""
|
||||
# Only add the Studio wrapper when on the container page. The unit page will remain as is for now.
|
||||
if context.get('container_view', None) and view == 'student_view':
|
||||
locator = loc_mapper().translate_location(xblock.course_id, xblock.location)
|
||||
locator = loc_mapper().translate_location(xblock.course_id, xblock.location, published=False)
|
||||
template_context = {
|
||||
'xblock_context': context,
|
||||
'xblock': xblock,
|
||||
|
||||
@@ -3,6 +3,7 @@ Unit tests for the container view.
|
||||
"""
|
||||
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.utils import compute_publish_state, PublishState
|
||||
from contentstore.views.helpers import xblock_studio_url
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
|
||||
@@ -26,13 +27,19 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
category="video", display_name="My Video")
|
||||
|
||||
def test_container_html(self):
|
||||
branch_name = "MITx.999.Robot_Super_Course/branch/draft/block"
|
||||
self._test_html_content(
|
||||
self.child_vertical,
|
||||
expected_section_tag='<section class="wrapper-xblock level-page" data-locator="MITx.999.Robot_Super_Course/branch/draft/block/Child_Vertical"/>',
|
||||
branch_name=branch_name,
|
||||
expected_section_tag=(
|
||||
'<section class="wrapper-xblock level-page is-hidden" '
|
||||
'data-locator="{branch_name}/Child_Vertical">'.format(branch_name=branch_name)
|
||||
),
|
||||
expected_breadcrumbs=(
|
||||
r'<a href="/unit/MITx.999.Robot_Super_Course/branch/draft/block/Unit"\s*'
|
||||
r'<a href="/unit/{branch_name}/Unit"\s*'
|
||||
r'class="navigation-link navigation-parent">Unit</a>\s*'
|
||||
r'<a href="#" class="navigation-link navigation-current">Child Vertical</a>'),
|
||||
r'<a href="#" class="navigation-link navigation-current">Child Vertical</a>'
|
||||
).format(branch_name=branch_name)
|
||||
)
|
||||
|
||||
def test_container_on_container_html(self):
|
||||
@@ -44,23 +51,30 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
category="wrapper", display_name="Wrapper")
|
||||
ItemFactory.create(parent_location=xblock_with_child.location,
|
||||
category="html", display_name="Child HTML")
|
||||
branch_name = "MITx.999.Robot_Super_Course/branch/draft/block"
|
||||
self._test_html_content(
|
||||
xblock_with_child,
|
||||
expected_section_tag='<section class="wrapper-xblock level-page" data-locator="MITx.999.Robot_Super_Course/branch/draft/block/Wrapper"/>',
|
||||
branch_name=branch_name,
|
||||
expected_section_tag=(
|
||||
'<section class="wrapper-xblock level-page is-hidden" '
|
||||
'data-locator="{branch_name}/Wrapper">'.format(branch_name=branch_name)
|
||||
),
|
||||
expected_breadcrumbs=(
|
||||
r'<a href="/unit/MITx.999.Robot_Super_Course/branch/draft/block/Unit"\s*'
|
||||
r'<a href="/unit/{branch_name}/Unit"\s*'
|
||||
r'class="navigation-link navigation-parent">Unit</a>\s*'
|
||||
r'<a href="/container/MITx.999.Robot_Super_Course/branch/draft/block/Child_Vertical"\s*'
|
||||
r'<a href="/container/{branch_name}/Child_Vertical"\s*'
|
||||
r'class="navigation-link navigation-parent">Child Vertical</a>\s*'
|
||||
r'<a href="#" class="navigation-link navigation-current">Wrapper</a>'),
|
||||
r'<a href="#" class="navigation-link navigation-current">Wrapper</a>'
|
||||
).format(branch_name=branch_name)
|
||||
)
|
||||
|
||||
def _test_html_content(self, xblock, expected_section_tag, expected_breadcrumbs):
|
||||
def _test_html_content(self, xblock, branch_name, expected_section_tag, expected_breadcrumbs):
|
||||
"""
|
||||
Get the HTML for a container page and verify the section tag is correct
|
||||
and the breadcrumbs trail is correct.
|
||||
"""
|
||||
url = xblock_studio_url(xblock, self.course)
|
||||
publish_state = compute_publish_state(xblock)
|
||||
resp = self.client.get_html(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
html = resp.content
|
||||
@@ -68,5 +82,12 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
# Verify the navigation link at the top of the page is correct.
|
||||
self.assertRegexpMatches(html, expected_breadcrumbs)
|
||||
# Verify the link that allows users to change publish status.
|
||||
expected_unit_link = 'This content is published with unit <a href="/unit/MITx.999.Robot_Super_Course/branch/draft/block/Unit">Unit</a>.'
|
||||
expected_message = None
|
||||
if publish_state == PublishState.public:
|
||||
expected_message = 'you need to edit unit <a href="/unit/{branch_name}/Unit">Unit</a> as a draft.'
|
||||
else:
|
||||
expected_message = 'your changes will be published with unit <a href="/unit/{branch_name}/Unit">Unit</a>.'
|
||||
expected_unit_link = expected_message.format(
|
||||
branch_name=branch_name
|
||||
)
|
||||
self.assertIn(expected_unit_link, html)
|
||||
|
||||
@@ -218,8 +218,13 @@ define([
|
||||
|
||||
"js/spec/views/unit_spec",
|
||||
"js/spec/views/xblock_spec",
|
||||
"js/spec/views/xblock_container_spec",
|
||||
"js/spec/views/xblock_editor_spec",
|
||||
|
||||
# these tests are run separate in the cms-squire suite, due to process
|
||||
"js/spec/views/modals/base_modal_spec",
|
||||
"js/spec/views/modals/edit_xblock_spec",
|
||||
|
||||
# these tests are run separately in the cms-squire suite, due to process
|
||||
# isolation issues with Squire.js
|
||||
# "coffee/spec/views/assets_spec"
|
||||
])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require ["jquery", "backbone", "coffee/src/main", "js/spec/create_sinon", "jasmine-stealth", "jquery.cookie"],
|
||||
require ["jquery", "backbone", "coffee/src/main", "js/spec_helpers/create_sinon", "jasmine-stealth", "jquery.cookie"],
|
||||
($, Backbone, main, create_sinon) ->
|
||||
describe "CMS", ->
|
||||
it "should initialize URL", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["js/models/section", "js/spec/create_sinon", "js/utils/module"], (Section, create_sinon, ModuleUtils) ->
|
||||
define ["js/models/section", "js/spec_helpers/create_sinon", "js/utils/module"], (Section, create_sinon, ModuleUtils) ->
|
||||
describe "Section", ->
|
||||
describe "basic", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["jasmine", "js/spec/create_sinon", "squire"],
|
||||
define ["jasmine", "js/spec_helpers/create_sinon", "squire"],
|
||||
(jasmine, create_sinon, Squire) ->
|
||||
|
||||
feedbackTpl = readFixtures('system-feedback.underscore')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info", "js/collections/course_update", "js/spec/create_sinon"],
|
||||
define ["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info", "js/collections/course_update", "js/spec_helpers/create_sinon"],
|
||||
(CourseInfoHandoutsView, CourseInfoUpdateView, ModuleInfo, CourseUpdateCollection, create_sinon) ->
|
||||
|
||||
describe "Course Updates and Handouts", ->
|
||||
|
||||
@@ -6,7 +6,8 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
id: "stub-id"
|
||||
|
||||
setFixtures """
|
||||
<li class="component" id="stub-id">
|
||||
<ul>
|
||||
<li class="component" id="stub-id" data-locator="stub-id">
|
||||
<div class="component-editor">
|
||||
<div class="module-editor">
|
||||
${editor}
|
||||
@@ -23,6 +24,8 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
<div id="stub-module-content"/>
|
||||
</section>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="edit-xblock-modal"/>
|
||||
"""
|
||||
spyOn($, 'ajax').andReturn(@moduleData)
|
||||
|
||||
@@ -55,11 +58,13 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
describe "render", ->
|
||||
beforeEach ->
|
||||
spyOn(@moduleEdit, 'loadDisplay')
|
||||
spyOn(@moduleEdit, 'loadEdit')
|
||||
spyOn(@moduleEdit, 'delegateEvents')
|
||||
spyOn($.fn, 'append')
|
||||
spyOn($, 'getScript').andReturn($.Deferred().resolve().promise())
|
||||
|
||||
window.MockXBlock = (runtime, element) ->
|
||||
return { }
|
||||
|
||||
window.loadedXBlockResources = undefined
|
||||
|
||||
@moduleEdit.render()
|
||||
@@ -75,6 +80,9 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
]
|
||||
)
|
||||
|
||||
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"
|
||||
@@ -92,10 +100,13 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
success: jasmine.any(Function)
|
||||
)
|
||||
expect(@moduleEdit.loadDisplay).toHaveBeenCalled()
|
||||
expect(@moduleEdit.loadEdit).not.toHaveBeenCalled()
|
||||
expect(@moduleEdit.delegateEvents).toHaveBeenCalled()
|
||||
|
||||
it "loads the editing view via ajax on demand", ->
|
||||
editorTemplate = readFixtures('edit-xblock-modal.underscore');
|
||||
feedbackTemplate = readFixtures('system-feedback.underscore')
|
||||
appendSetFixtures($("<script>", { id: "edit-xblock-modal-tpl", type: "text/template", html: editorTemplate }));
|
||||
appendSetFixtures($("<script>", { id: "system-feedback-tpl", type: "text/template" , html: feedbackTemplate }));
|
||||
expect($.ajax).not.toHaveBeenCalledWith(
|
||||
url: "/xblock/#{@moduleEdit.model.id}/studio_view"
|
||||
type: "GET"
|
||||
@@ -103,12 +114,13 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
Accept: 'application/json'
|
||||
success: jasmine.any(Function)
|
||||
)
|
||||
expect(@moduleEdit.loadEdit).not.toHaveBeenCalled()
|
||||
|
||||
@moduleEdit.clickEditButton({'preventDefault': jasmine.createSpy('event.preventDefault')})
|
||||
|
||||
mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore')
|
||||
|
||||
$.ajax.mostRecentCall.args[0].success(
|
||||
html: '<div>Response html</div>'
|
||||
html: mockXBlockEditorHtml
|
||||
resources: [
|
||||
['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
|
||||
['hash2', {kind: 'url', mimetype: 'text/css', data: 'css-url'}],
|
||||
@@ -126,7 +138,6 @@ define ["jquery", "coffee/src/views/module_edit", "js/models/module_info", "xmod
|
||||
Accept: 'application/json'
|
||||
success: jasmine.any(Function)
|
||||
)
|
||||
expect(@moduleEdit.loadEdit).toHaveBeenCalled()
|
||||
expect(@moduleEdit.delegateEvents).toHaveBeenCalled()
|
||||
|
||||
it "loads inline css from fragments", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["js/views/overview", "js/views/feedback_notification", "js/spec/create_sinon", "js/base", "date", "jquery.timepicker"],
|
||||
define ["js/views/overview", "js/views/feedback_notification", "js/spec_helpers/create_sinon", "js/base", "date", "jquery.timepicker"],
|
||||
(Overview, Notification, create_sinon) ->
|
||||
|
||||
describe "Course Overview", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["js/models/section", "js/views/section_show", "js/views/section_edit", "js/spec/create_sinon"], (Section, SectionShow, SectionEdit, create_sinon) ->
|
||||
define ["js/models/section", "js/views/section_show", "js/views/section_edit", "js/spec_helpers/create_sinon"], (Section, SectionShow, SectionEdit, create_sinon) ->
|
||||
|
||||
describe "SectionShow", ->
|
||||
describe "Basic", ->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js/models/course",
|
||||
"js/collections/textbook", "js/views/show_textbook", "js/views/edit_textbook", "js/views/list_textbooks",
|
||||
"js/views/edit_chapter", "js/views/feedback_prompt", "js/views/feedback_notification",
|
||||
"js/spec/create_sinon", "jasmine-stealth"],
|
||||
"js/spec_helpers/create_sinon", "jasmine-stealth"],
|
||||
(Textbook, Chapter, ChapterSet, Course, TextbookSet, ShowTextbook, EditTextbook, ListTexbook, EditChapter, Prompt, Notification, create_sinon) ->
|
||||
feedbackTpl = readFixtures('system-feedback.underscore')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "js/spec/create_sinon"], (FileUpload, UploadDialog, Chapter, create_sinon) ->
|
||||
define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "js/spec_helpers/create_sinon"], (FileUpload, UploadDialog, Chapter, create_sinon) ->
|
||||
|
||||
feedbackTpl = readFixtures('system-feedback.underscore')
|
||||
|
||||
|
||||
@@ -1,67 +1,22 @@
|
||||
define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
|
||||
"js/views/xblock", "js/views/feedback_notification", "js/views/metadata", "js/collections/metadata"
|
||||
"js/utils/modal", "jquery.inputnumber"],
|
||||
($, _, gettext, XBlock, XBlockView, NotificationView, MetadataView, MetadataCollection, ModalUtils) ->
|
||||
"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 .component-editor .cancel-button": 'clickCancelButton'
|
||||
"click .component-editor .save-button": 'clickSaveButton'
|
||||
"click .component-actions .edit-button": 'clickEditButton'
|
||||
"click .component-actions .delete-button": 'onDelete'
|
||||
"click .mode a": 'clickModeButton'
|
||||
|
||||
initialize: ->
|
||||
@onDelete = @options.onDelete
|
||||
@render()
|
||||
|
||||
$componentEditor: => @$el.find('.component-editor')
|
||||
$moduleEditor: => @$componentEditor().find('.module-editor')
|
||||
|
||||
loadDisplay: ->
|
||||
XBlock.initializeBlock(@$el.find('.xblock-student_view'))
|
||||
|
||||
loadEdit: ->
|
||||
@module = XBlock.initializeBlock(@$el.find('.xblock-studio_view'))
|
||||
# At this point, metadata-edit.html will be loaded, and the metadata (as JSON) is available.
|
||||
metadataEditor = @$el.find('.metadata_edit')
|
||||
metadataData = metadataEditor.data('metadata')
|
||||
models = [];
|
||||
for key of metadataData
|
||||
models.push(metadataData[key])
|
||||
@metadataEditor = new MetadataView.Editor({
|
||||
el: metadataEditor,
|
||||
collection: new MetadataCollection(models)
|
||||
})
|
||||
|
||||
@module.setMetadataEditor(@metadataEditor) if @module.setMetadataEditor
|
||||
|
||||
# Need to update set "active" class on data editor if there is one.
|
||||
# If we are only showing settings, hide the data editor controls and update settings accordingly.
|
||||
if @hasDataEditor()
|
||||
@selectMode(@editorMode)
|
||||
else
|
||||
@hideDataEditor()
|
||||
|
||||
title = interpolate(gettext('<em>Editing:</em> %s'),
|
||||
[@metadataEditor.getDisplayName()])
|
||||
@$el.find('.component-name').html(title)
|
||||
|
||||
customMetadata: ->
|
||||
# Hack to support metadata fields that aren't part of the metadata editor (ie, LaTeX high level source).
|
||||
# Walk through the set of elements which have the 'data-metadata_name' attribute and
|
||||
# build up an object to pass back to the server on the subsequent POST.
|
||||
# Note that these values will always be sent back on POST, even if they did not actually change.
|
||||
_metadata = {}
|
||||
_metadata[$(el).data("metadata-name")] = el.value for el in $('[data-metadata-name]', @$componentEditor())
|
||||
return _metadata
|
||||
|
||||
changedMetadata: ->
|
||||
return _.extend(@metadataEditor.getModifiedMetadataValues(), @customMetadata())
|
||||
|
||||
createItem: (parent, payload, callback=->) ->
|
||||
payload.parent_locator = parent
|
||||
$.postJSON(
|
||||
@@ -89,72 +44,10 @@ define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
|
||||
@delegateEvents()
|
||||
)
|
||||
|
||||
clickSaveButton: (event) =>
|
||||
event.preventDefault()
|
||||
data = @module.save()
|
||||
|
||||
analytics.track "Saved Module",
|
||||
course: course_location_analytics
|
||||
id: _this.model.id
|
||||
|
||||
data.metadata = _.extend(data.metadata || {}, @changedMetadata())
|
||||
ModalUtils.hideModalCover()
|
||||
saving = new NotificationView.Mini
|
||||
title: gettext('Saving…')
|
||||
saving.show()
|
||||
@model.save(data).done( =>
|
||||
@render()
|
||||
@$el.removeClass('editing')
|
||||
saving.hide()
|
||||
)
|
||||
|
||||
clickCancelButton: (event) ->
|
||||
event.preventDefault()
|
||||
@$el.removeClass('editing')
|
||||
@$componentEditor().slideUp(150)
|
||||
ModalUtils.hideModalCover()
|
||||
|
||||
clickEditButton: (event) ->
|
||||
event.preventDefault()
|
||||
@$el.addClass('editing')
|
||||
ModalUtils.showModalCover(true)
|
||||
@loadView('studio_view', @$moduleEditor(), =>
|
||||
@$componentEditor().slideDown(150)
|
||||
@loadEdit()
|
||||
@delegateEvents()
|
||||
)
|
||||
|
||||
clickModeButton: (event) ->
|
||||
event.preventDefault()
|
||||
if not @hasDataEditor()
|
||||
return
|
||||
@selectMode(event.currentTarget.parentElement.id)
|
||||
|
||||
hasDataEditor: =>
|
||||
return @$el.find('.wrapper-comp-editor').length > 0
|
||||
|
||||
selectMode: (mode) =>
|
||||
dataEditor = @$el.find('.wrapper-comp-editor')
|
||||
settingsEditor = @$el.find('.wrapper-comp-settings')
|
||||
editorModeButton = @$el.find('#editor-mode').find("a")
|
||||
settingsModeButton = @$el.find('#settings-mode').find("a")
|
||||
|
||||
if mode == @editorMode
|
||||
# Because of CodeMirror editor, cannot hide the data editor when it is first loaded. Therefore
|
||||
# we have to use a class of is-inactive instead of is-active.
|
||||
dataEditor.removeClass('is-inactive')
|
||||
editorModeButton.addClass('is-set')
|
||||
settingsEditor.removeClass('is-active')
|
||||
settingsModeButton.removeClass('is-set')
|
||||
else
|
||||
dataEditor.addClass('is-inactive')
|
||||
editorModeButton.removeClass('is-set')
|
||||
settingsEditor.addClass('is-active')
|
||||
settingsModeButton.addClass('is-set')
|
||||
|
||||
hideDataEditor: =>
|
||||
editorModeButtonParent = @$el.find('#editor-mode')
|
||||
editorModeButtonParent.addClass('inactive-mode')
|
||||
editorModeButtonParent.removeClass('active-mode')
|
||||
@$el.find('.wrapper-comp-settings').addClass('is-active')
|
||||
@$el.find('#settings-mode').find("a").addClass('is-set')
|
||||
modal = new EditXBlockModal({
|
||||
el: $('.edit-xblock-modal'),
|
||||
view: 'student_view'
|
||||
});
|
||||
modal.edit(this.$el, self.model, { refresh: _.bind(@render, this) })
|
||||
|
||||
@@ -9,8 +9,10 @@ define(["backbone", "js/utils/module"], function(Backbone, ModuleUtils) {
|
||||
"category": null,
|
||||
"is_draft": null,
|
||||
"is_container": null,
|
||||
"data": null,
|
||||
"metadata" : null,
|
||||
"children": []
|
||||
}
|
||||
});
|
||||
return XBlockInfo;
|
||||
});
|
||||
});
|
||||
|
||||
35
cms/static/js/spec/views/modals/base_modal_spec.js
Normal file
35
cms/static/js/spec/views/modals/base_modal_spec.js
Normal file
@@ -0,0 +1,35 @@
|
||||
define(["jquery", "underscore", "js/views/modals/base_modal"],
|
||||
function ($, _, BaseModal) {
|
||||
|
||||
describe("BaseModal", function() {
|
||||
var baseViewPrototype, MockModal;
|
||||
|
||||
MockModal = BaseModal.extend({
|
||||
initialize: function() {
|
||||
this.viewHtml = readFixtures('mock/mock-modal.underscore');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(this.viewHtml);
|
||||
}
|
||||
});
|
||||
|
||||
it('is visible after show is called', function () {
|
||||
var modal = new MockModal();
|
||||
modal.render();
|
||||
modal.show();
|
||||
expect($('body')).toHaveClass('modal-window-is-shown');
|
||||
expect(modal.$('.wrapper-modal-window')).toHaveClass('is-shown');
|
||||
modal.hide();
|
||||
});
|
||||
|
||||
it('is invisible after hide is called', function () {
|
||||
var modal = new MockModal();
|
||||
modal.render();
|
||||
modal.show();
|
||||
modal.hide();
|
||||
expect($('body')).not.toHaveClass('modal-window-is-shown');
|
||||
expect(modal.$('.wrapper-modal-window')).not.toHaveClass('is-shown');
|
||||
});
|
||||
});
|
||||
});
|
||||
94
cms/static/js/spec/views/modals/edit_xblock_spec.js
Normal file
94
cms/static/js/spec/views/modals/edit_xblock_spec.js
Normal file
@@ -0,0 +1,94 @@
|
||||
define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers",
|
||||
"js/views/modals/edit_xblock", "js/models/xblock_info"],
|
||||
function ($, _, create_sinon, edit_helpers, EditXBlockModal, XBlockInfo) {
|
||||
|
||||
describe("EditXBlockModal", function() {
|
||||
var model, modal, showModal;
|
||||
|
||||
showModal = function(requests, mockHtml) {
|
||||
var xblockElement = $('.xblock');
|
||||
return edit_helpers.showEditModal(requests, xblockElement, model, mockHtml);
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
edit_helpers.installEditTemplates();
|
||||
appendSetFixtures('<div class="xblock" data-locator="mock-xblock" data-display-name="Mock XBlock"></div>');
|
||||
model = new XBlockInfo({
|
||||
id: 'testCourse/branch/published/block/verticalFFF',
|
||||
display_name: 'Test Unit',
|
||||
category: 'vertical'
|
||||
});
|
||||
});
|
||||
|
||||
describe("Editing an xblock", function() {
|
||||
var mockXBlockEditorHtml;
|
||||
|
||||
mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore');
|
||||
|
||||
beforeEach(function () {
|
||||
window.MockXBlock = function(runtime, element) {
|
||||
return { };
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.MockXBlock = null;
|
||||
});
|
||||
|
||||
it('can show itself', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
modal = showModal(requests, mockXBlockEditorHtml);
|
||||
expect(modal.$('.wrapper-modal-window')).toHaveClass('is-shown');
|
||||
edit_helpers.cancelModal(modal);
|
||||
expect(modal.$('.wrapper-modal-window')).not.toHaveClass('is-shown');
|
||||
});
|
||||
|
||||
it('does not show any editor mode buttons', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
modal = showModal(requests, mockXBlockEditorHtml);
|
||||
expect(modal.$('.editor-modes a').length).toBe(0);
|
||||
edit_helpers.cancelModal(modal);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Editing an xmodule", function() {
|
||||
var mockXModuleEditorHtml;
|
||||
|
||||
mockXModuleEditorHtml = readFixtures('mock/mock-xmodule-editor.underscore');
|
||||
|
||||
beforeEach(function() {
|
||||
// Mock the VerticalDescriptor so that the module can be rendered
|
||||
window.VerticalDescriptor = XModule.Descriptor;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.VerticalDescriptor = null;
|
||||
});
|
||||
|
||||
it('can render itself', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
|
||||
// Show the modal using a mock xblock response
|
||||
modal = showModal(requests, mockXModuleEditorHtml);
|
||||
expect(modal.$('.wrapper-modal-window')).toHaveClass('is-shown');
|
||||
edit_helpers.cancelModal(modal);
|
||||
expect(modal.$('.wrapper-modal-window')).not.toHaveClass('is-shown');
|
||||
});
|
||||
|
||||
it('shows the correct default buttons', function() {
|
||||
var requests = create_sinon.requests(this),
|
||||
editorButton,
|
||||
settingsButton;
|
||||
modal = showModal(requests, mockXModuleEditorHtml);
|
||||
expect(modal.$('.editor-modes a').length).toBe(2);
|
||||
editorButton = modal.$('.editor-button');
|
||||
settingsButton = modal.$('.settings-button');
|
||||
expect(editorButton.length).toBe(1);
|
||||
expect(editorButton).toHaveClass('is-set');
|
||||
expect(settingsButton.length).toBe(1);
|
||||
expect(settingsButton).not.toHaveClass('is-set');
|
||||
edit_helpers.cancelModal(modal);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
define([ "jquery", "js/spec/create_sinon", "URI",
|
||||
define([ "jquery", "js/spec_helpers/create_sinon", "URI",
|
||||
"js/views/paging", "js/views/paging_header", "js/views/paging_footer",
|
||||
"js/models/asset", "js/collections/asset" ],
|
||||
function ($, create_sinon, URI, PagingView, PagingHeader, PagingFooter, AssetModel, AssetCollection) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define(["coffee/src/views/unit", "js/models/module_info", "js/spec/create_sinon", "js/views/feedback_notification",
|
||||
define(["coffee/src/views/unit", "js/models/module_info", "js/spec_helpers/create_sinon", "js/views/feedback_notification",
|
||||
"jasmine-stealth"],
|
||||
function (UnitEditView, ModuleModel, create_sinon, NotificationView) {
|
||||
var verifyJSON = function (requests, json) {
|
||||
|
||||
80
cms/static/js/spec/views/xblock_container_spec.js
Normal file
80
cms/static/js/spec/views/xblock_container_spec.js
Normal file
@@ -0,0 +1,80 @@
|
||||
define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers",
|
||||
"js/views/xblock_container", "js/models/xblock_info"],
|
||||
function ($, create_sinon, edit_helpers, XBlockContainerView, XBlockInfo) {
|
||||
|
||||
describe("XBlockContainerView", function() {
|
||||
var model, containerView, respondWithMockXBlockEditorFragment, mockContainerView;
|
||||
|
||||
mockContainerView = readFixtures('mock/mock-container-view.underscore');
|
||||
|
||||
beforeEach(function () {
|
||||
edit_helpers.installEditTemplates();
|
||||
appendSetFixtures(mockContainerView);
|
||||
|
||||
model = new XBlockInfo({
|
||||
id: 'testCourse/branch/published/block/verticalFFF',
|
||||
display_name: 'Test Unit',
|
||||
category: 'vertical'
|
||||
});
|
||||
containerView = new XBlockContainerView({
|
||||
model: model,
|
||||
el: $('#content')
|
||||
});
|
||||
});
|
||||
|
||||
respondWithMockXBlockEditorFragment = function(requests, response) {
|
||||
var requestIndex = requests.length - 1;
|
||||
create_sinon.respondWithJson(requests, response, requestIndex);
|
||||
};
|
||||
|
||||
describe("Editing an xblock", function() {
|
||||
var mockContainerXBlockHtml,
|
||||
mockXBlockEditorHtml;
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
window.MockXBlock = function(runtime, element) {
|
||||
return { };
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.MockXBlock = null;
|
||||
});
|
||||
|
||||
mockContainerXBlockHtml = readFixtures('mock/mock-container-xblock.underscore');
|
||||
mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore');
|
||||
|
||||
it('can render itself', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
containerView.render();
|
||||
respondWithMockXBlockEditorFragment(requests, {
|
||||
html: mockContainerXBlockHtml,
|
||||
"resources": []
|
||||
});
|
||||
|
||||
expect(containerView.$el.select('.xblock-header')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
it('can show an edit modal for a child xblock', function() {
|
||||
var requests = create_sinon.requests(this),
|
||||
editButtons;
|
||||
containerView.render();
|
||||
respondWithMockXBlockEditorFragment(requests, {
|
||||
html: mockContainerXBlockHtml,
|
||||
"resources": []
|
||||
});
|
||||
editButtons = containerView.$('.edit-button');
|
||||
expect(editButtons.length).toBe(4);
|
||||
editButtons.first().click();
|
||||
create_sinon.respondWithJson(requests, {
|
||||
html: mockXBlockEditorHtml,
|
||||
"resources": []
|
||||
});
|
||||
expect($('.wrapper-modal-window')).toHaveClass('is-shown');
|
||||
edit_helpers.cancelModal();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
76
cms/static/js/spec/views/xblock_editor_spec.js
Normal file
76
cms/static/js/spec/views/xblock_editor_spec.js
Normal file
@@ -0,0 +1,76 @@
|
||||
define([ "jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers",
|
||||
"js/views/xblock_editor", "js/models/xblock_info"],
|
||||
function ($, create_sinon, edit_helpers, XBlockEditorView, XBlockInfo) {
|
||||
|
||||
describe("XBlockEditorView", function() {
|
||||
var model, editor;
|
||||
|
||||
beforeEach(function () {
|
||||
model = new XBlockInfo({
|
||||
id: 'testCourse/branch/published/block/verticalFFF',
|
||||
display_name: 'Test Unit',
|
||||
category: 'vertical'
|
||||
});
|
||||
editor = new XBlockEditorView({
|
||||
model: model
|
||||
});
|
||||
});
|
||||
|
||||
describe("Editing an xblock", function() {
|
||||
var mockXBlockEditorHtml;
|
||||
|
||||
beforeEach(function () {
|
||||
window.MockXBlock = function(runtime, element) {
|
||||
return { };
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.MockXBlock = null;
|
||||
});
|
||||
|
||||
mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore');
|
||||
|
||||
it('can render itself', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
editor.render();
|
||||
create_sinon.respondWithJson(requests, {
|
||||
html: mockXBlockEditorHtml,
|
||||
"resources": []
|
||||
});
|
||||
|
||||
expect(editor.$el.select('.xblock-header')).toBeTruthy();
|
||||
expect(editor.getMode()).toEqual('editor');
|
||||
});
|
||||
});
|
||||
|
||||
describe("Editing an xmodule", function() {
|
||||
var mockXModuleEditorHtml;
|
||||
|
||||
mockXModuleEditorHtml = readFixtures('mock/mock-xmodule-editor.underscore');
|
||||
|
||||
beforeEach(function() {
|
||||
edit_helpers.installEditTemplates();
|
||||
|
||||
// Mock the VerticalDescriptor so that the module can be rendered
|
||||
window.VerticalDescriptor = XModule.Descriptor;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.VerticalDescriptor = null;
|
||||
});
|
||||
|
||||
it('can render itself', function() {
|
||||
var requests = create_sinon.requests(this);
|
||||
editor.render();
|
||||
create_sinon.respondWithJson(requests, {
|
||||
html: mockXModuleEditorHtml,
|
||||
"resources": []
|
||||
});
|
||||
|
||||
expect(editor.$el.select('.xblock-header')).toBeTruthy();
|
||||
expect(editor.getMode()).toEqual('editor');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
define([ "jquery", "js/spec/create_sinon", "URI", "js/views/xblock", "js/models/xblock_info",
|
||||
define([ "jquery", "js/spec_helpers/create_sinon", "URI", "js/views/xblock", "js/models/xblock_info",
|
||||
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
|
||||
function ($, create_sinon, URI, XBlockView, XBlockInfo) {
|
||||
|
||||
|
||||
60
cms/static/js/spec_helpers/edit_helpers.js
Normal file
60
cms/static/js/spec_helpers/edit_helpers.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Provides helper methods for invoking Studio editors in Jasmine tests.
|
||||
*/
|
||||
define(["jquery", "js/spec_helpers/create_sinon", "js/views/modals/edit_xblock",
|
||||
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
|
||||
function($, create_sinon, EditXBlockModal) {
|
||||
|
||||
var editorTemplate = readFixtures('metadata-editor.underscore'),
|
||||
numberEntryTemplate = readFixtures('metadata-number-entry.underscore'),
|
||||
stringEntryTemplate = readFixtures('metadata-string-entry.underscore'),
|
||||
feedbackTemplate = readFixtures('system-feedback.underscore'),
|
||||
editXBlockModalTemplate = readFixtures('edit-xblock-modal.underscore'),
|
||||
editorModeButtonTemplate = readFixtures('editor-mode-button.underscore'),
|
||||
installEditTemplates,
|
||||
showEditModal,
|
||||
cancelModal;
|
||||
|
||||
installEditTemplates = function() {
|
||||
setFixtures($("<script>", { id: "system-feedback-tpl", type: "text/template" }).text(feedbackTemplate));
|
||||
|
||||
// Add templates needed by the edit XBlock modal
|
||||
appendSetFixtures($("<script>", { id: "edit-xblock-modal-tpl", type: "text/template" }).text(editXBlockModalTemplate));
|
||||
appendSetFixtures($("<script>", { id: "editor-mode-button-tpl", type: "text/template" }).text(editorModeButtonTemplate));
|
||||
|
||||
// Add templates needed by the settings editor
|
||||
appendSetFixtures($("<script>", {id: "metadata-editor-tpl", type: "text/template"}).text(editorTemplate));
|
||||
appendSetFixtures($("<script>", {id: "metadata-number-entry", type: "text/template"}).text(numberEntryTemplate));
|
||||
appendSetFixtures($("<script>", {id: "metadata-string-entry", type: "text/template"}).text(stringEntryTemplate));
|
||||
};
|
||||
|
||||
|
||||
showEditModal = function(requests, xblockElement, model, mockHtml) {
|
||||
var modal = new EditXBlockModal({});
|
||||
modal.edit(xblockElement, model);
|
||||
create_sinon.respondWithJson(requests, {
|
||||
html: mockHtml,
|
||||
"resources": []
|
||||
});
|
||||
return modal;
|
||||
};
|
||||
|
||||
cancelModal = function(modal) {
|
||||
var modalElement, cancelButton;
|
||||
if (modal) {
|
||||
modalElement = modal.$el;
|
||||
} else {
|
||||
modalElement = $('.wrapper-modal-window');
|
||||
}
|
||||
cancelButton = modalElement.find('.action-cancel');
|
||||
expect(cancelButton.length).toBe(1);
|
||||
cancelButton.click();
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
"installEditTemplates": installEditTemplates,
|
||||
"showEditModal": showEditModal,
|
||||
"cancelModal": cancelModal
|
||||
};
|
||||
});
|
||||
@@ -19,9 +19,9 @@ define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"],
|
||||
constructor: function(options) {
|
||||
_.bindAll(this, 'beforeRender', 'render', 'afterRender');
|
||||
var _this = this;
|
||||
this.render = _.wrap(this.render, function (render) {
|
||||
this.render = _.wrap(this.render, function (render, options) {
|
||||
_this.beforeRender();
|
||||
render();
|
||||
render(options);
|
||||
_this.afterRender();
|
||||
return _this;
|
||||
});
|
||||
@@ -43,9 +43,11 @@ define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"],
|
||||
|
||||
toggleExpandCollapse: function(event) {
|
||||
var target = $(event.target);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
target.closest('.expand-collapse').toggleClass('expand').toggleClass('collapse');
|
||||
target.closest('.is-collapsible, .window').toggleClass('collapsed');
|
||||
target.closest('.is-collapsible').children('article').slideToggle();
|
||||
},
|
||||
|
||||
showLoadingIndicator: function() {
|
||||
|
||||
25
cms/static/js/views/modals/base_modal.js
Normal file
25
cms/static/js/views/modals/base_modal.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* This is a base modal implementation that provides common utilities.
|
||||
*/
|
||||
define(["jquery", "underscore", "underscore.string", "gettext", "js/views/baseview"],
|
||||
function($, _, str, gettext, BaseView) {
|
||||
var BaseModal = BaseView.extend({
|
||||
options: $.extend({}, BaseView.prototype.options, {
|
||||
type: "prompt",
|
||||
closeIcon: false,
|
||||
icon: false
|
||||
}),
|
||||
|
||||
show: function() {
|
||||
$('body').addClass('modal-window-is-shown');
|
||||
this.$('.wrapper-modal-window').addClass('is-shown');
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
$('body').removeClass('modal-window-is-shown');
|
||||
this.$('.wrapper-modal-window').removeClass('is-shown');
|
||||
}
|
||||
});
|
||||
|
||||
return BaseModal;
|
||||
});
|
||||
160
cms/static/js/views/modals/edit_xblock.js
Normal file
160
cms/static/js/views/modals/edit_xblock.js
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* The EditXBlockModal is a Backbone view that shows an xblock editor in a modal window.
|
||||
* It is invoked using the edit method which is passed an existing rendered xblock,
|
||||
* and upon save an optional refresh function can be invoked to update the display.
|
||||
*/
|
||||
define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
|
||||
"js/models/xblock_info", "js/views/xblock_editor"],
|
||||
function($, _, gettext, BaseModal, XBlockInfo, XBlockEditorView) {
|
||||
var EditXBlockModal = BaseModal.extend({
|
||||
events : {
|
||||
"click .action-save": "save",
|
||||
"click .action-cancel": "cancel",
|
||||
"click .action-modes a": "changeMode"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.template = _.template($("#edit-xblock-modal-tpl").text());
|
||||
this.editorModeButtonTemplate = _.template($("#editor-mode-button-tpl").text());
|
||||
},
|
||||
|
||||
/**
|
||||
* Show an edit modal for the specified xblock
|
||||
* @param xblockElement The
|
||||
* @param rootXBlockInfo
|
||||
* @param options
|
||||
*/
|
||||
edit: function(xblockElement, rootXBlockInfo, options) {
|
||||
this.xblockElement = xblockElement;
|
||||
this.xblockInfo = this.findXBlockInfo(xblockElement, rootXBlockInfo);
|
||||
this.editOptions = options;
|
||||
this.render();
|
||||
this.show();
|
||||
// Display the xblock after the modal is shown as there are some xblocks
|
||||
// that depend upon being visible when they initialize, e.g. the problem xmodule.
|
||||
this.displayXBlock();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var xblockInfo = this.xblockInfo;
|
||||
this.$el.html(this.template({
|
||||
xblockInfo: xblockInfo
|
||||
}));
|
||||
},
|
||||
|
||||
displayXBlock: function() {
|
||||
var xblockInfo = this.xblockInfo,
|
||||
editorView = new XBlockEditorView({
|
||||
el: this.$('.xblock-editor'),
|
||||
model: xblockInfo
|
||||
});
|
||||
this.editorView = editorView;
|
||||
editorView.render({
|
||||
success: _.bind(this.onDisplayXBlock, this)
|
||||
});
|
||||
},
|
||||
|
||||
onDisplayXBlock: function() {
|
||||
var editorView = this.editorView,
|
||||
displayName,
|
||||
title;
|
||||
displayName = editorView.getDisplayName();
|
||||
title = interpolate(gettext("Editing: %(title)s"), { title: displayName }, true);
|
||||
if (editorView.hasCustomTabs()) {
|
||||
// Hide the modal's header as the custom editor provides its own
|
||||
this.$('.modal-header').hide();
|
||||
|
||||
// Update the custom editor's title
|
||||
editorView.$('.component-name').text(title);
|
||||
} else {
|
||||
this.$('.modal-window-title').text(title);
|
||||
if (editorView.getMetadataEditor()) {
|
||||
this.addModeButton('editor', gettext("Editor"));
|
||||
this.addModeButton('settings', gettext("Settings"));
|
||||
this.selectMode(editorView.mode);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
changeMode: function(event) {
|
||||
var parent = $(event.target.parentElement),
|
||||
mode = parent.data('mode');
|
||||
event.preventDefault();
|
||||
this.selectMode(mode);
|
||||
},
|
||||
|
||||
selectMode: function(mode) {
|
||||
var editorView = this.editorView,
|
||||
buttonSelector;
|
||||
editorView.selectMode(mode);
|
||||
this.$('.editor-modes a').removeClass('is-set');
|
||||
if (mode) {
|
||||
buttonSelector = '.' + mode + '-button';
|
||||
this.$(buttonSelector).addClass('is-set');
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function(event) {
|
||||
event.preventDefault();
|
||||
this.hide();
|
||||
},
|
||||
|
||||
save: function(event) {
|
||||
var self = this,
|
||||
xblockInfo = this.xblockInfo,
|
||||
refresh = self.editOptions.refresh;
|
||||
event.preventDefault();
|
||||
this.editorView.save({
|
||||
success: function() {
|
||||
self.hide();
|
||||
self.$el.html("");
|
||||
if (refresh) {
|
||||
refresh(xblockInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
BaseModal.prototype.hide.call(this);
|
||||
|
||||
// Completely clear the contents of the modal
|
||||
this.undelegateEvents();
|
||||
this.$el.html("");
|
||||
},
|
||||
|
||||
findXBlockInfo: function(xblockElement, defaultXBlockInfo) {
|
||||
var xblockInfo = defaultXBlockInfo,
|
||||
locator,
|
||||
displayName,
|
||||
category;
|
||||
if (xblockElement.length > 0) {
|
||||
locator = xblockElement.data('locator');
|
||||
displayName = xblockElement.data('display-name');
|
||||
category = xblockElement.data('category');
|
||||
if (!displayName) {
|
||||
displayName = category;
|
||||
if (!category) {
|
||||
displayName = gettext('Empty');
|
||||
}
|
||||
}
|
||||
xblockInfo = new XBlockInfo({
|
||||
id: locator,
|
||||
display_name: displayName,
|
||||
category: category
|
||||
});
|
||||
}
|
||||
return xblockInfo;
|
||||
},
|
||||
|
||||
addModeButton: function(mode, displayName) {
|
||||
var buttonPanel = this.$('.editor-modes');
|
||||
buttonPanel.append(this.editorModeButtonTemplate({
|
||||
mode: mode,
|
||||
displayName: displayName
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return EditXBlockModal;
|
||||
});
|
||||
@@ -37,7 +37,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
|
||||
|
||||
|
||||
var closeModalNew = function () {
|
||||
$('body').removeClass('dialog-is-shown');
|
||||
$('body').removeClass('modal-window-is-shown');
|
||||
$('.edit-section-publish-settings').removeClass('is-shown');
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
|
||||
$modal.find('.save-button').hide();
|
||||
}
|
||||
$modal.find('.section-name').html('"' + $(this).closest('.courseware-section').find('.section-name-span').text() + '"');
|
||||
$('body').addClass('dialog-is-shown');
|
||||
$('body').addClass('modal-window-is-shown');
|
||||
$('.edit-section-publish-settings').addClass('is-shown');
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["js/views/baseview", "underscore", "jquery", "jquery.form"],
|
||||
function(BaseView, _, $) {
|
||||
var UploadDialog = BaseView.extend({
|
||||
define(["js/views/modals/base_modal", "underscore", "jquery", "jquery.form"],
|
||||
function(BaseModal, _, $) {
|
||||
var UploadDialog = BaseModal.extend({
|
||||
options: {
|
||||
shown: true,
|
||||
successMessageTimeout: 2000 // 2 seconds
|
||||
@@ -51,13 +51,13 @@ var UploadDialog = BaseView.extend({
|
||||
show: function(e) {
|
||||
if(e && e.preventDefault) { e.preventDefault(); }
|
||||
this.options.shown = true;
|
||||
$("body").addClass('dialog-is-shown');
|
||||
$("body").addClass('modal-window-is-shown');
|
||||
return this.render();
|
||||
},
|
||||
hide: function(e) {
|
||||
if(e && e.preventDefault) { e.preventDefault(); }
|
||||
this.options.shown = false;
|
||||
$("body").removeClass('dialog-is-shown');
|
||||
$("body").removeClass('modal-window-is-shown');
|
||||
return this.render();
|
||||
},
|
||||
hideAndRemove: function(e) {
|
||||
|
||||
@@ -20,7 +20,7 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
this.collection = new MetadataCollection(models);
|
||||
|
||||
// initialize MetadataView.Editor
|
||||
this.metadataEditor = new MetadataView.Editor({
|
||||
this.settingsView = new MetadataView.Editor({
|
||||
el: this.$el,
|
||||
collection: this.collection
|
||||
});
|
||||
@@ -72,7 +72,7 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
syncBasicTab: function (metadataCollection, metadataView) {
|
||||
var result = [],
|
||||
getField = Utils.getField,
|
||||
component_locator = this.$el.closest('.component').data('locator'),
|
||||
component_locator = this.$el.closest('[data-locator]').data('locator'),
|
||||
subs = getField(metadataCollection, 'sub'),
|
||||
values = {},
|
||||
videoUrl, metadata, modifiedValues;
|
||||
|
||||
@@ -31,7 +31,7 @@ function($, Backbone, _, Utils, FileUploader, gettext) {
|
||||
initialize: function () {
|
||||
_.bindAll(this);
|
||||
|
||||
this.component_locator = this.$el.closest('.component').data('locator');
|
||||
this.component_locator = this.$el.closest('[data-locator]').data('locator');
|
||||
|
||||
this.fileUploader = new FileUploader({
|
||||
el: this.$el,
|
||||
|
||||
@@ -46,7 +46,7 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
|
||||
_.debounce(_.bind(this.inputHandler, this), this.inputDelay)
|
||||
);
|
||||
|
||||
this.component_locator = this.$el.closest('.component').data('locator');
|
||||
this.component_locator = this.$el.closest('[data-locator]').data('locator');
|
||||
},
|
||||
|
||||
render: function () {
|
||||
@@ -55,7 +55,7 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
|
||||
.apply(this, arguments);
|
||||
|
||||
var self = this,
|
||||
component_locator = this.$el.closest('.component').data('locator'),
|
||||
component_locator = this.$el.closest('[data-locator]').data('locator'),
|
||||
videoList = this.getVideoObjectsList(),
|
||||
|
||||
showServerError = function (response) {
|
||||
|
||||
@@ -9,27 +9,49 @@ define(["jquery", "underscore", "js/views/baseview", "xblock/runtime.v1"],
|
||||
this.view = this.options.view;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render: function(options) {
|
||||
var self = this,
|
||||
view = this.view;
|
||||
view = this.view,
|
||||
xblockInfo = this.model,
|
||||
xblockUrl = xblockInfo.url();
|
||||
return $.ajax({
|
||||
url: decodeURIComponent(this.model.url()) + "/" + view,
|
||||
url: decodeURIComponent(xblockUrl) + "/" + view,
|
||||
type: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
headers: { Accept: 'application/json' },
|
||||
success: function(fragment) {
|
||||
var wrapper = self.$el,
|
||||
xblock;
|
||||
self.renderXBlockFragment(fragment, wrapper).done(function() {
|
||||
xblock = self.$('.xblock').first();
|
||||
XBlock.initializeBlock(xblock);
|
||||
self.delegateEvents();
|
||||
});
|
||||
self.handleXBlockFragment(fragment, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleXBlockFragment: function(fragment, options) {
|
||||
var wrapper = this.$el,
|
||||
xblockElement,
|
||||
success = options ? options.success : null,
|
||||
xblock;
|
||||
this.renderXBlockFragment(fragment, wrapper);
|
||||
xblockElement = this.$('.xblock').first();
|
||||
xblock = XBlock.initializeBlock(xblockElement);
|
||||
this.xblock = xblock;
|
||||
this.xblockReady(xblock);
|
||||
if (success) {
|
||||
success(xblock);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called upon successful rendering of an xblock.
|
||||
*/
|
||||
xblockReady: function(xblock) {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the specified xblock has children.
|
||||
*/
|
||||
hasChildXBlocks: function() {
|
||||
return this.$('.wrapper-xblock').length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders an xblock fragment into the specified element. The fragment has two attributes:
|
||||
|
||||
94
cms/static/js/views/xblock_container.js
Normal file
94
cms/static/js/views/xblock_container.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* XBlockContainerView is used to display an xblock which has children, and allows the
|
||||
* user to interact with the children.
|
||||
*/
|
||||
define(["jquery", "underscore", "js/views/baseview", "js/views/xblock", "js/views/modals/edit_xblock"],
|
||||
function ($, _, BaseView, XBlockView, EditXBlockModal) {
|
||||
|
||||
var XBlockContainerView = BaseView.extend({
|
||||
// takes XBlockInfo as a model
|
||||
|
||||
view: 'container_preview',
|
||||
|
||||
initialize: function() {
|
||||
BaseView.prototype.initialize.call(this);
|
||||
this.noContentElement = this.$('.no-container-content');
|
||||
this.xblockView = new XBlockView({
|
||||
el: this.$('.wrapper-xblock'),
|
||||
model: this.model,
|
||||
view: this.view
|
||||
});
|
||||
},
|
||||
|
||||
render: function(options) {
|
||||
var self = this,
|
||||
noContentElement = this.noContentElement,
|
||||
xblockView = this.xblockView,
|
||||
loadingElement = this.$('.ui-loading');
|
||||
loadingElement.removeClass('is-hidden');
|
||||
|
||||
// Hide both blocks until we know which one to show
|
||||
noContentElement.addClass('is-hidden');
|
||||
xblockView.$el.addClass('is-hidden');
|
||||
|
||||
// Add actions to any root buttons
|
||||
self.addButtonActions(this.$el);
|
||||
|
||||
// Render the xblock
|
||||
xblockView.render({
|
||||
success: function(xblock) {
|
||||
if (xblockView.hasChildXBlocks()) {
|
||||
xblockView.$el.removeClass('is-hidden');
|
||||
self.addButtonActions(xblockView.$el);
|
||||
} else {
|
||||
noContentElement.removeClass('is-hidden');
|
||||
}
|
||||
loadingElement.addClass('is-hidden');
|
||||
self.delegateEvents();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
findXBlockElement: function(target) {
|
||||
return $(target).closest('[data-locator]');
|
||||
},
|
||||
|
||||
addButtonActions: function(element) {
|
||||
var self = this;
|
||||
element.find('.edit-button').click(function(event) {
|
||||
var modal,
|
||||
target = event.target,
|
||||
xblockElement = self.findXBlockElement(target);
|
||||
event.preventDefault();
|
||||
modal = new EditXBlockModal({
|
||||
el: $('.edit-xblock-modal')
|
||||
});
|
||||
modal.edit(xblockElement, self.model,
|
||||
{
|
||||
refresh: function(xblockInfo) {
|
||||
self.refreshXBlock(xblockInfo, xblockElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
refreshXBlock: function(xblockInfo, xblockElement) {
|
||||
var self = this,
|
||||
temporaryView;
|
||||
// Create a temporary view to render the updated XBlock into
|
||||
temporaryView = new XBlockView({
|
||||
el: xblockElement,
|
||||
model: xblockInfo,
|
||||
view: this.view
|
||||
});
|
||||
temporaryView.render({
|
||||
success: function() {
|
||||
temporaryView.unbind(); // Remove the temporary view
|
||||
self.addButtonActions(xblockElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return XBlockContainerView;
|
||||
}); // end define();
|
||||
173
cms/static/js/views/xblock_editor.js
Normal file
173
cms/static/js/views/xblock_editor.js
Normal file
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* XBlockEditorView displays the authoring view of an xblock, and allows the user to switch between
|
||||
* the available modes.
|
||||
*/
|
||||
define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/xblock",
|
||||
"js/views/metadata", "js/collections/metadata", "jquery.inputnumber"],
|
||||
function ($, _, gettext, NotificationView, XBlockView, MetadataView, MetadataCollection) {
|
||||
|
||||
var XBlockEditorView = XBlockView.extend({
|
||||
// takes XBlockInfo as a model
|
||||
|
||||
options: $.extend({}, XBlockView.prototype.options, {
|
||||
view: 'studio_view'
|
||||
}),
|
||||
|
||||
initialize: function() {
|
||||
XBlockView.prototype.initialize.call(this);
|
||||
this.view = this.options.view;
|
||||
},
|
||||
|
||||
xblockReady: function(xblock) {
|
||||
XBlockView.prototype.xblockReady.call(this, xblock);
|
||||
this.initializeEditors();
|
||||
},
|
||||
|
||||
initializeEditors: function() {
|
||||
var metadataEditor,
|
||||
defaultMode = 'editor';
|
||||
metadataEditor = this.createMetadataEditor();
|
||||
this.metadataEditor = metadataEditor;
|
||||
if (!this.hasCustomTabs()) {
|
||||
if (this.getDataEditor()) {
|
||||
defaultMode = 'editor';
|
||||
} else if (metadataEditor) {
|
||||
defaultMode = 'settings';
|
||||
}
|
||||
this.selectMode(defaultMode);
|
||||
}
|
||||
},
|
||||
|
||||
hasCustomTabs: function() {
|
||||
return this.$('.editor-with-tabs').length > 0;
|
||||
},
|
||||
|
||||
createMetadataEditor: function() {
|
||||
var metadataEditor,
|
||||
metadataData,
|
||||
models = [],
|
||||
key,
|
||||
xblock = this.xblock,
|
||||
metadataView = null;
|
||||
metadataEditor = this.$('.metadata_edit');
|
||||
if (metadataEditor.length === 1) {
|
||||
metadataData = metadataEditor.data('metadata');
|
||||
for (key in metadataData) {
|
||||
if (metadataData.hasOwnProperty(key)) {
|
||||
models.push(metadataData[key]);
|
||||
}
|
||||
}
|
||||
metadataView = new MetadataView.Editor({
|
||||
el: metadataEditor,
|
||||
collection: new MetadataCollection(models)
|
||||
});
|
||||
if (xblock.setMetadataEditor) {
|
||||
xblock.setMetadataEditor(metadataView);
|
||||
}
|
||||
}
|
||||
return metadataView;
|
||||
},
|
||||
|
||||
getDataEditor: function() {
|
||||
var editor = this.$('.wrapper-comp-editor');
|
||||
return editor.length === 1 ? editor : null;
|
||||
},
|
||||
|
||||
getMetadataEditor: function() {
|
||||
return this.metadataEditor;
|
||||
},
|
||||
|
||||
save: function(options) {
|
||||
var xblock = this.xblock,
|
||||
xblockInfo = this.model,
|
||||
data,
|
||||
saving;
|
||||
data = this.getXBlockData();
|
||||
saving = new NotificationView.Mini({
|
||||
title: gettext('Saving…')
|
||||
});
|
||||
saving.show();
|
||||
return xblockInfo.save(data).done(function() {
|
||||
var success = options.success;
|
||||
saving.hide();
|
||||
if (success) {
|
||||
success();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getXBlockData: function() {
|
||||
var xblock = this.xblock,
|
||||
metadataEditor = this.getMetadataEditor(),
|
||||
metadata,
|
||||
data;
|
||||
data = xblock.save();
|
||||
metadata = data.metadata;
|
||||
if (metadataEditor) {
|
||||
metadata = _.extend(metadata || {}, this.getChangedMetadata());
|
||||
}
|
||||
data.metadata = metadata;
|
||||
return data;
|
||||
},
|
||||
|
||||
getDisplayName: function() {
|
||||
var metadataEditor = this.getMetadataEditor();
|
||||
if (metadataEditor) {
|
||||
return metadataEditor.getDisplayName();
|
||||
}
|
||||
return "How do we get the display name now?";
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the metadata that has changed in the editor. This is a combination of the metadata
|
||||
* modified in the "Settings" editor, as well as any custom metadata provided by the component.
|
||||
*/
|
||||
getChangedMetadata: function() {
|
||||
var metadataEditor = this.getMetadataEditor();
|
||||
return _.extend(metadataEditor.getModifiedMetadataValues(), this.getCustomMetadata());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns custom metadata defined by a particular xmodule that aren't part of the metadata editor.
|
||||
* In particular, this is used for LaTeX high level source.
|
||||
*/
|
||||
getCustomMetadata: function() {
|
||||
// Walk through the set of elements which have the 'data-metadata_name' attribute and
|
||||
// build up an object to pass back to the server on the subsequent POST.
|
||||
// Note that these values will always be sent back on POST, even if they did not actually change.
|
||||
var metadata = {},
|
||||
metadataNameElements;
|
||||
metadataNameElements = this.$('[data-metadata-name]');
|
||||
metadataNameElements.each(function (element) {
|
||||
var key = $(element).data("metadata-name"),
|
||||
value = element.value;
|
||||
metadata[key] = value;
|
||||
});
|
||||
return metadata;
|
||||
},
|
||||
|
||||
getMode: function() {
|
||||
return this.mode;
|
||||
},
|
||||
|
||||
selectMode: function(mode) {
|
||||
var showEditor = mode === 'editor',
|
||||
dataEditor = this.getDataEditor(),
|
||||
metadataEditor = this.getMetadataEditor();
|
||||
if (dataEditor) {
|
||||
this.setEditorActivation(dataEditor, showEditor);
|
||||
}
|
||||
if (metadataEditor) {
|
||||
this.setEditorActivation(metadataEditor.$el, !showEditor);
|
||||
}
|
||||
this.mode = mode;
|
||||
},
|
||||
|
||||
setEditorActivation: function(editor, isActive) {
|
||||
editor.removeClass('is-active').removeClass('is-inactive');
|
||||
editor.addClass(isActive ? 'is-active' : 'is-inactive');
|
||||
}
|
||||
});
|
||||
|
||||
return XBlockEditorView;
|
||||
}); // end define();
|
||||
@@ -754,10 +754,10 @@ hr.divide {
|
||||
.tooltip {
|
||||
@include font-size(12);
|
||||
@include transition(opacity $tmg-f3 ease-out 0s);
|
||||
@extend %ui-depth5;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10000;
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
|
||||
@@ -59,6 +59,20 @@ textarea.text {
|
||||
// forms - additional UI
|
||||
form {
|
||||
|
||||
.field {
|
||||
margin-bottom: ($baseline/2);
|
||||
}
|
||||
|
||||
label {
|
||||
@include font-size(14);
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
@extend %t-copy-sub1;
|
||||
}
|
||||
|
||||
.note {
|
||||
@include box-sizing(border-box);
|
||||
|
||||
|
||||
297
cms/static/sass/elements/_modal-window.scss
Normal file
297
cms/static/sass/elements/_modal-window.scss
Normal file
@@ -0,0 +1,297 @@
|
||||
// studio - elements - modal-window
|
||||
// ========================
|
||||
|
||||
// start with the view/body
|
||||
[class*="view-"] {
|
||||
overflow: auto;
|
||||
|
||||
// modal-window backdrop covers the window
|
||||
.wrapper-modal-window {
|
||||
@extend %ui-depth4;
|
||||
@include transition(all $tmg-f2 ease-in-out);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
overflow: scroll;
|
||||
background: $black-t2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
margin-right: -0.25em; /* Adjusts for spacing */
|
||||
}
|
||||
|
||||
// basic modal content
|
||||
.modal-window {
|
||||
@include box-sizing(border-box);
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 50%;
|
||||
box-shadow: 0px 0px 7px $shadow-d1;
|
||||
border-radius: ($baseline/5);
|
||||
background-color: $gray-l4;
|
||||
padding: 7px;
|
||||
text-align: left;
|
||||
|
||||
.modal-content {
|
||||
box-shadow: 0 0 3px $shadow-d1;
|
||||
background-color: $white;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
.title {
|
||||
@extend %t-title5;
|
||||
margin-bottom: ($baseline/2);
|
||||
font-weight: 600;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.message {
|
||||
@extend %t-copy-sub1;
|
||||
margin: $baseline 0;
|
||||
color: $gray;
|
||||
|
||||
&.error {
|
||||
border: 0;
|
||||
border-top: 3px solid $red-d2;
|
||||
background-color: $red;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.success {
|
||||
border: 0;
|
||||
border-top: 3px solid $green-d2;
|
||||
background-color: $green;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-chin,
|
||||
.modal-actions {
|
||||
padding: ($baseline*0.75) 2% ($baseline/2) 2%;
|
||||
|
||||
.action-item {
|
||||
@extend %t-action3;
|
||||
display: inline-block;
|
||||
margin-right: ($baseline*0.75);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.action-primary {
|
||||
@include blue-button();
|
||||
@include font-size(14); // needed due to bad button mixins for now
|
||||
border-color: $blue-d1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $blue;
|
||||
|
||||
&:hover {
|
||||
color: $blue-s2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// small modals - editors and dialogs
|
||||
.modal-sm {
|
||||
width: 30%;
|
||||
min-width: ($baseline*15);
|
||||
|
||||
.modal-content {
|
||||
padding: 5% 4%;
|
||||
}
|
||||
}
|
||||
|
||||
// medium modals - forms and interactives
|
||||
.modal-med {
|
||||
width: 40%;
|
||||
min-width: ($baseline*18);
|
||||
|
||||
.modal-content {
|
||||
padding: 4%;
|
||||
}
|
||||
}
|
||||
|
||||
// large modals - component editors and interactives
|
||||
.modal-lg {
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
top: ($baseline*2);
|
||||
width: 65%;
|
||||
height: auto;
|
||||
min-width: ($baseline*30);
|
||||
|
||||
&.modal-editor {
|
||||
|
||||
.modal-header {
|
||||
margin: ($baseline/4) ($baseline/2);
|
||||
|
||||
.title {
|
||||
width: 48%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.editor-modes {
|
||||
width: 49%;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
|
||||
.action-item {
|
||||
display: inline-block;
|
||||
margin-left: ($baseline/2);
|
||||
|
||||
.editor-button,
|
||||
.settings-button {
|
||||
@extend %btn-secondary-gray;
|
||||
@extend %t-copy-sub1;
|
||||
border: 0;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover {
|
||||
}
|
||||
|
||||
&.is-set {
|
||||
background-color: $gray-d1;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// specific modal overrides
|
||||
|
||||
// component editor
|
||||
.modal-window {
|
||||
.wrapper-comp-settings {
|
||||
|
||||
.list-input {
|
||||
|
||||
&.settings-list {
|
||||
height: ($baseline*21.25);
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// special overrides for video module editor/hidden tab editors
|
||||
.wrapper-modal-window .modal-type-video {
|
||||
|
||||
.modal-content {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.xmodule_edit.xmodule_VideoDescriptor .editor-with-tabs {
|
||||
|
||||
.edit-header {
|
||||
border: 0;
|
||||
background-color: $gray-l4;
|
||||
padding: ($baseline/2);
|
||||
|
||||
.component-name {
|
||||
@extend %t-title5;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 48%;
|
||||
margin-left: ($baseline/2);
|
||||
font-weight: 600;
|
||||
color: $black;
|
||||
|
||||
em {
|
||||
color: inherit;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-tabs {
|
||||
display: inline-block;
|
||||
width: 48%;
|
||||
position: relative;
|
||||
top: auto;
|
||||
right: auto;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
|
||||
.inner_tab_wrap {
|
||||
padding: 0;
|
||||
|
||||
a.tab {
|
||||
@extend %btn-secondary-gray;
|
||||
@extend %t-copy-sub1;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
padding: ($baseline/4) ($baseline/2);
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
|
||||
&.current {
|
||||
background-color: $gray-d1;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-wrapper {
|
||||
box-shadow: 0 0 3px $shadow-d1;
|
||||
|
||||
.component-tab {
|
||||
border-top: 1px solid $gray-l3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// modal-window set-up
|
||||
.wrapper-modal-window {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
|
||||
.modal-window {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// modal-window showing/hiding
|
||||
&.modal-window-is-shown {
|
||||
overflow: hidden;
|
||||
|
||||
.wrapper-modal-window.is-shown {
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
display: block;
|
||||
|
||||
.modal-window {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,12 @@ body.course.feature-upload {
|
||||
}
|
||||
}
|
||||
|
||||
.message-status.error {
|
||||
border-color: $red-d2;
|
||||
background: $red-l1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding: ($baseline*0.75) $baseline ($baseline/2) $baseline;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// extends - UI archetypes - xblock rendering
|
||||
%wrap-xblock {
|
||||
margin: $baseline;
|
||||
margin: ($baseline/2);
|
||||
border: 1px solid $gray-l4;
|
||||
border-radius: ($baseline/5);
|
||||
background: $white;
|
||||
@@ -23,12 +23,12 @@
|
||||
border-radius: ($baseline/5) ($baseline/5) 0 0;
|
||||
min-height: ($baseline*2.5);
|
||||
background-color: $gray-l6;
|
||||
padding: ($baseline/2) ($baseline/2) ($baseline/2) ($baseline);
|
||||
|
||||
.header-details {
|
||||
@extend %cont-truncated;
|
||||
@extend %ui-justify-left-flex;
|
||||
@include ui-flexbox();
|
||||
padding-left: flex-gutter();
|
||||
width: flex-grid(6,12);
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
// UI: xblock render
|
||||
.xblock-render {
|
||||
@extend %anim-fadeIn;
|
||||
// @extend %anim-fadeIn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,14 +64,20 @@
|
||||
.expand-collapse {
|
||||
@extend %expand-collapse;
|
||||
margin: 0 ($baseline/4);
|
||||
height: ($baseline*1.25);
|
||||
width: $baseline;
|
||||
}
|
||||
|
||||
&.collapsed .xblock-render {
|
||||
display: none;
|
||||
//display: none;
|
||||
}
|
||||
|
||||
.action-view {
|
||||
|
||||
.action-button {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.action-button-text {
|
||||
padding-right: ($baseline/5);
|
||||
padding-left: 0;
|
||||
|
||||
@@ -39,3 +39,4 @@
|
||||
@import 'elements/vendor'; // overrides to vendor-provided styling
|
||||
@import 'elements/uploads';
|
||||
@import 'elements/edit_dialog';
|
||||
@import 'elements/modal-window';
|
||||
|
||||
@@ -14,6 +14,14 @@ body.view-container {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.wrapper-mast .mast.has-navigation .nav-actions {
|
||||
bottom: -($baseline*.75);
|
||||
|
||||
.nav-item .button {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
float: left;
|
||||
@@ -22,6 +30,23 @@ body.view-container {
|
||||
.content-primary {
|
||||
margin-right: flex-gutter();
|
||||
width: flex-grid(9,12);
|
||||
|
||||
.no-container-content {
|
||||
@extend %ui-well;
|
||||
padding: ($baseline*2);
|
||||
background-color: $gray-l4;
|
||||
text-align: center;
|
||||
color: $gray;
|
||||
|
||||
.new-button {
|
||||
@include font-size(14);
|
||||
margin-left: $baseline;
|
||||
|
||||
[class^="icon-"] {
|
||||
margin-right: ($baseline/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-supplementary {
|
||||
@@ -35,10 +60,35 @@ body.view-container {
|
||||
margin-bottom: $baseline;
|
||||
border-top: 5px solid $blue;
|
||||
background-color: $white;
|
||||
padding: ($baseline*.75) ($baseline*.75) ($baseline) ($baseline*.75);
|
||||
|
||||
.pub-status {
|
||||
@extend %t-title6;
|
||||
display: block;
|
||||
background-color: $blue-l2;
|
||||
padding: ($baseline/2) ($baseline*.75);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.published {
|
||||
border-top: 5px solid $blue;
|
||||
|
||||
.pub-status {
|
||||
background-color: $blue-t0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.draft {
|
||||
border-top: 5px solid $gray-l1;
|
||||
|
||||
.pub-status {
|
||||
background-color: $gray-l4;
|
||||
}
|
||||
}
|
||||
|
||||
.copy {
|
||||
@extend %t-copy-sub1;
|
||||
padding: ($baseline*.75) ($baseline*.75) ($baseline) ($baseline*.75);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +129,7 @@ body.view-container .content-primary {
|
||||
}
|
||||
|
||||
.xblock-render {
|
||||
margin: 0 $baseline $baseline $baseline;
|
||||
margin: ($baseline/2);
|
||||
}
|
||||
|
||||
// STATE: nesting level xblock is collapsed
|
||||
@@ -108,7 +158,8 @@ body.view-container .content-primary {
|
||||
}
|
||||
|
||||
.xblock-render {
|
||||
margin: $baseline;
|
||||
margin: ($baseline/2);
|
||||
padding: ($baseline/2);
|
||||
}
|
||||
|
||||
// STATE: xBlock containers styled as rows.
|
||||
@@ -145,3 +196,4 @@ body.view-container .content-primary {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,7 @@
|
||||
height: ($baseline*1.5);
|
||||
border-radius: 3px;
|
||||
color: $gray-l1;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover {
|
||||
background-color: $blue;
|
||||
|
||||
@@ -633,10 +633,11 @@ body.course.unit,.view-unit {
|
||||
|
||||
//component-setting-entry
|
||||
.field.comp-setting-entry {
|
||||
background-color: $white;
|
||||
padding: $baseline;
|
||||
border-bottom: 1px solid $gray-l2;
|
||||
opacity: 0.7;
|
||||
margin: 0 $baseline;
|
||||
border-top: 1px solid $gray-l4;
|
||||
background-color: $white;
|
||||
padding: $baseline ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
//margin-bottom: 0;
|
||||
@@ -1085,6 +1086,7 @@ body.unit {
|
||||
|
||||
.delete-draft {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.delete-button,
|
||||
@@ -1353,6 +1355,7 @@ body.unit .xblock-type-container {
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
border-radius: ($baseline/5);
|
||||
padding: ($baseline/2);
|
||||
|
||||
.xblock-details {
|
||||
font-size: .9em;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<%!
|
||||
import json
|
||||
|
||||
from contentstore.utils import PublishState
|
||||
from contentstore.views.helpers import xblock_studio_url
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
@@ -12,27 +13,33 @@ from django.utils.translation import ugettext as _
|
||||
<%namespace name="units" file="widgets/units.html" />
|
||||
|
||||
|
||||
<%block name="header_extras">
|
||||
% for template_name in ["edit-xblock-modal", "editor-mode-button"]:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
% endfor
|
||||
</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<%
|
||||
xblock_info = {
|
||||
main_xblock_info = {
|
||||
'id': str(xblock_locator),
|
||||
'display-name': xblock.display_name_with_default,
|
||||
'display_name': xblock.display_name_with_default,
|
||||
'category': xblock.category,
|
||||
};
|
||||
%>
|
||||
<script type='text/javascript'>
|
||||
require(["domReady!", "jquery", "js/models/xblock_info", "js/views/xblock",
|
||||
"js/models/module_info", "coffee/src/views/unit",
|
||||
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1",
|
||||
"js/views/metadata", "js/collections/metadata"],
|
||||
function(doc, $, XBlockInfo, XBlockView) {
|
||||
var model,
|
||||
view;
|
||||
model = new XBlockInfo(${json.dumps(xblock_info) | n});
|
||||
view = new XBlockView({
|
||||
el: $('.wrapper-xblock.level-page').first(),
|
||||
model: model,
|
||||
view: 'container_preview'
|
||||
require(["domReady!", "jquery", "js/models/xblock_info", "js/views/xblock_container",
|
||||
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
|
||||
function(doc, $, XBlockInfo, XBlockContainerView) {
|
||||
var view, mainXBlockInfo;
|
||||
|
||||
mainXBlockInfo = new XBlockInfo(${json.dumps(main_xblock_info) | n});
|
||||
|
||||
view = new XBlockContainerView({
|
||||
el: $('#content'),
|
||||
model: mainXBlockInfo
|
||||
});
|
||||
view.render();
|
||||
});
|
||||
@@ -43,7 +50,7 @@ xblock_info = {
|
||||
<%block name="content">
|
||||
|
||||
|
||||
<div class="wrapper-mast wrapper">
|
||||
<div class="wrapper-mast wrapper" data-location="" data-display-name="" data-category="">
|
||||
<header class="mast has-actions has-navigation">
|
||||
<h1 class="page-header">
|
||||
<small class="navigation navigation-parents">
|
||||
@@ -63,9 +70,6 @@ xblock_info = {
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">${_("Page Actions")}</h3>
|
||||
<ul>
|
||||
<li class="sr nav-item">
|
||||
${_("No Actions")}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -76,22 +80,50 @@ xblock_info = {
|
||||
<section class="content-area">
|
||||
|
||||
<article class="content-primary window">
|
||||
<section class="wrapper-xblock level-page" data-locator="${xblock_locator}"/>
|
||||
<section class="wrapper-xblock level-page is-hidden" data-locator="${xblock_locator}">
|
||||
</section>
|
||||
<div class="no-container-content is-hidden">
|
||||
<p>${_("This page has no content yet.")}</p>
|
||||
</div>
|
||||
<div class="ui-loading">
|
||||
<p><span class="spin"><i class="icon-refresh"></i></span> <span class="copy">${_("Loading...")}</span></p>
|
||||
</div>
|
||||
</article>
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit-publishing">
|
||||
<h3 class="title-3">${_("Publishing Status")}</h3>
|
||||
<p class="copy">${_('This content is published with unit {unit_name}.').format(
|
||||
unit_name=u'<a href="{unit_address}">{unit_display_name}</a>'.format(
|
||||
unit_address=xblock_studio_url(unit),
|
||||
unit_display_name=unit.display_name_with_default,
|
||||
)
|
||||
)}</p>
|
||||
</div>
|
||||
% if unit:
|
||||
% if unit_publish_state == PublishState.public:
|
||||
<div class="bit-publishing published">
|
||||
<h3 class="title pub-status"><span class="sr">${_("Publishing Status")} </span>${_("Published")}</h3>
|
||||
<p class="copy">
|
||||
<%
|
||||
unit_link=u'<a href="{unit_address}">{unit_display_name}</a>'.format(
|
||||
unit_address=xblock_studio_url(unit),
|
||||
unit_display_name=unit.display_name_with_default,
|
||||
)
|
||||
%>
|
||||
${_('To make changes to the content of this page, you need to edit unit {unit_link} as a draft.'
|
||||
).format(unit_link=unit_link)}
|
||||
</p>
|
||||
</div>
|
||||
% else:
|
||||
<div class="bit-publishing draft">
|
||||
<h3 class="title pub-status"><span class="sr">${_("Publishing Status")} </span>${_("Draft")}</h3>
|
||||
<p class="copy">
|
||||
<%
|
||||
unit_link=u'<a href="{unit_address}">{unit_display_name}</a>'.format(
|
||||
unit_address=xblock_studio_url(unit),
|
||||
unit_display_name=unit.display_name_with_default,
|
||||
)
|
||||
%>
|
||||
${_('You can edit the content of this page, and your changes will be published with unit {unit_link}.').format(unit_link=unit_link)}
|
||||
</p>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("What can I do on this page?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("You can view course components that contain other components on this page. In the case of experiment blocks, this allows you to confirm that you have properly configured your experiment groups.")}</li>
|
||||
<li class="item-detail">${_("You can view and edit course components that contain other components on this page. In the case of experiment blocks, this allows you to confirm that you have properly configured your experiment groups and make changes to existing content.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -99,4 +131,6 @@ xblock_info = {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-xblock-modal"/>
|
||||
|
||||
</%block>
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
<%block name="title">${_("Pages")}</%block>
|
||||
<%block name="bodyclass">is-signedin course view-static-pages</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
% for template_name in ["edit-xblock-modal", "editor-mode-button"]:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
% endfor
|
||||
</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type='text/javascript'>
|
||||
require(["js/models/explicit_url", "coffee/src/views/tabs",
|
||||
@@ -170,4 +178,6 @@
|
||||
<span class="label">${_("close modal")}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="edit-xblock-modal"/>
|
||||
</%block>
|
||||
|
||||
30
cms/templates/js/edit-xblock-modal.underscore
Normal file
30
cms/templates/js/edit-xblock-modal.underscore
Normal file
@@ -0,0 +1,30 @@
|
||||
<div
|
||||
class="wrapper wrapper-modal-window wrapper-modal-window-edit-xblock"
|
||||
aria-describedby="modal-window-description"
|
||||
aria-labelledby="modal-window-title"
|
||||
aria-hidden=""
|
||||
role="dialog">
|
||||
<div class="modal-window confirm modal-editor modal-lg modal-type-<%= xblockInfo.get('category') %>">
|
||||
<div class="edit-xblock-modal" action="#">
|
||||
<div class="modal-header">
|
||||
<h2 class="title modal-window-title"></h2>
|
||||
<ul class="editor-modes action-list action-modes">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="xblock-editor" data-locator="<%= xblockInfo.get('id') %>"></div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<h3 class="sr"><%= gettext("Actions") %></h3>
|
||||
<ul>
|
||||
<li class="action-item">
|
||||
<a href="#" class="button action-primary action-save"><%= gettext("Save") %></a>
|
||||
</li>
|
||||
<li class="action-item">
|
||||
<a href="#" class="button action-secondary action-cancel"><%= gettext("Cancel") %></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
3
cms/templates/js/editor-mode-button.underscore
Normal file
3
cms/templates/js/editor-mode-button.underscore
Normal file
@@ -0,0 +1,3 @@
|
||||
<li class="action-item" data-mode="<%= mode %>">
|
||||
<a href="#" class="<%= mode %>-button"><%= displayName %></a>
|
||||
</li>
|
||||
122
cms/templates/js/mock/mock-container-view.underscore
Normal file
122
cms/templates/js/mock/mock-container-view.underscore
Normal file
@@ -0,0 +1,122 @@
|
||||
<div id="content">
|
||||
|
||||
|
||||
|
||||
<div class="wrapper-mast wrapper" data-location="" data-display-name="" data-category="">
|
||||
<header class="mast has-actions has-navigation">
|
||||
<h1 class="page-header">
|
||||
<small class="navigation navigation-parents">
|
||||
|
||||
<a href="/unit/AndyA.ABT101.2014/branch/draft/block/vertical8eb" class="navigation-link navigation-parent">Unit 1</a>
|
||||
<a href="#" class="navigation-link navigation-current">Nested Vertical Test</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<section class="content-area">
|
||||
|
||||
<article class="content-primary window">
|
||||
<section class="wrapper-xblock level-page" data-locator="AndyA.ABT101.2014/branch/draft/block/vertical131">
|
||||
</section>
|
||||
<div class="no-container-content is-hidden">
|
||||
<p>This page has no content yet.</p>
|
||||
</div>
|
||||
<div class="ui-loading is-hidden">
|
||||
<p><span class="spin"><i class="icon-refresh"></i></span> <span class="copy">Loading...</span></p>
|
||||
</div>
|
||||
</article>
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit-publishing">
|
||||
<h3 class="title-3">Publishing Status</h3>
|
||||
<p class="copy">
|
||||
|
||||
This content is published with unit <a href="/unit/AndyA.ABT101.2014/branch/draft/block/vertical8eb">Unit 1</a>.
|
||||
Say something useful about <a href="/unit/AndyA.ABT101.2014/branch/draft/block/vertical8eb">Unit 1</a> being in draft or private mode.
|
||||
</p>
|
||||
</div>
|
||||
<div class="bit">
|
||||
<h3 class="title-3">What can I do on this page?</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">You can view course components that contain other components on this page. In the case of experiment blocks, this allows you to confirm that you have properly configured your experiment groups.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-xblock-modal">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
require(['js/sock']);
|
||||
</script>
|
||||
|
||||
|
||||
<div class="wrapper-sock wrapper">
|
||||
<ul class="list-actions list-cta">
|
||||
<li class="action-item">
|
||||
<a href="#sock" class="cta cta-show-sock"><i class="icon-question-sign"></i> <span class="copy">Looking for Help with Studio?</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="wrapper-inner wrapper">
|
||||
<section class="sock" id="sock">
|
||||
<header>
|
||||
<h2 class="title sr">edX Studio Help</h2>
|
||||
</header>
|
||||
|
||||
<div class="support">
|
||||
<h3 class="title">Studio Support</h3>
|
||||
|
||||
<div class="copy">
|
||||
<p>Need help with Studio? Creating a course is complex, so we're here to help. Take advantage of our documentation, help center, as well as our edX101 introduction course for course authors.</p>
|
||||
</div>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action-item">
|
||||
<a href="http://files.edx.org/Getting_Started_with_Studio.pdf" class="action action-primary" title="This is a PDF Document">Download Studio Documentation</a>
|
||||
<span class="tip">How to use Studio to build your course</span>
|
||||
</li>
|
||||
<li class="action-item">
|
||||
<a href="http://help.edge.edx.org/" rel="external" class="action action-primary" title="This link will open in a new browser window/tab">Studio Help Center</a>
|
||||
<span class="tip">Studio Help Center</span>
|
||||
</li>
|
||||
<li class="action-item">
|
||||
<a href="https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about" rel="external" class="action action-primary" title="This link will open in a new browser window/tab">Enroll in edX101</a>
|
||||
<span class="tip">How to use Studio to build your course</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="feedback">
|
||||
<h3 class="title">Contact us about Studio</h3>
|
||||
|
||||
<div class="copy">
|
||||
<p>Have problems, questions, or suggestions about Studio? We're also here to listen to any feedback you want to share.</p>
|
||||
</div>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action-item">
|
||||
|
||||
<a href="http://help.edge.edx.org/discussion/new" class="action action-primary show-tender" title="Use our feedback tool, Tender, to share your feedback"><i class="icon-comments"></i>Contact Us</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="page-notification"></div>
|
||||
</div>
|
||||
300
cms/templates/js/mock/mock-container-xblock.underscore
Normal file
300
cms/templates/js/mock/mock-container-xblock.underscore
Normal file
@@ -0,0 +1,300 @@
|
||||
<section class="wrapper-xblock level-page" data-locator="AndyA.ABT101.2014/branch/draft/block/vertical131">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
<a href="#" data-tooltip="Expand or Collapse" class="action expand-collapse collapse">
|
||||
<i class="icon-caret-down ui-toggle-expansion"></i>
|
||||
<span class="sr">Expand or Collapse</span>
|
||||
</a>
|
||||
<span>Nested Vertical Test</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="sr action-item">No Actions</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_VerticalModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_vertical;_131a499ddaa3474194c1aa2eced34455" data-type="None" data-block-type="vertical">
|
||||
<div class="vert-mod">
|
||||
<div class="vert vert-0" data-id="i4x://AndyA/ABT101/vertical/2758bbc495dd40d59050da15b40bd9a5">
|
||||
|
||||
<section class="wrapper-xblock level-nesting is-collapsible" data-locator="AndyA.ABT101.2014/branch/draft/block/vertical275">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
<a href="#" data-tooltip="Expand or Collapse" class="action expand-collapse collapse">
|
||||
<i class="icon-caret-down ui-toggle-expansion"></i>
|
||||
<span class="sr">Expand or Collapse</span>
|
||||
</a>
|
||||
<span>Group A</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="sr action-item">No Actions</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_VerticalModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_vertical;_2758bbc495dd40d59050da15b40bd9a5" data-type="None" data-block-type="vertical">
|
||||
<div class="vert-mod">
|
||||
<div class="vert vert-0" data-id="i4x://AndyA/ABT101/html/4471618afafb45bfb86cbe511973e225">
|
||||
|
||||
<section class="wrapper-xblock level-element" data-locator="AndyA.ABT101.2014/branch/draft/block/html447" data-display-name="" data-category="html">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="action-item action-edit">
|
||||
<a href="#" class="edit-button action-button">
|
||||
<i class="icon-edit"></i>
|
||||
<span class="action-button-text">Edit</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_HtmlModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_html;_4471618afafb45bfb86cbe511973e225" data-type="HTMLModule" data-block-type="html">
|
||||
<p>This is group A.</p>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<div class="vert vert-1" data-id="i4x://AndyA/ABT101/video/fbd800d0bdbd4cb69ac70c47c9f699e1">
|
||||
|
||||
<section class="wrapper-xblock level-element" data-locator="AndyA.ABT101.2014/branch/draft/block/videofbd" data-display-name="Video" data-category="video">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
Video
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="action-item action-edit">
|
||||
<a href="#" class="edit-button action-button">
|
||||
<i class="icon-edit"></i>
|
||||
<span class="action-button-text">Edit</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_VideoModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_video;_fbd800d0bdbd4cb69ac70c47c9f699e1" data-type="Video" data-block-type="video">
|
||||
|
||||
|
||||
<h2>Video</h2>
|
||||
|
||||
<div id="video_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1" class="video closed is-initialized" data-streams="1.00:OEoXaMPEzfM" data-save-state-url="/preview/xblock/i4x:;_;_AndyA;_ABT101;_video;_fbd800d0bdbd4cb69ac70c47c9f699e1/handler/xmodule_handler/save_user_state" data-caption-data-dir="None" data-show-captions="true" data-general-speed="1.0" data-speed="null" data-saved-video-position="0.0" data-start="0.0" data-end="0.0" data-transcript-language="en" data-transcript-languages="{"en": "English"}" data-autoplay="False" data-yt-test-timeout="1500" data-yt-api-url="www.youtube.com/iframe_api" data-yt-test-url="gdata.youtube.com/feeds/api/videos/" data-transcript-translation-url="/preview/xblock/i4x:;_;_AndyA;_ABT101;_video;_fbd800d0bdbd4cb69ac70c47c9f699e1/handler/transcript/translation" data-transcript-available-translations-url="/preview/xblock/i4x:;_;_AndyA;_ABT101;_video;_fbd800d0bdbd4cb69ac70c47c9f699e1/handler/transcript/available_translations" data-autohide-html5="False" tabindex="-1">
|
||||
<div class="focus_grabber first" tabindex="-1"></div>
|
||||
|
||||
<div class="tc-wrapper">
|
||||
<a href="#before-transcript_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1" class="nav-skip sr">Skip to a navigable version of this video's transcript.</a>
|
||||
|
||||
<article class="video-wrapper">
|
||||
<span tabindex="-1" class="spinner" aria-hidden="true" aria-label="Loading video player"></span>
|
||||
<span tabindex="-1" class="btn-play is-hidden" aria-hidden="true" aria-label="Play video"></span>
|
||||
<div class="video-player-pre"></div>
|
||||
<section class="video-player">
|
||||
<iframe id="i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1" frameborder="0" allowfullscreen="1" title="YouTube video player" width="640" height="360" src="https://www.youtube.com/embed/OEoXaMPEzfM?controls=0&wmode=transparent&rel=0&showinfo=0&enablejsapi=1&modestbranding=1&html5=1&origin=http%3A%2F%2Flocalhost%3A8001" style="height: 476.4375px; width: 847px; top: -31.71875px; left: 0px;"></iframe>
|
||||
<h3 class="hidden">ERROR: No playable video sources found!</h3>
|
||||
</section>
|
||||
<div class="video-player-post"></div>
|
||||
<section class="video-controls" style="">
|
||||
<div class="slider ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all" title="Video position" aria-disabled="false" tabindex="-1" style=""><div class="ui-slider-range ui-widget-header ui-slider-range-min" style="width: 0%;"></div><a class="ui-slider-handle ui-state-default ui-corner-all" href="#" role="slider" title="Video position" aria-disabled="false" aria-valuetext="0 seconds" style="left: 0%;"></a></div>
|
||||
<div>
|
||||
<ul class="vcr">
|
||||
<li><a class="video_control play" href="#" title="Play" role="button" aria-disabled="false"></a></li>
|
||||
<li><div class="vidtime">0:00 / 1:56</div></li>
|
||||
</ul>
|
||||
<div class="secondary-controls"><div class="speeds menu-container">
|
||||
<a href="#" title="Speeds" role="button" aria-disabled="false">
|
||||
<h3>Speed</h3>
|
||||
<p class="active">1.0x</p>
|
||||
</a>
|
||||
<ol class="video_speeds menu" role="menu"><li data-speed="2.0" role="presentation"><a class="speed_link" href="#" role="menuitem">2.0x</a></li><li data-speed="1.50" role="presentation"><a class="speed_link" href="#" role="menuitem">1.50x</a></li><li data-speed="1.0" role="presentation" class="active"><a class="speed_link" href="#" role="menuitem">1.0x</a></li><li data-speed="0.50" role="presentation"><a class="speed_link" href="#" role="menuitem">0.50x</a></li></ol>
|
||||
</div><div class="volume">
|
||||
<a href="#" title="Volume" role="button" aria-disabled="false" aria-label="Volume"></a>
|
||||
<div class="volume-slider-container">
|
||||
<div class="volume-slider ui-slider ui-slider-vertical ui-widget ui-widget-content ui-corner-all" aria-disabled="false"><div class="ui-slider-range ui-widget-header ui-slider-range-min" style="height: 100%;"></div><a class="ui-slider-handle ui-state-default ui-corner-all" href="#" role="slider" title="Volume" aria-disabled="false" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" aria-valuetext="Maximum" style="bottom: 100%;"></a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<a href="#" class="add-fullscreen" title="Fill browser" role="button" aria-disabled="false">Fill browser</a>
|
||||
<a href="#" class="quality_control" title="HD off" role="button" aria-disabled="false" style="display: inline;">HD off</a>
|
||||
|
||||
<div class="lang menu-container">
|
||||
<a href="#" class="hide-subtitles" title="Turn on captions" role="button" aria-="" disabled="false" style="display: none;">Turn on captions</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<a class="nav-skip sr" id="before-transcript_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1" href="#after-transcript_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1">Skip to end of transcript.</a>
|
||||
</article>
|
||||
|
||||
<ol id="transcript-captions" class="subtitles" tabindex="0" role="group" aria-label="Activating an item in this group will spool the video to the corresponding time point. To skip transcript, go to previous item." style="max-height: 476px;">
|
||||
<li style=""></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<a class="nav-skip sr" id="after-transcript_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1" href="#before-transcript_i4x-AndyA-ABT101-video-fbd800d0bdbd4cb69ac70c47c9f699e1">Go back to start of transcript.</a>
|
||||
|
||||
<div class="focus_grabber last" tabindex="-1"></div>
|
||||
<ul class="wrapper-downloads">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<div class="vert vert-1" data-id="i4x://AndyA/ABT101/vertical/c5c8b27c2c5546e784432f3b2b6cf2ea">
|
||||
|
||||
<section class="wrapper-xblock level-nesting is-collapsible" data-locator="AndyA.ABT101.2014/branch/draft/block/verticalc5c">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
<a href="#" data-tooltip="Expand or Collapse" class="action expand-collapse collapse">
|
||||
<i class="icon-caret-down ui-toggle-expansion"></i>
|
||||
<span class="sr">Expand or Collapse</span>
|
||||
</a>
|
||||
<span>Group B</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="sr action-item">No Actions</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_VerticalModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_vertical;_c5c8b27c2c5546e784432f3b2b6cf2ea" data-type="None" data-block-type="vertical">
|
||||
<div class="vert-mod">
|
||||
<div class="vert vert-0" data-id="i4x://AndyA/ABT101/html/dd6ef295fda74a639842e1a49c66b2c7">
|
||||
|
||||
<section class="wrapper-xblock level-element" data-locator="AndyA.ABT101.2014/branch/draft/block/htmldd6" data-display-name="Text" data-category="html">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
Text
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="action-item action-edit">
|
||||
<a href="#" class="edit-button action-button">
|
||||
<i class="icon-edit"></i>
|
||||
<span class="action-button-text">Edit</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_HtmlModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_html;_dd6ef295fda74a639842e1a49c66b2c7" data-type="HTMLModule" data-block-type="html">
|
||||
<p>This is group B.</p>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<div class="vert vert-1" data-id="i4x://AndyA/ABT101/problem/b40ecbe4ed1b4280ae93e2a158edae6f">
|
||||
|
||||
<section class="wrapper-xblock level-element" data-locator="AndyA.ABT101.2014/branch/draft/block/problemb40" data-display-name="Checkboxes" data-category="problem">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
Checkboxes
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<ul class="actions-list">
|
||||
<li class="action-item action-edit">
|
||||
<a href="#" class="edit-button action-button">
|
||||
<i class="icon-edit"></i>
|
||||
<span class="action-button-text">Edit</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<article class="xblock-render">
|
||||
<div class="xblock xblock-student_view xmodule_display xmodule_CapaModule xblock-initialized" data-runtime-class="PreviewRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_problem;_b40ecbe4ed1b4280ae93e2a158edae6f" data-type="Problem" data-block-type="problem">
|
||||
<div id="problem_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f" class="problems-wrapper" data-problem-id="i4x://AndyA/ABT101/problem/b40ecbe4ed1b4280ae93e2a158edae6f" data-url="/preview/xblock/i4x:;_;_AndyA;_ABT101;_problem;_b40ecbe4ed1b4280ae93e2a158edae6f/handler/xmodule_handler" data-progress_status="none" data-progress_detail="0/1">
|
||||
|
||||
|
||||
<h2 class="problem-header">
|
||||
Checkboxes
|
||||
</h2>
|
||||
|
||||
<div class="problem-progress">(1 point possible)</div>
|
||||
|
||||
<div class="problem" role="application">
|
||||
<div>
|
||||
<p>A checkboxes problem presents checkbox buttons for student input. Students can select more than one option presented.</p>
|
||||
<span><form class="choicegroup capa_inputtype" id="inputtype_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1">
|
||||
<div class="indicator_container">
|
||||
<span class="status unanswered" id="status_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1" aria-describedby="inputtype_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1">
|
||||
<span class="sr">
|
||||
-
|
||||
unanswered
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<fieldset role="checkboxgroup" aria-label="Select the answer that matches">
|
||||
|
||||
<label for="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_0">
|
||||
<input type="checkbox" name="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1[]" id="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_0" aria-role="radio" aria-describedby="answer_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1" value="choice_0" aria-multiselectable="true"> correct
|
||||
|
||||
</label>
|
||||
<label for="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_1">
|
||||
<input type="checkbox" name="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1[]" id="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_1" aria-role="radio" aria-describedby="answer_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1" value="choice_1" aria-multiselectable="true"> incorrect
|
||||
|
||||
</label>
|
||||
<label for="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_2">
|
||||
<input type="checkbox" name="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1[]" id="input_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1_choice_2" aria-role="radio" aria-describedby="answer_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1" value="choice_2" aria-multiselectable="true"> correct
|
||||
|
||||
</label>
|
||||
<span id="answer_i4x-AndyA-ABT101-problem-b40ecbe4ed1b4280ae93e2a158edae6f_2_1"></span>
|
||||
</fieldset>
|
||||
|
||||
</form></span>
|
||||
</div>
|
||||
|
||||
<div class="action">
|
||||
<input type="hidden" name="problem_id" value="Checkboxes">
|
||||
|
||||
<input class="check Check" type="button" value="Check">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
22
cms/templates/js/mock/mock-modal.underscore
Normal file
22
cms/templates/js/mock/mock-modal.underscore
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class="wrapper wrapper-modal-window wrapper-modal-window-mock">
|
||||
<div class="modal-window confirm modal-editor modal-lg modal-type-<%= xblockInfo.get('category') %>">
|
||||
<form class="edit-xblock-modal" action="#">
|
||||
<div class="modal-header">
|
||||
<h2 class="title modal-window-title">Mock Modal Title</h2>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="xblock-editor"></div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<ul>
|
||||
<li class="action-item">
|
||||
<a href="#" class="button action-primary action-save">Save</a>
|
||||
</li>
|
||||
<li class="action-item">
|
||||
<a href="#" class="button action-secondary action-cancel">Cancel</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
8
cms/templates/js/mock/mock-xblock-editor.underscore
Normal file
8
cms/templates/js/mock/mock-xblock-editor.underscore
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="xblock xblock-studio_view" data-runtime-version="1" data-usage-id="i4x:;_;_edX;_mock"
|
||||
data-init="MockXBlock" data-runtime-class="StudioRuntime" data-block-type="mock" tabindex="0">
|
||||
|
||||
<div class="mock-xblock">
|
||||
<h3>Mock XBlock Editor</h3>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
170
cms/templates/js/mock/mock-xmodule-editor.underscore
Normal file
170
cms/templates/js/mock/mock-xmodule-editor.underscore
Normal file
@@ -0,0 +1,170 @@
|
||||
<div class="xblock xblock-studio_view xmodule_edit xmodule_WrapperDescriptor" data-runtime-class="StudioRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_wrapper;_wrapper_l1_poll" data-type="VerticalDescriptor" data-block-type="wrapper" tabindex="0">
|
||||
<div class="wrapper-comp-editor is-active" id="editor-tab" data-base-asset-url="/c4x/AndyA/ABT101/asset/">
|
||||
</div>
|
||||
<section class="sequence-edit">
|
||||
<section class="filters wip">
|
||||
<ul>
|
||||
<li>
|
||||
<h2>Sort:</h2>
|
||||
<select>
|
||||
<option value="">Linear Order</option>
|
||||
<option value="">Recently Modified</option>
|
||||
<option value="">Type</option>
|
||||
<option value="">Alphabetically</option>
|
||||
</select>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>Filter:</h2>
|
||||
<select>
|
||||
<option value="">All content</option>
|
||||
<option value="">Videos</option>
|
||||
<option value="">Problems</option>
|
||||
<option value="">Labs</option>
|
||||
<option value="">Tutorials</option>
|
||||
<option value="">HTML</option>
|
||||
</select>
|
||||
<a href="#" class="more">More</a>
|
||||
</li>
|
||||
<li class="search">
|
||||
<input type="search" name="" id="" value="" placeholder="Search" />
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="content">
|
||||
<section class="modules">
|
||||
<ol>
|
||||
<li>
|
||||
<ol id="sortable">
|
||||
<li class="wrapper">
|
||||
<a href="#" class="module-edit"
|
||||
data-id="i4x://AndyA/ABT101/poll_question/T1_poll"
|
||||
data-type="None"
|
||||
data-preview-type="Poll">Poll Question</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li class="wrapper">
|
||||
<a href="#" class="module-edit"
|
||||
data-id="i4x://AndyA/ABT101/conditional/cond_l1_poll_yes_foo"
|
||||
data-type="SequenceDescriptor"
|
||||
data-preview-type="Conditional">Challenge question for yes</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li class="wrapper">
|
||||
<a href="#" class="module-edit"
|
||||
data-id="i4x://AndyA/ABT101/conditional/cond_l1_poll_no_foo"
|
||||
data-type="SequenceDescriptor"
|
||||
data-preview-type="Conditional">Challenge question for no</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script id="metadata-editor-tpl" type="text/template">
|
||||
<ul class="list-input settings-list">
|
||||
<% _.each(_.range(numEntries), function() { %>
|
||||
<li class="field comp-setting-entry metadata_entry">
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
</script>
|
||||
|
||||
<script id="metadata-number-entry" type="text/template">
|
||||
<div class="wrapper-comp-setting">
|
||||
\t<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name') %></label>
|
||||
\t<input class="input setting-input setting-input-number" type="number" id="<%= uniqueId %>" value='<%= model.get("value") %>'/>
|
||||
<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
|
||||
<i class="icon-undo"></i><span class="sr">"<%= gettext("Clear Value") %>"</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="tip setting-help"><%= model.get('help') %></span>
|
||||
|
||||
</script>
|
||||
<script id="metadata-string-entry" type="text/template">
|
||||
<div class="wrapper-comp-setting">
|
||||
\t<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name') %></label>
|
||||
\t<input class="input setting-input" type="text" id="<%= uniqueId %>" value='<%= model.get("value") %>'/>
|
||||
\t<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
|
||||
<i class="icon-undo"></i><span class="sr">"<%= gettext("Clear Value") %>"</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="tip setting-help"><%= model.get('help') %></span>
|
||||
|
||||
</script>
|
||||
<script id="metadata-option-entry" type="text/template">
|
||||
<div class="wrapper-comp-setting">
|
||||
<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name') %></label>
|
||||
<select class="input setting-input" id="<%= uniqueId %>" name="<%= model.get('display_name') %>">
|
||||
<% _.each(model.get('options'), function(option) { %>
|
||||
<% if (option.display_name !== undefined) { %>
|
||||
<option value="<%= option['display_name'] %>"><%= option['display_name'] %></option>
|
||||
<% } else { %>
|
||||
<option value="<%= option %>"><%= option %></option>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
</select>
|
||||
<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
|
||||
<i class="icon-undo"></i><span class="sr">"<%= gettext("Clear Value") %>"</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="tip setting-help"><%= model.get('help') %></span>
|
||||
|
||||
</script>
|
||||
<script id="metadata-list-entry" type="text/template">
|
||||
<div class="wrapper-comp-setting metadata-list-enum">
|
||||
<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name')%></label>
|
||||
<div id="<%= uniqueId %>" class="wrapper-list-settings">
|
||||
<ol class="list-settings">
|
||||
|
||||
</ol>
|
||||
|
||||
<a href="#" class="create-action create-setting">
|
||||
<i class="icon-plus"></i><%= gettext("Add") %> <span class="sr"><%= model.get('display_name')%></span>
|
||||
</a>
|
||||
</div>
|
||||
<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
|
||||
<i class="icon-undo"></i>
|
||||
<span class="sr">"<%= gettext("Clear Value") %>"</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="tip setting-help"><%= model.get('help') %></span>
|
||||
|
||||
</script>
|
||||
<script id="metadata-dict-entry" type="text/template">
|
||||
<div class="wrapper-comp-setting metadata-dict">
|
||||
<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name')%></label>
|
||||
<div id="<%= uniqueId %>" class="wrapper-dict-settings">
|
||||
<ol class="list-settings"></ol>
|
||||
<a href="#" class="create-action create-setting">
|
||||
<i class="icon-plus"></i><%= gettext("Add") %> <span class="sr"><%= model.get('display_name')%></span>
|
||||
</a>
|
||||
</div>
|
||||
<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
|
||||
<i class="icon-undo"></i>
|
||||
<span class="sr">"<%= gettext("Clear Value") %>"</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="tip setting-help"><%= model.get('help') %></span>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="wrapper-comp-settings metadata_edit" id="settings-tab" data-metadata='{"display_name": {"default_value": null, "explicitly_set": true, "display_name": "Display Name", "help": "This name appears in the horizontal navigation at the top of the page.", "type": "Generic", "value": "Poll Question", "field_name": "display_name", "options": []}, "due": {"default_value": null, "explicitly_set": false, "display_name": "due", "help": "Date that this problem is due by", "type": "Generic", "value": null, "field_name": "due", "options": []}}'/>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -1,16 +1,16 @@
|
||||
<div id="dialog-assetupload"
|
||||
class="wrapper wrapper-dialog wrapper-dialog-assetupload <% if(shown) { print('is-shown') } %>"
|
||||
class="wrapper wrapper-modal-window wrapper-modal-window-assetupload <% if(shown) { print('is-shown') } %>"
|
||||
aria-describedby="dialog-assetupload-description"
|
||||
aria-labelledby="dialog-assetupload-title"
|
||||
aria-hidden="<%= !shown %>"
|
||||
role="dialog">
|
||||
<div class="dialog confirm">
|
||||
<div class="modal-window modal-sm confirm">
|
||||
|
||||
<form class="upload-dialog" method="POST" action="<%= url %>" enctype="multipart/form-data">
|
||||
<div class="form-content">
|
||||
<div class="modal-content">
|
||||
<h2 class="title"><%= title %></h2>
|
||||
<% if(error) {%>
|
||||
<div id="upload_error" class="message message-status message-status error is-shown" name="upload_error">
|
||||
<div id="upload_error" class="message message-status error is-shown" name="upload_error">
|
||||
<p><%= error.message %></p>
|
||||
</div>
|
||||
<% } %>
|
||||
@@ -30,7 +30,7 @@
|
||||
<% } %>
|
||||
|
||||
<% if(finished) { %>
|
||||
<div id="upload_confirm" class="message message-status message-status confirm is-shown" name="upload_confirm">
|
||||
<div id="upload_confirm" class="message message-status message-status confirm success is-shown" name="upload_confirm">
|
||||
<p><%= gettext("Success!") %></p>
|
||||
</div>
|
||||
<% } %>
|
||||
@@ -38,7 +38,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="actions modal-actions">
|
||||
<h3 class="sr"><%= gettext('Form Actions') %></h3>
|
||||
<ul>
|
||||
<li class="action-item">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
from xmodule.modulestore.django import loc_mapper
|
||||
%>
|
||||
<%block name="title">${_("Course Outline")}</%block>
|
||||
<%block name="bodyclass">is-signedin course view-outline feature-edit-dialog</%block>
|
||||
<%block name="bodyclass">is-signedin course view-outline</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%namespace name="units" file="widgets/units.html" />
|
||||
@@ -277,14 +277,14 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
|
||||
<footer></footer>
|
||||
|
||||
<div
|
||||
class="wrapper wrapper-dialog wrapper-dialog-edit-sectionrelease edit-section-publish-settings"
|
||||
class="wrapper wrapper-modal-window wrapper-dialog-edit-sectionrelease edit-section-publish-settings"
|
||||
aria-describedby="dialog-edit-sectionrelease-description"
|
||||
aria-labelledby="dialog-edit-sectionrelease-title"
|
||||
aria-hidden=""
|
||||
role="dialog">
|
||||
<div class="dialog confirm">
|
||||
<div class="modal-window modal-med confirm">
|
||||
<form class="edit-sectionrelease-dialog" action="#">
|
||||
<div class="form-content">
|
||||
<div class="modal-content">
|
||||
<h2 class="title dialog-edit-sectionrelease-title">${_("Section Release Date")}</h2>
|
||||
<p id="dialog-edit-sectionrelease-description" class="message">${_('On the date set below, this section - {name} - will be released to students. Any units marked private will only be visible to admins.').format(name='<strong class="section-name"></strong>')}</p>
|
||||
<ul class="list-input picker datepair">
|
||||
@@ -298,7 +298,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="actions modal-actions">
|
||||
<h3 class="sr">${_("Form Actions")}</h3>
|
||||
<ul>
|
||||
<li class="action-item">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
% if xblock.location != xblock_context['root_xblock'].location:
|
||||
% if xblock.has_children:
|
||||
<section class="wrapper-xblock level-nesting" data-locator="${locator}">
|
||||
<section class="wrapper-xblock level-nesting" data-locator="${locator}" data-display-name="${xblock.display_name_with_default | h}" data-category="${xblock.category | h}">
|
||||
% else:
|
||||
<section class="wrapper-xblock level-element" data-locator="${locator}">
|
||||
<section class="wrapper-xblock level-element" data-locator="${locator}" data-display-name="${xblock.display_name_with_default | h}" data-category="${xblock.category | h}">
|
||||
% endif
|
||||
% endif
|
||||
<header class="xblock-header">
|
||||
@@ -19,18 +19,6 @@
|
||||
<span class="action-button-text">${_("Edit")}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="action-item action-duplicate">
|
||||
<a href="#" data-tooltip="${_("Duplicate")}" class="duplicate-button action-button">
|
||||
<i class="icon-copy"></i>
|
||||
<span class="sr">${_("Duplicate this component")}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="action-item action-delete">
|
||||
<a href="#" data-tooltip="${_("Delete")}" class="delete-button action-button">
|
||||
<i class="icon-trash"></i>
|
||||
<span class="sr">${_("Delete this component")}</span>
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<%block name="title">${_("Textbooks")}</%block>
|
||||
<%block name="bodyclass">is-signedin course view-textbooks feature-upload</%block>
|
||||
<%block name="bodyclass">is-signedin course view-textbooks</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
% for template_name in ["edit-textbook", "show-textbook", "edit-chapter", "no-textbooks", "upload-dialog"]:
|
||||
|
||||
@@ -9,6 +9,14 @@ from xmodule.modulestore.django import loc_mapper
|
||||
<%block name="title">${_("Individual Unit")}</%block>
|
||||
<%block name="bodyclass">is-signedin course unit view-unit feature-upload</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
% for template_name in ["image-modal", "edit-xblock-modal", "editor-mode-button", "upload-dialog"]:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
% endfor
|
||||
</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type='text/javascript'>
|
||||
require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit", "jquery.ui",
|
||||
@@ -34,15 +42,8 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="image-modal-tpl">
|
||||
<%static:include path="js/imageModal.underscore" />
|
||||
</script>
|
||||
<script type="text/template" id="upload-dialog-tpl">
|
||||
<%static:include path="js/upload-dialog.underscore" />
|
||||
</script>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="main-wrapper edit-state-${unit_state}" data-locator="${unit_locator}">
|
||||
<div class="inner-wrapper">
|
||||
@@ -58,8 +59,8 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
|
||||
<article class="unit-body window">
|
||||
<p class="unit-name-input"><label for="unit-display-name-input">${_("Display Name:")}</label><input type="text" value="${unit.display_name_with_default | h}" id="unit-display-name-input" class="unit-display-name-input" /></p>
|
||||
<ol class="components">
|
||||
% for locator in locators:
|
||||
<li class="component" data-locator="${locator}"/>
|
||||
% for (locator, xblock) in zip(locators, xblocks):
|
||||
<li class="component" data-locator="${locator}" data-display-name="${xblock.display_name_with_default |h}" data-category="${xblock.category}"/>
|
||||
% endfor
|
||||
<li class="new-component-item adding">
|
||||
<div class="new-component">
|
||||
@@ -223,7 +224,5 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="edit-xblock-modal"></div>
|
||||
</%block>
|
||||
|
||||
@@ -36,10 +36,12 @@ from django.core.urlresolvers import reverse
|
||||
<section class="content-area">
|
||||
<article class="content-primary">
|
||||
|
||||
<div class="ui-loading">
|
||||
<p><span class="spin"><i class="icon-refresh"></i></span> <span class="copy">Loading...</span></p>
|
||||
</div>
|
||||
|
||||
<div class="ui-loading">
|
||||
<p><span class="spin"><i class="icon-refresh"></i></span> <span class="copy">Loading...</span></p>
|
||||
</div>
|
||||
<div class="no-container-content">
|
||||
<p>This page has no content yet.</p>
|
||||
</div>
|
||||
<section class="wrapper-xblock level-page">
|
||||
<header class="xblock-header">
|
||||
<div class="header-details">
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
hlskey = hashlib.md5(module.location.url()).hexdigest()
|
||||
%>
|
||||
|
||||
<section id="hls-modal-${hlskey}" class="upload-modal modal" style="width:90%!important; left:5%!important; margin-left:0px!important; height:90%; overflow:auto; background:#ECF7D3;" >
|
||||
<a href="#" class="close-button"><span class="close-icon"></span></a>
|
||||
<section id="hls-modal-${hlskey}" class="upload-modal modal" style="overflow:scroll; background:#ddd; padding: 10px 0;box-shadow: 0 0 5px 0 #555;" >
|
||||
<a href="#" class="close-button" style="background: #eee; float: right;padding: 5px 10px; margin: 0 10px 0 0; border: 0;"><span class="close-icon"></span></a>
|
||||
<div id="hls-div">
|
||||
<header>
|
||||
<h2>High Level Source Editing</h2>
|
||||
@@ -33,7 +33,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
hlsmodal = $('#hls-modal-${hlskey}');
|
||||
|
||||
hlstrig.leanModal({
|
||||
top: 40,
|
||||
top: 0,
|
||||
overlay: 0.8,
|
||||
closeButton: ".close-button"
|
||||
});
|
||||
@@ -46,7 +46,10 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
$('#hls-trig-${hlskey}').click(function() {
|
||||
|
||||
## this ought to be done using css instead
|
||||
hlsmodal.attr('style', function(i,s) { return s + ' margin-left:0px !important; left:5%' });
|
||||
// hlsmodal.attr('style', function(i,s) { return s + ' margin-left:0px !important; left:5%' });
|
||||
var editorH = $( window ).height() - 100;
|
||||
var editorW = $( window ).innerWidth() - 70;
|
||||
hlsmodal.attr('style', function(i,s) { return s + 'margin: 2% 0 0 10% !important; left:0; height:' + editorH + 'px;'});
|
||||
|
||||
## setup file input
|
||||
## need to insert this only after hls triggered, because otherwise it
|
||||
@@ -104,6 +107,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
hlsmodal.find('.hls-compile').click(function() {
|
||||
var el = $('#hls-modal-${hlskey}');
|
||||
compile_hls(el);
|
||||
$(el).css('display', 'none')
|
||||
});
|
||||
|
||||
// connect to server using POST (requires cross-domain-access)
|
||||
@@ -123,7 +127,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
if (xml.length == 0) {
|
||||
alert('Conversion failed! error:' + data.message);
|
||||
} else {
|
||||
el.closest('.component').find('.CodeMirror-wrap')[0].CodeMirror.setValue(xml);
|
||||
el.closest('.xblock-studio_view').find('.CodeMirror-wrap')[0].CodeMirror.setValue(xml);
|
||||
save_hls(el);
|
||||
}
|
||||
},
|
||||
@@ -149,7 +153,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
function set_raw_edit_box(data, key) {
|
||||
// get the codemirror editor for the raw-edit-box
|
||||
// it's a CodeMirror-wrap class element
|
||||
$('#hls-modal-' + key).closest('.component').find('.CodeMirror-wrap')[0].CodeMirror.setValue(data);
|
||||
$('#hls-modal-' + key).closest('.xblock-studio_view').find('.CodeMirror-wrap')[0].CodeMirror.setValue(data);
|
||||
}
|
||||
|
||||
// save button
|
||||
@@ -160,7 +164,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
|
||||
|
||||
function save_hls(el) {
|
||||
el.find('.hls-data').val(el.data('editor').getValue());
|
||||
el.closest('.component').find('.save-button').click();
|
||||
el.closest('.xblock-studio_view').find('.save-button').click();
|
||||
}
|
||||
}); // end require()
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
.editor-tabs {
|
||||
|
||||
.advanced-toggle {
|
||||
@include white-button;
|
||||
height: auto;
|
||||
margin-top: -4px;
|
||||
padding: 3px 9px;
|
||||
@@ -19,6 +18,7 @@
|
||||
|
||||
&:hover, &:focus {
|
||||
box-shadow: 0 0 0 0 !important;
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ describe "TabsEditingDescriptor", ->
|
||||
|
||||
describe "editor/settings header", ->
|
||||
it "is hidden", ->
|
||||
expect(@descriptor.element.closest(".component-editor").find(".component-edit-header")).toBeHidden()
|
||||
expect(@descriptor.element.closest(".modal-editor").find(".modal-header")).toBeHidden()
|
||||
|
||||
describe "TabsEditingDescriptor special save cases", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -8,9 +8,6 @@ class @TabsEditingDescriptor
|
||||
(Like many CodeMirrors).
|
||||
###
|
||||
|
||||
# hide editor/settings bar
|
||||
@element.closest('.component-editor').find('.component-edit-header').hide()
|
||||
|
||||
@$tabs = $(".tab", @element)
|
||||
@$content = $(".component-tab", @element)
|
||||
|
||||
@@ -25,7 +22,7 @@ class @TabsEditingDescriptor
|
||||
currentTab.trigger("click", [true, @html_id])
|
||||
|
||||
onSwitchEditor: (e, firstTime, html_id) =>
|
||||
e.preventDefault();
|
||||
e.preventDefault()
|
||||
|
||||
isInactiveClass = TabsEditingDescriptor.isInactiveClass
|
||||
$currentTarget = $(e.currentTarget)
|
||||
|
||||
@@ -283,6 +283,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// extends - content - removes list styling/spacing when using uls, ols for navigation and less content-centric cases
|
||||
|
||||
@@ -15,6 +15,15 @@ ${page_title_breadcrumbs(course_name())}
|
||||
%endif
|
||||
</title></%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
% for template_name in ["image-modal"]:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
% endfor
|
||||
</%block>
|
||||
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='style-course-vendor'/>
|
||||
<%static:css group='style-course'/>
|
||||
@@ -155,10 +164,6 @@ ${page_title_breadcrumbs(course_name())}
|
||||
</script>
|
||||
% endif
|
||||
|
||||
<script type="text/template" id="image-modal-tpl">
|
||||
<%static:include path="js/imageModal.underscore" />
|
||||
</script>
|
||||
|
||||
${fragment.foot_html()}
|
||||
|
||||
</%block>
|
||||
|
||||
Reference in New Issue
Block a user