diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js
index 185325512c..9cee42feb2 100644
--- a/common/static/js/capa/drag_and_drop/base_image.js
+++ b/common/static/js/capa/drag_and_drop/base_image.js
@@ -8,53 +8,25 @@ define(['logme'], function (logme) {
return BaseImage;
function BaseImage(state) {
- var targetImgSrc, baseImageElContainer, mouseMoveDiv;
-
- targetImgSrc = state.config.imageDir + '/' + state.config.base_image;
+ var baseImageElContainer;
baseImageElContainer = $(
'
'
);
state.baseImageEl = $(
'
'
);
state.baseImageEl.appendTo(baseImageElContainer);
- state.baseImageElWidth = null;
- $('
') // Make in memory copy of image to avoid css issues.
- .attr('src', state.baseImageEl.attr('src'))
- .load(function () {
- state.baseImageElWidth = this.width;
- });
-
- // state.baseImageEl.mousemove(
- // function (event) {
- // mouseMoveDiv.html(
- // '[' + event.offsetX + ', ' + event.offsetY + ']'
- // );
- // }
- // );
-
- mouseMoveDiv = $(
- ''
- );
- mouseMoveDiv.appendTo(baseImageElContainer);
-
baseImageElContainer.appendTo(state.containerEl);
}
});
diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js
index 58f6114009..664324e0b9 100644
--- a/common/static/js/capa/drag_and_drop/config_parser.js
+++ b/common/static/js/capa/drag_and_drop/config_parser.js
@@ -78,9 +78,9 @@ define(['logme'], function (logme) {
if (typeof config.target_outline === 'string') {
if (config.target_outline.toLowerCase() === 'true') {
- state.config.target_outline = true;
+ state.config.targetOutline = true;
} else if (config.target_outline.toLowerCase() === 'false') {
- state.config.target_outline = false;
+ state.config.targetOutline = false;
} else {
logme('ERROR: Property config.target_outline can either be "true", or "false".');
returnStatus = false;
diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js
index 714fe19597..faf0f7b3f3 100644
--- a/common/static/js/capa/drag_and_drop/draggables.js
+++ b/common/static/js/capa/drag_and_drop/draggables.js
@@ -8,22 +8,13 @@ define(['logme', 'update_input'], function (logme, updateInput) {
return Draggables;
function Draggables(state) {
- var _draggables, numDraggables;
+ var c1;
- numDraggables = state.config.draggables.length;
- _draggables = [];
state.draggables = [];
- (function (i) {
- while (i < numDraggables) {
- processDraggable(state.config.draggables[i], i + 1);
- i += 1;
- }
-
- if (state.individualTargets === false) {
- updateInput(state, true);
- }
- }(0));
+ for (c1 = 0; c1 < state.config.draggables.length; c1 += 1) {
+ processDraggable(state.config.draggables[c1], c1 + 1);
+ }
state.currentMovingDraggable = null;
@@ -39,8 +30,8 @@ define(['logme', 'update_input'], function (logme, updateInput) {
return;
function processDraggable(obj, objIndex) {
- var draggableContainerEl, imgEl, inContainer, ousePressed,
- onTarget, draggableObj, marginCss;
+ var draggableContainerEl, inContainer, mousePressed, onTarget,
+ draggableObj, marginCss;
draggableContainerEl = $(
''
);
if (obj.icon.length > 0) {
- imgEl = $(
- '
'
+ draggableContainerEl.append(
+ $('
')
);
-
- draggableContainerEl.append(imgEl);
}
if (obj.label.length > 0) {
@@ -80,7 +67,6 @@ define(['logme', 'update_input'], function (logme, updateInput) {
}
draggableContainerEl.appendTo(state.sliderEl);
- _draggables.push(draggableContainerEl);
inContainer = true;
mousePressed = false;
@@ -102,12 +88,6 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableContainerEl.mouseup(mouseUp);
draggableContainerEl.mousemove(mouseMove);
- if (objIndex + 1 === numDraggables) {
- state.draggablesLoaded = true;
-
- state.updateArrowOpacity();
- }
-
return;
function mouseDown(event) {
@@ -134,10 +114,9 @@ define(['logme', 'update_input'], function (logme, updateInput) {
}
}
- function mouseUp(event) {
+ function mouseUp() {
if (mousePressed === true) {
state.currentMovingDraggable = null;
- normalizeEvent(event);
checkLandingElement(event);
}
@@ -150,14 +129,30 @@ define(['logme', 'update_input'], function (logme, updateInput) {
}
}
- function checkLandingElement(event) {
+ // At this point the mouse was realeased, and we need to check
+ // where the draggable eneded up. Based on several things, we
+ // will either move the draggable back to the slider, or update
+ // the input with the user's answer (X-Y position of the draggable,
+ // or the ID of the target where it landed.
+ function checkLandingElement() {
var offsetDE, indexes, DEindex, targetFound;
mousePressed = false;
offsetDE = draggableContainerEl.position();
- if (state.individualTargets === false) {
+ if (state.individualTargets === true) {
+ targetFound = false;
+
+ checkIfOnTarget();
+
+ if (targetFound === true) {
+ correctZIndexes();
+ } else {
+ moveBackToSlider();
+ removeObjIdFromTarget();
+ }
+ } else {
if (
(offsetDE.left < 0) ||
(offsetDE.left + 100 > state.baseImageEl.width()) ||
@@ -174,21 +169,9 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.x = offsetDE.left + 50;
draggableObj.y = offsetDE.top + 50;
}
- } else if (state.individualTargets === true) {
- targetFound = false;
-
- checkIfOnTarget();
-
- if (targetFound === true) {
- correctZIndexes();
- } else {
- moveBackToSlider();
- removeObjIdFromTarget();
- }
}
state.updateArrowOpacity();
-
updateInput(state);
return;
@@ -197,25 +180,26 @@ define(['logme', 'update_input'], function (logme, updateInput) {
var c1;
if (onTarget !== null) {
- c1 = 0;
-
- while (c1 < onTarget.draggable.length) {
+ for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) {
if (onTarget.draggable[c1] === obj.id) {
onTarget.draggable.splice(c1, 1);
break;
}
- c1 += 1;
}
onTarget = null;
}
}
+ // Determine if a draggable, after it was relased, ends up on a
+ // target. We do this by iterating over all of the targets, and
+ // for each one we check whether the draggable's center is
+ // within the target's dimensions.
function checkIfOnTarget() {
var c1, target;
- for (c1 = 0; c1 < state.targets.length; c1++) {
+ for (c1 = 0; c1 < state.targets.length; c1 += 1) {
target = state.targets[c1];
if (offsetDE.top + 50 < target.offset.top) {
@@ -241,10 +225,21 @@ define(['logme', 'update_input'], function (logme, updateInput) {
targetFound = true;
- removeObjIdFromTarget();
- onTarget = target;
+ // If the draggable was moved from one target to
+ // another, then we need to remove it's ID from the
+ // previous target's draggables list, and add it to the
+ // new target's draggables list.
+ if ((onTarget !== null) && (onTarget.id !== target.id)) {
+ removeObjIdFromTarget();
+ onTarget = target;
+ target.draggable.push(obj.id);
+ } else if (onTarget === null) {
+ onTarget = target;
+ target.draggable.push(obj.id);
+ }
- target.draggable.push(obj.id);
+ // Reposition the draggable so that it's center
+ // coincides with the center of the target.
snapToTarget(target);
break;
@@ -256,30 +251,47 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableContainerEl.css('top', target.offset.top + 0.5 * target.h - 50);
}
+ // Go through all of the draggables subtract 1 from the z-index
+ // of all whose z-index is higher than the old z-index of the
+ // current element. After, set the z-index of the current
+ // element to 1 + N (where N is the number of draggables - i.e.
+ // the highest z-index possible).
+ //
+ // This will make sure that after releasing a draggable, it
+ // will be on top of all of the other draggables. Also, the
+ // ordering of the visibility (z-index) of the other draggables
+ // will not change.
function correctZIndexes() {
var c1;
- c1 = 0;
-
- while (c1 < _draggables.length) {
- if (parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < parseInt(_draggables[c1].css('z-index'), 10)) {
- _draggables[c1].css('z-index', parseInt(_draggables[c1].css('z-index'), 10) - 1);
+ for (c1 = 0; c1 < state.draggables.length; c1++) {
+ if (
+ parseInt(draggableContainerEl.attr('data-old-z-index'), 10) <
+ parseInt(state.draggables[c1].el.css('z-index'), 10)
+ ) {
+ state.draggables[c1].el.css(
+ 'z-index',
+ parseInt(state.draggables[c1].el.css('z-index'), 10) - 1
+ );
}
- c1 += 1;
}
draggableContainerEl.css('z-index', c1);
}
+ // If a draggable was released in a wrong positione, we will
+ // move it back to the slider, placing it in the same position
+ // that it was dragged out of.
function moveBackToSlider() {
var c1;
draggableContainerEl.detach();
draggableContainerEl.css('position', 'static');
+ // Get the position indexes of all draggables that are
+ // currently in the slider, along with the corresponding
+ // jQuery element.
indexes = [];
- DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10);
-
state.sliderEl.children().each(function (index, value) {
indexes.push({
'index': parseInt($(value).attr('data-draggable-position-index'), 10),
@@ -287,27 +299,39 @@ define(['logme', 'update_input'], function (logme, updateInput) {
});
});
- c1 = 0;
+ // Get the position index of the element that we are
+ // inserting back into the slider.
+ DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10);
- while (c1 < indexes.length) {
+ // Starting from the first position index that we
+ // retrieved, and going up, if we find a position index
+ // that is more than 'DEindex', we know that we must insert
+ // the current element before the element with the greater
+ // position index.
+ for (c1 = 0; c1 < indexes.length; c1 += 1) {
if ((inContainer === false) && (indexes[c1].index > DEindex)) {
indexes[c1].el.before(draggableContainerEl);
inContainer = true;
}
-
- c1 += 1;
}
+ // If we did not find a greater postion index, then either
+ // there are no elements in the slider, or all of them
+ // have a lesser position index. In both cases we add the
+ // current draggable to the end.
if (inContainer === false) {
draggableContainerEl.appendTo(state.sliderEl);
- inContainer = true;
}
+ inContainer = true;
+
draggableContainerEl.css('border', '1px solid gray');
}
}
}
+ // In firefox the event does not have a proper pageX and pageY
+ // coordinates.
function normalizeEvent(event) {
if(!event.offsetX) {
event.offsetX = (event.pageX - $(event.target).offset().left);
diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js
index 9027f55d01..0ccfb47051 100644
--- a/common/static/js/capa/drag_and_drop/main.js
+++ b/common/static/js/capa/drag_and_drop/main.js
@@ -5,8 +5,10 @@
(function (requirejs, require, define) {
define(
- ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets'],
- function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets) {
+ ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller',
+ 'draggables', 'targets', 'update_input'],
+ function (logme, State, configParser, Container, BaseImage, Scroller,
+ Draggables, Targets, updateInput) {
return Main;
function Main() {
@@ -58,12 +60,13 @@ define(
Container(state);
BaseImage(state);
+ Targets(state);
Scroller(state);
Draggables(state);
- Targets(state);
- logme('config', config);
- logme('state', state);
+ // Update the input element, checking first that it is not filled with
+ // an answer from the server.
+ updateInput(state, true);
}
});
diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js
index 46af42bb1b..d3a4d23eab 100644
--- a/common/static/js/capa/drag_and_drop/scroller.js
+++ b/common/static/js/capa/drag_and_drop/scroller.js
@@ -61,14 +61,20 @@ define(['logme'], function (logme) {
moveLeftEl.mousemove(function (event) { event.preventDefault(); });
moveLeftEl.mousedown(function (event) { event.preventDefault(); });
+ // This event will be responsible for moving the scroller left.
+ // Hidden draggables will be shown.
moveLeftEl.mouseup(function (event) {
event.preventDefault();
+ // When there are no more hidden draggables, prevent from
+ // scrolling infinitely.
if (showElLeftMargin > -102) {
return;
}
showElLeftMargin += 102;
+
+ // We scroll by changing the 'margin-left' CSS property smoothly.
state.sliderEl.animate({
'margin-left': showElLeftMargin + 'px'
}, 100, function () {
@@ -91,6 +97,10 @@ define(['logme'], function (logme) {
showElLeftMargin = 0;
+ // Element where the draggables will be contained. It is very long
+ // so that any SANE number of draggables will fit in a single row. It
+ // will be contained in a parent element whose 'overflow' CSS value
+ // will be hidden, preventing the long row from fully being visible.
state.sliderEl = $(
''
);
- tEl.appendTo(state.baseImageEl.parent());
+ targetEl.appendTo(state.baseImageEl.parent());
state.targets.push({
'id': obj.id,
- 'offset': tEl.position(),
+
'w': obj.w,
'h': obj.h,
- 'el': tEl,
+
+ 'el': targetEl,
+ 'offset': targetEl.position(),
+
'draggable': []
});
-
- if (objIndex + 1 === numTargets) {
- state.targetsLoaded = true;
- }
}
}
});
diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js
index 7b5cd6acfa..6ebf62ef11 100644
--- a/common/static/js/capa/drag_and_drop/update_input.js
+++ b/common/static/js/capa/drag_and_drop/update_input.js
@@ -54,6 +54,8 @@ define(['logme'], function (logme) {
inputEl = $('#input_' + state.problemId);
inputEl.val(stateStr);
+ logme(inputEl.val());
+
return;
// Check if input has an answer from server. If yes, then position
@@ -75,16 +77,7 @@ define(['logme'], function (logme) {
var draggableId, draggable, targetId, target, draggablePosition,
c1;
- if (
- ((state.individualTargets === true) && (state.targetsLoaded === false)) ||
- (state.draggablesLoaded === false)
- ) {
- window.setTimeout(function () {
- repositionDraggables(answer);
- }, 50);
-
- return;
- }
+ logme(answer);
if (
((typeof answer.use_targets === 'boolean') && (answer.use_targets === true)) ||