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):
if tag_type == 'draggable' and not self.no_labels:
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
# add labels to images?:
......@@ -909,15 +913,15 @@ registry.register(DesignProtein2dInput)
class EditAGeneInput(InputTypeBase):
"""
An input type for editing a gene. Integrates with the genex java applet.
Example:
<editagene width="800" hight="500" dna_sequence="ETAAGGCTATAACCGA" />
"""
template = "editageneinput.html"
tags = ['editageneinput']
@classmethod
def get_attributes(cls):
"""
......@@ -927,14 +931,14 @@ class EditAGeneInput(InputTypeBase):
Attribute('height'),
Attribute('dna_sequence')
]
def _extra_context(self):
"""
"""
context = {
'applet_loader': '/static/js/capa/edit-a-gene.js',
}
return context
registry.register(EditAGeneInput)
......
......@@ -539,14 +539,14 @@ class DragAndDropTest(unittest.TestCase):
"target_outline": "false",
"base_image": "/static/images/about_1.png",
"draggables": [
{"can_reuse": "", "label": "Label 1", "id": "1", "icon": ""},
{"can_reuse": "", "label": "cc", "id": "name_with_icon", "icon": "/static/images/cc.jpg", },
{"can_reuse": "", "label": "arrow-left", "id": "with_icon", "icon": "/static/images/arrow-left.png", "can_reuse": ""},
{"can_reuse": "", "label": "Label2", "id": "5", "icon": "", "can_reuse": ""},
{"can_reuse": "", "label": "Mute", "id": "2", "icon": "/static/images/mute.png", "can_reuse": ""},
{"can_reuse": "", "label": "spinner", "id": "name_label_icon3", "icon": "/static/images/spinner.gif", "can_reuse": ""},
{"can_reuse": "", "label": "Star", "id": "name4", "icon": "/static/images/volume.png", "can_reuse": ""},
{"can_reuse": "", "label": "Label3", "id": "7", "icon": "", "can_reuse": ""}],
{"can_reuse": "", "label": "Label 1", "id": "1", "icon": "", "target_fields": []},
{"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": "", "target_fields": []},
{"can_reuse": "", "label": "Label2", "id": "5", "icon": "", "can_reuse": "", "target_fields": []},
{"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": "", "target_fields": []},
{"can_reuse": "", "label": "Star", "id": "name4", "icon": "/static/images/volume.png", "can_reuse": "", "target_fields": []},
{"can_reuse": "", "label": "Label3", "id": "7", "icon": "", "can_reuse": "", "target_fields": []}],
"one_per_target": "True",
"targets": [
{"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"},
......
......@@ -27,6 +27,49 @@ values are (x,y) coordinates of centers of dragged images.
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 for comparing positions.
......@@ -116,37 +159,36 @@ class DragAndDrop(object):
# Number of draggables in user_groups may be differ that in
# 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
# 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']}
# if '+number' is in rule - do not remove duplicates and strip
# '+number' from rule
current_rule = self.correct_positions[groupname].keys()[0]
current_rule = self.correct_positions[index].keys()[0]
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
self.correct_positions[groupname].pop(current_rule, None)
self.correct_positions[index].pop(current_rule, None)
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
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
# Check that in every group, for rule of that group, user positions of
# every element are equal with correct positions
for groupname in self.correct_groups:
for index, _ in enumerate(self.correct_groups):
rules_executed = 0
for rule in ('exact', 'anyof', 'unordered_equal'):
# 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
if not self.compare_positions(
self.correct_positions[groupname][rule],
self.user_positions[groupname]['user'], flag=rule):
self.correct_positions[index][rule],
self.user_positions[index]['user'], flag=rule):
return False
if not rules_executed: # no correct rules for current group
# probably xml content mistake - wrong rules names
......@@ -248,7 +290,7 @@ class DragAndDrop(object):
correct_answer = {'name4': 't1',
'name_with_icon': 't1',
'5': 't2',
'7':'t2'}
'7': 't2'}
It is draggable_name: dragable_position mapping.
......@@ -284,24 +326,25 @@ class DragAndDrop(object):
Args:
user_answer: json
correct_answer: dict or list
correct_answer: dict or list
"""
self.correct_groups = dict() # correct groups from xml
self.correct_positions = dict() # correct positions for comparing
self.user_groups = dict() # will be populated from user answer
self.user_positions = dict() # will be populated from user answer
self.correct_groups = [] # Correct groups from xml.
self.correct_positions = [] # Correct positions for comparing.
self.user_groups = [] # 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):
tmp = []
for key, value in correct_answer.items():
tmp_dict = {'draggables': [], 'targets': [], 'rule': 'exact'}
tmp_dict['draggables'].append(key)
tmp_dict['targets'].append(value)
tmp.append(tmp_dict)
tmp.append({
'draggables': [key],
'targets': [value],
'rule': 'exact'})
correct_answer = tmp
# Convert string `user_answer` to object.
user_answer = json.loads(user_answer)
# This dictionary will hold a key for each draggable the user placed on
......@@ -312,24 +355,29 @@ class DragAndDrop(object):
self.excess_draggables = dict((users_draggable.keys()[0],True)
for users_draggable in user_answer['draggables'])
# create identical data structures from user answer and correct answer
for i in xrange(0, len(correct_answer)):
groupname = str(i)
self.correct_groups[groupname] = correct_answer[i]['draggables']
self.correct_positions[groupname] = {correct_answer[i]['rule']:
correct_answer[i]['targets']}
self.user_groups[groupname] = []
self.user_positions[groupname] = {'user': []}
for draggable_dict in user_answer['draggables']:
# draggable_dict is 1-to-1 {draggable_name: position}
# Convert nested `user_answer` to flat format.
user_answer = flat_user_answer(user_answer)
# Create identical data structures from user answer and correct answer.
for answer in correct_answer:
user_groups_data = []
user_positions_data = []
for draggable_dict in user_answer:
# Draggable_dict is 1-to-1 {draggable_name: position}.
draggable_name = draggable_dict.keys()[0]
if draggable_name in self.correct_groups[groupname]:
self.user_groups[groupname].append(draggable_name)
self.user_positions[groupname]['user'].append(
if draggable_name in answer['draggables']:
user_groups_data.append(draggable_name)
user_positions_data.append(
draggable_dict[draggable_name])
# proved that this is not excess
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):
""" Creates DragAndDrop instance from user_input and correct_answer and
calls DragAndDrop.grade for grading.
......
......@@ -2,6 +2,7 @@ import unittest
import draganddrop
from draganddrop import PositionsCompare
import json
class Test_PositionsCompare(unittest.TestCase):
......@@ -40,10 +41,242 @@ class Test_PositionsCompare(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):
user_input = '{"draggables": [{"1": "t1"}, \
{"name_with_icon": "t2"}]}'
correct_answer = {'1': 't1', 'name_with_icon': 't2'}
user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2'}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_expect_no_actions_wrong(self):
......@@ -59,71 +292,63 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_targets_false(self):
user_input = '{"draggables": [{"1": "t1"}, \
{"name_with_icon": "t2"}]}'
correct_answer = {'1': 't3', 'name_with_icon': 't2'}
user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]'
correct_answer = {'1': 't3', 'name_with_icon': 't2'}
self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_multiple_images_per_target_true(self):
user_input = '{\
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]}'
correct_answer = {'1': 't1', 'name_with_icon': 't2',
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2',
'2': 't1'}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_multiple_images_per_target_false(self):
user_input = '{\
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]}'
correct_answer = {'1': 't2', 'name_with_icon': 't2',
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]'
correct_answer = {'1': 't2', 'name_with_icon': 't2',
'2': 't1'}
self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_targets_and_positions(self):
user_input = '{"draggables": [{"1": [10,10]}, \
{"name_with_icon": [[10,10],4]}]}'
user_input = '[{"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))
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'}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_exact(self):
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"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))
def test_positions_false(self):
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]}
self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_positions_true_in_radius(self):
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_true_in_manual_radius(self):
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_positions_false_in_manual_radius(self):
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
self.assertFalse(draganddrop.grade(user_input, correct_answer))
def test_correct_answer_not_has_key_from_user_answer(self):
user_input = '{"draggables": [{"1": "t1"}, \
{"name_with_icon": "t2"}]}'
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]'
correct_answer = {'3': 't3', 'name_with_icon': 't2'}
self.assertFalse(draganddrop.grade(user_input, correct_answer))
......@@ -131,20 +356,20 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
"""Draggables can be places anywhere on base image.
Place grass in the middle of the image and ant in the
right upper corner."""
user_input = '{"draggables": \
[{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}'
user_input = '[{"ant":[610.5,57.449951171875]},\
{"grass":[322.5,199.449951171875]}]'
correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]}
self.assertTrue(draganddrop.grade(user_input, correct_answer))
def test_lcao_correct(self):
"""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"}, \
{"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \
{"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"}, \
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}'
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer = [{
'draggables': ['1', '2', '3', '4', '5', '6'],
......@@ -178,12 +403,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_lcao_extra_element_incorrect(self):
"""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"}, \
{"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"}, \
{"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 = [{
'draggables': ['1', '2', '3', '4', '5', '6'],
......@@ -217,9 +442,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_draggable_no_mupliples(self):
"""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"}, \
{"3":"target6"}]}'
{"3":"target6"}]'
correct_answer = [
{
'draggables': ['1'],
......@@ -240,9 +465,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_draggable_with_mupliples(self):
"""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"}, \
{"3":"target6"}]}'
{"3":"target6"}]'
correct_answer = [
{
'draggables': ['1'],
......@@ -263,10 +488,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_many_draggable_with_mupliples(self):
"""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"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]}'
{"5": "target5"}, {"6": "target2"}]'
correct_answer = [
{
'draggables': ['1', '4'],
......@@ -292,12 +517,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_reuse_many_draggable_with_mupliples_wrong(self):
"""Test reusable draggables with mupltiple draggables per target"""
user_input = '{"draggables":[{"1":"target1"}, \
user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"}, \
{"2":"target3"}, \
{"2":"target4"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]}'
{"5": "target5"}, {"6": "target2"}]'
correct_answer = [
{
'draggables': ['1', '4'],
......@@ -323,10 +548,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_false(self):
"""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"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}'
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a'],
......@@ -347,10 +572,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_(self):
"""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"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}'
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a'],
......@@ -371,10 +596,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_multiple(self):
"""Test reusable draggables (mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}'
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'a'],
......@@ -395,10 +620,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_multiple_false(self):
"""Test reusable draggables (mupltiple draggables per target)"""
user_input = '{"draggables":[{"a":"target1"}, \
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]}'
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'a'],
......@@ -419,10 +644,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_reused(self):
"""Test a b c in 10 labels reused"""
user_input = '{"draggables":[{"a":"target1"}, \
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}'
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a'],
......@@ -443,10 +668,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_label_10_targets_with_a_b_c_reused_false(self):
"""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"},\
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]}'
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a'],
......@@ -467,9 +692,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse(self):
"""Test reusable draggables """
user_input = '{"draggables":[{"a":"target1"}, \
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"},\
{"a":"target5"}]}'
{"a":"target5"}]'
correct_answer = [
{
'draggables': ['a', 'b'],
......@@ -485,8 +710,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse_number(self):
"""Test reusable draggables with number """
user_input = '{"draggables":[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]}'
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'b'],
......@@ -502,8 +727,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def test_mixed_reuse_and_not_reuse_number_false(self):
"""Test reusable draggables with numbers, but wrong"""
user_input = '{"draggables":[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]}'
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'b'],
......@@ -518,9 +743,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
self.assertFalse(draganddrop.grade(user_input, correct_answer))
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"}, \
{"name4":"t1"}]}'
{"name4":"t1"}]'
correct_answer = [
{'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'},
{'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'],
......@@ -533,14 +758,13 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
def test_1(self):
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
user_input = '{"draggables": \
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
dnd = draganddrop.DragAndDrop(correct_answer, user_input)
correct_groups = {'1': ['name_with_icon'], '0': ['1']}
correct_positions = {'1': {'exact': [[20, 20]]}, '0': {'exact': [[[40, 10], 29]]}}
user_groups = {'1': [u'name_with_icon'], '0': [u'1']}
user_positions = {'1': {'user': [[20, 20]]}, '0': {'user': [[10, 10]]}}
correct_groups = [['1'], ['name_with_icon']]
correct_positions = [{'exact': [[[40, 10], 29]]}, {'exact': [[20, 20]]}]
user_groups = [['1'], ['name_with_icon']]
user_positions = [{'user': [[10, 10]]}, {'user': [[20, 20]]}]
self.assertEqual(correct_groups, dnd.correct_groups)
self.assertEqual(correct_positions, dnd.correct_positions)
......@@ -551,49 +775,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
class Test_DraAndDrop_Compare_Positions(unittest.TestCase):
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]],
user=[[2, 3], [1, 1]],
flag='anyof'))
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]],
user=[[2, 3], [1, 1]],
flag='exact'))
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]],
user=[[2, 13], [1, 1]],
flag='exact'))
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"],
user=["a", "b", "c"],
flag='anyof'))
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"],
user=["a", "b"],
flag='anyof'))
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"],
user=["a", "c", "b"],
flag='exact'))
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"],
user=["a", "c", "b"],
flag='anyof'))
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"],
user=["a", "c", "b"],
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) {
define(['logme'], function (logme) {
return BaseImage;
......@@ -50,10 +45,5 @@ define(['logme'], function (logme) {
baseImageElContainer.appendTo(state.containerEl);
});
}
});
// 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)
}); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) {
define(['logme'], function (logme) {
return configParser;
......@@ -16,7 +11,7 @@ define(['logme'], function (logme) {
'targetOutline': true,
'labelBgColor': '#d6d6d6',
'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);
......@@ -28,7 +23,7 @@ define(['logme'], function (logme) {
setIndividualTargets(state);
if (state.config.errors !== 0) {
if (state.config.foundErrors !== false) {
return false;
}
......@@ -38,35 +33,34 @@ define(['logme'], function (logme) {
function getDraggables(state, config) {
if (config.hasOwnProperty('draggables') === false) {
logme('ERROR: "config" does not have a property "draggables".');
state.config.errors += 1;
state.config.foundErrors = true;
} else if ($.isArray(config.draggables) === true) {
(function (i) {
while (i < config.draggables.length) {
if (processDraggable(state, config.draggables[i]) !== true) {
state.config.errors += 1;
}
i += 1;
config.draggables.every(function (draggable) {
if (processDraggable(state, draggable) !== true) {
state.config.foundErrors = true;
// Exit immediately from .every() call.
return false;
}
}(0));
} else if ($.isPlainObject(config.draggables) === true) {
if (processDraggable(state, config.draggables) !== true) {
state.config.errors += 1;
}
// Continue to next .every() call.
return true;
});
} else {
logme('ERROR: The type of config.draggables is no supported.');
state.config.errors += 1;
state.config.foundErrors = true;
}
}
function getBaseImage(state, config) {
if (config.hasOwnProperty('base_image') === false) {
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') {
state.config.baseImage = config.base_image;
} else {
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) {
// 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.
} else if ($.isArray(config.targets) === true) {
(function (i) {
while (i < config.targets.length) {
if (processTarget(state, config.targets[i]) !== true) {
state.config.errors += 1;
}
i += 1;
config.targets.every(function (target) {
if (processTarget(state, target) !== true) {
state.config.foundErrors = true;
// Exit immediately from .every() call.
return false;
}
}(0));
} else if ($.isPlainObject(config.targets) === true) {
if (processTarget(state, config.targets) !== true) {
state.config.errors += 1;
}
// Continue to next .every() call.
return true;
});
} else {
logme('ERROR: Property config.targets is not of a supported type.');
state.config.errors += 1;
state.config.foundErrors = true;
}
}
function getOnePerTarget(state, config) {
if (config.hasOwnProperty('one_per_target') === false) {
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') {
if (config.one_per_target.toLowerCase() === 'true') {
state.config.onePerTarget = true;
......@@ -106,42 +99,45 @@ define(['logme'], function (logme) {
state.config.onePerTarget = false;
} else {
logme('ERROR: Property config.one_per_target can either be "true", or "false".');
state.config.errors += 1;
state.config.foundErrors = true;
}
} else {
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) {
if (config.hasOwnProperty('target_outline') === false) {
// 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.
} else if (typeof config.target_outline === 'string') {
if (config.target_outline.toLowerCase() === 'true') {
state.config.targetOutline = true;
} else if (config.target_outline.toLowerCase() === 'false') {
state.config.targetOutline = false;
// 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.
if (config.hasOwnProperty('target_outline') === true) {
if (typeof config.target_outline === 'string') {
if (config.target_outline.toLowerCase() === 'true') {
state.config.targetOutline = true;
} 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 {
logme('ERROR: Property config.target_outline can either be "true", or "false".');
state.config.errors += 1;
logme('ERROR: Property config.target_outline is not of a supported type.');
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) {
if (config.hasOwnProperty('label_bg_color') === false) {
// 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.
} else if (typeof config.label_bg_color === 'string') {
state.config.labelBgColor = config.label_bg_color;
} else {
logme('ERROR: Property config.label_bg_color is not of a supported type.');
returnStatus = false;
// 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.
if (config.hasOwnProperty('label_bg_color') === true) {
if (typeof config.label_bg_color === 'string') {
state.config.labelBgColor = config.label_bg_color;
} else {
logme('ERROR: Property config.label_bg_color is not of a supported type.');
}
}
}
......@@ -159,17 +155,36 @@ define(['logme'], function (logme) {
(attrIsString(obj, 'icon') === false) ||
(attrIsString(obj, 'label') === false) ||
(attrIsBoolean(obj, 'can_reuse', false) === false)
(attrIsBoolean(obj, 'can_reuse', false) === false) ||
(obj.hasOwnProperty('target_fields') === 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);
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 (
(attrIsString(obj, 'id') === false) ||
......@@ -182,7 +197,9 @@ define(['logme'], function (logme) {
return false;
}
state.config.targets.push(obj);
if (pushToState !== false) {
state.config.targets.push(obj);
}
return true;
}
......@@ -250,10 +267,5 @@ define(['logme'], function (logme) {
return 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)
}); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) {
define(['logme'], function (logme) {
return Container;
......@@ -21,10 +16,5 @@ define(['logme'], function (logme) {
$('#inputtype_' + state.problemId).before(state.containerEl);
}
});
// 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)
}); // End-of: define(['logme'], function (logme) {
}(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) {
define(['logme', 'update_input'], function (logme, updateInput) {
define(['logme', 'draggable_events', 'draggable_logic'], function (logme, draggableEvents, draggableLogic) {
return {
'init': init
};
function init(state) {
(function (c1) {
while (c1 < state.config.draggables.length) {
processDraggable(state, state.config.draggables[c1]);
c1 += 1
}
}(0));
state.config.draggables.every(function (draggable) {
processDraggable(state, draggable);
return true;
});
}
function makeDraggableCopy(callbackFunc) {
......@@ -34,13 +28,18 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.stateDraggablesIndex = null; // Will be set.
draggableObj.containerEl = null; // Not needed, since a copy will never return to a container element.
draggableObj.iconEl = null; // Will be created.
draggableObj.iconImgEl = null; // Will be created.
draggableObj.labelEl = null; // Will be created.
draggableObj.targetField = []; // Will be populated.
// Create DOM elements and attach events.
if (draggableObj.originalConfigObj.icon.length > 0) {
draggableObj.iconEl = $('<img />');
draggableObj.iconEl.attr('src', draggableObj.originalConfigObj.icon);
draggableObj.iconEl.load(function () {
draggableObj.iconEl = $('<div></div>');
draggableObj.iconImgEl = $('<img />');
draggableObj.iconImgEl.attr('src', draggableObj.originalConfigObj.icon);
draggableObj.iconImgEl.load(function () {
draggableObj.iconEl.css({
'position': 'absolute',
'width': draggableObj.iconWidthSmall,
......@@ -48,6 +47,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left': 50 - draggableObj.iconWidthSmall * 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) {
draggableObj.labelEl = $(
......@@ -71,7 +78,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.attachMouseEventsTo('iconEl');
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj);
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj) - 1;
setTimeout(function () {
callbackFunc(draggableObj);
......@@ -99,7 +106,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.attachMouseEventsTo('iconEl');
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj);
draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj) - 1;
setTimeout(function () {
callbackFunc(draggableObj);
......@@ -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) {
var draggableObj;
......@@ -234,6 +132,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'zIndex': 1,
'containerEl': null,
'iconEl': null,
'iconImgEl': null,
'iconElBGColor': null,
'iconElPadding': null,
'iconElBorder': null,
......@@ -251,17 +150,23 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'onTargetIndex': null,
'state': state,
'mouseDown': mouseDown,
'mouseUp': mouseUp,
'mouseMove': mouseMove,
'checkLandingElement': checkLandingElement,
'checkIfOnTarget': checkIfOnTarget,
'snapToTarget': snapToTarget,
'correctZIndexes': correctZIndexes,
'moveBackToSlider': moveBackToSlider,
'moveDraggableTo': moveDraggableTo,
'mouseDown': draggableEvents.mouseDown,
'mouseUp': draggableEvents.mouseUp,
'mouseMove': draggableEvents.mouseMove,
'checkLandingElement': draggableLogic.checkLandingElement,
'checkIfOnTarget': draggableLogic.checkIfOnTarget,
'snapToTarget': draggableLogic.snapToTarget,
'correctZIndexes': draggableLogic.correctZIndexes,
'moveBackToSlider': draggableLogic.moveBackToSlider,
'moveDraggableTo': draggableLogic.moveDraggableTo,
'makeDraggableCopy': makeDraggableCopy,
'attachMouseEventsTo': attachMouseEventsTo
'attachMouseEventsTo': draggableEvents.attachMouseEventsTo,
'targetField': [],
'numDraggablesOnMe': 0
};
draggableObj.containerEl = $(
......@@ -288,9 +193,11 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj.iconElBorder = 'none';
draggableObj.iconElLeftOffset = 0;
draggableObj.iconEl = $('<img />');
draggableObj.iconEl.attr('src', obj.icon);
draggableObj.iconEl.load(function () {
draggableObj.iconEl = $('<div></div>');
draggableObj.iconImgEl = $('<img />');
draggableObj.iconImgEl.attr('src', obj.icon);
draggableObj.iconImgEl.load(function () {
draggableObj.iconWidth = this.width;
draggableObj.iconHeight = this.height;
......@@ -309,6 +216,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left': 50 - draggableObj.iconWidthSmall * 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);
if (obj.label.length > 0) {
......@@ -384,357 +299,5 @@ define(['logme', 'update_input'], function (logme, updateInput) {
state.numDraggablesInSlider += 1;
draggableObj.stateDraggablesIndex = state.draggables.push(draggableObj) - 1;
}
function mouseDown(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 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)
}); // End-of: define(['logme', 'draggable_events', 'draggable_logic'], function (logme, draggableEvents, draggableLogic) {
}(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) {
define([], function () {
var debugMode;
......@@ -27,10 +22,5 @@ define([], function () {
i += 1;
}
}
});
// 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)
}); // End-of: define([], function () {
}(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) {
define(
['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets', 'update_input'],
function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets, updateInput) {
return Main;
function Main() {
// 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);
}
......@@ -59,7 +85,7 @@ define(
return;
}
Targets(state);
Targets.initializeBaseTargets(state);
Scroller(state);
Draggables.init(state);
......@@ -72,10 +98,5 @@ 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)
}); // End-of: define(
}(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) {
define(['logme'], function (logme) {
return Scroller;
......@@ -206,10 +201,5 @@ define(['logme'], function (logme) {
}
}
} // End-of: function Scroller(state)
});
// 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)
}); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) {
define([], function () {
return State;
......@@ -96,10 +91,5 @@ define([], function () {
}
}
}
});
// 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)
}); // End-of: define([], function () {
}(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) {
define(['logme'], function (logme) {
return Targets;
return {
'initializeBaseTargets': initializeBaseTargets,
'initializeTargetField': initializeTargetField,
'destroyTargetField': destroyTargetField
};
function Targets(state) {
function initializeBaseTargets(state) {
(function (c1) {
while (c1 < state.config.targets.length) {
processTarget(state, state.config.targets[c1]);
......@@ -17,7 +16,58 @@ define(['logme'], function (logme) {
}(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;
borderCss = '';
......@@ -38,7 +88,13 @@ define(['logme'], function (logme) {
'" ' +
'></div>'
);
targetEl.appendTo(state.baseImageEl.parent());
if (fromTargetField === true) {
targetEl.appendTo(draggableObj.iconEl);
} else {
targetEl.appendTo(state.baseImageEl.parent());
}
targetEl.mousedown(function (event) {
event.preventDefault();
});
......@@ -68,8 +124,13 @@ define(['logme'], function (logme) {
}
targetObj = {
'uniqueId': state.getUniqueId(),
'id': obj.id,
'x': obj.x,
'y': obj.y,
'w': obj.w,
'h': obj.h,
......@@ -86,9 +147,21 @@ define(['logme'], function (logme) {
'updateNumTextEl': updateNumTextEl,
'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) {
numTextEl.appendTo(state.baseImageEl.parent());
numTextEl.mousedown(function (event) {
......@@ -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) {
......@@ -121,6 +198,10 @@ define(['logme'], function (logme) {
draggable.onTarget = null;
draggable.onTargetIndex = null;
if (this.type === 'on_drag') {
this.draggableObj.numDraggablesOnMe -= 1;
}
this.updateNumTextEl();
}
......@@ -128,6 +209,10 @@ define(['logme'], function (logme) {
draggable.onTarget = this;
draggable.onTargetIndex = this.draggableList.push(draggable) - 1;
if (this.type === 'on_drag') {
this.draggableObj.numDraggablesOnMe += 1;
}
this.updateNumTextEl();
}
......@@ -183,10 +268,5 @@ define(['logme'], function (logme) {
this.numTextEl.html(this.draggableList.length);
}
}
});
// 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)
}); // End-of: define(['logme'], function (logme) {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
// define() functions from Require JS available inside the anonymous function.
//
// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system
(function (requirejs, require, define) {
define(['logme'], function (logme) {
return {
'check': check,
......@@ -37,7 +32,12 @@ define(['logme'], function (logme) {
(function (c2) {
while (c2 < state.targets[c1].draggableList.length) {
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);
tempObj = null;
......@@ -50,7 +50,18 @@ define(['logme'], function (logme) {
}(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
......@@ -59,6 +70,7 @@ define(['logme'], function (logme) {
var inputElVal;
inputElVal = $('#input_' + state.problemId).val();
if (inputElVal.length === 0) {
return false;
}
......@@ -68,95 +80,147 @@ define(['logme'], function (logme) {
return true;
}
function getUseTargets(answer) {
if ($.isArray(answer.draggables) === false) {
logme('ERROR: answer.draggables is not an array.');
function processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i) {
var baseDraggableId, baseDraggable, baseTargetId, baseTarget,
layeredDraggableId, layeredDraggable, layeredTargetId, layeredTarget,
chain;
if (depth === 0) {
// We are at the lowest depth? The end.
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;
}
if ($.isPlainObject(answer.draggables[0]) === false) {
logme('ERROR: answer.draggables array does not contain objects.');
if (answerSortedByDepth[depth].length <= i) {
// We ran out of answers at this depth, go to the next depth down.
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth - 1, 0);
return;
}
for (c1 in answer.draggables[0]) {
if (answer.draggables[0].hasOwnProperty(c1) === false) {
continue;
}
chain = answerSortedByDepth[depth][i];
if (typeof answer.draggables[0][c1] === 'string') {
// use_targets = true;
baseDraggableId = Object.keys(chain)[0];
return true;
} else if (
($.isArray(answer.draggables[0][c1]) === true) &&
(answer.draggables[0][c1].length === 2)
) {
// use_targets = false;
// This is a hack. For now we will work with depths 1 and 3.
if (depth === 1) {
baseTargetId = chain[baseDraggableId];
return false;
} else {
logme('ERROR: answer.draggables[0] is inconsidtent.');
layeredTargetId = null;
layeredDraggableId = null;
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) {
var draggableId, draggable, targetId, target;
baseTargetId = chain[layeredDraggableId][layeredTargetId][baseDraggableId];
}
(function (c1) {
while (c1 < answer.draggables.length) {
for (draggableId in answer.draggables[c1]) {
if (answer.draggables[c1].hasOwnProperty(draggableId) === false) {
continue;
}
checkBaseDraggable();
if ((draggable = getById(state, 'draggables', draggableId)) === null) {
logme(
'ERROR: In answer there exists a ' +
'draggable ID "' + draggableId + '". No ' +
'draggable with this ID could be found.'
);
return;
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 ((target = getById(state, 'targets', targetId)) === null) {
logme(
'ERROR: In answer there exists a target ' +
'ID "' + targetId + '". No target with this ' +
'ID could be found.'
);
if ((layeredTargetId === null) || (layeredDraggableId === null)) {
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, depth, i + 1);
} else {
checklayeredDraggable();
}
}
}
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) {
var draggableId, draggable;
(function (c1) {
while (c1 < answer.draggables.length) {
for (draggableId in answer.draggables[c1]) {
if (answer.draggables[c1].hasOwnProperty(draggableId) === false) {
while (c1 < answer.length) {
for (draggableId in answer[c1]) {
if (answer[c1].hasOwnProperty(draggableId) === false) {
continue;
}
......@@ -171,8 +235,8 @@ define(['logme'], function (logme) {
}
draggable.moveDraggableTo('XY', {
'x': answer.draggables[c1][draggableId][0],
'y': answer.draggables[c1][draggableId][1]
'x': answer[c1][draggableId][0],
'y': answer[c1][draggableId][1]
});
}
......@@ -182,33 +246,110 @@ define(['logme'], function (logme) {
}
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;
}
if (state.config.individualTargets !== getUseTargets(answer)) {
logme('ERROR: JSON config is not consistent with server response.');
// For now we support only one case.
if ((minDepth < 1) || (maxDepth > 3)) {
return;
}
if (state.config.individualTargets === true) {
processAnswerTargets(state, answer);
processAnswerTargets(state, answerSortedByDepth, minDepth, maxDepth, maxDepth, 0);
} else if (state.config.individualTargets === false) {
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) {
while (c1 < state[type].length) {
if (type === 'draggables') {
if ((state[type][c1].id === id) && (state[type][c1].isOriginal === true)) {
return state[type][c1];
if ((targetId !== undefined) && (inContainer === false) && (baseDraggableId !== undefined) && (baseTargetId !== undefined)) {
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'
if (state[type][c1].id === id) {
return state[type][c1];
if (fromTargetField === true) {
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) {
return null;
}(0));
}
});
// 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)
}); // End-of: define(['logme'], function (logme) {
}(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