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?:
...@@ -909,15 +913,15 @@ registry.register(DesignProtein2dInput) ...@@ -909,15 +913,15 @@ registry.register(DesignProtein2dInput)
class EditAGeneInput(InputTypeBase): class EditAGeneInput(InputTypeBase):
""" """
An input type for editing a gene. Integrates with the genex java applet. An input type for editing a gene. Integrates with the genex java applet.
Example: Example:
<editagene width="800" hight="500" dna_sequence="ETAAGGCTATAACCGA" /> <editagene width="800" hight="500" dna_sequence="ETAAGGCTATAACCGA" />
""" """
template = "editageneinput.html" template = "editageneinput.html"
tags = ['editageneinput'] tags = ['editageneinput']
@classmethod @classmethod
def get_attributes(cls): def get_attributes(cls):
""" """
...@@ -927,14 +931,14 @@ class EditAGeneInput(InputTypeBase): ...@@ -927,14 +931,14 @@ class EditAGeneInput(InputTypeBase):
Attribute('height'), Attribute('height'),
Attribute('dna_sequence') Attribute('dna_sequence')
] ]
def _extra_context(self): def _extra_context(self):
""" """
""" """
context = { context = {
'applet_loader': '/static/js/capa/edit-a-gene.js', 'applet_loader': '/static/js/capa/edit-a-gene.js',
} }
return context return context
registry.register(EditAGeneInput) registry.register(EditAGeneInput)
......
...@@ -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.
...@@ -284,24 +326,25 @@ class DragAndDrop(object): ...@@ -284,24 +326,25 @@ class DragAndDrop(object):
Args: Args:
user_answer: json user_answer: json
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.
......
...@@ -2,6 +2,7 @@ import unittest ...@@ -2,6 +2,7 @@ import unittest
import draganddrop import draganddrop
from draganddrop import PositionsCompare from draganddrop import PositionsCompare
import json
class Test_PositionsCompare(unittest.TestCase): class Test_PositionsCompare(unittest.TestCase):
...@@ -40,10 +41,242 @@ class Test_PositionsCompare(unittest.TestCase): ...@@ -40,10 +41,242 @@ class Test_PositionsCompare(unittest.TestCase):
class Test_DragAndDrop_Grade(unittest.TestCase): class Test_DragAndDrop_Grade(unittest.TestCase):
def test_targets_are_draggable_1(self):
user_input = json.dumps([
{'p': 'p_l'},
{'up': {'first': {'p': 'p_l'}}}
])
correct_answer = [
{
'draggables': ['p'],
'targets': [
'p_l', 'p_r'
],
'rule': 'anyof'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][first]'
],
'rule': 'anyof'
}
]
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_targets_are_draggable_2(self):
user_input = json.dumps([
{'p': 'p_l'},
{'p': 'p_r'},
{'s': 's_l'},
{'s': 's_r'},
{'up': {'1': {'p': 'p_l'}}},
{'up': {'3': {'p': 'p_l'}}},
{'up': {'1': {'p': 'p_r'}}},
{'up': {'3': {'p': 'p_r'}}},
{'up_and_down': {'1': {'s': 's_l'}}},
{'up_and_down': {'1': {'s': 's_r'}}}
])
correct_answer = [
{
'draggables': ['p'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': ['s_l', 's_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': [
's_l[s][1]', 's_r[s][1]'
],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][1]', 'p_l[p][3]', 'p_r[p][1]', 'p_r[p][3]'
],
'rule': 'unordered_equal'
}
]
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_targets_are_draggable_2_manual_parsing(self):
user_input = json.dumps([
{'up': 'p_l[p][1]'},
{'p': 'p_l'},
{'up': 'p_l[p][3]'},
{'up': 'p_r[p][1]'},
{'p': 'p_r'},
{'up': 'p_r[p][3]'},
{'up_and_down': 's_l[s][1]'},
{'s': 's_l'},
{'up_and_down': 's_r[s][1]'},
{'s': 's_r'}
])
correct_answer = [
{
'draggables': ['p'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': ['s_l', 's_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': [
's_l[s][1]', 's_r[s][1]'
],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][1]', 'p_l[p][3]', 'p_r[p][1]', 'p_r[p][3]'
],
'rule': 'unordered_equal'
}
]
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_targets_are_draggable_3_nested(self):
user_input = json.dumps([
{'molecule': 'left_side_tagret'},
{'molecule': 'right_side_tagret'},
{'p': {'p_target': {'molecule': 'left_side_tagret'}}},
{'p': {'p_target': {'molecule': 'right_side_tagret'}}},
{'s': {'s_target': {'molecule': 'left_side_tagret'}}},
{'s': {'s_target': {'molecule': 'right_side_tagret'}}},
{'up': {'1': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}},
{'up': {'3': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}},
{'up': {'1': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}},
{'up': {'3': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}},
{'up_and_down': {'1': {'s': {'s_target': {'molecule': 'left_side_tagret'}}}}},
{'up_and_down': {'1': {'s': {'s_target': {'molecule': 'right_side_tagret'}}}}}
])
correct_answer = [
{
'draggables': ['molecule'],
'targets': ['left_side_tagret', 'right_side_tagret'],
'rule': 'unordered_equal'
},
{
'draggables': ['p'],
'targets': [
'left_side_tagret[molecule][p_target]',
'right_side_tagret[molecule][p_target]'
],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': [
'left_side_tagret[molecule][s_target]',
'right_side_tagret[molecule][s_target]'
],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': [
'left_side_tagret[molecule][s_target][s][1]',
'right_side_tagret[molecule][s_target][s][1]'
],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'left_side_tagret[molecule][p_target][p][1]',
'left_side_tagret[molecule][p_target][p][3]',
'right_side_tagret[molecule][p_target][p][1]',
'right_side_tagret[molecule][p_target][p][3]'
],
'rule': 'unordered_equal'
}
]
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_targets_are_draggable_4_real_example(self):
user_input = json.dumps([
{'single_draggable': 's_l'},
{'single_draggable': 's_r'},
{'single_draggable': 'p_sigma'},
{'single_draggable': 'p_sigma*'},
{'single_draggable': 's_sigma'},
{'single_draggable': 's_sigma*'},
{'double_draggable': 'p_pi*'},
{'double_draggable': 'p_pi'},
{'triple_draggable': 'p_l'},
{'triple_draggable': 'p_r'},
{'up': {'1': {'triple_draggable': 'p_l'}}},
{'up': {'2': {'triple_draggable': 'p_l'}}},
{'up': {'2': {'triple_draggable': 'p_r'}}},
{'up': {'3': {'triple_draggable': 'p_r'}}},
{'up_and_down': {'1': {'single_draggable': 's_l'}}},
{'up_and_down': {'1': {'single_draggable': 's_r'}}},
{'up_and_down': {'1': {'single_draggable': 's_sigma'}}},
{'up_and_down': {'1': {'single_draggable': 's_sigma*'}}},
{'up_and_down': {'1': {'double_draggable': 'p_pi'}}},
{'up_and_down': {'2': {'double_draggable': 'p_pi'}}}
])
# 10 targets:
# s_l, s_r, p_l, p_r, s_sigma, s_sigma*, p_pi, p_sigma, p_pi*, p_sigma*
#
# 3 draggable objects, which have targets (internal target ids - 1, 2, 3):
# single_draggable, double_draggable, triple_draggable
#
# 2 draggable objects:
# up, up_and_down
correct_answer = [
{
'draggables': ['triple_draggable'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['double_draggable'],
'targets': ['p_pi', 'p_pi*'],
'rule': 'unordered_equal'
},
{
'draggables': ['single_draggable'],
'targets': ['s_l', 's_r', 's_sigma', 's_sigma*', 'p_sigma', 'p_sigma*'],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': ['p_l[triple_draggable][1]', 'p_l[triple_draggable][2]',
'p_r[triple_draggable][2]', 'p_r[triple_draggable][3]'],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': ['s_l[single_draggable][1]', 's_r[single_draggable][1]',
's_sigma[single_draggable][1]', 's_sigma*[single_draggable][1]',
'p_pi[double_draggable][1]', 'p_pi[double_draggable][2]'],
'rule': 'unordered_equal'
},
]
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_targets_true(self): def test_targets_true(self):
user_input = '{"draggables": [{"1": "t1"}, \ user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]}' {"name_with_icon": "t2"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2'} correct_answer = {'1': 't1', 'name_with_icon': 't2'}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_expect_no_actions_wrong(self): def test_expect_no_actions_wrong(self):
...@@ -59,71 +292,63 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -59,71 +292,63 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_targets_false(self): def test_targets_false(self):
user_input = '{"draggables": [{"1": "t1"}, \ user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]}' {"name_with_icon": "t2"}]'
correct_answer = {'1': 't3', 'name_with_icon': 't2'} correct_answer = {'1': 't3', 'name_with_icon': 't2'}
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_multiple_images_per_target_true(self): def test_multiple_images_per_target_true(self):
user_input = '{\ user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \ {"2": "t1"}]'
{"2": "t1"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't2',
correct_answer = {'1': 't1', 'name_with_icon': 't2',
'2': 't1'} '2': 't1'}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_multiple_images_per_target_false(self): def test_multiple_images_per_target_false(self):
user_input = '{\ user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \ {"2": "t1"}]'
{"2": "t1"}]}' correct_answer = {'1': 't2', 'name_with_icon': 't2',
correct_answer = {'1': 't2', 'name_with_icon': 't2',
'2': 't1'} '2': 't1'}
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_targets_and_positions(self): def test_targets_and_positions(self):
user_input = '{"draggables": [{"1": [10,10]}, \ user_input = '[{"1": [10,10]}, \
{"name_with_icon": [[10,10],4]}]}' {"name_with_icon": [[10,10],4]}]'
correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_position_and_targets(self): def test_position_and_targets(self):
user_input = '{"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2'} correct_answer = {'1': 't1', 'name_with_icon': 't2'}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_exact(self): def test_positions_exact(self):
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]} correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_false(self): def test_positions_false(self):
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]} correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]}
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_positions_true_in_radius(self): def test_positions_true_in_radius(self):
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]} correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_true_in_manual_radius(self): def test_positions_true_in_manual_radius(self):
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]} correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_false_in_manual_radius(self): def test_positions_false_in_manual_radius(self):
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_correct_answer_not_has_key_from_user_answer(self): def test_correct_answer_not_has_key_from_user_answer(self):
user_input = '{"draggables": [{"1": "t1"}, \ user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]'
{"name_with_icon": "t2"}]}'
correct_answer = {'3': 't3', 'name_with_icon': 't2'} correct_answer = {'3': 't3', 'name_with_icon': 't2'}
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
...@@ -131,20 +356,20 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -131,20 +356,20 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
"""Draggables can be places anywhere on base image. """Draggables can be places anywhere on base image.
Place grass in the middle of the image and ant in the Place grass in the middle of the image and ant in the
right upper corner.""" right upper corner."""
user_input = '{"draggables": \ user_input = '[{"ant":[610.5,57.449951171875]},\
[{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}' {"grass":[322.5,199.449951171875]}]'
correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]}
self.assertTrue(draganddrop.grade(user_input, correct_answer)) self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_lcao_correct(self): def test_lcao_correct(self):
"""Describe carbon molecule in LCAO-MO""" """Describe carbon molecule in LCAO-MO"""
user_input = '{"draggables":[{"1":"s_left"}, \ user_input = '[{"1":"s_left"}, \
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \
{"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \ {"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer = [{ correct_answer = [{
'draggables': ['1', '2', '3', '4', '5', '6'], 'draggables': ['1', '2', '3', '4', '5', '6'],
...@@ -178,12 +403,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -178,12 +403,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_lcao_extra_element_incorrect(self): def test_lcao_extra_element_incorrect(self):
"""Describe carbon molecule in LCAO-MO""" """Describe carbon molecule in LCAO-MO"""
user_input = '{"draggables":[{"1":"s_left"}, \ user_input = '[{"1":"s_left"}, \
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \
{"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \ {"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer = [{ correct_answer = [{
'draggables': ['1', '2', '3', '4', '5', '6'], 'draggables': ['1', '2', '3', '4', '5', '6'],
...@@ -217,9 +442,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -217,9 +442,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_draggable_no_mupliples(self): def test_reuse_draggable_no_mupliples(self):
"""Test reusable draggables (no mupltiple draggables per target)""" """Test reusable draggables (no mupltiple draggables per target)"""
user_input = '{"draggables":[{"1":"target1"}, \ user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"}, \ {"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"}, \
{"3":"target6"}]}' {"3":"target6"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['1'], 'draggables': ['1'],
...@@ -240,9 +465,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -240,9 +465,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_draggable_with_mupliples(self): def test_reuse_draggable_with_mupliples(self):
"""Test reusable draggables with mupltiple draggables per target""" """Test reusable draggables with mupltiple draggables per target"""
user_input = '{"draggables":[{"1":"target1"}, \ user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \
{"3":"target6"}]}' {"3":"target6"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['1'], 'draggables': ['1'],
...@@ -263,10 +488,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -263,10 +488,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_many_draggable_with_mupliples(self): def test_reuse_many_draggable_with_mupliples(self):
"""Test reusable draggables with mupltiple draggables per target""" """Test reusable draggables with mupltiple draggables per target"""
user_input = '{"draggables":[{"1":"target1"}, \ user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]}' {"5": "target5"}, {"6": "target2"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['1', '4'], 'draggables': ['1', '4'],
...@@ -292,12 +517,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -292,12 +517,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_many_draggable_with_mupliples_wrong(self): def test_reuse_many_draggable_with_mupliples_wrong(self):
"""Test reusable draggables with mupltiple draggables per target""" """Test reusable draggables with mupltiple draggables per target"""
user_input = '{"draggables":[{"1":"target1"}, \ user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"}, \ {"2":"target2"},{"1":"target1"}, \
{"2":"target3"}, \ {"2":"target3"}, \
{"2":"target4"}, \ {"2":"target4"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]}' {"5": "target5"}, {"6": "target2"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['1', '4'], 'draggables': ['1', '4'],
...@@ -323,10 +548,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -323,10 +548,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_false(self): def test_label_10_targets_with_a_b_c_false(self):
"""Test reusable draggables (no mupltiple draggables per target)""" """Test reusable draggables (no mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}' {"a":"target1"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a'], 'draggables': ['a'],
...@@ -347,10 +572,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -347,10 +572,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_(self): def test_label_10_targets_with_a_b_c_(self):
"""Test reusable draggables (no mupltiple draggables per target)""" """Test reusable draggables (no mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}' {"a":"target10"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a'], 'draggables': ['a'],
...@@ -371,10 +596,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -371,10 +596,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_multiple(self): def test_label_10_targets_with_a_b_c_multiple(self):
"""Test reusable draggables (mupltiple draggables per target)""" """Test reusable draggables (mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \ {"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}' {"a":"target1"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a', 'a'], 'draggables': ['a', 'a', 'a'],
...@@ -395,10 +620,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -395,10 +620,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_multiple_false(self): def test_label_10_targets_with_a_b_c_multiple_false(self):
"""Test reusable draggables (mupltiple draggables per target)""" """Test reusable draggables (mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}' {"a":"target1"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a', 'a'], 'draggables': ['a', 'a', 'a'],
...@@ -419,10 +644,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -419,10 +644,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_reused(self): def test_label_10_targets_with_a_b_c_reused(self):
"""Test a b c in 10 labels reused""" """Test a b c in 10 labels reused"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \ {"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}' {"a":"target10"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a'], 'draggables': ['a', 'a'],
...@@ -443,10 +668,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -443,10 +668,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_reused_false(self): def test_label_10_targets_with_a_b_c_reused_false(self):
"""Test a b c in 10 labels reused false""" """Test a b c in 10 labels reused false"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\ {"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}' {"a":"target10"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a'], 'draggables': ['a', 'a'],
...@@ -467,9 +692,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -467,9 +692,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse(self): def test_mixed_reuse_and_not_reuse(self):
"""Test reusable draggables """ """Test reusable draggables """
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"},\ {"b":"target2"},{"c":"target3"}, {"a":"target4"},\
{"a":"target5"}]}' {"a":"target5"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'b'], 'draggables': ['a', 'b'],
...@@ -485,8 +710,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -485,8 +710,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse_number(self): def test_mixed_reuse_and_not_reuse_number(self):
"""Test reusable draggables with number """ """Test reusable draggables with number """
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]}' {"b":"target2"},{"c":"target3"}, {"a":"target4"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a', 'b'], 'draggables': ['a', 'a', 'b'],
...@@ -502,8 +727,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -502,8 +727,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse_number_false(self): def test_mixed_reuse_and_not_reuse_number_false(self):
"""Test reusable draggables with numbers, but wrong""" """Test reusable draggables with numbers, but wrong"""
user_input = '{"draggables":[{"a":"target1"}, \ user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]}' {"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]'
correct_answer = [ correct_answer = [
{ {
'draggables': ['a', 'a', 'b'], 'draggables': ['a', 'a', 'b'],
...@@ -518,9 +743,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase): ...@@ -518,9 +743,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
self.assertFalse(draganddrop.grade(user_input, correct_answer)) self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_alternative_correct_answer(self): def test_alternative_correct_answer(self):
user_input = '{"draggables":[{"name_with_icon":"t1"},\ user_input = '[{"name_with_icon":"t1"},\
{"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"}, \ {"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"}, \
{"name4":"t1"}]}' {"name4":"t1"}]'
correct_answer = [ correct_answer = [
{'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'}, {'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'},
{'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'], {'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'],
...@@ -533,14 +758,13 @@ class Test_DragAndDrop_Populate(unittest.TestCase): ...@@ -533,14 +758,13 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
def test_1(self): def test_1(self):
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
user_input = '{"draggables": \ user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
dnd = draganddrop.DragAndDrop(correct_answer, user_input) dnd = draganddrop.DragAndDrop(correct_answer, user_input)
correct_groups = {'1': ['name_with_icon'], '0': ['1']} correct_groups = [['1'], ['name_with_icon']]
correct_positions = {'1': {'exact': [[20, 20]]}, '0': {'exact': [[[40, 10], 29]]}} correct_positions = [{'exact': [[[40, 10], 29]]}, {'exact': [[20, 20]]}]
user_groups = {'1': [u'name_with_icon'], '0': [u'1']} user_groups = [['1'], ['name_with_icon']]
user_positions = {'1': {'user': [[20, 20]]}, '0': {'user': [[10, 10]]}} user_positions = [{'user': [[10, 10]]}, {'user': [[20, 20]]}]
self.assertEqual(correct_groups, dnd.correct_groups) self.assertEqual(correct_groups, dnd.correct_groups)
self.assertEqual(correct_positions, dnd.correct_positions) self.assertEqual(correct_positions, dnd.correct_positions)
...@@ -551,49 +775,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase): ...@@ -551,49 +775,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
class Test_DraAndDrop_Compare_Positions(unittest.TestCase): class Test_DraAndDrop_Compare_Positions(unittest.TestCase):
def test_1(self): def test_1(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]],
user=[[2, 3], [1, 1]], user=[[2, 3], [1, 1]],
flag='anyof')) flag='anyof'))
def test_2a(self): def test_2a(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]],
user=[[2, 3], [1, 1]], user=[[2, 3], [1, 1]],
flag='exact')) flag='exact'))
def test_2b(self): def test_2b(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertFalse(dnd.compare_positions(correct=[[1, 1], [2, 3]], self.assertFalse(dnd.compare_positions(correct=[[1, 1], [2, 3]],
user=[[2, 13], [1, 1]], user=[[2, 13], [1, 1]],
flag='exact')) flag='exact'))
def test_3(self): def test_3(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertFalse(dnd.compare_positions(correct=["a", "b"], self.assertFalse(dnd.compare_positions(correct=["a", "b"],
user=["a", "b", "c"], user=["a", "b", "c"],
flag='anyof')) flag='anyof'))
def test_4(self): def test_4(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"],
user=["a", "b"], user=["a", "b"],
flag='anyof')) flag='anyof'))
def test_5(self): def test_5(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertFalse(dnd.compare_positions(correct=["a", "b", "c"], self.assertFalse(dnd.compare_positions(correct=["a", "b", "c"],
user=["a", "c", "b"], user=["a", "c", "b"],
flag='exact')) flag='exact'))
def test_6(self): def test_6(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"],
user=["a", "c", "b"], user=["a", "c", "b"],
flag='anyof')) flag='anyof'))
def test_7(self): def test_7(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
self.assertFalse(dnd.compare_positions(correct=["a", "b", "b"], self.assertFalse(dnd.compare_positions(correct=["a", "b", "b"],
user=["a", "c", "b"], user=["a", "c", "b"],
flag='anyof')) flag='anyof'))
......
// 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) { // Continue to next .every() call.
if (processDraggable(state, config.draggables) !== true) { return true;
state.config.errors += 1; });
}
} 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) { // Continue to next .every() call.
if (processTarget(state, config.targets) !== true) { return true;
state.config.errors += 1; });
}
} 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 (config.target_outline.toLowerCase() === 'true') { if (typeof config.target_outline === 'string') {
state.config.targetOutline = true; if (config.target_outline.toLowerCase() === 'true') {
} else if (config.target_outline.toLowerCase() === 'false') { state.config.targetOutline = true;
state.config.targetOutline = false; } else if (config.target_outline.toLowerCase() === 'false') {
state.config.targetOutline = false;
} else {
logme('ERROR: Property config.target_outline can either be "true", or "false".');
state.config.foundErrors = true;
}
} else { } else {
logme('ERROR: Property config.target_outline can either be "true", or "false".'); logme('ERROR: Property config.target_outline is not of a supported type.');
state.config.errors += 1; state.config.foundErrors = true;
} }
} else {
logme('ERROR: Property config.target_outline is not of a supported type.');
state.config.errors += 1;
} }
} }
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) {
state.config.labelBgColor = config.label_bg_color; if (typeof config.label_bg_color === 'string') {
} else { state.config.labelBgColor = config.label_bg_color;
logme('ERROR: Property config.label_bg_color is not of a supported type.'); } else {
returnStatus = false; logme('ERROR: Property config.label_bg_color is not of a supported type.');
}
} }
} }
...@@ -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;
} }
state.config.targets.push(obj); if (pushToState !== false) {
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) {
(function (requirejs, require, define) {
define(['logme', 'update_input', 'targets'], function (logme, updateInput, Targets) {
return {
'moveDraggableTo': function (moveType, target, funcCallback) {
var self, offset;
if (this.hasLoaded === false) {
self = this;
setTimeout(function () {
self.moveDraggableTo(moveType, target, funcCallback);
}, 50);
return;
}
if ((this.isReusable === true) && (this.isOriginal === true)) {
this.makeDraggableCopy(function (draggableCopy) {
draggableCopy.moveDraggableTo(moveType, target, funcCallback);
});
return;
}
offset = 0;
if (this.state.config.targetOutline === true) {
offset = 1;
}
this.inContainer = false;
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
});
if (moveType === 'target') {
this.iconEl.css({
'left': target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset
});
} else {
this.iconEl.css({
'left': target.x - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.y - this.iconHeight * 0.5 + offset
});
}
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'
});
if (moveType === 'target') {
this.labelEl.css({
'left': target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9, // Account for padding, border.
'top': target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset
});
} else {
this.labelEl.css({
'left': target.x - this.labelWidth * 0.5 + offset - 9, // Account for padding, border.
'top': target.y - this.iconHeight * 0.5 + this.iconHeight + 5 + offset
});
}
this.labelEl.appendTo(this.state.baseImageEl.parent());
}
if (moveType === 'target') {
target.addDraggable(this);
} else {
this.x = target.x;
this.y = target.y;
}
this.zIndex = 1000;
this.correctZIndexes();
Targets.initializeTargetField(this);
if (this.isOriginal === true) {
this.state.numDraggablesInSlider -= 1;
this.state.updateArrowOpacity();
}
if ($.isFunction(funcCallback) === true) {
funcCallback();
}
},
// 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.
'checkLandingElement': function () {
var positionIE;
this.mousePressed = false;
positionIE = this.iconEl.position();
if (this.state.config.individualTargets === true) {
if (this.checkIfOnTarget(positionIE) === true) {
this.correctZIndexes();
Targets.initializeTargetField(this);
} else {
if (this.onTarget !== null) {
this.onTarget.removeDraggable(this);
}
this.moveBackToSlider();
if (this.isOriginal === true) {
this.state.numDraggablesInSlider += 1;
}
}
} else {
if (
(positionIE.left < 0) ||
(positionIE.left + this.iconWidth > this.state.baseImageEl.width()) ||
(positionIE.top < 0) ||
(positionIE.top + this.iconHeight > this.state.baseImageEl.height())
) {
this.moveBackToSlider();
this.x = -1;
this.y = -1;
if (this.isOriginal === true) {
this.state.numDraggablesInSlider += 1;
}
} else {
this.correctZIndexes();
this.x = positionIE.left + this.iconWidth * 0.5;
this.y = positionIE.top + this.iconHeight * 0.5;
Targets.initializeTargetField(this);
}
}
if (this.isOriginal === true) {
this.state.updateArrowOpacity();
}
updateInput.update(this.state);
},
// 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.
//
// positionIE is the object as returned by
//
// this.iconEl.position()
'checkIfOnTarget': function (positionIE) {
var c1, target;
for (c1 = 0; c1 < this.state.targets.length; c1 += 1) {
target = this.state.targets[c1];
// If only one draggable per target is allowed, and
// the current target already has a draggable on it
// (with an ID different from the one we are checking
// against), then go to next target.
if (
(this.state.config.onePerTarget === true) &&
(target.draggableList.length === 1) &&
(target.draggableList[0].uniqueId !== this.uniqueId)
) {
continue;
}
// If the target is on a draggable (from target field), we must make sure that
// this draggable is not the same as "this" one.
if ((target.type === 'on_drag') && (target.draggableObj.uniqueId === this.uniqueId)) {
continue;
}
// Check if the draggable's center coordinate is within
// the target's dimensions. If not, go to next target.
if (
(positionIE.top + this.iconHeight * 0.5 < target.offset.top) ||
(positionIE.top + this.iconHeight * 0.5 > target.offset.top + target.h) ||
(positionIE.left + this.iconWidth * 0.5 < target.offset.left) ||
(positionIE.left + this.iconWidth * 0.5 > target.offset.left + target.w)
) {
continue;
}
// If the draggable was moved from one target to
// another, then we need to remove it from the
// previous target's draggables list, and add it to the
// new target's draggables list.
if ((this.onTarget !== null) && (this.onTarget.uniqueId !== target.uniqueId)) {
this.onTarget.removeDraggable(this);
target.addDraggable(this);
}
// If the draggable was moved from the slider to a
// target, remember the target, and add ID to the
// target's draggables list.
else if (this.onTarget === null) {
target.addDraggable(this);
}
// Reposition the draggable so that it's center
// coincides with the center of the target.
this.snapToTarget(target);
// Target was found.
return true;
}
// Target was not found.
return false;
},
'snapToTarget': function (target) {
var offset;
offset = 0;
if (this.state.config.targetOutline === true) {
offset = 1;
}
this.iconEl.css({
'left': target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset
});
if (this.labelEl !== null) {
this.labelEl.css({
'left': target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9, // Acoount for padding, border.
'top': target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset
});
}
},
// 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.
'correctZIndexes': function () {
var c1, highestZIndex;
highestZIndex = -10000;
if (this.state.config.individualTargets === true) {
if (this.onTarget.draggableList.length > 0) {
for (c1 = 0; c1 < this.onTarget.draggableList.length; c1 += 1) {
if (
(this.onTarget.draggableList[c1].zIndex > highestZIndex) &&
(this.onTarget.draggableList[c1].zIndex !== 1000)
) {
highestZIndex = this.onTarget.draggableList[c1].zIndex;
}
}
} else {
highestZIndex = 0;
}
} else {
for (c1 = 0; c1 < this.state.draggables.length; c1++) {
if (this.inContainer === false) {
if (
(this.state.draggables[c1].zIndex > highestZIndex) &&
(this.state.draggables[c1].zIndex !== 1000)
) {
highestZIndex = this.state.draggables[c1].zIndex;
}
}
}
}
if (highestZIndex === -10000) {
highestZIndex = 0;
}
this.zIndex = highestZIndex + 1;
this.iconEl.css('z-index', this.zIndex);
if (this.labelEl !== null) {
this.labelEl.css('z-index', this.zIndex);
}
},
// 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.
'moveBackToSlider': function () {
var c1;
Targets.destroyTargetField(this);
if (this.isOriginal === false) {
this.iconEl.remove();
if (this.labelEl !== null) {
this.labelEl.remove();
}
this.state.draggables.splice(this.stateDraggablesIndex, 1);
for (c1 = 0; c1 < this.state.draggables.length; c1 += 1) {
if (this.state.draggables[c1].stateDraggablesIndex > this.stateDraggablesIndex) {
this.state.draggables[c1].stateDraggablesIndex -= 1;
}
}
return;
}
this.containerEl.show();
this.zIndex = 1;
this.iconEl.detach();
if (this.iconImgEl !== null) {
this.iconImgEl.css({
'width': this.iconWidthSmall,
'height': this.iconHeightSmall
});
}
this.iconEl.css({
'border': 'none',
'background-color': 'transparent',
'padding-left': 0,
'padding-right': 0,
'z-index': this.zIndex,
'width': this.iconWidthSmall,
'height': this.iconHeightSmall,
'left': 50 - this.iconWidthSmall * 0.5,
'top': ((this.labelEl !== null) ? 5 : 50 - this.iconHeightSmall * 0.5)
});
this.iconEl.appendTo(this.containerEl);
if (this.labelEl !== null) {
this.labelEl.detach();
this.labelEl.css({
'border': 'none',
'background-color': 'transparent',
'padding-left': 0,
'padding-right': 0,
'z-index': this.zIndex,
'left': 50 - this.labelWidth * 0.5,
'top': 5 + this.iconHeightSmall + 5
});
this.labelEl.appendTo(this.containerEl);
}
this.inContainer = true;
}
}; // End-of: return {
}); // End-of: define(['logme', 'update_input', 'targets'], function (logme, updateInput, Targets) {
}(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', 'draggable_events', 'draggable_logic'], function (logme, draggableEvents, draggableLogic) {
define(['logme', 'update_input'], function (logme, updateInput) {
return { return {
'init': init 'init': init
}; };
function init(state) { function init(state) {
(function (c1) { state.config.draggables.every(function (draggable) {
while (c1 < state.config.draggables.length) { processDraggable(state, draggable);
processDraggable(state, state.config.draggables[c1]);
c1 += 1 return true;
} });
}(0));
} }
function makeDraggableCopy(callbackFunc) { function makeDraggableCopy(callbackFunc) {
...@@ -34,13 +28,18 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -34,13 +28,18 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.stateDraggablesIndex = null; // Will be set. draggableObj.stateDraggablesIndex = null; // Will be set.
draggableObj.containerEl = null; // Not needed, since a copy will never return to a container element. draggableObj.containerEl = null; // Not needed, since a copy will never return to a container element.
draggableObj.iconEl = null; // Will be created. draggableObj.iconEl = null; // Will be created.
draggableObj.iconImgEl = null; // Will be created.
draggableObj.labelEl = null; // Will be created. draggableObj.labelEl = null; // Will be created.
draggableObj.targetField = []; // Will be populated.
// Create DOM elements and attach events. // Create DOM elements and attach events.
if (draggableObj.originalConfigObj.icon.length > 0) { if (draggableObj.originalConfigObj.icon.length > 0) {
draggableObj.iconEl = $('<img />');
draggableObj.iconEl.attr('src', draggableObj.originalConfigObj.icon); draggableObj.iconEl = $('<div></div>');
draggableObj.iconEl.load(function () { draggableObj.iconImgEl = $('<img />');
draggableObj.iconImgEl.attr('src', draggableObj.originalConfigObj.icon);
draggableObj.iconImgEl.load(function () {
draggableObj.iconEl.css({ draggableObj.iconEl.css({
'position': 'absolute', 'position': 'absolute',
'width': draggableObj.iconWidthSmall, 'width': draggableObj.iconWidthSmall,
...@@ -48,6 +47,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -48,6 +47,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left': 50 - draggableObj.iconWidthSmall * 0.5, 'left': 50 - draggableObj.iconWidthSmall * 0.5,
'top': ((draggableObj.originalConfigObj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5) 'top': ((draggableObj.originalConfigObj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5)
}); });
draggableObj.iconImgEl.css({
'position': 'absolute',
'width': draggableObj.iconWidthSmall,
'height': draggableObj.iconHeightSmall,
'left': 0,
'top': 0
});
draggableObj.iconImgEl.appendTo(draggableObj.iconEl);
if (draggableObj.originalConfigObj.label.length > 0) { if (draggableObj.originalConfigObj.label.length > 0) {
draggableObj.labelEl = $( draggableObj.labelEl = $(
...@@ -71,7 +78,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -71,7 +78,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.attachMouseEventsTo('iconEl'); draggableObj.attachMouseEventsTo('iconEl');
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj) - 1;
setTimeout(function () { setTimeout(function () {
callbackFunc(draggableObj); callbackFunc(draggableObj);
...@@ -99,7 +106,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -99,7 +106,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.attachMouseEventsTo('iconEl'); draggableObj.attachMouseEventsTo('iconEl');
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj) - 1;
setTimeout(function () { setTimeout(function () {
callbackFunc(draggableObj); callbackFunc(draggableObj);
...@@ -110,115 +117,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -110,115 +117,6 @@ define(['logme', 'update_input'], function (logme, updateInput) {
} }
} }
function attachMouseEventsTo(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);
});
}
function moveDraggableTo(moveType, target) {
var self, offset;
if (this.hasLoaded === false) {
self = this;
setTimeout(function () {
self.moveDraggableTo(moveType, target);
}, 50);
return;
}
if ((this.isReusable === true) && (this.isOriginal === true)) {
this.makeDraggableCopy(function (draggableCopy) {
draggableCopy.moveDraggableTo(moveType, target);
});
return;
}
offset = 0;
if (this.state.config.targetOutline === true) {
offset = 1;
}
this.inContainer = false;
if (this.isOriginal === true) {
this.containerEl.hide();
this.iconEl.detach();
}
this.iconEl.css({
'background-color': this.iconElBGColor,
'padding-left': this.iconElPadding,
'padding-right': this.iconElPadding,
'border': this.iconElBorder,
'width': this.iconWidth,
'height': this.iconHeight
});
if (moveType === 'target') {
this.iconEl.css({
'left': target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset
});
} else {
this.iconEl.css({
'left': target.x - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.y - this.iconHeight * 0.5 + offset
});
}
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'
});
if (moveType === 'target') {
this.labelEl.css({
'left': target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9, // Account for padding, border.
'top': target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset
});
} else {
this.labelEl.css({
'left': target.x - this.labelWidth * 0.5 + offset - 9, // Account for padding, border.
'top': target.y - this.iconHeight * 0.5 + this.iconHeight + 5 + offset
});
}
this.labelEl.appendTo(this.state.baseImageEl.parent());
}
if (moveType === 'target') {
target.addDraggable(this);
} else {
this.x = target.x;
this.y = target.y;
}
this.zIndex = 1000;
this.correctZIndexes();
if (this.isOriginal === true) {
this.state.numDraggablesInSlider -= 1;
this.state.updateArrowOpacity();
}
}
function processDraggable(state, obj) { function processDraggable(state, obj) {
var draggableObj; var draggableObj;
...@@ -234,6 +132,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -234,6 +132,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'zIndex': 1, 'zIndex': 1,
'containerEl': null, 'containerEl': null,
'iconEl': null, 'iconEl': null,
'iconImgEl': null,
'iconElBGColor': null, 'iconElBGColor': null,
'iconElPadding': null, 'iconElPadding': null,
'iconElBorder': null, 'iconElBorder': null,
...@@ -251,17 +150,23 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -251,17 +150,23 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'onTargetIndex': null, 'onTargetIndex': null,
'state': state, 'state': state,
'mouseDown': mouseDown, 'mouseDown': draggableEvents.mouseDown,
'mouseUp': mouseUp, 'mouseUp': draggableEvents.mouseUp,
'mouseMove': mouseMove, 'mouseMove': draggableEvents.mouseMove,
'checkLandingElement': checkLandingElement,
'checkIfOnTarget': checkIfOnTarget, 'checkLandingElement': draggableLogic.checkLandingElement,
'snapToTarget': snapToTarget, 'checkIfOnTarget': draggableLogic.checkIfOnTarget,
'correctZIndexes': correctZIndexes, 'snapToTarget': draggableLogic.snapToTarget,
'moveBackToSlider': moveBackToSlider, 'correctZIndexes': draggableLogic.correctZIndexes,
'moveDraggableTo': moveDraggableTo, 'moveBackToSlider': draggableLogic.moveBackToSlider,
'moveDraggableTo': draggableLogic.moveDraggableTo,
'makeDraggableCopy': makeDraggableCopy, 'makeDraggableCopy': makeDraggableCopy,
'attachMouseEventsTo': attachMouseEventsTo
'attachMouseEventsTo': draggableEvents.attachMouseEventsTo,
'targetField': [],
'numDraggablesOnMe': 0
}; };
draggableObj.containerEl = $( draggableObj.containerEl = $(
...@@ -288,9 +193,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -288,9 +193,11 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.iconElBorder = 'none'; draggableObj.iconElBorder = 'none';
draggableObj.iconElLeftOffset = 0; draggableObj.iconElLeftOffset = 0;
draggableObj.iconEl = $('<img />'); draggableObj.iconEl = $('<div></div>');
draggableObj.iconEl.attr('src', obj.icon);
draggableObj.iconEl.load(function () { draggableObj.iconImgEl = $('<img />');
draggableObj.iconImgEl.attr('src', obj.icon);
draggableObj.iconImgEl.load(function () {
draggableObj.iconWidth = this.width; draggableObj.iconWidth = this.width;
draggableObj.iconHeight = this.height; draggableObj.iconHeight = this.height;
...@@ -309,6 +216,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -309,6 +216,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left': 50 - draggableObj.iconWidthSmall * 0.5, 'left': 50 - draggableObj.iconWidthSmall * 0.5,
'top': ((obj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5) 'top': ((obj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5)
}); });
draggableObj.iconImgEl.css({
'position': 'absolute',
'width': draggableObj.iconWidthSmall,
'height': draggableObj.iconHeightSmall,
'left': 0,
'top': 0
});
draggableObj.iconImgEl.appendTo(draggableObj.iconEl);
draggableObj.iconEl.appendTo(draggableObj.containerEl); draggableObj.iconEl.appendTo(draggableObj.containerEl);
if (obj.label.length > 0) { if (obj.label.length > 0) {
...@@ -384,357 +299,5 @@ define(['logme', 'update_input'], function (logme, updateInput) { ...@@ -384,357 +299,5 @@ define(['logme', 'update_input'], function (logme, updateInput) {
state.numDraggablesInSlider += 1; state.numDraggablesInSlider += 1;
draggableObj.stateDraggablesIndex = state.draggables.push(draggableObj) - 1; draggableObj.stateDraggablesIndex = state.draggables.push(draggableObj) - 1;
} }
}); // End-of: define(['logme', 'draggable_events', 'draggable_logic'], function (logme, draggableEvents, draggableLogic) {
function mouseDown(event) { }(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
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 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();
}
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;
}
}
function mouseUp() {
if (this.mousePressed === true) {
this.state.currentMovingDraggable = null;
this.checkLandingElement();
}
}
function mouseMove(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
});
}
}
}
// 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 positionIE;
this.mousePressed = false;
positionIE = this.iconEl.position();
if (this.state.config.individualTargets === true) {
if (this.checkIfOnTarget(positionIE) === true) {
this.correctZIndexes();
} else {
if (this.onTarget !== null) {
this.onTarget.removeDraggable(this);
}
this.moveBackToSlider();
if (this.isOriginal === true) {
this.state.numDraggablesInSlider += 1;
}
}
} else {
if (
(positionIE.left < 0) ||
(positionIE.left + this.iconWidth > this.state.baseImageEl.width()) ||
(positionIE.top < 0) ||
(positionIE.top + this.iconHeight > this.state.baseImageEl.height())
) {
this.moveBackToSlider();
this.x = -1;
this.y = -1;
if (this.isOriginal === true) {
this.state.numDraggablesInSlider += 1;
}
} else {
this.correctZIndexes();
this.x = positionIE.left + this.iconWidth * 0.5;
this.y = positionIE.top + this.iconHeight * 0.5;
}
}
if (this.isOriginal === true) {
this.state.updateArrowOpacity();
}
updateInput.update(this.state);
}
// 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.
//
// positionIE is the object as returned by
//
// this.iconEl.position()
function checkIfOnTarget(positionIE) {
var c1, target;
for (c1 = 0; c1 < this.state.targets.length; c1 += 1) {
target = this.state.targets[c1];
// If only one draggable per target is allowed, and
// the current target already has a draggable on it
// (with an ID different from the one we are checking
// against), then go to next target.
if (
(this.state.config.onePerTarget === true) &&
(target.draggableList.length === 1) &&
(target.draggableList[0].uniqueId !== this.uniqueId)
) {
continue;
}
// Check if the draggable's center coordinate is within
// the target's dimensions. If not, go to next target.
if (
(positionIE.top + this.iconHeight * 0.5 < target.offset.top) ||
(positionIE.top + this.iconHeight * 0.5 > target.offset.top + target.h) ||
(positionIE.left + this.iconWidth * 0.5 < target.offset.left) ||
(positionIE.left + this.iconWidth * 0.5 > target.offset.left + target.w)
) {
continue;
}
// If the draggable was moved from one target to
// another, then we need to remove it from the
// previous target's draggables list, and add it to the
// new target's draggables list.
if ((this.onTarget !== null) && (this.onTarget.id !== target.id)) {
this.onTarget.removeDraggable(this);
target.addDraggable(this);
}
// If the draggable was moved from the slider to a
// target, remember the target, and add ID to the
// target's draggables list.
else if (this.onTarget === null) {
target.addDraggable(this);
}
// Reposition the draggable so that it's center
// coincides with the center of the target.
this.snapToTarget(target);
// Target was found.
return true;
}
// Target was not found.
return false;
}
function snapToTarget(target) {
var offset;
offset = 0;
if (this.state.config.targetOutline === true) {
offset = 1;
}
this.iconEl.css({
'left': target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset,
'top': target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset
});
if (this.labelEl !== null) {
this.labelEl.css({
'left': target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9, // Acoount for padding, border.
'top': target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset
});
}
}
// 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, highestZIndex;
highestZIndex = -10000;
if (this.state.config.individualTargets === true) {
if (this.onTarget.draggableList.length > 0) {
for (c1 = 0; c1 < this.onTarget.draggableList.length; c1 += 1) {
if (
(this.onTarget.draggableList[c1].zIndex > highestZIndex) &&
(this.onTarget.draggableList[c1].zIndex !== 1000)
) {
highestZIndex = this.onTarget.draggableList[c1].zIndex;
}
}
} else {
highestZIndex = 0;
}
} else {
for (c1 = 0; c1 < this.state.draggables.length; c1++) {
if (this.inContainer === false) {
if (
(this.state.draggables[c1].zIndex > highestZIndex) &&
(this.state.draggables[c1].zIndex !== 1000)
) {
highestZIndex = this.state.draggables[c1].zIndex;
}
}
}
}
if (highestZIndex === -10000) {
highestZIndex = 0;
}
this.zIndex = highestZIndex + 1;
this.iconEl.css('z-index', this.zIndex);
if (this.labelEl !== null) {
this.labelEl.css('z-index', this.zIndex);
}
}
// 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;
if (this.isOriginal === false) {
this.iconEl.remove();
if (this.labelEl !== null) {
this.labelEl.remove();
}
this.state.draggables.splice(this.stateDraggablesIndex, 1);
for (c1 = 0; c1 < this.state.draggables; c1 += 1) {
if (this.state.draggables[c1].stateDraggablesIndex > this.stateDraggablesIndex) {
this.state.draggables[c1].stateDraggablesIndex -= 1;
}
}
return;
}
this.containerEl.show();
this.zIndex = 1;
this.iconEl.detach();
this.iconEl.css({
'border': 'none',
'background-color': 'transparent',
'padding-left': 0,
'padding-right': 0,
'z-index': this.zIndex,
'width': this.iconWidthSmall,
'height': this.iconHeightSmall,
'left': 50 - this.iconWidthSmall * 0.5,
'top': ((this.labelEl !== null) ? 5 : 50 - this.iconHeightSmall * 0.5)
});
this.iconEl.appendTo(this.containerEl);
if (this.labelEl !== null) {
this.labelEl.detach();
this.labelEl.css({
'border': 'none',
'background-color': 'transparent',
'padding-left': 0,
'padding-right': 0,
'z-index': this.zIndex,
'left': 50 - this.labelWidth * 0.5,
'top': 5 + this.iconHeightSmall + 5
});
this.labelEl.appendTo(this.containerEl);
}
this.inContainer = true;
}
});
// 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 () {
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>'
); );
targetEl.appendTo(state.baseImageEl.parent());
if (fromTargetField === true) {
targetEl.appendTo(draggableObj.iconEl);
} else {
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)
// 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 { return {
'check': check, 'check': check,
...@@ -37,7 +32,12 @@ define(['logme'], function (logme) { ...@@ -37,7 +32,12 @@ define(['logme'], function (logme) {
(function (c2) { (function (c2) {
while (c2 < state.targets[c1].draggableList.length) { while (c2 < state.targets[c1].draggableList.length) {
tempObj = {}; tempObj = {};
tempObj[state.targets[c1].draggableList[c2].id] = state.targets[c1].id;
if (state.targets[c1].type === 'base') {
tempObj[state.targets[c1].draggableList[c2].id] = state.targets[c1].id;
} else {
addTargetRecursively(tempObj, state.targets[c1].draggableList[c2], state.targets[c1]);
}
draggables.push(tempObj); draggables.push(tempObj);
tempObj = null; tempObj = null;
...@@ -50,7 +50,18 @@ define(['logme'], function (logme) { ...@@ -50,7 +50,18 @@ define(['logme'], function (logme) {
}(0)); }(0));
} }
$('#input_' + state.problemId).val(JSON.stringify({'draggables': draggables})); $('#input_' + state.problemId).val(JSON.stringify(draggables));
}
function addTargetRecursively(tempObj, draggable, target) {
if (target.type === 'base') {
tempObj[draggable.id] = target.id;
} else {
tempObj[draggable.id] = {};
tempObj[draggable.id][target.id] = {};
addTargetRecursively(tempObj[draggable.id][target.id], target.draggableObj, target.draggableObj.onTarget);
}
} }
// Check if input has an answer from server. If yes, then position // Check if input has an answer from server. If yes, then position
...@@ -59,6 +70,7 @@ define(['logme'], function (logme) { ...@@ -59,6 +70,7 @@ define(['logme'], function (logme) {
var inputElVal; var inputElVal;
inputElVal = $('#input_' + state.problemId).val(); inputElVal = $('#input_' + state.problemId).val();
if (inputElVal.length === 0) { if (inputElVal.length === 0) {
return false; return false;
} }
...@@ -68,95 +80,147 @@ define(['logme'], function (logme) { ...@@ -68,95 +80,147 @@ define(['logme'], function (logme) {
return true; return true;
} }
function getUseTargets(answer) { function processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i) {
if ($.isArray(answer.draggables) === false) { var baseDraggableId, baseDraggable, baseTargetId, baseTarget,
logme('ERROR: answer.draggables is not an array.'); layeredDraggableId, layeredDraggable, layeredTargetId, layeredTarget,
chain;
if (depth === 0) {
// We are at the lowest depth? The end.
return; return;
} else if (answer.draggables.length === 0) { }
if (answerSortedByDepth.hasOwnProperty(depth) === false) {
// We have a depth that ts not valid, we decrease the depth by one.
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth - 1, 0);
return; return;
} }
if ($.isPlainObject(answer.draggables[0]) === false) { if (answerSortedByDepth[depth].length <= i) {
logme('ERROR: answer.draggables array does not contain objects.'); // We ran out of answers at this depth, go to the next depth down.
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth - 1, 0);
return; return;
} }
for (c1 in answer.draggables[0]) { chain = answerSortedByDepth[depth][i];
if (answer.draggables[0].hasOwnProperty(c1) === false) {
continue;
}
if (typeof answer.draggables[0][c1] === 'string') { baseDraggableId = Object.keys(chain)[0];
// use_targets = true;
return true; // This is a hack. For now we will work with depths 1 and 3.
} else if ( if (depth === 1) {
($.isArray(answer.draggables[0][c1]) === true) && baseTargetId = chain[baseDraggableId];
(answer.draggables[0][c1].length === 2)
) {
// use_targets = false;
return false; layeredTargetId = null;
} else { layeredDraggableId = null;
logme('ERROR: answer.draggables[0] is inconsidtent.');
return; // createBaseDraggableOnTarget(state, baseDraggableId, baseTargetId);
} } else if (depth === 3) {
} layeredDraggableId = baseDraggableId;
logme('ERROR: answer.draggables[0] is an empty object.'); layeredTargetId = Object.keys(chain[layeredDraggableId])[0];
return; baseDraggableId = Object.keys(chain[layeredDraggableId][layeredTargetId])[0];
}
function processAnswerTargets(state, answer) { baseTargetId = chain[layeredDraggableId][layeredTargetId][baseDraggableId];
var draggableId, draggable, targetId, target; }
(function (c1) { checkBaseDraggable();
while (c1 < answer.draggables.length) {
for (draggableId in answer.draggables[c1]) {
if (answer.draggables[c1].hasOwnProperty(draggableId) === false) {
continue;
}
if ((draggable = getById(state, 'draggables', draggableId)) === null) { return;
logme(
'ERROR: In answer there exists a ' +
'draggable ID "' + draggableId + '". No ' +
'draggable with this ID could be found.'
);
continue; function checkBaseDraggable() {
if ((baseDraggable = getById(state, 'draggables', baseDraggableId, null, false, baseTargetId)) === null) {
createBaseDraggableOnTarget(state, baseDraggableId, baseTargetId, true, function () {
if ((baseDraggable = getById(state, 'draggables', baseDraggableId, null, false, baseTargetId)) === null) {
console.log('ERROR: Could not successfully create a base draggable on a base target.');
} else {
baseTarget = baseDraggable.onTarget;
if ((layeredTargetId === null) || (layeredDraggableId === null)) {
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
} else {
checklayeredDraggable();
}
} }
});
} else {
baseTarget = baseDraggable.onTarget;
targetId = answer.draggables[c1][draggableId]; if ((layeredTargetId === null) || (layeredDraggableId === null)) {
if ((target = getById(state, 'targets', targetId)) === null) { processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
logme( } else {
'ERROR: In answer there exists a target ' + checklayeredDraggable();
'ID "' + targetId + '". No target with this ' + }
'ID could be found.' }
); }
continue; function checklayeredDraggable() {
if ((layeredDraggable = getById(state, 'draggables', layeredDraggableId, null, false, layeredTargetId, baseDraggableId, baseTargetId)) === null) {
layeredDraggable = getById(state, 'draggables', layeredDraggableId);
layeredTarget = null;
baseDraggable.targetField.every(function (target) {
if (target.id === layeredTargetId) {
layeredTarget = target;
} }
draggable.moveDraggableTo('target', target); return true;
});
if ((layeredDraggable !== null) && (layeredTarget !== null)) {
layeredDraggable.moveDraggableTo('target', layeredTarget, function () {
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
});
} else {
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
} }
} else {
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
}
}
}
c1 += 1; function createBaseDraggableOnTarget(state, draggableId, targetId, reportError, funcCallback) {
var draggable, target;
if ((draggable = getById(state, 'draggables', draggableId)) === null) {
if (reportError !== false) {
logme(
'ERROR: In answer there exists a ' +
'draggable ID "' + draggableId + '". No ' +
'draggable with this ID could be found.'
);
} }
}(0));
return false;
}
if ((target = getById(state, 'targets', targetId)) === null) {
if (reportError !== false) {
logme(
'ERROR: In answer there exists a target ' +
'ID "' + targetId + '". No target with this ' +
'ID could be found.'
);
}
return false;
}
draggable.moveDraggableTo('target', target, funcCallback);
return true;
} }
function processAnswerPositions(state, answer) { function processAnswerPositions(state, answer) {
var draggableId, draggable; var draggableId, draggable;
(function (c1) { (function (c1) {
while (c1 < answer.draggables.length) { while (c1 < answer.length) {
for (draggableId in answer.draggables[c1]) { for (draggableId in answer[c1]) {
if (answer.draggables[c1].hasOwnProperty(draggableId) === false) { if (answer[c1].hasOwnProperty(draggableId) === false) {
continue; continue;
} }
...@@ -171,8 +235,8 @@ define(['logme'], function (logme) { ...@@ -171,8 +235,8 @@ define(['logme'], function (logme) {
} }
draggable.moveDraggableTo('XY', { draggable.moveDraggableTo('XY', {
'x': answer.draggables[c1][draggableId][0], 'x': answer[c1][draggableId][0],
'y': answer.draggables[c1][draggableId][1] 'y': answer[c1][draggableId][1]
}); });
} }
...@@ -182,33 +246,110 @@ define(['logme'], function (logme) { ...@@ -182,33 +246,110 @@ define(['logme'], function (logme) {
} }
function repositionDraggables(state, answer) { function repositionDraggables(state, answer) {
if (answer.draggables.length === 0) { var answerSortedByDepth, minDepth, maxDepth;
answerSortedByDepth = {};
minDepth = 1000;
maxDepth = 0;
answer.every(function (chain) {
var depth;
depth = findDepth(chain, 0);
if (depth < minDepth) {
minDepth = depth;
}
if (depth > maxDepth) {
maxDepth = depth;
}
if (answerSortedByDepth.hasOwnProperty(depth) === false) {
answerSortedByDepth[depth] = [];
}
answerSortedByDepth[depth].push(chain);
return true;
});
if (answer.length === 0) {
return; return;
} }
if (state.config.individualTargets !== getUseTargets(answer)) { // For now we support only one case.
logme('ERROR: JSON config is not consistent with server response.'); if ((minDepth < 1) || (maxDepth > 3)) {
return; return;
} }
if (state.config.individualTargets === true) { if (state.config.individualTargets === true) {
processAnswerTargets(state, answer); processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, maxDepth, 0);
} else if (state.config.individualTargets === false) { } else if (state.config.individualTargets === false) {
processAnswerPositions(state, answer); processAnswerPositions(state, answer);
} }
} }
function getById(state, type, id) { function findDepth(tempObj, depth) {
var i;
if ($.isPlainObject(tempObj) === false) {
return depth;
}
depth += 1;
for (i in tempObj) {
if (tempObj.hasOwnProperty(i) === true) {
depth = findDepth(tempObj[i], depth);
}
}
return depth;
}
function getById(state, type, id, fromTargetField, inContainer, targetId, baseDraggableId, baseTargetId) {
return (function (c1) { return (function (c1) {
while (c1 < state[type].length) { while (c1 < state[type].length) {
if (type === 'draggables') { if (type === 'draggables') {
if ((state[type][c1].id === id) && (state[type][c1].isOriginal === true)) { if ((targetId !== undefined) && (inContainer === false) && (baseDraggableId !== undefined) && (baseTargetId !== undefined)) {
return state[type][c1]; if (
(state[type][c1].id === id) &&
(state[type][c1].inContainer === false) &&
(state[type][c1].onTarget.id === targetId) &&
(state[type][c1].onTarget.type === 'on_drag') &&
(state[type][c1].onTarget.draggableObj.id === baseDraggableId) &&
(state[type][c1].onTarget.draggableObj.onTarget.id === baseTargetId)
) {
return state[type][c1];
}
} else if ((targetId !== undefined) && (inContainer === false)) {
if (
(state[type][c1].id === id) &&
(state[type][c1].inContainer === false) &&
(state[type][c1].onTarget.id === targetId)
) {
return state[type][c1];
}
} else {
if (inContainer === false) {
if ((state[type][c1].id === id) && (state[type][c1].inContainer === false)) {
return state[type][c1];
}
} else {
if ((state[type][c1].id === id) && (state[type][c1].inContainer === true)) {
return state[type][c1];
}
}
} }
} else { // 'targets' } else { // 'targets'
if (state[type][c1].id === id) { if (fromTargetField === true) {
return state[type][c1]; if ((state[type][c1].id === id) && (state[type][c1].type === 'on_drag')) {
return state[type][c1];
}
} else {
if ((state[type][c1].id === id) && (state[type][c1].type === 'base')) {
return state[type][c1];
}
} }
} }
...@@ -218,10 +359,5 @@ define(['logme'], function (logme) { ...@@ -218,10 +359,5 @@ define(['logme'], function (logme) {
return null; return null;
}(0)); }(0));
} }
}); }); // 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