Commit 513573a3 by Braden MacDonald

Studio editing of resizable images

parent ed9513f4
......@@ -164,7 +164,10 @@ class DragAndDropBlock(XBlock):
for js_url in js_urls:
fragment.add_javascript_url(self.runtime.local_resource_url(self, js_url))
fragment.initialize_js('DragAndDropEditBlock')
fragment.initialize_js('DragAndDropEditBlock', {
'data': self.data,
'target_img_expanded_url': self.target_img_expanded_url,
})
return fragment
......@@ -267,6 +270,37 @@ class DragAndDropBlock(XBlock):
self.item_state = {}
return self._get_data()
def _expand_static_url(self, url):
"""
This is required to make URLs like '/static/dnd-test-image.png' work (note: that is the
only portable URL format for static files that works across export/import and reruns).
This method is unfortunately a bit hackish since XBlock does not provide a low-level API
for this.
"""
if hasattr(self.runtime, 'replace_urls'):
url = self.runtime.replace_urls('"{}"'.format(url))[1:-1]
elif hasattr(self.runtime, 'course_id'):
# edX Studio uses a different runtime for 'studio_view' than 'student_view',
# and the 'studio_view' runtime doesn't provide the replace_urls API.
try:
from static_replace import replace_static_urls
url = replace_static_urls('"{}"'.format(url), None, course_id=self.runtime.course_id)[1:-1]
except ImportError:
pass
return url
@XBlock.json_handler
def expand_static_url(self, url, suffix=''):
""" AJAX-accessible handler for expanding URLs to static [image] files """
return {'url': self._expand_static_url(url)}
@property
def target_img_expanded_url(self):
""" Get the expanded URL to the target image (the image items are dragged onto). """
if self.data.get("targetImg"):
return self._expand_static_url(self.data["targetImg"])
return self.runtime.local_resource_url(self, "public/img/triangle.png")
def _get_data(self):
data = copy.deepcopy(self.data)
......@@ -299,8 +333,7 @@ class DragAndDropBlock(XBlock):
if self.item_text_color:
data['item_text_color'] = self.item_text_color
if not data.get("targetImg"):
data["targetImg"] = self.runtime.local_resource_url(self, "public/img/triangle.png")
data["targetImg"] = self.target_img_expanded_url
return data
......
/*** xBlock styles ***/
.xblock--drag-and-drop {
width: 100%;
margin: 0;
padding: 0;
background: #fff;
}
.modal-window .editor-with-buttons.xblock--drag-and-drop {
/* Fix Studio edito height */
.modal-window .xblock--drag-and-drop--editor.editor-with-buttons {
/* Fix Studio editor height */
margin-bottom: 0;
height: 380px;
}
/** Draggable Items **/
.xblock--drag-and-drop .items {
width: 210px;
margin: 10px;
padding: 0;
font-size: 14px;
position: relative;
display: inline;
float: left;
list-style-type: none;
}
/*** Drop Target ***/
.xblock--drag-and-drop .target {
width: 515px;
height: 510px;
position: relative;
display: inline;
float: left;
margin: 10px 0 15px 5px;
background: #fff;
z-index: 1;
}
.xblock--drag-and-drop .target-img {
background: url('../img/triangle.png') no-repeat;
width: 100%;
height: 100%;
}
.xblock--drag-and-drop .zone {
.xblock--drag-and-drop--editor .zone {
position: absolute;
display: -webkit-box;
......@@ -70,7 +32,7 @@
}
.xblock--drag-and-drop .zone p {
.xblock--drag-and-drop--editor .zone p {
width: 100%;
font-family: Arial;
font-size: 16px;
......@@ -82,153 +44,172 @@
}
/*** IE9 alignment fix ***/
.lt-ie10 .xblock--drag-and-drop .zone {
.lt-ie10 .xblock--drag-and-drop--editor .zone {
display: table;
}
.lt-ie10 .xblock--drag-and-drop .zone p {
.lt-ie10 .xblock--drag-and-drop--editor .zone p {
display: table-cell;
vertical-align: middle;
text-align: center;
}
/** Builder **/
.xblock--drag-and-drop .hidden {
.xblock--drag-and-drop--editor .hidden {
display: none !important;
}
.xblock--drag-and-drop .drag-builder {
.xblock--drag-and-drop--editor {
height: 100%;
overflow: scroll;
}
.xblock--drag-and-drop .drag-builder .tab {
.xblock--drag-and-drop--editor .tab {
width: 100%;
background: #eee;
padding: 3px 0;
position: relative;
}
.xblock--drag-and-drop .drag-builder .tab:after,
.xblock--drag-and-drop .drag-builder .tab-footer:after,
.xblock--drag-and-drop .drag-builder .target:after {
.xblock--drag-and-drop--editor .tab:after,
.xblock--drag-and-drop--editor .tab-footer:after,
.xblock--drag-and-drop--editor .target:after {
content: "";
display: table;
clear: both;
}
.xblock--drag-and-drop .drag-builder .tab h3 {
.xblock--drag-and-drop--editor .tab h3 {
margin: 20px 0 8px 0;
}
.xblock--drag-and-drop .drag-builder .tab-header,
.xblock--drag-and-drop .drag-builder .tab-content,
.xblock--drag-and-drop .drag-builder .tab-footer {
.xblock--drag-and-drop--editor .tab-header,
.xblock--drag-and-drop--editor .tab-content,
.xblock--drag-and-drop--editor .tab-footer {
width: 96%;
margin: 2%;
}
.xblock--drag-and-drop .drag-builder .tab-footer {
.xblock--drag-and-drop--editor .tab-footer {
height: 25px;
position: relative;
display: block;
float: left;
}
.xblock--drag-and-drop .drag-builder .items {
.xblock--drag-and-drop--editor .items {
width: calc(100% - 515px);
margin: 10px 0 0 0;
}
.xblock--drag-and-drop .drag-builder .target {
margin-left: 0;
.xblock--drag-and-drop--editor .target-image-form input {
width: 50%;
}
.xblock--drag-and-drop .drag-builder .target-image-form input {
width: 50%;
/* Zones Tab */
.xblock--drag-and-drop--editor .zones-tab .zone-editor {
position: relative;
}
.xblock--drag-and-drop--editor .zones-tab .tab-content .controls {
width: 40%;
}
.xblock--drag-and-drop--editor .zones-tab .tab-content .target {
position: absolute;
left: 40%;
top: 0;
right: 0;
bottom: 0;
border: 1px solid #ccc;
}
.xblock--drag-and-drop--editor .zones-tab .tab-content .target-img {
width: auto;
height: auto;
max-width: 100%;
}
.xblock--drag-and-drop .zones-form .zone-row label {
.xblock--drag-and-drop--editor .zones-form .zone-row label {
display: inline-block;
width: 18%;
}
.xblock--drag-and-drop .zones-form .zone-row .title {
.xblock--drag-and-drop--editor .zones-form .zone-row .title {
width: 60%;
margin: 0 0 5px;
}
.xblock--drag-and-drop .zones-form .zone-row .layout {
.xblock--drag-and-drop--editor .zones-form .zone-row .layout {
margin-bottom: 15px;
}
.xblock--drag-and-drop .zones-form .zone-row .layout .size,
.xblock--drag-and-drop .zones-form .zone-row .layout .coord {
.xblock--drag-and-drop--editor .zones-form .zone-row .layout .size,
.xblock--drag-and-drop--editor .zones-form .zone-row .layout .coord {
width: 15%;
margin: 0 19px 5px 0;
}
.xblock--drag-and-drop .drag-builder .target {
.xblock--drag-and-drop--editor .target {
margin-bottom: 40px;
}
.xblock--drag-and-drop .drag-builder .zone {
.xblock--drag-and-drop--editor .zone {
border: 1px dotted #666;
}
.xblock--drag-and-drop .feedback-form textarea {
.xblock--drag-and-drop--editor .feedback-form textarea {
width: 99%;
height: 128px;
}
.xblock--drag-and-drop .item-styles-form .item-styles-form-help {
.xblock--drag-and-drop--editor .item-styles-form .item-styles-form-help {
margin-top: 5px;
font-size: small;
}
.xblock--drag-and-drop .item-styles-form,
.xblock--drag-and-drop .items-form {
.xblock--drag-and-drop--editor .item-styles-form,
.xblock--drag-and-drop--editor .items-form {
margin-bottom: 30px;
}
.xblock--drag-and-drop .items-form .item {
.xblock--drag-and-drop--editor .items-form .item {
background: #73bde7;
padding: 10px 0 1px;
margin: 15px 0;
}
.xblock--drag-and-drop .items-form label {
.xblock--drag-and-drop--editor .items-form label {
margin: 0 1%;
}
.xblock--drag-and-drop .items-form input,
.xblock--drag-and-drop .items-form select {
.xblock--drag-and-drop--editor .items-form input,
.xblock--drag-and-drop--editor .items-form select {
width: 35%;
}
.xblock--drag-and-drop .items-form .item-width,
.xblock--drag-and-drop .items-form .item-height {
.xblock--drag-and-drop--editor .items-form .item-width,
.xblock--drag-and-drop--editor .items-form .item-height {
width: 40px;
}
.xblock--drag-and-drop .items-form .item-numerical-value,
.xblock--drag-and-drop .items-form .item-numerical-margin {
.xblock--drag-and-drop--editor .items-form .item-numerical-value,
.xblock--drag-and-drop--editor .items-form .item-numerical-margin {
width: 60px;
}
.xblock--drag-and-drop .items-form textarea {
.xblock--drag-and-drop--editor .items-form textarea {
width: 97%;
margin: 0 1%;
}
.xblock--drag-and-drop .items-form .row {
.xblock--drag-and-drop--editor .items-form .row {
margin-bottom: 20px;
}
/** Buttons **/
.xblock--drag-and-drop .btn {
.xblock--drag-and-drop--editor .btn {
background: #2e83cd;
color: #fff;
border: 1px solid #156ab4;
......@@ -236,33 +217,33 @@
padding: 5px 10px;
}
.xblock--drag-and-drop .btn:hover {
.xblock--drag-and-drop--editor .btn:hover {
opacity: 0.8;
cursor: pointer;
}
.xblock--drag-and-drop .btn:focus {
.xblock--drag-and-drop--editor .btn:focus {
outline: none;
opacity: 0.5;
}
.xblock--drag-and-drop .add-element {
.xblock--drag-and-drop--editor .add-element {
text-decoration: none;
color: #2e83cd;
}
.xblock--drag-and-drop .remove-zone {
.xblock--drag-and-drop--editor .remove-zone {
float: right;
margin-top: 2px;
margin-right: 16px;
}
.xblock--drag-and-drop .remove-item {
.xblock--drag-and-drop--editor .remove-item {
display: inline-block;
margin-left: 95px;
}
.xblock--drag-and-drop .icon {
.xblock--drag-and-drop--editor .icon {
width: 14px;
height: 14px;
border-radius: 7px;
......@@ -272,14 +253,14 @@
margin: 0 5px 0 0;
}
.xblock--drag-and-drop .add-zone:hover,
.xblock--drag-and-drop .add-zone:hover .icon,
.xblock--drag-and-drop .remove-zone:hover,
.xblock--drag-and-drop .remove-zone:hover .icon {
.xblock--drag-and-drop--editor .add-zone:hover,
.xblock--drag-and-drop--editor .add-zone:hover .icon,
.xblock--drag-and-drop--editor .remove-zone:hover,
.xblock--drag-and-drop--editor .remove-zone:hover .icon {
opacity: 0.7;
}
.xblock--drag-and-drop .icon.add:before {
.xblock--drag-and-drop--editor .icon.add:before {
content: '';
height: 10px;
width: 2px;
......@@ -291,7 +272,7 @@
left: 6px;
}
.xblock--drag-and-drop .icon.add:after {
.xblock--drag-and-drop--editor .icon.add:after {
content: '';
height: 2px;
width: 10px;
......@@ -303,7 +284,7 @@
left: 0;
}
.xblock--drag-and-drop .icon.remove:before {
.xblock--drag-and-drop--editor .icon.remove:before {
content: '';
height: 10px;
width: 2px;
......@@ -318,7 +299,7 @@
transform: rotate(45deg);
}
.xblock--drag-and-drop .icon.remove:after {
.xblock--drag-and-drop--editor .icon.remove:after {
content: '';
height: 2px;
width: 10px;
......@@ -333,10 +314,10 @@
transform: rotate(45deg);
}
.xblock--drag-and-drop .remove-item .icon.remove {
.xblock--drag-and-drop--editor .remove-item .icon.remove {
background: #fff;
}
.xblock--drag-and-drop .remove-item .icon.remove:before,
.xblock--drag-and-drop .remove-item .icon.remove:after {
.xblock--drag-and-drop--editor .remove-item .icon.remove:before,
.xblock--drag-and-drop--editor .remove-item .icon.remove:after {
background: #2e83cd;
}
function DragAndDropEditBlock(runtime, element) {
function DragAndDropEditBlock(runtime, element, params) {
// Set up gettext in case it isn't available in the client runtime:
if (typeof gettext == "undefined") {
......@@ -14,7 +14,7 @@ function DragAndDropEditBlock(runtime, element) {
var _fn = {
// DOM Elements
$target: $('.xblock--drag-and-drop .target-img', element),
$target: $('.target-img', element),
tpl: {
init: function() {
......@@ -30,21 +30,21 @@ function DragAndDropEditBlock(runtime, element) {
build: {
$el: {
feedback: {
form: $('.xblock--drag-and-drop .drag-builder .feedback-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .feedback-tab', element)
form: $('.drag-builder .feedback-form', element),
tab: $('.drag-builder .feedback-tab', element)
},
zones: {
form: $('.xblock--drag-and-drop .drag-builder .zones-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .zones-tab', element)
form: $('.drag-builder .zones-form', element),
tab: $('.drag-builder .zones-tab', element)
},
items: {
form: $('.xblock--drag-and-drop .drag-builder .items-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .items-tab', element)
form: $('.drag-builder .items-form', element),
tab: $('.drag-builder .items-tab', element)
},
target: $('.xblock--drag-and-drop .drag-builder .target-img', element)
target: $('.drag-builder .target', element)
},
init: function(data) {
_fn.data = data;
init: function() {
_fn.data = params.data;
// Compile templates
_fn.tpl.init();
......@@ -71,9 +71,9 @@ function DragAndDropEditBlock(runtime, element) {
_fn.build.form.zone.add();
}
if (_fn.data.targetImg) {
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
}
// Set the target image:
$('.target-image-form input', element).val(_fn.data.targetImg);
$('.target-img', element).attr('src', params.target_img_expanded_url);
if (_fn.data.displayLabels) {
$('.display-labels-form input', element).prop('checked', true);
......@@ -122,8 +122,14 @@ function DragAndDropEditBlock(runtime, element) {
.on('click', '.target-image-form button', function(e) {
e.preventDefault();
_fn.data.targetImg = $('.target-image-form input', element).val();
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
var new_img_url = $('.target-image-form input', element).val();
_fn.data.targetImg = new_img_url;
// We may need to 'expand' the URL before it will be valid.
// e.g. '/static/blah.png' becomes '/asset-v1:course+id/blah.png'
var handlerUrl = runtime.handlerUrl(element, 'expand_static_url');
$.post(handlerUrl, JSON.stringify(new_img_url), function(result) {
_fn.$target.attr('src', result.url);
}).error(function(a, b, c) { console.log(a, b, c); });
// Placeholder shim for IE9
$.placeholder.shim();
......@@ -450,7 +456,7 @@ function DragAndDropEditBlock(runtime, element) {
};
return {
builder: _fn.build.init
init: _fn.build.init
};
})(jQuery);
......@@ -458,5 +464,5 @@ function DragAndDropEditBlock(runtime, element) {
runtime.notify('cancel', {});
});
dragAndDrop.builder(window.DragAndDropV2BlockPreviousData);
dragAndDrop.init();
}
......@@ -114,7 +114,6 @@
renderCollection(zoneTemplate, ctx.zones, ctx),
renderCollection(itemTemplate, items_placed, ctx),
]),
//h('div.clear'),
]),
feedbackTemplate(ctx),
])
......
{% load i18n %}
<div class="xblock--drag-and-drop editor-with-buttons">
<div class="xblock--drag-and-drop--editor editor-with-buttons">
{{ js_templates|safe }}
<script type="text/javascript">
var DragAndDropV2BlockPreviousData = JSON.parse(decodeURIComponent('{{ data|safe }}'));
</script>
<section class="drag-builder">
<div class="tab feedback-tab">
<p class="tab-content">
......@@ -57,12 +53,14 @@
<label for="display-labels">{% trans "Display label names on the image" %}:</label>
<input name="display-labels" id="display-labels" type="checkbox" />
</section>
<div class="items">
<form class="zones-form"></form>
<a href="#" class="add-zone add-element"><div class="icon add"></div>{% trans "Add a zone" %}</a>
</div>
<div class="target">
<div class="target-img"></div>
<div class="zone-editor">
<div class="items">
<form class="zones-form"></form>
<a href="#" class="add-zone add-element"><div class="icon add"></div>{% trans "Add a zone" %}</a>
</div>
<div class="target">
<img class="target-img">
</div>
</div>
</section>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment