Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
0d83fefe
Commit
0d83fefe
authored
Mar 05, 2013
by
Vasyl Nakvasiuk
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
merge feature/alex/drag_and_drop-mitx into feature/alex/poll-merged
parent
f87a5fbb
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1409 additions
and
861 deletions
+1409
-861
common/lib/capa/capa/inputtypes.py
+10
-6
common/lib/capa/capa/tests/test_inputtypes.py
+8
-8
common/lib/capa/capa/verifiers/draganddrop.py
+84
-36
common/lib/capa/capa/verifiers/tests_draganddrop.py
+301
-77
common/static/js/capa/drag_and_drop/base_image.js
+2
-12
common/static/js/capa/drag_and_drop/config_parser.js
+80
-68
common/static/js/capa/drag_and_drop/container.js
+2
-12
common/static/js/capa/drag_and_drop/draggable_events.js
+131
-0
common/static/js/capa/drag_and_drop/draggable_logic.js
+379
-0
common/static/js/capa/drag_and_drop/draggables.js
+56
-493
common/static/js/capa/drag_and_drop/logme.js
+2
-12
common/static/js/capa/drag_and_drop/main.js
+34
-13
common/static/js/capa/drag_and_drop/scroller.js
+2
-12
common/static/js/capa/drag_and_drop/state.js
+2
-12
common/static/js/capa/drag_and_drop/targets.js
+98
-18
common/static/js/capa/drag_and_drop/update_input.js
+218
-82
No files found.
common/lib/capa/capa/inputtypes.py
View file @
0d83fefe
...
...
@@ -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
)
...
...
common/lib/capa/capa/tests/test_inputtypes.py
View file @
0d83fefe
...
...
@@ -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"
},
...
...
common/lib/capa/capa/verifiers/draganddrop.py
View file @
0d83fefe
...
...
@@ -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.
...
...
common/lib/capa/capa/verifiers/tests_draganddrop.py
View file @
0d83fefe
...
...
@@ -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'
))
...
...
common/static/js/capa/drag_and_drop/base_image.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/config_parser.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/container.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/draggable_events.js
0 → 100644
View file @
0d83fefe
(
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) {
common/static/js/capa/drag_and_drop/draggable_logic.js
0 → 100644
View file @
0d83fefe
(
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) {
common/static/js/capa/drag_and_drop/draggables.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/logme.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/main.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/scroller.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/state.js
View file @
0d83fefe
// 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) {
common/static/js/capa/drag_and_drop/targets.js
View file @
0d83fefe
// 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
initializeBase
Targets
(
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) {
common/static/js/capa/drag_and_drop/update_input.js
View file @
0d83fefe
// 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
,
answer
SortedByDepth
,
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) {
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment