Commit 0d83fefe by Vasyl Nakvasiuk

merge feature/alex/drag_and_drop-mitx into feature/alex/poll-merged

parent f87a5fbb
...@@ -798,6 +798,10 @@ class DragAndDropInput(InputTypeBase): ...@@ -798,6 +798,10 @@ class DragAndDropInput(InputTypeBase):
if tag_type == 'draggable' and not self.no_labels: if tag_type == 'draggable' and not self.no_labels:
dic['label'] = dic['label'] or dic['id'] dic['label'] = dic['label'] or dic['id']
if tag_type == 'draggable':
dic['target_fields'] = [parse(target, 'target') for target in
tag.iterchildren('target')]
return dic return dic
# add labels to images?: # add labels to images?:
......
...@@ -539,14 +539,14 @@ class DragAndDropTest(unittest.TestCase): ...@@ -539,14 +539,14 @@ class DragAndDropTest(unittest.TestCase):
"target_outline": "false", "target_outline": "false",
"base_image": "/static/images/about_1.png", "base_image": "/static/images/about_1.png",
"draggables": [ "draggables": [
{"can_reuse": "", "label": "Label 1", "id": "1", "icon": ""}, {"can_reuse": "", "label": "Label 1", "id": "1", "icon": "", "target_fields": []},
{"can_reuse": "", "label": "cc", "id": "name_with_icon", "icon": "/static/images/cc.jpg", }, {"can_reuse": "", "label": "cc", "id": "name_with_icon", "icon": "/static/images/cc.jpg", "target_fields": []},
{"can_reuse": "", "label": "arrow-left", "id": "with_icon", "icon": "/static/images/arrow-left.png", "can_reuse": ""}, {"can_reuse": "", "label": "arrow-left", "id": "with_icon", "icon": "/static/images/arrow-left.png", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "Label2", "id": "5", "icon": "", "can_reuse": ""}, {"can_reuse": "", "label": "Label2", "id": "5", "icon": "", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "Mute", "id": "2", "icon": "/static/images/mute.png", "can_reuse": ""}, {"can_reuse": "", "label": "Mute", "id": "2", "icon": "/static/images/mute.png", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "spinner", "id": "name_label_icon3", "icon": "/static/images/spinner.gif", "can_reuse": ""}, {"can_reuse": "", "label": "spinner", "id": "name_label_icon3", "icon": "/static/images/spinner.gif", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "Star", "id": "name4", "icon": "/static/images/volume.png", "can_reuse": ""}, {"can_reuse": "", "label": "Star", "id": "name4", "icon": "/static/images/volume.png", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "Label3", "id": "7", "icon": "", "can_reuse": ""}], {"can_reuse": "", "label": "Label3", "id": "7", "icon": "", "can_reuse": "", "target_fields": []}],
"one_per_target": "True", "one_per_target": "True",
"targets": [ "targets": [
{"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"}, {"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"},
......
...@@ -27,6 +27,49 @@ values are (x,y) coordinates of centers of dragged images. ...@@ -27,6 +27,49 @@ values are (x,y) coordinates of centers of dragged images.
import json import json
def flat_user_answer(user_answer):
"""
Convert nested `user_answer` to flat format.
{'up': {'first': {'p': 'p_l'}}}
to
{'up': 'p_l[p][first]'}
"""
def parse_user_answer(answer):
key = answer.keys()[0]
value = answer.values()[0]
if isinstance(value, dict):
# Make complex value:
# Example:
# Create like 'p_l[p][first]' from {'first': {'p': 'p_l'}
complex_value_list = []
v_value = value
while isinstance(v_value, dict):
v_key = v_value.keys()[0]
v_value = v_value.values()[0]
complex_value_list.append(v_key)
complex_value = '{0}'.format(v_value)
for i in reversed(complex_value_list):
complex_value = '{0}[{1}]'.format(complex_value, i)
res = {key: complex_value}
return res
else:
return answer
result = []
for answer in user_answer:
parse_answer = parse_user_answer(answer)
result.append(parse_answer)
return result
class PositionsCompare(list): class PositionsCompare(list):
""" Class for comparing positions. """ Class for comparing positions.
...@@ -116,37 +159,36 @@ class DragAndDrop(object): ...@@ -116,37 +159,36 @@ class DragAndDrop(object):
# Number of draggables in user_groups may be differ that in # Number of draggables in user_groups may be differ that in
# correct_groups, that is incorrect, except special case with 'number' # correct_groups, that is incorrect, except special case with 'number'
for groupname, draggable_ids in self.correct_groups.items(): for index, draggable_ids in enumerate(self.correct_groups):
# 'number' rule special case # 'number' rule special case
# for reusable draggables we may get in self.user_groups # for reusable draggables we may get in self.user_groups
# {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']} # {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']}
# if '+number' is in rule - do not remove duplicates and strip # if '+number' is in rule - do not remove duplicates and strip
# '+number' from rule # '+number' from rule
current_rule = self.correct_positions[groupname].keys()[0] current_rule = self.correct_positions[index].keys()[0]
if 'number' in current_rule: if 'number' in current_rule:
rule_values = self.correct_positions[groupname][current_rule] rule_values = self.correct_positions[index][current_rule]
# clean rule, do not do clean duplicate items # clean rule, do not do clean duplicate items
self.correct_positions[groupname].pop(current_rule, None) self.correct_positions[index].pop(current_rule, None)
parsed_rule = current_rule.replace('+', '').replace('number', '') parsed_rule = current_rule.replace('+', '').replace('number', '')
self.correct_positions[groupname][parsed_rule] = rule_values self.correct_positions[index][parsed_rule] = rule_values
else: # remove dublicates else: # remove dublicates
self.user_groups[groupname] = list(set(self.user_groups[groupname])) self.user_groups[index] = list(set(self.user_groups[index]))
if sorted(draggable_ids) != sorted(self.user_groups[groupname]): if sorted(draggable_ids) != sorted(self.user_groups[index]):
return False return False
# Check that in every group, for rule of that group, user positions of # Check that in every group, for rule of that group, user positions of
# every element are equal with correct positions # every element are equal with correct positions
for groupname in self.correct_groups: for index, _ in enumerate(self.correct_groups):
rules_executed = 0 rules_executed = 0
for rule in ('exact', 'anyof', 'unordered_equal'): for rule in ('exact', 'anyof', 'unordered_equal'):
# every group has only one rule # every group has only one rule
if self.correct_positions[groupname].get(rule, None): if self.correct_positions[index].get(rule, None):
rules_executed += 1 rules_executed += 1
if not self.compare_positions( if not self.compare_positions(
self.correct_positions[groupname][rule], self.correct_positions[index][rule],
self.user_positions[groupname]['user'], flag=rule): self.user_positions[index]['user'], flag=rule):
return False return False
if not rules_executed: # no correct rules for current group if not rules_executed: # no correct rules for current group
# probably xml content mistake - wrong rules names # probably xml content mistake - wrong rules names
...@@ -248,7 +290,7 @@ class DragAndDrop(object): ...@@ -248,7 +290,7 @@ class DragAndDrop(object):
correct_answer = {'name4': 't1', correct_answer = {'name4': 't1',
'name_with_icon': 't1', 'name_with_icon': 't1',
'5': 't2', '5': 't2',
'7':'t2'} '7': 't2'}
It is draggable_name: dragable_position mapping. It is draggable_name: dragable_position mapping.
...@@ -287,21 +329,22 @@ class DragAndDrop(object): ...@@ -287,21 +329,22 @@ class DragAndDrop(object):
correct_answer: dict or list correct_answer: dict or list
""" """
self.correct_groups = dict() # correct groups from xml self.correct_groups = [] # Correct groups from xml.
self.correct_positions = dict() # correct positions for comparing self.correct_positions = [] # Correct positions for comparing.
self.user_groups = dict() # will be populated from user answer self.user_groups = [] # Will be populated from user answer.
self.user_positions = dict() # will be populated from user answer self.user_positions = [] # Will be populated from user answer.
# convert from dict answer format to list format # Convert from dict answer format to list format.
if isinstance(correct_answer, dict): if isinstance(correct_answer, dict):
tmp = [] tmp = []
for key, value in correct_answer.items(): for key, value in correct_answer.items():
tmp_dict = {'draggables': [], 'targets': [], 'rule': 'exact'} tmp.append({
tmp_dict['draggables'].append(key) 'draggables': [key],
tmp_dict['targets'].append(value) 'targets': [value],
tmp.append(tmp_dict) 'rule': 'exact'})
correct_answer = tmp correct_answer = tmp
# Convert string `user_answer` to object.
user_answer = json.loads(user_answer) user_answer = json.loads(user_answer)
# This dictionary will hold a key for each draggable the user placed on # This dictionary will hold a key for each draggable the user placed on
...@@ -312,24 +355,29 @@ class DragAndDrop(object): ...@@ -312,24 +355,29 @@ class DragAndDrop(object):
self.excess_draggables = dict((users_draggable.keys()[0],True) self.excess_draggables = dict((users_draggable.keys()[0],True)
for users_draggable in user_answer['draggables']) for users_draggable in user_answer['draggables'])
# create identical data structures from user answer and correct answer # Convert nested `user_answer` to flat format.
for i in xrange(0, len(correct_answer)): user_answer = flat_user_answer(user_answer)
groupname = str(i)
self.correct_groups[groupname] = correct_answer[i]['draggables'] # Create identical data structures from user answer and correct answer.
self.correct_positions[groupname] = {correct_answer[i]['rule']: for answer in correct_answer:
correct_answer[i]['targets']} user_groups_data = []
self.user_groups[groupname] = [] user_positions_data = []
self.user_positions[groupname] = {'user': []} for draggable_dict in user_answer:
for draggable_dict in user_answer['draggables']: # Draggable_dict is 1-to-1 {draggable_name: position}.
# draggable_dict is 1-to-1 {draggable_name: position}
draggable_name = draggable_dict.keys()[0] draggable_name = draggable_dict.keys()[0]
if draggable_name in self.correct_groups[groupname]: if draggable_name in answer['draggables']:
self.user_groups[groupname].append(draggable_name) user_groups_data.append(draggable_name)
self.user_positions[groupname]['user'].append( user_positions_data.append(
draggable_dict[draggable_name]) draggable_dict[draggable_name])
# proved that this is not excess # proved that this is not excess
self.excess_draggables[draggable_name] = False self.excess_draggables[draggable_name] = False
self.correct_groups.append(answer['draggables'])
self.correct_positions.append({answer['rule']: answer['targets']})
self.user_groups.append(user_groups_data)
self.user_positions.append({'user': user_positions_data})
def grade(user_input, correct_answer): def grade(user_input, correct_answer):
""" Creates DragAndDrop instance from user_input and correct_answer and """ Creates DragAndDrop instance from user_input and correct_answer and
calls DragAndDrop.grade for grading. calls DragAndDrop.grade for grading.
......
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define(['logme'], function (logme) { define(['logme'], function (logme) {
return BaseImage; return BaseImage;
...@@ -50,10 +45,5 @@ define(['logme'], function (logme) { ...@@ -50,10 +45,5 @@ define(['logme'], function (logme) {
baseImageElContainer.appendTo(state.containerEl); baseImageElContainer.appendTo(state.containerEl);
}); });
} }
}); }); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define(['logme'], function (logme) { define(['logme'], function (logme) {
return configParser; return configParser;
...@@ -16,7 +11,7 @@ define(['logme'], function (logme) { ...@@ -16,7 +11,7 @@ define(['logme'], function (logme) {
'targetOutline': true, 'targetOutline': true,
'labelBgColor': '#d6d6d6', 'labelBgColor': '#d6d6d6',
'individualTargets': null, // Depends on 'targets'. 'individualTargets': null, // Depends on 'targets'.
'errors': 0 // Number of errors found while parsing config. 'foundErrors': false // Whether or not we find errors while processing the config.
}; };
getDraggables(state, config); getDraggables(state, config);
...@@ -28,7 +23,7 @@ define(['logme'], function (logme) { ...@@ -28,7 +23,7 @@ define(['logme'], function (logme) {
setIndividualTargets(state); setIndividualTargets(state);
if (state.config.errors !== 0) { if (state.config.foundErrors !== false) {
return false; return false;
} }
...@@ -38,35 +33,34 @@ define(['logme'], function (logme) { ...@@ -38,35 +33,34 @@ define(['logme'], function (logme) {
function getDraggables(state, config) { function getDraggables(state, config) {
if (config.hasOwnProperty('draggables') === false) { if (config.hasOwnProperty('draggables') === false) {
logme('ERROR: "config" does not have a property "draggables".'); logme('ERROR: "config" does not have a property "draggables".');
state.config.errors += 1; state.config.foundErrors = true;
} else if ($.isArray(config.draggables) === true) { } else if ($.isArray(config.draggables) === true) {
(function (i) { config.draggables.every(function (draggable) {
while (i < config.draggables.length) { if (processDraggable(state, draggable) !== true) {
if (processDraggable(state, config.draggables[i]) !== true) { state.config.foundErrors = true;
state.config.errors += 1;
} // Exit immediately from .every() call.
i += 1; return false;
}
}(0));
} else if ($.isPlainObject(config.draggables) === true) {
if (processDraggable(state, config.draggables) !== true) {
state.config.errors += 1;
} }
// Continue to next .every() call.
return true;
});
} else { } else {
logme('ERROR: The type of config.draggables is no supported.'); logme('ERROR: The type of config.draggables is no supported.');
state.config.errors += 1; state.config.foundErrors = true;
} }
} }
function getBaseImage(state, config) { function getBaseImage(state, config) {
if (config.hasOwnProperty('base_image') === false) { if (config.hasOwnProperty('base_image') === false) {
logme('ERROR: "config" does not have a property "base_image".'); logme('ERROR: "config" does not have a property "base_image".');
state.config.errors += 1; state.config.foundErrors = true;
} else if (typeof config.base_image === 'string') { } else if (typeof config.base_image === 'string') {
state.config.baseImage = config.base_image; state.config.baseImage = config.base_image;
} else { } else {
logme('ERROR: Property config.base_image is not of type "string".'); logme('ERROR: Property config.base_image is not of type "string".');
state.config.errors += 1; state.config.foundErrors = true;
} }
} }
...@@ -77,28 +71,27 @@ define(['logme'], function (logme) { ...@@ -77,28 +71,27 @@ define(['logme'], function (logme) {
// Draggables can be positioned anywhere on the image, and the server will // Draggables can be positioned anywhere on the image, and the server will
// get an answer in the form of (x, y) coordinates for each draggable. // get an answer in the form of (x, y) coordinates for each draggable.
} else if ($.isArray(config.targets) === true) { } else if ($.isArray(config.targets) === true) {
(function (i) { config.targets.every(function (target) {
while (i < config.targets.length) { if (processTarget(state, target) !== true) {
if (processTarget(state, config.targets[i]) !== true) { state.config.foundErrors = true;
state.config.errors += 1;
} // Exit immediately from .every() call.
i += 1; return false;
}
}(0));
} else if ($.isPlainObject(config.targets) === true) {
if (processTarget(state, config.targets) !== true) {
state.config.errors += 1;
} }
// Continue to next .every() call.
return true;
});
} else { } else {
logme('ERROR: Property config.targets is not of a supported type.'); logme('ERROR: Property config.targets is not of a supported type.');
state.config.errors += 1; state.config.foundErrors = true;
} }
} }
function getOnePerTarget(state, config) { function getOnePerTarget(state, config) {
if (config.hasOwnProperty('one_per_target') === false) { if (config.hasOwnProperty('one_per_target') === false) {
logme('ERROR: "config" does not have a property "one_per_target".'); logme('ERROR: "config" does not have a property "one_per_target".');
state.config.errors += 1; state.config.foundErrors = true;
} else if (typeof config.one_per_target === 'string') { } else if (typeof config.one_per_target === 'string') {
if (config.one_per_target.toLowerCase() === 'true') { if (config.one_per_target.toLowerCase() === 'true') {
state.config.onePerTarget = true; state.config.onePerTarget = true;
...@@ -106,42 +99,45 @@ define(['logme'], function (logme) { ...@@ -106,42 +99,45 @@ define(['logme'], function (logme) {
state.config.onePerTarget = false; state.config.onePerTarget = false;
} else { } else {
logme('ERROR: Property config.one_per_target can either be "true", or "false".'); logme('ERROR: Property config.one_per_target can either be "true", or "false".');
state.config.errors += 1; state.config.foundErrors = true;
} }
} else { } else {
logme('ERROR: Property config.one_per_target is not of a supported type.'); logme('ERROR: Property config.one_per_target is not of a supported type.');
state.config.errors += 1; state.config.foundErrors = true;
} }
} }
function getTargetOutline(state, config) { function getTargetOutline(state, config) {
if (config.hasOwnProperty('target_outline') === false) {
// It is possible that no "target_outline" was specified. This is not an error. // It is possible that no "target_outline" was specified. This is not an error.
// In this case the default value of 'true' (boolean) will be used. // In this case the default value of 'true' (boolean) will be used.
} else if (typeof config.target_outline === 'string') {
if (config.hasOwnProperty('target_outline') === true) {
if (typeof config.target_outline === 'string') {
if (config.target_outline.toLowerCase() === 'true') { if (config.target_outline.toLowerCase() === 'true') {
state.config.targetOutline = true; state.config.targetOutline = true;
} else if (config.target_outline.toLowerCase() === 'false') { } else if (config.target_outline.toLowerCase() === 'false') {
state.config.targetOutline = false; state.config.targetOutline = false;
} else { } else {
logme('ERROR: Property config.target_outline can either be "true", or "false".'); logme('ERROR: Property config.target_outline can either be "true", or "false".');
state.config.errors += 1; state.config.foundErrors = true;
} }
} else { } else {
logme('ERROR: Property config.target_outline is not of a supported type.'); logme('ERROR: Property config.target_outline is not of a supported type.');
state.config.errors += 1; state.config.foundErrors = true;
}
} }
} }
function getLabelBgColor(state, config) { function getLabelBgColor(state, config) {
if (config.hasOwnProperty('label_bg_color') === false) {
// It is possible that no "label_bg_color" was specified. This is not an error. // It is possible that no "label_bg_color" was specified. This is not an error.
// In this case the default value of '#d6d6d6' (string) will be used. // In this case the default value of '#d6d6d6' (string) will be used.
} else if (typeof config.label_bg_color === 'string') {
if (config.hasOwnProperty('label_bg_color') === true) {
if (typeof config.label_bg_color === 'string') {
state.config.labelBgColor = config.label_bg_color; state.config.labelBgColor = config.label_bg_color;
} else { } else {
logme('ERROR: Property config.label_bg_color is not of a supported type.'); logme('ERROR: Property config.label_bg_color is not of a supported type.');
returnStatus = false; }
} }
} }
...@@ -159,17 +155,36 @@ define(['logme'], function (logme) { ...@@ -159,17 +155,36 @@ define(['logme'], function (logme) {
(attrIsString(obj, 'icon') === false) || (attrIsString(obj, 'icon') === false) ||
(attrIsString(obj, 'label') === false) || (attrIsString(obj, 'label') === false) ||
(attrIsBoolean(obj, 'can_reuse', false) === false) (attrIsBoolean(obj, 'can_reuse', false) === false) ||
(obj.hasOwnProperty('target_fields') === false)
) { ) {
return false; return false;
} }
// Check that all targets in the 'target_fields' property are proper target objects.
// We will be testing the return value from .every() call (it can be 'true' or 'false').
if (obj.target_fields.every(
function (targetObj) {
return processTarget(state, targetObj, false);
}
) === false) {
return false;
}
state.config.draggables.push(obj); state.config.draggables.push(obj);
return true; return true;
} }
function processTarget(state, obj) { // We need 'pushToState' parameter in order to simply test an object for the fact that it is a
// proper target (without pushing it to the 'state' object). When
//
// pushToState === false
//
// the object being tested is not going to be pushed to 'state'. The function will onyl return
// 'true' or 'false.
function processTarget(state, obj, pushToState) {
if ( if (
(attrIsString(obj, 'id') === false) || (attrIsString(obj, 'id') === false) ||
...@@ -182,7 +197,9 @@ define(['logme'], function (logme) { ...@@ -182,7 +197,9 @@ define(['logme'], function (logme) {
return false; return false;
} }
if (pushToState !== false) {
state.config.targets.push(obj); state.config.targets.push(obj);
}
return true; return true;
} }
...@@ -250,10 +267,5 @@ define(['logme'], function (logme) { ...@@ -250,10 +267,5 @@ define(['logme'], function (logme) {
return true; return true;
} }
}); }); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define(['logme'], function (logme) { define(['logme'], function (logme) {
return Container; return Container;
...@@ -21,10 +16,5 @@ define(['logme'], function (logme) { ...@@ -21,10 +16,5 @@ define(['logme'], function (logme) {
$('#inputtype_' + state.problemId).before(state.containerEl); $('#inputtype_' + state.problemId).before(state.containerEl);
} }
}); }); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
(function (requirejs, require, define) {
define(['logme'], function (logme) {
return {
'attachMouseEventsTo': function (element) {
var self;
self = this;
this[element].mousedown(function (event) {
self.mouseDown(event);
});
this[element].mouseup(function (event) {
self.mouseUp(event);
});
this[element].mousemove(function (event) {
self.mouseMove(event);
});
},
'mouseDown': function (event) {
if (this.mousePressed === false) {
// So that the browser does not perform a default drag.
// If we don't do this, each drag operation will
// potentially cause the highlghting of the dragged element.
event.preventDefault();
event.stopPropagation();
if (this.numDraggablesOnMe > 0) {
return;
}
// If this draggable is just being dragged out of the
// container, we must perform some additional tasks.
if (this.inContainer === true) {
if ((this.isReusable === true) && (this.isOriginal === true)) {
this.makeDraggableCopy(function (draggableCopy) {
draggableCopy.mouseDown(event);
});
return;
}
if (this.isOriginal === true) {
this.containerEl.hide();
this.iconEl.detach();
}
if (this.iconImgEl !== null) {
this.iconImgEl.css({
'width': this.iconWidth,
'height': this.iconHeight
});
}
this.iconEl.css({
'background-color': this.iconElBGColor,
'padding-left': this.iconElPadding,
'padding-right': this.iconElPadding,
'border': this.iconElBorder,
'width': this.iconWidth,
'height': this.iconHeight,
'left': event.pageX - this.state.baseImageEl.offset().left - this.iconWidth * 0.5 - this.iconElLeftOffset,
'top': event.pageY - this.state.baseImageEl.offset().top - this.iconHeight * 0.5
});
this.iconEl.appendTo(this.state.baseImageEl.parent());
if (this.labelEl !== null) {
if (this.isOriginal === true) {
this.labelEl.detach();
}
this.labelEl.css({
'background-color': this.state.config.labelBgColor,
'padding-left': 8,
'padding-right': 8,
'border': '1px solid black',
'left': event.pageX - this.state.baseImageEl.offset().left - this.labelWidth * 0.5 - 9, // Account for padding, border.
'top': event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5
});
this.labelEl.appendTo(this.state.baseImageEl.parent());
}
this.inContainer = false;
if (this.isOriginal === true) {
this.state.numDraggablesInSlider -= 1;
}
}
this.zIndex = 1000;
this.iconEl.css('z-index', '1000');
if (this.labelEl !== null) {
this.labelEl.css('z-index', '1000');
}
this.mousePressed = true;
this.state.currentMovingDraggable = this;
}
},
'mouseUp': function () {
if (this.mousePressed === true) {
this.state.currentMovingDraggable = null;
this.checkLandingElement();
}
},
'mouseMove': function (event) {
if (this.mousePressed === true) {
// Because we have also attached a 'mousemove' event to the
// 'document' (that will do the same thing), let's tell the
// browser not to bubble up this event. The attached event
// on the 'document' will only be triggered when the mouse
// pointer leaves the draggable while it is in the middle
// of a drag operation (user moves the mouse very quickly).
event.stopPropagation();
this.iconEl.css({
'left': event.pageX - this.state.baseImageEl.offset().left - this.iconWidth * 0.5 - this.iconElLeftOffset,
'top': event.pageY - this.state.baseImageEl.offset().top - this.iconHeight * 0.5
});
if (this.labelEl !== null) {
this.labelEl.css({
'left': event.pageX - this.state.baseImageEl.offset().left - this.labelWidth * 0.5 - 9, // Acoount for padding, border.
'top': event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5
});
}
}
}
}; // End-of: return {
}); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define([], function () { define([], function () {
var debugMode; var debugMode;
...@@ -27,10 +22,5 @@ define([], function () { ...@@ -27,10 +22,5 @@ define([], function () {
i += 1; i += 1;
} }
} }
}); }); // End-of: define([], function () {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define( define(
['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets', 'update_input'], ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets', 'update_input'],
function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets, updateInput) { function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets, updateInput) {
return Main; return Main;
function Main() { function Main() {
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every
//
// Array.prototype.every is a recent addition to the ECMA-262 standard; as such it may not be present in
// other implementations of the standard.
if (!Array.prototype.every) {
Array.prototype.every = function(fun /*, thisp */) {
var thisp, t, len, i;
if (this == null) {
throw new TypeError();
}
t = Object(this);
len = t.length >>> 0;
if (typeof fun != 'function') {
throw new TypeError();
}
thisp = arguments[1];
for (i = 0; i < len; i++) {
if (i in t && !fun.call(thisp, t[i], i, t)) {
return false;
}
}
return true;
};
}
$('.drag_and_drop_problem_div').each(processProblem); $('.drag_and_drop_problem_div').each(processProblem);
} }
...@@ -59,7 +85,7 @@ define( ...@@ -59,7 +85,7 @@ define(
return; return;
} }
Targets(state); Targets.initializeBaseTargets(state);
Scroller(state); Scroller(state);
Draggables.init(state); Draggables.init(state);
...@@ -72,10 +98,5 @@ define( ...@@ -72,10 +98,5 @@ define(
} }
}()); }());
} }
}); }); // End-of: define(
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define(['logme'], function (logme) { define(['logme'], function (logme) {
return Scroller; return Scroller;
...@@ -206,10 +201,5 @@ define(['logme'], function (logme) { ...@@ -206,10 +201,5 @@ define(['logme'], function (logme) {
} }
} }
} // End-of: function Scroller(state) } // End-of: function Scroller(state)
}); }); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define([], function () { define([], function () {
return State; return State;
...@@ -96,10 +91,5 @@ define([], function () { ...@@ -96,10 +91,5 @@ define([], function () {
} }
} }
} }
}); }); // End-of: define([], function () {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) { (function (requirejs, require, define) {
define(['logme'], function (logme) { define(['logme'], function (logme) {
return Targets; return {
'initializeBaseTargets': initializeBaseTargets,
'initializeTargetField': initializeTargetField,
'destroyTargetField': destroyTargetField
};
function Targets(state) { function initializeBaseTargets(state) {
(function (c1) { (function (c1) {
while (c1 < state.config.targets.length) { while (c1 < state.config.targets.length) {
processTarget(state, state.config.targets[c1]); processTarget(state, state.config.targets[c1]);
...@@ -17,7 +16,58 @@ define(['logme'], function (logme) { ...@@ -17,7 +16,58 @@ define(['logme'], function (logme) {
}(0)); }(0));
} }
function processTarget(state, obj) { function initializeTargetField(draggableObj) {
var iconElOffset;
if (draggableObj.targetField.length === 0) {
draggableObj.originalConfigObj.target_fields.every(function (targetObj) {
processTarget(draggableObj.state, targetObj, true, draggableObj);
return true;
});
} else {
iconElOffset = draggableObj.iconEl.position();
draggableObj.targetField.every(function (targetObj) {
targetObj.offset.top = iconElOffset.top + targetObj.y;
targetObj.offset.left = iconElOffset.left + targetObj.x;
return true;
});
}
}
function destroyTargetField(draggableObj) {
var indexOffset, lowestRemovedIndex;
indexOffset = 0;
lowestRemovedIndex = draggableObj.state.targets.length + 1;
draggableObj.targetField.every(function (target) {
target.el.remove();
if (lowestRemovedIndex > target.indexInStateArray) {
lowestRemovedIndex = target.indexInStateArray;
}
draggableObj.state.targets.splice(target.indexInStateArray - indexOffset, 1);
indexOffset += 1;
return true;
});
draggableObj.state.targets.every(function (target) {
if (target.indexInStateArray > lowestRemovedIndex) {
target.indexInStateArray -= indexOffset;
}
return true;
});
draggableObj.targetField = [];
}
function processTarget(state, obj, fromTargetField, draggableObj) {
var targetEl, borderCss, numTextEl, targetObj; var targetEl, borderCss, numTextEl, targetObj;
borderCss = ''; borderCss = '';
...@@ -38,7 +88,13 @@ define(['logme'], function (logme) { ...@@ -38,7 +88,13 @@ define(['logme'], function (logme) {
'" ' + '" ' +
'></div>' '></div>'
); );
if (fromTargetField === true) {
targetEl.appendTo(draggableObj.iconEl);
} else {
targetEl.appendTo(state.baseImageEl.parent()); targetEl.appendTo(state.baseImageEl.parent());
}
targetEl.mousedown(function (event) { targetEl.mousedown(function (event) {
event.preventDefault(); event.preventDefault();
}); });
...@@ -68,8 +124,13 @@ define(['logme'], function (logme) { ...@@ -68,8 +124,13 @@ define(['logme'], function (logme) {
} }
targetObj = { targetObj = {
'uniqueId': state.getUniqueId(),
'id': obj.id, 'id': obj.id,
'x': obj.x,
'y': obj.y,
'w': obj.w, 'w': obj.w,
'h': obj.h, 'h': obj.h,
...@@ -86,9 +147,21 @@ define(['logme'], function (logme) { ...@@ -86,9 +147,21 @@ define(['logme'], function (logme) {
'updateNumTextEl': updateNumTextEl, 'updateNumTextEl': updateNumTextEl,
'removeDraggable': removeDraggable, 'removeDraggable': removeDraggable,
'addDraggable': addDraggable 'addDraggable': addDraggable,
'type': 'base',
'draggableObj': null
}; };
if (fromTargetField === true) {
targetObj.offset = draggableObj.iconEl.position();
targetObj.offset.top += obj.y;
targetObj.offset.left += obj.x;
targetObj.type = 'on_drag';
targetObj.draggableObj = draggableObj;
}
if (state.config.onePerTarget === false) { if (state.config.onePerTarget === false) {
numTextEl.appendTo(state.baseImageEl.parent()); numTextEl.appendTo(state.baseImageEl.parent());
numTextEl.mousedown(function (event) { numTextEl.mousedown(function (event) {
...@@ -99,7 +172,11 @@ define(['logme'], function (logme) { ...@@ -99,7 +172,11 @@ define(['logme'], function (logme) {
}); });
} }
state.targets.push(targetObj); targetObj.indexInStateArray = state.targets.push(targetObj) - 1;
if (fromTargetField === true) {
draggableObj.targetField.push(targetObj);
}
} }
function removeDraggable(draggable) { function removeDraggable(draggable) {
...@@ -121,6 +198,10 @@ define(['logme'], function (logme) { ...@@ -121,6 +198,10 @@ define(['logme'], function (logme) {
draggable.onTarget = null; draggable.onTarget = null;
draggable.onTargetIndex = null; draggable.onTargetIndex = null;
if (this.type === 'on_drag') {
this.draggableObj.numDraggablesOnMe -= 1;
}
this.updateNumTextEl(); this.updateNumTextEl();
} }
...@@ -128,6 +209,10 @@ define(['logme'], function (logme) { ...@@ -128,6 +209,10 @@ define(['logme'], function (logme) {
draggable.onTarget = this; draggable.onTarget = this;
draggable.onTargetIndex = this.draggableList.push(draggable) - 1; draggable.onTargetIndex = this.draggableList.push(draggable) - 1;
if (this.type === 'on_drag') {
this.draggableObj.numDraggablesOnMe += 1;
}
this.updateNumTextEl(); this.updateNumTextEl();
} }
...@@ -183,10 +268,5 @@ define(['logme'], function (logme) { ...@@ -183,10 +268,5 @@ define(['logme'], function (logme) {
this.numTextEl.html(this.draggableList.length); this.numTextEl.html(this.draggableList.length);
} }
} }
}); }); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
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