Commit 0290f4ff by Xavier Antoviaque

Merge pull request #1 from FiloSottile/xblock

XBlock
parents 0fe52737 7dca95f7
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
from .drag_and_drop_v2 import DragAndDropBlock
# -*- coding: utf-8 -*-
#
# Imports ###########################################################
import logging
import textwrap
import json
import webob
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
from .utils import render_template, load_resource
# Globals ###########################################################
log = logging.getLogger(__name__)
# Classes ###########################################################
class DragAndDropBlock(XBlock):
"""
XBlock providing a Drag and Drop question
"""
display_name = String(
display_name="Title",
help="The title of the Drag and Drop that is displayed to the user",
scope=Scope.settings,
default="Drag and Drop"
)
question_text = String(
display_name="Question text",
help="The question text that is displayed to the user",
scope=Scope.settings
)
data = String(
display_name="Drag and Drop",
help="JSON spec as generated by the builder",
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":"https://i.imgur.com/PoI27ox.png"}'
# default=textwrap.dedent("""
# {
# feedback: {},
# items: [],
# zones: [],
# targetImg: 'img/triangle.png'
# }
# """)
)
def student_view(self, context):
"""
Player view, displayed to the student
"""
context = {
'title': self.display_name,
'question_text': self.question_text
}
fragment = Fragment()
fragment.add_content(render_template('/templates/html/drag_and_drop.html', context))
fragment.add_css(load_resource('public/css/drag_and_drop.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.js'))
fragment.initialize_js('DragAndDropBlock')
return fragment
def studio_view(self, context):
"""
Editing view in Studio
"""
context = {}
fragment = Fragment()
fragment.add_content(render_template('/templates/html/drag_and_drop_edit.html', context))
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.initialize_js('DragAndDropEditBlock')
return fragment
@XBlock.json_handler
def studio_submit(self, submissions, suffix=''):
self.display_name = submissions['display_name']
self.question_text = submissions['question_text']
data = submissions['data']
try:
json.loads(data)
self.data = data
except ValueError as e:
return {
'result': 'error',
'message': e.message
}
return {
'result': 'success',
}
@XBlock.handler
def get_data(self, request, suffix=''):
return webob.response.Response(body=self.data)
.xblock--drag-and-drop {
width: 770px;
margin: 0;
padding: 0;
background: #fff;
}
.xblock--drag-and-drop .problem-header {
display: inline-block;
margin: 0 0 15px 0;
}
.xblock--drag-and-drop .problem-progress {
display: inline-block;
padding-left: 5px;
color: #666;
font-weight: 100;
font-size: 1em;
}
.xblock--drag-and-drop .problem p {
margin-bottom: 1.41575em;
}
.xblock--drag-and-drop .drag-container {
width: 760px;
background: #ebf0f2;
position: relative;
}
.xblock--drag-and-drop .clear {
clear: both;
}
/** Draggable Items **/
.xblock--drag-and-drop .drag-container .items {
width: 210px;
margin: 10px;
padding: 0 !important; /* LMS tries to override this */
font-size: 14px;
position: relative;
display: inline;
float: left;
list-style-type: none;
}
.xblock--drag-and-drop .drag-container .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 {
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 p {
line-height: 1.5em;
font-weight: bold;
margin-bottom: 1.41575em;
}
.no-close .ui-dialog-titlebar-close {
display: none;
}
/*** xBlock styles ***/ /*** xBlock styles ***/
.xblock--drag-and-drop { .xblock--drag-and-drop {
width: 770px; width: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
background: #fff; background: #fff;
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
} }
.xblock--drag-and-drop .target-img { .xblock--drag-and-drop .target-img {
background: url(../img/triangle.png) no-repeat;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
...@@ -161,17 +162,29 @@ ...@@ -161,17 +162,29 @@
/*** FEEDBACK ***/ /*** FEEDBACK ***/
.xblock--drag-and-drop .feedback { .xblock--drag-and-drop .feedback {
line-height: 1.5em; width: 740px;
font-weight: bold; border-top: #ccc 1px solid;
margin: 20px 10px;
padding-top: 10px;
}
.xblock--drag-and-drop .feedback .message {
margin: 5px 0 0;
} }
/** Builder **/ /** Builder **/
.xblock--drag-and-drop .hidden { .xblock--drag-and-drop .hidden {
display: none!important; display: none !important;
}
.xblock--drag-and-drop .drag-builder {
/* TODO */
height: 375px;
overflow: scroll;
} }
.xblock--drag-and-drop .drag-builder .tab { .xblock--drag-and-drop .drag-builder .tab {
width: 98%; width: 100%;
background: #eee; background: #eee;
padding: 3px 0; padding: 3px 0;
position: relative; position: relative;
...@@ -218,10 +231,6 @@ ...@@ -218,10 +231,6 @@
margin-left: 0; margin-left: 0;
} }
.xblock--drag-and-drop .drag-builder .target-image-form input {
width: 400px;
}
.xblock--drag-and-drop .zones-form .zone-row label { .xblock--drag-and-drop .zones-form .zone-row label {
display: inline-block; display: inline-block;
width: 18%; width: 18%;
...@@ -278,6 +287,11 @@ ...@@ -278,6 +287,11 @@
width: 35%; 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 { .xblock--drag-and-drop .items-form textarea {
width: 97%; width: 97%;
margin: 0 1%; margin: 0 1%;
...@@ -287,12 +301,6 @@ ...@@ -287,12 +301,6 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.xblock--drag-and-drop .items-form .item-width,
.xblock--drag-and-drop .items-form .item-height {
width: 30px;
margin-right: 50px;
}
/** Buttons **/ /** Buttons **/
.xblock--drag-and-drop .btn { .xblock--drag-and-drop .btn {
...@@ -407,18 +415,3 @@ ...@@ -407,18 +415,3 @@
.xblock--drag-and-drop .remove-item .icon.remove:after { .xblock--drag-and-drop .remove-item .icon.remove:after {
background: #2e83cd; background: #2e83cd;
} }
/*** Temp. styles for surrounding area & dev ***/
body,
.container {
margin: 0;
padding: 0;
background: #e5ebee;
}
.xblock--drag-and-drop {
margin: 20px auto 0;
padding: 10px 0 10px 10px;
}
.reset { margin: 0 0 15px 700px; }
function DragAndDropBlock(runtime, element) {
var dragAndDrop = (function($) {
var _fn = {
// DOM Elements
$block: $('.xblock--drag-and-drop'),
$app: $('.xblock--drag-and-drop .drag-container'),
$ul: $('.xblock--drag-and-drop .items'),
$target: $('.xblock--drag-and-drop .target-img'),
$feedback: $('.xblock--drag-and-drop .feedback .message'),
// 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" />',
'<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('');
}
},
init: function(data) {
_fn.data = data;
// Add the items to the page
_fn.items.draw();
_fn.zones.draw();
// Load welcome feedback
_fn.feedback.set( _fn.data.feedback.start );
// Init drag and drop plugin
_fn.$items.draggable( _fn.options.drag );
_fn.$zones.droppable( _fn.options.drop );
// Init click handlers
_fn.clickHandlers.init( _fn.$items, _fn.$zones );
// Get count of all active items
_fn.items.init();
// Set the target image
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
},
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');
}
},
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' : 'Incorrect')
.text(str)
.dialog({
dialogClass: "no-close",
modal: true,
buttons: {
Ok: function() {
$( this ).dialog( "close" );
}
}
});
}
},
data: {
feedback: {},
items: [],
zones: [],
targetImg: 'img/triangle.png'
}
};
return {
init: _fn.init,
data: _fn.data
};
})(jQuery);
$.ajax(runtime.handlerUrl(element, 'get_data')).done(function(data){
dragAndDrop.init(data);
});
}
var dragAndDrop = (function() { function DragAndDropEditBlock(runtime, element) {
var dragAndDrop = (function($) {
var _fn = { var _fn = {
// DOM Elements // DOM Elements
$block: $('.xblock--drag-and-drop'), $block: $('.xblock--drag-and-drop', element),
$app: $('.xblock--drag-and-drop .drag-container'), $app: $('.xblock--drag-and-drop .drag-container', element),
$ul: $('.xblock--drag-and-drop .items'), $ul: $('.xblock--drag-and-drop .items', element),
$target: $('.xblock--drag-and-drop .target-img'), $target: $('.xblock--drag-and-drop .target-img', element),
$feedback: $('.xblock--drag-and-drop .feedback .message'), $feedback: $('.xblock--drag-and-drop .feedback .message', element),
// Cannot set until items added to DOM // Cannot set until items added to DOM
$items: {}, // $('.xblock--drag-and-drop .items .option'), $items: {}, // $('.xblock--drag-and-drop .items .option'),
...@@ -56,6 +57,7 @@ var dragAndDrop = (function() { ...@@ -56,6 +57,7 @@ var dragAndDrop = (function() {
'<input type="text" class="size width" value="200" />', '<input type="text" class="size width" value="200" />',
'<label>height</label>', '<label>height</label>',
'<input type="text" class="size height" value="100" />', '<input type="text" class="size height" value="100" />',
'<br />',
'<label>x</label>', '<label>x</label>',
'<input type="text" class="coord x" value="0" />', '<input type="text" class="coord x" value="0" />',
'<label>y</label>', '<label>y</label>',
...@@ -111,43 +113,21 @@ var dragAndDrop = (function() { ...@@ -111,43 +113,21 @@ var dragAndDrop = (function() {
} }
}, },
init: function() {
// Add the items to the page
_fn.items.draw();
_fn.zones.draw();
// Load welcome feedback
_fn.feedback.set( _fn.data.feedback.start );
// Init drag and drop plugin
_fn.$items.draggable( _fn.options.drag );
_fn.$zones.droppable( _fn.options.drop );
// Init click handlers
_fn.clickHandlers.init( _fn.$items, _fn.$zones );
// Get count of all active items
_fn.items.init();
// Set the target image
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
},
build: { build: {
$el: { $el: {
feedback: { feedback: {
form: $('.xblock--drag-and-drop .drag-builder .feedback-form'), form: $('.xblock--drag-and-drop .drag-builder .feedback-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .feedback-tab') tab: $('.xblock--drag-and-drop .drag-builder .feedback-tab', element)
}, },
zones: { zones: {
form: $('.xblock--drag-and-drop .drag-builder .zones-form'), form: $('.xblock--drag-and-drop .drag-builder .zones-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .zones-tab') tab: $('.xblock--drag-and-drop .drag-builder .zones-tab', element)
}, },
items: { items: {
form: $('.xblock--drag-and-drop .drag-builder .items-form'), form: $('.xblock--drag-and-drop .drag-builder .items-form', element),
tab: $('.xblock--drag-and-drop .drag-builder .items-tab') tab: $('.xblock--drag-and-drop .drag-builder .items-tab', element)
}, },
target: $('.xblock--drag-and-drop .drag-builder .target-img') target: $('.xblock--drag-and-drop .drag-builder .target-img', element)
}, },
init: function() { init: function() {
_fn.build.clickHandlers(); _fn.build.clickHandlers();
...@@ -159,7 +139,7 @@ var dragAndDrop = (function() { ...@@ -159,7 +139,7 @@ var dragAndDrop = (function() {
$zoneTab = _fn.build.$el.zones.tab, $zoneTab = _fn.build.$el.zones.tab,
$itemTab = _fn.build.$el.items.tab; $itemTab = _fn.build.$el.items.tab;
$fbkTab.on( 'click', '.goto-zones', function(e) { $(element).one( 'click', '.continue-button', function(e) {
e.preventDefault(); e.preventDefault();
_fn.build.form.feedback( _fn.build.$el.feedback.form ); _fn.build.form.feedback( _fn.build.$el.feedback.form );
...@@ -168,12 +148,8 @@ var dragAndDrop = (function() { ...@@ -168,12 +148,8 @@ var dragAndDrop = (function() {
// Placeholder shim for IE9 // Placeholder shim for IE9
$.placeholder.shim(); $.placeholder.shim();
});
$zoneTab $(this).one( 'click', function(e) {
.on( 'click', '.add-zone', _fn.build.form.zone.add )
.on( 'click', '.remove-zone', _fn.build.form.zone.remove )
.on( 'click', '.goto-items', function(e) {
e.preventDefault(); e.preventDefault();
_fn.build.form.zone.setAll(); _fn.build.form.zone.setAll();
_fn.build.form.item.add(); _fn.build.form.item.add();
...@@ -183,11 +159,24 @@ var dragAndDrop = (function() { ...@@ -183,11 +159,24 @@ var dragAndDrop = (function() {
// Placeholder shim for IE9 // Placeholder shim for IE9
$.placeholder.shim(); $.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) { .on( 'click', '.target-image-form button', function(e) {
e.preventDefault(); e.preventDefault();
_fn.data.targetImg = $('.target-image-form input').val(); _fn.data.targetImg = $('.target-image-form input', element).val();
_fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat'); _fn.$target.css('background', 'url(' + _fn.data.targetImg + ') no-repeat');
// Placeholder shim for IE9 // Placeholder shim for IE9
...@@ -196,11 +185,7 @@ var dragAndDrop = (function() { ...@@ -196,11 +185,7 @@ var dragAndDrop = (function() {
$itemTab $itemTab
.on( 'click', '.add-item', _fn.build.form.item.add ) .on( 'click', '.add-item', _fn.build.form.item.add )
.on( 'click', '.remove-item', _fn.build.form.item.remove ) .on( 'click', '.remove-item', _fn.build.form.item.remove );
.on( 'click', '.goto-exercise', function(e) {
e.preventDefault();
_fn.build.form.submit();
});
}, },
form: { form: {
zone: { zone: {
...@@ -263,7 +248,7 @@ var dragAndDrop = (function() { ...@@ -263,7 +248,7 @@ var dragAndDrop = (function() {
e.preventDefault(); e.preventDefault();
$el.detach(); $el.detach();
$('#' + id).detach(); $('#' + id, element).detach();
_fn.build.form.zone.formCount--; _fn.build.form.zone.formCount--;
_fn.build.form.zone.disableDelete(); _fn.build.form.zone.disableDelete();
...@@ -297,7 +282,7 @@ var dragAndDrop = (function() { ...@@ -297,7 +282,7 @@ var dragAndDrop = (function() {
_fn.build.form.createDropdown(zones); _fn.build.form.createDropdown(zones);
}, },
clickHandler: function( num ) { clickHandler: function( num ) {
var $div = $('#zone-' + num), var $div = $('#zone-' + num, element),
$form = _fn.build.$el.zones.form.find('.zone-row.zone-' + num); $form = _fn.build.$el.zones.form.find('.zone-row.zone-' + num);
// Listen to form changes and update zone div position // Listen to form changes and update zone div position
...@@ -463,21 +448,26 @@ var dragAndDrop = (function() { ...@@ -463,21 +448,26 @@ var dragAndDrop = (function() {
_fn.data.items = items; _fn.data.items = items;
_fn.data.zones = _fn.build.form.zone.cleanObject( _fn.build.form.zone.obj ); _fn.data.zones = _fn.build.form.zone.cleanObject( _fn.build.form.zone.obj );
_fn.init();
_fn.build.$el.items.tab.addClass('hidden'); var data = {
_fn.$app.removeClass('hidden'); 'display_name': $(element).find('.display-name').val(),
_fn.$block.children('header, footer').removeClass('hidden'); 'question_text': $(element).find('.question-text').val(),
_fn.$block.children('.feedback').removeClass('hidden'); 'data': JSON.stringify(_fn.data),
};
$('.xblock-editor-error-message', element).html();
$('.xblock-editor-error-message', element).css('display', 'none');
var handlerUrl = runtime.handlerUrl(element, 'studio_submit');
$.post(handlerUrl, JSON.stringify(data)).done(function(response) {
if (response.result === 'success') {
window.location.reload(false);
} else {
$('.xblock-editor-error-message', element).html('Error: '+response.message);
$('.xblock-editor-error-message', element).css('display', 'block');
}
});
} }
} }
},
// DEV-ONLY: For easier testing
reset: function() {
_fn.clickHandlers.drag.reset( _fn.$items );
_fn.feedback.set( _fn.data.feedback.start );
_fn.$items.draggable('enable');
_fn.test.completed = 0;
}, },
finish: function() { finish: function() {
...@@ -498,8 +488,6 @@ var dragAndDrop = (function() { ...@@ -498,8 +488,6 @@ var dragAndDrop = (function() {
$dropzone.on( 'drop', clk.drop.success ); $dropzone.on( 'drop', clk.drop.success );
$dropzone.on( 'dropover', clk.drop.hover.in ); $dropzone.on( 'dropover', clk.drop.hover.in );
$dropzone.on( 'dropout', clk.drop.hover.out ); $dropzone.on( 'dropout', clk.drop.hover.out );
_fn.$block.find('.reset').on('click', _fn.reset )
}, },
drag: { drag: {
start: function( event, ui ) { start: function( event, ui ) {
...@@ -590,7 +578,7 @@ var dragAndDrop = (function() { ...@@ -590,7 +578,7 @@ var dragAndDrop = (function() {
_fn.$ul.html( list.join('') ); _fn.$ul.html( list.join('') );
// Set variable // Set variable
_fn.$items = $('.xblock--drag-and-drop .items .option'); _fn.$items = $('.xblock--drag-and-drop .items .option', element);
} }
}, },
...@@ -656,14 +644,18 @@ var dragAndDrop = (function() { ...@@ -656,14 +644,18 @@ var dragAndDrop = (function() {
feedback: {}, feedback: {},
items: [], items: [],
zones: [], zones: [],
targetImg: 'img/triangle.png' targetImg: 'https://i.imgur.com/PoI27ox.png'
} }
}; };
return { return {
init: _fn.init,
builder: _fn.build.init builder: _fn.build.init
}; };
})(); })(jQuery);
$(element).find('.cancel-button').bind('click', function() {
runtime.notify('cancel', {});
});
dragAndDrop.builder(); dragAndDrop.builder();
}
...@@ -283,7 +283,6 @@ ...@@ -283,7 +283,6 @@
}; };
// Shuffle an array, using the modern version of the // Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) { _.shuffle = function(obj) {
var rand; var rand;
var index = 0; var index = 0;
......
<section class="xblock--drag-and-drop">
<link rel="stylesheet" href="https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<h2 class="problem-header">
{{ title }}
</h2>
<div class="problem-progress">(1 point possible)</div>
<section class="problem" role="application">
<p>
{{ question_text }}
</p>
</section>
<section class="feedback">
<p class="message"></p>
</section>
<section class="drag-container">
<ul class="items"></ul>
<div class="target">
<div class="target-img"></div>
</div>
<div class="clear"></div>
</section>
</section>
<!doctype html> {% load i18n %}
<!--[if lt IE 7 ]> <html class="lt-ie10"> <![endif]-->
<!--[if IE 7 ]> <html class="lt-ie10"> <![endif]-->
<!--[if IE 8 ]> <html class="lt-ie10"> <![endif]-->
<!--[if IE 9 ]> <html class="lt-ie10"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title>draggable demo</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" href="css/draggable_builder.css">
</head>
<body>
<div class="container">
<section class="xblock--drag-and-drop">
<header class="hidden">
<h1>Title <span class="small">(1 point possible)</span></h1>
<p>Lorem ipsum?</p>
</header>
<section class="feedback hidden"> <div class="xblock--drag-and-drop editor-with-buttons">
<p class="message"></p> <link rel="stylesheet" href="https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
</section>
<section class="drag-builder"> <section class="drag-builder">
<div class="tab feedback-tab"> <div class="tab feedback-tab">
<section class="tab-content"> <section class="tab-content">
<form class="feedback-form"> <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> <h3>Introduction Feedback</h3>
<textarea class="intro-feedback"></textarea> <textarea class="intro-feedback"></textarea>
...@@ -37,20 +21,15 @@ ...@@ -37,20 +21,15 @@
<textarea class="final-feedback"></textarea> <textarea class="final-feedback"></textarea>
</form> </form>
</section> </section>
<footer class="tab-footer"> <!-- <footer class="tab-footer">
<button class="btn continue goto-zones">Continue</button> <button class="btn continue goto-zones">Continue</button>
</footer> </footer> -->
</div> </div>
<div class="tab zones-tab hidden"> <div class="tab zones-tab hidden">
<header class="tab-header"> <header class="tab-header">
<h3>Zone Positions</h3> <h3>Zone Positions</h3>
</header> </header>
<section class="tab-content target-image-form">
<label>New background URL:</label>
<input type="text">
<button class="btn">Change background</button>
</section>
<section class="tab-content"> <section class="tab-content">
<div class="items"> <div class="items">
<form class="zones-form"></form> <form class="zones-form"></form>
...@@ -60,9 +39,9 @@ ...@@ -60,9 +39,9 @@
<div class="target-img"></div> <div class="target-img"></div>
</div> </div>
</section> </section>
<footer class="tab-footer"> <!-- <footer class="tab-footer">
<button class="btn continue goto-items">Continue</button> <button class="btn continue goto-items">Continue</button>
</footer> </footer> -->
</div> </div>
<div class="tab items-tab hidden"> <div class="tab items-tab hidden">
...@@ -74,29 +53,26 @@ ...@@ -74,29 +53,26 @@
</section> </section>
<footer class="tab-footer"> <footer class="tab-footer">
<a href="#" class="add-item add-element"><div class="icon add"></div>Add an item</a> <a href="#" class="add-item add-element"><div class="icon add"></div>Add an item</a>
<button class="btn continue goto-exercise">Finish</button> <!-- <button class="btn continue goto-exercise">Finish</button> -->
</footer> </footer>
</div> </div>
</section> </section>
<section class="drag-container hidden"> <div class="xblock-actions">
<ul class="items"></ul> <span class="xblock-editor-error-message"></span>
<ul>
<li class="action-item">
<a href="#" class="button action-primary continue-button">{% trans "Continue" %}</a>
</li>
<div class="target"> <li class="action-item hidden">
<div class="target-img"></div> <a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
</div> </li>
<!-- Only added for testing purposes --> <li class="action-item">
<button class="btn reset">Reset</button> <a href="#" class="button cancel-button">{% trans "Cancel" %}</a>
</section> </li>
</section> </ul>
</div> </div>
</div>
<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>
<script type="text/javascript" src="js/vendor/jquery.html5-placeholder-shim.js"></script>
<script type="text/javascript" src="js/vendor/underscore1.6.0.js"></script>
<script type="text/javascript" src="js/draggable_builder.js"></script>
</body>
</html>
# -*- coding: utf-8 -*-
#
# Imports ###########################################################
import logging
import pkg_resources
from django.template import Context, Template
# Globals ###########################################################
log = logging.getLogger(__name__)
# Functions #########################################################
def load_resource(resource_path):
"""
Gets the content of a resource
"""
resource_content = pkg_resources.resource_string(__name__, resource_path)
return resource_content
def render_template(template_path, context={}):
"""
Evaluate a template by resource path, applying the provided context
"""
template_str = load_resource(template_path)
template = Template(template_str)
return template.render(Context(context))
# -*- coding: utf-8 -*-
# Imports ###########################################################
import os
from setuptools import setup
# Functions #########################################################
def package_data(pkg, root_list):
"""Generic function to find package_data for `pkg` under `root`."""
data = []
for root in root_list:
for dirname, _, files in os.walk(os.path.join(pkg, root)):
for fname in files:
data.append(os.path.relpath(os.path.join(dirname, fname), pkg))
return {pkg: data}
# Main ##############################################################
setup(
name='xblock-drag-and-drop-v2',
version='0.1',
description='XBlock - Drag-and-Drop v2',
packages=['drag_and_drop_v2'],
install_requires=[
'XBlock',
],
entry_points={
'xblock.v1': 'drag-and-drop-v2 = drag_and_drop_v2:DragAndDropBlock',
},
package_data=package_data("drag_and_drop_v2", ["static", "templates", "public"]),
)
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