Commit 7dca95f7 by Filippo Valsorda

Add basic support for the builder in Studio

parent bfd34fef
...@@ -43,7 +43,7 @@ class DragAndDropBlock(XBlock): ...@@ -43,7 +43,7 @@ class DragAndDropBlock(XBlock):
display_name="Drag and Drop", display_name="Drag and Drop",
help="JSON spec as generated by the builder", help="JSON spec as generated by the builder",
scope=Scope.content, scope=Scope.content,
default='{"feedback":{"start":"Intro","finish":"Final"},"items":[{"displayName":"A","zone":"Uno","id":0,"feedback":{"correct":"Si","incorrect":"No"},"size":{"width":"190px","height":"auto"},"backgroundImage":""},{"displayName":"B","zone":"none","id":1,"feedback":{"correct":"","incorrect":""},"size":{"width":"190px","height":"auto"},"backgroundImage":""}],"zones":[{"title":"Uno","id":"zone-1","active":true,"index":1,"width":200,"height":100,"x":0,"y":0},{"title":"Due","id":"zone-2","active":true,"index":2,"width":200,"height":100,"x":"300","y":"210"}],"targetImg":"http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png?1318992465"}' default='{"feedback":{"start":"Intro","finish":"Final"},"items":[{"displayName":"A","zone":"Uno","id":0,"feedback":{"correct":"Si","incorrect":"No"},"size":{"width":"190px","height":"auto"},"backgroundImage":""},{"displayName":"B","zone":"none","id":1,"feedback":{"correct":"","incorrect":""},"size":{"width":"190px","height":"auto"},"backgroundImage":""}],"zones":[{"title":"Uno","id":"zone-1","active":true,"index":1,"width":200,"height":100,"x":0,"y":0},{"title":"Due","id":"zone-2","active":true,"index":2,"width":200,"height":100,"x":"300","y":"210"}],"targetImg":"https://i.imgur.com/PoI27ox.png"}'
# default=textwrap.dedent(""" # default=textwrap.dedent("""
# { # {
# feedback: {}, # feedback: {},
...@@ -79,10 +79,14 @@ class DragAndDropBlock(XBlock): ...@@ -79,10 +79,14 @@ class DragAndDropBlock(XBlock):
""" """
Editing view in Studio Editing view in Studio
""" """
context = {}
fragment = Fragment() fragment = Fragment()
fragment.add_content(render_template('/templates/html/drag_and_drop_edit.html', { fragment.add_content(render_template('/templates/html/drag_and_drop_edit.html', context))
'self': self, fragment.add_css(load_resource('public/css/drag_and_drop_edit.css'))
})) fragment.add_javascript(load_resource('public/js/vendor/jquery.html5-placeholder-shim.js'))
fragment.add_javascript(load_resource('public/js/vendor/underscore1.6.0.js'))
fragment.add_javascript(load_resource('public/js/drag_and_drop_edit.js')) fragment.add_javascript(load_resource('public/js/drag_and_drop_edit.js'))
fragment.initialize_js('DragAndDropEditBlock') fragment.initialize_js('DragAndDropEditBlock')
......
/*** xBlock styles ***/
.xblock--drag-and-drop {
width: 100%;
margin: 0;
padding: 0;
background: #fff;
}
.xblock--drag-and-drop h1,
.xblock--drag-and-drop h2,
.xblock--drag-and-drop h3,
.xblock--drag-and-drop h4,
.xblock--drag-and-drop h5,
.xblock--drag-and-drop h6,
.xblock--drag-and-drop p,
.xblock--drag-and-drop li,
.xblock--drag-and-drop a {
font-family: Arial;
}
.xblock--drag-and-drop h1 {
color: #adadad;
}
.xblock--drag-and-drop h2 {
color: #333;
margin: 0;
text-transform: uppercase;
}
.xblock--drag-and-drop header p,
.xblock--drag-and-drop footer p {
color: #adadad;
line-height: 1.5em;
}
.xblock--drag-and-drop .small {
font-size: 0.6em;
}
.xblock--drag-and-drop .drag-container {
width: 760px;
background: #ebf0f2;
position: relative;
}
/** 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;
}
.xblock--drag-and-drop .items .option {
width: 190px;
background: #2e83cd;
color: #fff;
position: relative;
float: left;
display: inline;
z-index: 100;
margin-bottom: 5px;
padding: 10px;
}
.xblock--drag-and-drop .option.hover { background: #ccc; }
.xblock--drag-and-drop .option.fade { opacity: 0.6; }
/*** 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 {
/*border: 1px solid #000;*/
position: absolute;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
/* Internet Explorer 10 */
-ms-flex-pack:center;
-ms-flex-align:center;
/* Firefox */
-moz-box-pack:center;
-moz-box-align:center;
/* Safari, Opera, and Chrome */
-webkit-box-pack:center;
-webkit-box-align:center;
/* W3C */
box-pack:center;
box-align:center;
}
.xblock--drag-and-drop .zone p {
width: 100%;
font-family: Arial;
font-size: 16px;
font-weight: bold;
text-align: center;
text-transform: uppercase;
margin-top: auto;
margin-bottom: auto;
}
.xblock--drag-and-drop .zone.one {
height: 75px;
width: 115px;
top: 130px;
left: 200px;
}
.xblock--drag-and-drop .zone.two {
height: 120px;
width: 200px;
top: 220px;
left: 157px;
}
.xblock--drag-and-drop .zone.three {
height: 120px;
width: 200px;
bottom: 30px;
left: 157px;
}
/*** IE9 alignment fix ***/
.lt-ie10 .xblock--drag-and-drop .zone {
display: table;
}
.lt-ie10 .xblock--drag-and-drop .zone p {
display: table-cell;
vertical-align: middle;
text-align: center;
}
/*** FEEDBACK ***/
.xblock--drag-and-drop .feedback {
width: 740px;
border-top: #ccc 1px solid;
margin: 20px 10px;
padding-top: 10px;
}
.xblock--drag-and-drop .feedback .message {
margin: 5px 0 0;
}
/** Builder **/
.xblock--drag-and-drop .hidden {
display: none !important;
}
.xblock--drag-and-drop .drag-builder {
/* TODO */
height: 375px;
overflow: scroll;
}
.xblock--drag-and-drop .drag-builder .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 {
content: "";
display: table;
clear: both;
}
.xblock--drag-and-drop .drag-builder .tab h3 {
margin: 10px 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 {
width: 96%;
margin: 2%;
}
.xblock--drag-and-drop .drag-builder .tab-footer {
height: 25px;
position: relative;
display: block;
float: left;
}
.xblock--drag-and-drop .drag-builder .continue {
position: absolute;
right: 0;
top: -5px;
}
.xblock--drag-and-drop .drag-builder .items {
width: calc(100% - 515px);
margin: 10px 0 0 0;
}
.xblock--drag-and-drop .drag-builder .target {
margin-left: 0;
}
.xblock--drag-and-drop .zones-form .zone-row label {
display: inline-block;
width: 18%;
}
.xblock--drag-and-drop .zones-form .zone-row .title {
width: 60%;
margin: 0 0 5px;
}
.xblock--drag-and-drop .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 {
width: 15%;
margin: 0 19px 5px 0;
}
.xblock--drag-and-drop .drag-builder .target {
margin-bottom: 40px;
}
.xblock--drag-and-drop .drag-builder .zone {
width: 200px;
height: 100px;
border: 1px dotted #666;
}
.xblock--drag-and-drop .feedback-form textarea {
width: 99%;
height: 128px;
margin-bottom: 30px;
}
.xblock--drag-and-drop .items-form {
margin-bottom: 30px;
}
.xblock--drag-and-drop .items-form .item {
background: #73bde7;
padding: 10px 0 1px;
margin: 15px 0;
}
.xblock--drag-and-drop .items-form label {
margin: 0 1%;
}
.xblock--drag-and-drop .items-form input,
.xblock--drag-and-drop .items-form select {
width: 35%;
}
.xblock--drag-and-drop .items-form .item-width,
.xblock--drag-and-drop .items-form .item-height {
width: 40px;
}
.xblock--drag-and-drop .items-form textarea {
width: 97%;
margin: 0 1%;
}
.xblock--drag-and-drop .items-form .row {
margin-bottom: 20px;
}
/** Buttons **/
.xblock--drag-and-drop .btn {
background: #2e83cd;
color: #fff;
border: 1px solid #156ab4;
border-radius: 6px;
padding: 5px 10px;
}
.xblock--drag-and-drop .btn:hover {
opacity: 0.8;
cursor: pointer;
}
.xblock--drag-and-drop .btn:focus {
outline: none;
opacity: 0.5;
}
.xblock--drag-and-drop .add-element {
text-decoration: none;
color: #2e83cd;
}
.xblock--drag-and-drop .remove-zone {
float: right;
margin-top: 2px;
margin-right: 16px;
}
.xblock--drag-and-drop .remove-item {
display: inline-block;
margin-left: 95px;
}
.xblock--drag-and-drop .icon {
width: 14px;
height: 14px;
border-radius: 7px;
background: #2e83cd;
position: relative;
float: left;
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 {
opacity: 0.7;
}
.xblock--drag-and-drop .icon.add:before {
content: '';
height: 10px;
width: 2px;
background: #fff;
position: relative;
display: inline;
float: left;
top: 2px;
left: 6px;
}
.xblock--drag-and-drop .icon.add:after {
content: '';
height: 2px;
width: 10px;
background: #fff;
position: relative;
display: inline;
float: left;
top: 6px;
left: 0;
}
.xblock--drag-and-drop .icon.remove:before {
content: '';
height: 10px;
width: 2px;
background: #fff;
position: relative;
display: inline;
float: left;
top: 2px;
left: 6px;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.xblock--drag-and-drop .icon.remove:after {
content: '';
height: 2px;
width: 10px;
background: #fff;
position: relative;
display: inline;
float: left;
top: 6px;
left: 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.xblock--drag-and-drop .remove-item .icon.remove {
background: #fff;
}
.xblock--drag-and-drop .remove-item .icon.remove:before,
.xblock--drag-and-drop .remove-item .icon.remove:after {
background: #2e83cd;
}
function DragAndDropEditBlock(runtime, element) { function DragAndDropEditBlock(runtime, element) {
$(element).find('.save-button').bind('click', function() { var dragAndDrop = (function($) {
var _fn = {
// DOM Elements
$block: $('.xblock--drag-and-drop', element),
$app: $('.xblock--drag-and-drop .drag-container', element),
$ul: $('.xblock--drag-and-drop .items', element),
$target: $('.xblock--drag-and-drop .target-img', element),
$feedback: $('.xblock--drag-and-drop .feedback .message', element),
// Cannot set until items added to DOM
$items: {}, // $('.xblock--drag-and-drop .items .option'),
$zones: {}, // $('.xblock--drag-and-drop .target .zone'),
// jQuery UI Draggable options
options: {
drag: {
containment: '.xblock--drag-and-drop .drag-container',
cursor: 'move',
stack: '.xblock--drag-and-drop .items .option'
},
drop: {
accept: '.xblock--drag-and-drop .items .option',
tolerance: 'pointer'
}
},
// item template
tpl: {
item: function() {
return [
'<li class="option" data-value="<%= id %>"',
'style="width: <%= size.width %>; height: <%= size.height %>">',
'<%= displayName %>',
'</li>'
].join('');
},
image_item: function() {
return [
'<li class="option" data-value="<%= id %>"',
'style="width: <%= size.width %>; height: <%= size.height %>">',
'<img src="<%= backgroundImage %>" />',
'</li>'
].join('');
},
zoneInput: function() {
return [
'<div class="zone-row <%= name %>">',
'<label>Text</label>',
'<input type="text" class="title" placeholder="<%= title %>" />',
'<a href="#" class="remove-zone hidden">',
'<div class="icon remove"></div>',
'</a>',
'<div class="layout">',
'<label>width</label>',
'<input type="text" class="size width" value="200" />',
'<label>height</label>',
'<input type="text" class="size height" value="100" />',
'<br />',
'<label>x</label>',
'<input type="text" class="coord x" value="0" />',
'<label>y</label>',
'<input type="text" class="coord y" value="0" />',
'</div>',
'</div>'
].join('');
},
zoneElement: function() {
return [
'<div id="<%= id %>" class="zone" data-zone="<%= title %>" style="',
'top:<%= y %>px;',
'left:<%= x %>px;',
'width:<%= width %>px;',
'height:<%= height %>px;">',
'<p><%= title %></p>',
'</div>'
].join('');
},
zoneDropdown: '<option value="<%= value %>"><%= value %></option>',
itemInput: function() {
return [
'<div class="item">',
'<div class="row">',
'<label>Text</label>',
'<input type="text" class="item-text"></input>',
'<label>Zone</label>',
'<select class="zone-select"><%= dropdown %></select>',
'<a href="#" class="remove-item hidden">',
'<div class="icon remove"></div>',
'</a>',
'</div>',
'<div class="row">',
'<label>Background image URL (alternative to the text)</label>',
'<textarea class="background-image"></textarea>',
'</div>',
'<div class="row">',
'<label>Success Feedback</label>',
'<textarea class="success-feedback"></textarea>',
'</div>',
'<div class="row">',
'<label>Error Feedback</label>',
'<textarea class="error-feedback"></textarea>',
'</div>',
'<div class="row">',
'<label>Width (px - 0 for auto)</label>',
'<input type="text" class="item-width" value="190"></input>',
'<label>Height (px - 0 for auto)</label>',
'<input type="text" class="item-height" value="0"></input>',
'</div>',
'</div>'
].join('');
}
},
build: {
$el: {
feedback: {
form: $('.xblock--drag-and-drop .drag-builder .feedback-form', element),
tab: $('.xblock--drag-and-drop .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)
},
items: {
form: $('.xblock--drag-and-drop .drag-builder .items-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .items-tab', element)
},
target: $('.xblock--drag-and-drop .drag-builder .target-img', element)
},
init: function() {
_fn.build.clickHandlers();
_fn.build.form.zone.add();
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
},
clickHandlers: function() {
var $fbkTab = _fn.build.$el.feedback.tab,
$zoneTab = _fn.build.$el.zones.tab,
$itemTab = _fn.build.$el.items.tab;
$(element).one( 'click', '.continue-button', function(e) {
e.preventDefault();
_fn.build.form.feedback( _fn.build.$el.feedback.form );
$fbkTab.addClass('hidden');
$zoneTab.removeClass('hidden');
// Placeholder shim for IE9
$.placeholder.shim();
$(this).one( 'click', function(e) {
e.preventDefault();
_fn.build.form.zone.setAll();
_fn.build.form.item.add();
$zoneTab.addClass('hidden');
$itemTab.removeClass('hidden');
// Placeholder shim for IE9
$.placeholder.shim();
$(this).addClass('hidden');
$('.save-button', element).parent()
.removeClass('hidden')
.one( 'click', function(e) {
e.preventDefault();
_fn.build.form.submit();
});
});
});
$zoneTab
.on( 'click', '.add-zone', _fn.build.form.zone.add )
.on( 'click', '.remove-zone', _fn.build.form.zone.remove )
.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');
// Placeholder shim for IE9
$.placeholder.shim();
});
$itemTab
.on( 'click', '.add-item', _fn.build.form.item.add )
.on( 'click', '.remove-item', _fn.build.form.item.remove );
},
form: {
zone: {
count: 0,
formCount: 0,
dropdown: '',
list: [],
obj: [],
add: function(e) {
var inputTemplate = _fn.tpl.zoneInput(),
zoneTemplate = _fn.tpl.zoneElement(),
name = 'zone-',
$elements = _fn.build.$el,
num,
obj;
if (e) {
e.preventDefault();
}
_fn.build.form.zone.count++;
_fn.build.form.zone.formCount++;
num = _fn.build.form.zone.count;
name += num;
// Update zone obj
zoneObj = {
title: 'Zone ' + num,
id: name,
active: false,
index: num,
width: 200,
height: 100,
x: 0,
y: 0
};
_fn.build.form.zone.obj.push( zoneObj );
// Add fields to zone position form
$elements.zones.form.append( _.template( inputTemplate, {
title: 'Zone ' + num,
name: name
}));
_fn.build.form.zone.enableDelete();
// Add zone div to target
$elements.target.append( _.template( zoneTemplate, zoneObj ) );
// Listen to changes in form to update zone div
_fn.build.form.zone.clickHandler( num );
// Placeholder shim for IE9
$.placeholder.shim();
},
remove: function(e) {
var $el = $(e.currentTarget).closest('.zone-row'),
classes = $el.attr('class'),
id = classes.slice(classes.indexOf('zone-row') + 9);
e.preventDefault();
$el.detach();
$('#' + id, element).detach();
_fn.build.form.zone.formCount--;
_fn.build.form.zone.disableDelete();
// Placeholder shim for IE9
$.placeholder.shim();
},
enableDelete: function() {
if ( _fn.build.form.zone.formCount > 1 ) {
_fn.build.$el.zones.form.find('.remove-zone').removeClass('hidden');
}
},
disableDelete: function() {
if ( _fn.build.form.zone.formCount === 1 ) {
_fn.build.$el.zones.form.find('.remove-zone').addClass('hidden');
}
},
setAll: function() {
var zones = [],
$form = _fn.build.$el.zones.form.find('.title');
$form.each(function(i, el) {
var val = $(el).val();
if ( val.length > 0 ) {
zones.push( val );
}
});
_fn.build.form.zone.list = zones;
_fn.build.form.createDropdown(zones);
},
clickHandler: function( num ) {
var $div = $('#zone-' + num, element),
$form = _fn.build.$el.zones.form.find('.zone-row.zone-' + num);
// Listen to form changes and update zone div position
$form.on('keyup', '.title', function(e) {
var text = $(e.currentTarget).val(),
record = _.findWhere( _fn.build.form.zone.obj, {
index: num
});
$div.find('p').html(text);
record.title = text;
if ( !record.active ) {
record.active = true;
}
}).on('keyup', '.width', function(e) {
var width = $(e.currentTarget).val(),
record = _.findWhere( _fn.build.form.zone.obj, {
index: num
});
$div.css('width', width + 'px');
record.width = width;
}).on('keyup', '.height', function(e) {
var height = $(e.currentTarget).val(),
record = _.findWhere( _fn.build.form.zone.obj, {
index: num
});
$div.css('height', height + 'px');
record.height = height;
}).on('keyup', '.x', function(e) {
var x = $(e.currentTarget).val(),
record = _.findWhere( _fn.build.form.zone.obj, {
index: num
});
$div.css('left', x + 'px');
record.x = x;
}).on('keyup', '.y', function(e) {
var y = $(e.currentTarget).val(),
record = _.findWhere( _fn.build.form.zone.obj, {
index: num
});
$div.css('top', y + 'px');
record.y = y;
});
},
cleanObject: function( arr ) {
var clean = [],
i,
len = arr.length;
for ( i=0; i<len; i++ ) {
if (arr[i].active) {
clean.push( arr[i] );
}
}
return clean;
}
},
createDropdown: function( arr ) {
var tpl = _fn.tpl.zoneDropdown,
i,
len = arr.length,
dropdown = [],
html;
for ( i=0; i<len; i++ ) {
dropdown.push( _.template( tpl, { value: arr[i] } ) );
}
// Add option to include dummy answers
dropdown.push( _.template( tpl, { value: 'none' } ) );
html = dropdown.join('');
_fn.build.form.zone.dropdown = html;
_fn.build.$el.items.form.find('.zone-select').html( html );
},
feedback: function( $form ) {
_fn.data.feedback = {
start: $form.find('.intro-feedback').val(),
finish: $form.find('.final-feedback').val()
};
},
item: {
count: 0,
add: function(e) {
var $form = _fn.build.$el.items.form,
tpl = _fn.tpl.itemInput();
if ( e ) {
e.preventDefault();
}
_fn.build.form.item.count++;
$form.append( _.template( tpl, { dropdown: _fn.build.form.zone.dropdown } ) );
_fn.build.form.item.enableDelete();
// Placeholder shim for IE9
$.placeholder.shim();
},
remove: function(e) {
var $el = $(e.currentTarget).closest('.item');
e.preventDefault();
$el.detach();
_fn.build.form.item.count--;
_fn.build.form.item.disableDelete();
// Placeholder shim for IE9
$.placeholder.shim();
},
enableDelete: function() {
if ( _fn.build.form.item.count > 1 ) {
_fn.build.$el.items.form.find('.remove-item').removeClass('hidden');
}
},
disableDelete: function() {
if ( _fn.build.form.item.count === 1 ) {
_fn.build.$el.items.form.find('.remove-item').addClass('hidden');
}
}
},
submit: function() {
var items = [],
$form = _fn.build.$el.items.form.find('.item');
$form.each( function(i, el) {
var $el = $(el),
name = $el.find('.item-text').val(),
backgroundImage = $el.find('.background-image').val();
if (name.length > 0 || backgroundImage.length > 0) {
var width = $el.find('.item-width').val(),
height = $el.find('.item-height').val();
if (height === '0') height = 'auto';
else height = height + 'px';
if (width === '0') width = 'auto';
else width = width + 'px';
items.push({
displayName: name,
zone: $el.find('.zone-select').val(),
id: i,
feedback: {
correct: $el.find('.success-feedback').val(),
incorrect: $el.find('.error-feedback').val()
},
size: {
width: width,
height: height
},
backgroundImage: backgroundImage
});
}
});
_fn.data.items = items;
_fn.data.zones = _fn.build.form.zone.cleanObject( _fn.build.form.zone.obj );
var data = { var data = {
'display_name': $(element).find('.edit-display-name').val(), 'display_name': $(element).find('.display-name').val(),
'question_text': $(element).find('.edit-question-text').val(), 'question_text': $(element).find('.question-text').val(),
'data': $(element).find('.edit-data').val() 'data': JSON.stringify(_fn.data),
}; };
$('.xblock-editor-error-message', element).html(); $('.xblock-editor-error-message', element).html();
...@@ -17,9 +466,196 @@ function DragAndDropEditBlock(runtime, element) { ...@@ -17,9 +466,196 @@ function DragAndDropEditBlock(runtime, element) {
$('.xblock-editor-error-message', element).css('display', 'block'); $('.xblock-editor-error-message', element).css('display', 'block');
} }
}); });
}
}
},
finish: function() {
// Disable any decoy items
_fn.$items.draggable('disable');
// Show final feedback
_fn.feedback.set( _fn.data.feedback.finish );
},
clickHandlers: {
init: function( $drag, $dropzone ) {
var clk = _fn.clickHandlers;
$drag.on( 'dragstart', clk.drag.start );
$drag.on( 'dragstop', clk.drag.stop );
$dropzone.on( 'drop', clk.drop.success );
$dropzone.on( 'dropover', clk.drop.hover.in );
$dropzone.on( 'dropout', clk.drop.hover.out );
},
drag: {
start: function( event, ui ) {
$(event.currentTarget).removeClass('within-dropzone fade');
},
stop: function( event, ui ) {
var $el = $(event.currentTarget),
val = $el.data('value'),
zone = $el.data('zone') || null;
if ( $el.hasClass('within-dropzone') && _fn.test.match( val, zone ) ) {
$el.removeClass('hover')
.draggable('disable');
_fn.test.completed++;
_fn.feedback.popup( _fn.feedback.get(val, true), true );
if ( _fn.items.allSubmitted() ) {
_fn.finish();
}
} else {
// Return to original position
_fn.clickHandlers.drag.reset( $el );
_fn.feedback.popup( _fn.feedback.get(val, false), false );
}
},
reset: function( $el ) {
$el.removeClass('within-dropzone fade hover')
.css({
top: '',
left: ''
});
}
},
drop: {
hover: {
in: function( event, ui ) {
var zone = $(event.currentTarget).data('zone');
ui.draggable.addClass('hover').data('zone', zone);
},
out: function( event, ui ) {
ui.draggable.removeClass('hover');
}
},
success: function( event, ui ) {
ui.draggable.addClass('within-dropzone')
}
}
},
items: {
count: 0,
init: function() {
var items = _fn.data.items,
i,
len = items.length,
total = 0;
for ( i=0; i<len; i++ ) {
if ( items[i].zone !== 'none' ) {
total++;
}
}
_fn.items.count = total;
},
allSubmitted: function() {
return _fn.test.completed === _fn.items.count;
},
draw: function() {
var list = [],
items = _fn.data.items,
tpl = _fn.tpl.item(),
img_tpl = _fn.tpl.image_item();
_.each(items, function(item) {
if (item.backgroundImage.length > 0) {
list.push( _.template( img_tpl, item ) );
} else {
list.push( _.template( tpl, item ) );
}
}); });
// Update DOM
_fn.$ul.html( list.join('') );
// Set variable
_fn.$items = $('.xblock--drag-and-drop .items .option', element);
}
},
zones: {
draw: function() {
var html = [],
zones = _fn.data.zones,
tpl = _fn.tpl.zoneElement(),
i,
len = zones.length;
for ( i=0; i<len; i++ ) {
html.push( _.template( tpl, zones[i] ) );
}
// Update DOM
_fn.$target.html( html.join('') );
// Set variable
_fn.$zones = _fn.$target.find('.zone');
}
},
test: {
completed: 0,
match: function( id, zone ) {
var item = _.findWhere( _fn.data.items, { id: id } );
return item.zone === zone;
}
},
feedback: {
// Returns string based on user's answer
get: function( id, boo ) {
var item,
type = boo ? 'correct' : 'incorrect';
// Null loses its string-ness
if ( id === null ) {
id = 'null';
}
// Get object from data.items that matches val
item = _.findWhere( _fn.data.items, { id: id });
return item.feedback[type];
},
// Update DOM with feedback
set: function(str) {
return _fn.$feedback.html(str);
},
// Show a feedback popup
popup: function(str, boo) {
if (str === undefined || str === '') return;
return $("<div>").attr('title', boo ? 'Correct' : 'Error').text(str).dialog();
}
},
data: {
feedback: {},
items: [],
zones: [],
targetImg: 'https://i.imgur.com/PoI27ox.png'
}
};
return {
builder: _fn.build.init
};
})(jQuery);
$(element).find('.cancel-button').bind('click', function() { $(element).find('.cancel-button').bind('click', function() {
runtime.notify('cancel', {}); runtime.notify('cancel', {});
}); });
dragAndDrop.builder();
} }
...@@ -26,7 +26,4 @@ ...@@ -26,7 +26,4 @@
<div class="clear"></div> <div class="clear"></div>
</section> </section>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
</section> </section>
{% load i18n %} {% load i18n %}
<!-- TODO: Replace by default edit view once available in Studio --> <div class="xblock--drag-and-drop editor-with-buttons">
<div class="wrapper-comp-settings is-active editor-with-buttons " id="settings-tab"> <link rel="stylesheet" href="https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set"> <section class="drag-builder">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_display_name">Title</label> <div class="tab feedback-tab">
<input class="input setting-input edit-display-name" id="edit_display_name" value="{{ self.display_name }}" type="text"> <section class="tab-content">
<form class="feedback-form">
<h3>Question title</h3>
<input class="display-name" />
<h3>Question text</h3>
<textarea class="question-text"></textarea>
<h3>Introduction Feedback</h3>
<textarea class="intro-feedback"></textarea>
<h3>Final Feedback</h3>
<textarea class="final-feedback"></textarea>
</form>
</section>
<!-- <footer class="tab-footer">
<button class="btn continue goto-zones">Continue</button>
</footer> -->
</div> </div>
<span class="tip setting-help">The title of the Drag and Drop that is displayed to the user</span>
</li> <div class="tab zones-tab hidden">
<li class="field comp-setting-entry is-set"> <header class="tab-header">
<div class="wrapper-comp-setting"> <h3>Zone Positions</h3>
<label class="label setting-label" for="edit_question_text">Question text</label> </header>
<input class="input setting-input edit-question-text" id="edit_question_text" value="{{ self.question_text }}" type="text"> <section class="tab-content">
<div class="items">
<form class="zones-form"></form>
<a href="#" class="add-zone add-element"><div class="icon add"></div>Add a zone</a>
</div> </div>
<span class="tip setting-help">The question text that is displayed to the user</span> <div class="target">
</li> <div class="target-img"></div>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_data">Title</label>
<input class="input setting-input edit-data" id="edit_data" value="{{ self.data }}" type="text">
</div> </div>
<span class="tip setting-help">JSON spec as generated by the builder</span> </section>
</li> <!-- <footer class="tab-footer">
</ul> <button class="btn continue goto-items">Continue</button>
</footer> -->
</div>
<div class="tab items-tab hidden">
<header class="tab-header">
<h3>Items</h3>
</header>
<section class="tab-content">
<form class="items-form"></form>
</section>
<footer class="tab-footer">
<a href="#" class="add-item add-element"><div class="icon add"></div>Add an item</a>
<!-- <button class="btn continue goto-exercise">Finish</button> -->
</footer>
</div>
</section>
<div class="xblock-actions"> <div class="xblock-actions">
<span class="xblock-editor-error-message"></span> <span class="xblock-editor-error-message"></span>
<ul> <ul>
<li class="action-item"> <li class="action-item">
<a href="#" class="button action-primary continue-button">{% trans "Continue" %}</a>
</li>
<li class="action-item hidden">
<a href="#" class="button action-primary save-button">{% trans "Save" %}</a> <a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
</li> </li>
......
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