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
12 years ago
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):
...
@@ -798,6 +798,10 @@ class DragAndDropInput(InputTypeBase):
if
tag_type
==
'draggable'
and
not
self
.
no_labels
:
if
tag_type
==
'draggable'
and
not
self
.
no_labels
:
dic
[
'label'
]
=
dic
[
'label'
]
or
dic
[
'id'
]
dic
[
'label'
]
=
dic
[
'label'
]
or
dic
[
'id'
]
if
tag_type
==
'draggable'
:
dic
[
'target_fields'
]
=
[
parse
(
target
,
'target'
)
for
target
in
tag
.
iterchildren
(
'target'
)]
return
dic
return
dic
# add labels to images?:
# add labels to images?:
...
@@ -909,15 +913,15 @@ registry.register(DesignProtein2dInput)
...
@@ -909,15 +913,15 @@ registry.register(DesignProtein2dInput)
class
EditAGeneInput
(
InputTypeBase
):
class
EditAGeneInput
(
InputTypeBase
):
"""
"""
An input type for editing a gene. Integrates with the genex java applet.
An input type for editing a gene. Integrates with the genex java applet.
Example:
Example:
<editagene width="800" hight="500" dna_sequence="ETAAGGCTATAACCGA" />
<editagene width="800" hight="500" dna_sequence="ETAAGGCTATAACCGA" />
"""
"""
template
=
"editageneinput.html"
template
=
"editageneinput.html"
tags
=
[
'editageneinput'
]
tags
=
[
'editageneinput'
]
@classmethod
@classmethod
def
get_attributes
(
cls
):
def
get_attributes
(
cls
):
"""
"""
...
@@ -927,14 +931,14 @@ class EditAGeneInput(InputTypeBase):
...
@@ -927,14 +931,14 @@ class EditAGeneInput(InputTypeBase):
Attribute
(
'height'
),
Attribute
(
'height'
),
Attribute
(
'dna_sequence'
)
Attribute
(
'dna_sequence'
)
]
]
def
_extra_context
(
self
):
def
_extra_context
(
self
):
"""
"""
"""
"""
context
=
{
context
=
{
'applet_loader'
:
'/static/js/capa/edit-a-gene.js'
,
'applet_loader'
:
'/static/js/capa/edit-a-gene.js'
,
}
}
return
context
return
context
registry
.
register
(
EditAGeneInput
)
registry
.
register
(
EditAGeneInput
)
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/tests/test_inputtypes.py
View file @
0d83fefe
...
@@ -539,14 +539,14 @@ class DragAndDropTest(unittest.TestCase):
...
@@ -539,14 +539,14 @@ class DragAndDropTest(unittest.TestCase):
"target_outline"
:
"false"
,
"target_outline"
:
"false"
,
"base_image"
:
"/static/images/about_1.png"
,
"base_image"
:
"/static/images/about_1.png"
,
"draggables"
:
[
"draggables"
:
[
{
"can_reuse"
:
""
,
"label"
:
"Label 1"
,
"id"
:
"1"
,
"icon"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"Label 1"
,
"id"
:
"1"
,
"icon"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"cc"
,
"id"
:
"name_with_icon"
,
"icon"
:
"/static/images/cc.jpg"
,
},
{
"can_reuse"
:
""
,
"label"
:
"cc"
,
"id"
:
"name_with_icon"
,
"icon"
:
"/static/images/cc.jpg"
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"arrow-left"
,
"id"
:
"with_icon"
,
"icon"
:
"/static/images/arrow-left.png"
,
"can_reuse"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"arrow-left"
,
"id"
:
"with_icon"
,
"icon"
:
"/static/images/arrow-left.png"
,
"can_reuse"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"Label2"
,
"id"
:
"5"
,
"icon"
:
""
,
"can_reuse"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"Label2"
,
"id"
:
"5"
,
"icon"
:
""
,
"can_reuse"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"Mute"
,
"id"
:
"2"
,
"icon"
:
"/static/images/mute.png"
,
"can_reuse"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"Mute"
,
"id"
:
"2"
,
"icon"
:
"/static/images/mute.png"
,
"can_reuse"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"spinner"
,
"id"
:
"name_label_icon3"
,
"icon"
:
"/static/images/spinner.gif"
,
"can_reuse"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"spinner"
,
"id"
:
"name_label_icon3"
,
"icon"
:
"/static/images/spinner.gif"
,
"can_reuse"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"Star"
,
"id"
:
"name4"
,
"icon"
:
"/static/images/volume.png"
,
"can_reuse"
:
""
},
{
"can_reuse"
:
""
,
"label"
:
"Star"
,
"id"
:
"name4"
,
"icon"
:
"/static/images/volume.png"
,
"can_reuse"
:
""
,
"target_fields"
:
[]
},
{
"can_reuse"
:
""
,
"label"
:
"Label3"
,
"id"
:
"7"
,
"icon"
:
""
,
"can_reuse"
:
""
}],
{
"can_reuse"
:
""
,
"label"
:
"Label3"
,
"id"
:
"7"
,
"icon"
:
""
,
"can_reuse"
:
""
,
"target_fields"
:
[]
}],
"one_per_target"
:
"True"
,
"one_per_target"
:
"True"
,
"targets"
:
[
"targets"
:
[
{
"y"
:
"90"
,
"x"
:
"210"
,
"id"
:
"t1"
,
"w"
:
"90"
,
"h"
:
"90"
},
{
"y"
:
"90"
,
"x"
:
"210"
,
"id"
:
"t1"
,
"w"
:
"90"
,
"h"
:
"90"
},
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/verifiers/draganddrop.py
View file @
0d83fefe
...
@@ -27,6 +27,49 @@ values are (x,y) coordinates of centers of dragged images.
...
@@ -27,6 +27,49 @@ values are (x,y) coordinates of centers of dragged images.
import
json
import
json
def
flat_user_answer
(
user_answer
):
"""
Convert nested `user_answer` to flat format.
{'up': {'first': {'p': 'p_l'}}}
to
{'up': 'p_l[p][first]'}
"""
def
parse_user_answer
(
answer
):
key
=
answer
.
keys
()[
0
]
value
=
answer
.
values
()[
0
]
if
isinstance
(
value
,
dict
):
# Make complex value:
# Example:
# Create like 'p_l[p][first]' from {'first': {'p': 'p_l'}
complex_value_list
=
[]
v_value
=
value
while
isinstance
(
v_value
,
dict
):
v_key
=
v_value
.
keys
()[
0
]
v_value
=
v_value
.
values
()[
0
]
complex_value_list
.
append
(
v_key
)
complex_value
=
'{0}'
.
format
(
v_value
)
for
i
in
reversed
(
complex_value_list
):
complex_value
=
'{0}[{1}]'
.
format
(
complex_value
,
i
)
res
=
{
key
:
complex_value
}
return
res
else
:
return
answer
result
=
[]
for
answer
in
user_answer
:
parse_answer
=
parse_user_answer
(
answer
)
result
.
append
(
parse_answer
)
return
result
class
PositionsCompare
(
list
):
class
PositionsCompare
(
list
):
""" Class for comparing positions.
""" Class for comparing positions.
...
@@ -116,37 +159,36 @@ class DragAndDrop(object):
...
@@ -116,37 +159,36 @@ class DragAndDrop(object):
# Number of draggables in user_groups may be differ that in
# Number of draggables in user_groups may be differ that in
# correct_groups, that is incorrect, except special case with 'number'
# correct_groups, that is incorrect, except special case with 'number'
for
groupname
,
draggable_ids
in
self
.
correct_groups
.
items
():
for
index
,
draggable_ids
in
enumerate
(
self
.
correct_groups
):
# 'number' rule special case
# 'number' rule special case
# for reusable draggables we may get in self.user_groups
# for reusable draggables we may get in self.user_groups
# {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']}
# {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']}
# if '+number' is in rule - do not remove duplicates and strip
# if '+number' is in rule - do not remove duplicates and strip
# '+number' from rule
# '+number' from rule
current_rule
=
self
.
correct_positions
[
groupname
]
.
keys
()[
0
]
current_rule
=
self
.
correct_positions
[
index
]
.
keys
()[
0
]
if
'number'
in
current_rule
:
if
'number'
in
current_rule
:
rule_values
=
self
.
correct_positions
[
groupname
][
current_rule
]
rule_values
=
self
.
correct_positions
[
index
][
current_rule
]
# clean rule, do not do clean duplicate items
# clean rule, do not do clean duplicate items
self
.
correct_positions
[
groupname
]
.
pop
(
current_rule
,
None
)
self
.
correct_positions
[
index
]
.
pop
(
current_rule
,
None
)
parsed_rule
=
current_rule
.
replace
(
'+'
,
''
)
.
replace
(
'number'
,
''
)
parsed_rule
=
current_rule
.
replace
(
'+'
,
''
)
.
replace
(
'number'
,
''
)
self
.
correct_positions
[
groupname
][
parsed_rule
]
=
rule_values
self
.
correct_positions
[
index
][
parsed_rule
]
=
rule_values
else
:
# remove dublicates
else
:
# remove dublicates
self
.
user_groups
[
groupname
]
=
list
(
set
(
self
.
user_groups
[
groupname
]))
self
.
user_groups
[
index
]
=
list
(
set
(
self
.
user_groups
[
index
]))
if
sorted
(
draggable_ids
)
!=
sorted
(
self
.
user_groups
[
groupname
]):
if
sorted
(
draggable_ids
)
!=
sorted
(
self
.
user_groups
[
index
]):
return
False
return
False
# Check that in every group, for rule of that group, user positions of
# Check that in every group, for rule of that group, user positions of
# every element are equal with correct positions
# every element are equal with correct positions
for
groupname
in
self
.
correct_groups
:
for
index
,
_
in
enumerate
(
self
.
correct_groups
)
:
rules_executed
=
0
rules_executed
=
0
for
rule
in
(
'exact'
,
'anyof'
,
'unordered_equal'
):
for
rule
in
(
'exact'
,
'anyof'
,
'unordered_equal'
):
# every group has only one rule
# every group has only one rule
if
self
.
correct_positions
[
groupname
]
.
get
(
rule
,
None
):
if
self
.
correct_positions
[
index
]
.
get
(
rule
,
None
):
rules_executed
+=
1
rules_executed
+=
1
if
not
self
.
compare_positions
(
if
not
self
.
compare_positions
(
self
.
correct_positions
[
groupname
][
rule
],
self
.
correct_positions
[
index
][
rule
],
self
.
user_positions
[
groupname
][
'user'
],
flag
=
rule
):
self
.
user_positions
[
index
][
'user'
],
flag
=
rule
):
return
False
return
False
if
not
rules_executed
:
# no correct rules for current group
if
not
rules_executed
:
# no correct rules for current group
# probably xml content mistake - wrong rules names
# probably xml content mistake - wrong rules names
...
@@ -248,7 +290,7 @@ class DragAndDrop(object):
...
@@ -248,7 +290,7 @@ class DragAndDrop(object):
correct_answer = {'name4': 't1',
correct_answer = {'name4': 't1',
'name_with_icon': 't1',
'name_with_icon': 't1',
'5': 't2',
'5': 't2',
'7':'t2'}
'7':
't2'}
It is draggable_name: dragable_position mapping.
It is draggable_name: dragable_position mapping.
...
@@ -284,24 +326,25 @@ class DragAndDrop(object):
...
@@ -284,24 +326,25 @@ class DragAndDrop(object):
Args:
Args:
user_answer: json
user_answer: json
correct_answer: dict
or list
correct_answer: dict or list
"""
"""
self
.
correct_groups
=
dict
()
# correct groups from xml
self
.
correct_groups
=
[]
# Correct groups from xml.
self
.
correct_positions
=
dict
()
# correct positions for comparing
self
.
correct_positions
=
[]
# Correct positions for comparing.
self
.
user_groups
=
dict
()
# will be populated from user answer
self
.
user_groups
=
[]
# Will be populated from user answer.
self
.
user_positions
=
dict
()
# will be populated from user answer
self
.
user_positions
=
[]
# Will be populated from user answer.
#
convert from dict answer format to list format
#
Convert from dict answer format to list format.
if
isinstance
(
correct_answer
,
dict
):
if
isinstance
(
correct_answer
,
dict
):
tmp
=
[]
tmp
=
[]
for
key
,
value
in
correct_answer
.
items
():
for
key
,
value
in
correct_answer
.
items
():
tmp
_dict
=
{
'draggables'
:
[],
'targets'
:
[],
'rule'
:
'exact'
}
tmp
.
append
({
tmp_dict
[
'draggables'
]
.
append
(
key
)
'draggables'
:
[
key
],
tmp_dict
[
'targets'
]
.
append
(
value
)
'targets'
:
[
value
],
tmp
.
append
(
tmp_dict
)
'rule'
:
'exact'
}
)
correct_answer
=
tmp
correct_answer
=
tmp
# Convert string `user_answer` to object.
user_answer
=
json
.
loads
(
user_answer
)
user_answer
=
json
.
loads
(
user_answer
)
# This dictionary will hold a key for each draggable the user placed on
# This dictionary will hold a key for each draggable the user placed on
...
@@ -312,24 +355,29 @@ class DragAndDrop(object):
...
@@ -312,24 +355,29 @@ class DragAndDrop(object):
self
.
excess_draggables
=
dict
((
users_draggable
.
keys
()[
0
],
True
)
self
.
excess_draggables
=
dict
((
users_draggable
.
keys
()[
0
],
True
)
for
users_draggable
in
user_answer
[
'draggables'
])
for
users_draggable
in
user_answer
[
'draggables'
])
# create identical data structures from user answer and correct answer
# Convert nested `user_answer` to flat format.
for
i
in
xrange
(
0
,
len
(
correct_answer
)):
user_answer
=
flat_user_answer
(
user_answer
)
groupname
=
str
(
i
)
self
.
correct_groups
[
groupname
]
=
correct_answer
[
i
][
'draggables'
]
# Create identical data structures from user answer and correct answer.
self
.
correct_positions
[
groupname
]
=
{
correct_answer
[
i
][
'rule'
]:
for
answer
in
correct_answer
:
correct_answer
[
i
][
'targets'
]}
user_groups_data
=
[]
self
.
user_groups
[
groupname
]
=
[]
user_positions_data
=
[]
self
.
user_positions
[
groupname
]
=
{
'user'
:
[]}
for
draggable_dict
in
user_answer
:
for
draggable_dict
in
user_answer
[
'draggables'
]:
# Draggable_dict is 1-to-1 {draggable_name: position}.
# draggable_dict is 1-to-1 {draggable_name: position}
draggable_name
=
draggable_dict
.
keys
()[
0
]
draggable_name
=
draggable_dict
.
keys
()[
0
]
if
draggable_name
in
self
.
correct_groups
[
groupname
]:
if
draggable_name
in
answer
[
'draggables'
]:
self
.
user_groups
[
groupname
]
.
append
(
draggable_name
)
user_groups_data
.
append
(
draggable_name
)
self
.
user_positions
[
groupname
][
'user'
]
.
append
(
user_positions_data
.
append
(
draggable_dict
[
draggable_name
])
draggable_dict
[
draggable_name
])
# proved that this is not excess
# proved that this is not excess
self
.
excess_draggables
[
draggable_name
]
=
False
self
.
excess_draggables
[
draggable_name
]
=
False
self
.
correct_groups
.
append
(
answer
[
'draggables'
])
self
.
correct_positions
.
append
({
answer
[
'rule'
]:
answer
[
'targets'
]})
self
.
user_groups
.
append
(
user_groups_data
)
self
.
user_positions
.
append
({
'user'
:
user_positions_data
})
def
grade
(
user_input
,
correct_answer
):
def
grade
(
user_input
,
correct_answer
):
""" Creates DragAndDrop instance from user_input and correct_answer and
""" Creates DragAndDrop instance from user_input and correct_answer and
calls DragAndDrop.grade for grading.
calls DragAndDrop.grade for grading.
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/verifiers/tests_draganddrop.py
View file @
0d83fefe
...
@@ -2,6 +2,7 @@ import unittest
...
@@ -2,6 +2,7 @@ import unittest
import
draganddrop
import
draganddrop
from
draganddrop
import
PositionsCompare
from
draganddrop
import
PositionsCompare
import
json
class
Test_PositionsCompare
(
unittest
.
TestCase
):
class
Test_PositionsCompare
(
unittest
.
TestCase
):
...
@@ -40,10 +41,242 @@ class Test_PositionsCompare(unittest.TestCase):
...
@@ -40,10 +41,242 @@ class Test_PositionsCompare(unittest.TestCase):
class
Test_DragAndDrop_Grade
(
unittest
.
TestCase
):
class
Test_DragAndDrop_Grade
(
unittest
.
TestCase
):
def
test_targets_are_draggable_1
(
self
):
user_input
=
json
.
dumps
([
{
'p'
:
'p_l'
},
{
'up'
:
{
'first'
:
{
'p'
:
'p_l'
}}}
])
correct_answer
=
[
{
'draggables'
:
[
'p'
],
'targets'
:
[
'p_l'
,
'p_r'
],
'rule'
:
'anyof'
},
{
'draggables'
:
[
'up'
],
'targets'
:
[
'p_l[p][first]'
],
'rule'
:
'anyof'
}
]
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_are_draggable_2
(
self
):
user_input
=
json
.
dumps
([
{
'p'
:
'p_l'
},
{
'p'
:
'p_r'
},
{
's'
:
's_l'
},
{
's'
:
's_r'
},
{
'up'
:
{
'1'
:
{
'p'
:
'p_l'
}}},
{
'up'
:
{
'3'
:
{
'p'
:
'p_l'
}}},
{
'up'
:
{
'1'
:
{
'p'
:
'p_r'
}}},
{
'up'
:
{
'3'
:
{
'p'
:
'p_r'
}}},
{
'up_and_down'
:
{
'1'
:
{
's'
:
's_l'
}}},
{
'up_and_down'
:
{
'1'
:
{
's'
:
's_r'
}}}
])
correct_answer
=
[
{
'draggables'
:
[
'p'
],
'targets'
:
[
'p_l'
,
'p_r'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
's'
],
'targets'
:
[
's_l'
,
's_r'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up_and_down'
],
'targets'
:
[
's_l[s][1]'
,
's_r[s][1]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up'
],
'targets'
:
[
'p_l[p][1]'
,
'p_l[p][3]'
,
'p_r[p][1]'
,
'p_r[p][3]'
],
'rule'
:
'unordered_equal'
}
]
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_are_draggable_2_manual_parsing
(
self
):
user_input
=
json
.
dumps
([
{
'up'
:
'p_l[p][1]'
},
{
'p'
:
'p_l'
},
{
'up'
:
'p_l[p][3]'
},
{
'up'
:
'p_r[p][1]'
},
{
'p'
:
'p_r'
},
{
'up'
:
'p_r[p][3]'
},
{
'up_and_down'
:
's_l[s][1]'
},
{
's'
:
's_l'
},
{
'up_and_down'
:
's_r[s][1]'
},
{
's'
:
's_r'
}
])
correct_answer
=
[
{
'draggables'
:
[
'p'
],
'targets'
:
[
'p_l'
,
'p_r'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
's'
],
'targets'
:
[
's_l'
,
's_r'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up_and_down'
],
'targets'
:
[
's_l[s][1]'
,
's_r[s][1]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up'
],
'targets'
:
[
'p_l[p][1]'
,
'p_l[p][3]'
,
'p_r[p][1]'
,
'p_r[p][3]'
],
'rule'
:
'unordered_equal'
}
]
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_are_draggable_3_nested
(
self
):
user_input
=
json
.
dumps
([
{
'molecule'
:
'left_side_tagret'
},
{
'molecule'
:
'right_side_tagret'
},
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'left_side_tagret'
}}},
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'right_side_tagret'
}}},
{
's'
:
{
's_target'
:
{
'molecule'
:
'left_side_tagret'
}}},
{
's'
:
{
's_target'
:
{
'molecule'
:
'right_side_tagret'
}}},
{
'up'
:
{
'1'
:
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'left_side_tagret'
}}}}},
{
'up'
:
{
'3'
:
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'left_side_tagret'
}}}}},
{
'up'
:
{
'1'
:
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'right_side_tagret'
}}}}},
{
'up'
:
{
'3'
:
{
'p'
:
{
'p_target'
:
{
'molecule'
:
'right_side_tagret'
}}}}},
{
'up_and_down'
:
{
'1'
:
{
's'
:
{
's_target'
:
{
'molecule'
:
'left_side_tagret'
}}}}},
{
'up_and_down'
:
{
'1'
:
{
's'
:
{
's_target'
:
{
'molecule'
:
'right_side_tagret'
}}}}}
])
correct_answer
=
[
{
'draggables'
:
[
'molecule'
],
'targets'
:
[
'left_side_tagret'
,
'right_side_tagret'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'p'
],
'targets'
:
[
'left_side_tagret[molecule][p_target]'
,
'right_side_tagret[molecule][p_target]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
's'
],
'targets'
:
[
'left_side_tagret[molecule][s_target]'
,
'right_side_tagret[molecule][s_target]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up_and_down'
],
'targets'
:
[
'left_side_tagret[molecule][s_target][s][1]'
,
'right_side_tagret[molecule][s_target][s][1]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up'
],
'targets'
:
[
'left_side_tagret[molecule][p_target][p][1]'
,
'left_side_tagret[molecule][p_target][p][3]'
,
'right_side_tagret[molecule][p_target][p][1]'
,
'right_side_tagret[molecule][p_target][p][3]'
],
'rule'
:
'unordered_equal'
}
]
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_are_draggable_4_real_example
(
self
):
user_input
=
json
.
dumps
([
{
'single_draggable'
:
's_l'
},
{
'single_draggable'
:
's_r'
},
{
'single_draggable'
:
'p_sigma'
},
{
'single_draggable'
:
'p_sigma*'
},
{
'single_draggable'
:
's_sigma'
},
{
'single_draggable'
:
's_sigma*'
},
{
'double_draggable'
:
'p_pi*'
},
{
'double_draggable'
:
'p_pi'
},
{
'triple_draggable'
:
'p_l'
},
{
'triple_draggable'
:
'p_r'
},
{
'up'
:
{
'1'
:
{
'triple_draggable'
:
'p_l'
}}},
{
'up'
:
{
'2'
:
{
'triple_draggable'
:
'p_l'
}}},
{
'up'
:
{
'2'
:
{
'triple_draggable'
:
'p_r'
}}},
{
'up'
:
{
'3'
:
{
'triple_draggable'
:
'p_r'
}}},
{
'up_and_down'
:
{
'1'
:
{
'single_draggable'
:
's_l'
}}},
{
'up_and_down'
:
{
'1'
:
{
'single_draggable'
:
's_r'
}}},
{
'up_and_down'
:
{
'1'
:
{
'single_draggable'
:
's_sigma'
}}},
{
'up_and_down'
:
{
'1'
:
{
'single_draggable'
:
's_sigma*'
}}},
{
'up_and_down'
:
{
'1'
:
{
'double_draggable'
:
'p_pi'
}}},
{
'up_and_down'
:
{
'2'
:
{
'double_draggable'
:
'p_pi'
}}}
])
# 10 targets:
# s_l, s_r, p_l, p_r, s_sigma, s_sigma*, p_pi, p_sigma, p_pi*, p_sigma*
#
# 3 draggable objects, which have targets (internal target ids - 1, 2, 3):
# single_draggable, double_draggable, triple_draggable
#
# 2 draggable objects:
# up, up_and_down
correct_answer
=
[
{
'draggables'
:
[
'triple_draggable'
],
'targets'
:
[
'p_l'
,
'p_r'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'double_draggable'
],
'targets'
:
[
'p_pi'
,
'p_pi*'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'single_draggable'
],
'targets'
:
[
's_l'
,
's_r'
,
's_sigma'
,
's_sigma*'
,
'p_sigma'
,
'p_sigma*'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up'
],
'targets'
:
[
'p_l[triple_draggable][1]'
,
'p_l[triple_draggable][2]'
,
'p_r[triple_draggable][2]'
,
'p_r[triple_draggable][3]'
],
'rule'
:
'unordered_equal'
},
{
'draggables'
:
[
'up_and_down'
],
'targets'
:
[
's_l[single_draggable][1]'
,
's_r[single_draggable][1]'
,
's_sigma[single_draggable][1]'
,
's_sigma*[single_draggable][1]'
,
'p_pi[double_draggable][1]'
,
'p_pi[double_draggable][2]'
],
'rule'
:
'unordered_equal'
},
]
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_true
(
self
):
def
test_targets_true
(
self
):
user_input
=
'
{"draggables":
[{"1": "t1"},
\
user_input
=
'[{"1": "t1"},
\
{"name_with_icon": "t2"}]
}
'
{"name_with_icon": "t2"}]'
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
}
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_expect_no_actions_wrong
(
self
):
def
test_expect_no_actions_wrong
(
self
):
...
@@ -59,71 +292,63 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -59,71 +292,63 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_targets_false
(
self
):
def
test_targets_false
(
self
):
user_input
=
'
{"draggables":
[{"1": "t1"},
\
user_input
=
'[{"1": "t1"},
\
{"name_with_icon": "t2"}]
}
'
{"name_with_icon": "t2"}]'
correct_answer
=
{
'1'
:
't3'
,
'name_with_icon'
:
't2'
}
correct_answer
=
{
'1'
:
't3'
,
'name_with_icon'
:
't2'
}
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_multiple_images_per_target_true
(
self
):
def
test_multiple_images_per_target_true
(
self
):
user_input
=
'{
\
user_input
=
'[{"1": "t1"}, {"name_with_icon": "t2"},
\
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"},
\
{"2": "t1"}]'
{"2": "t1"}]}'
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
,
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
,
'2'
:
't1'
}
'2'
:
't1'
}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_multiple_images_per_target_false
(
self
):
def
test_multiple_images_per_target_false
(
self
):
user_input
=
'{
\
user_input
=
'[{"1": "t1"}, {"name_with_icon": "t2"},
\
"draggables": [{"1": "t1"}, {"name_with_icon": "t2"},
\
{"2": "t1"}]'
{"2": "t1"}]}'
correct_answer
=
{
'1'
:
't2'
,
'name_with_icon'
:
't2'
,
correct_answer
=
{
'1'
:
't2'
,
'name_with_icon'
:
't2'
,
'2'
:
't1'
}
'2'
:
't1'
}
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_targets_and_positions
(
self
):
def
test_targets_and_positions
(
self
):
user_input
=
'
{"draggables":
[{"1": [10,10]},
\
user_input
=
'[{"1": [10,10]},
\
{"name_with_icon": [[10,10],4]}]
}
'
{"name_with_icon": [[10,10],4]}]'
correct_answer
=
{
'1'
:
[
10
,
10
],
'name_with_icon'
:
[[
10
,
10
],
4
]}
correct_answer
=
{
'1'
:
[
10
,
10
],
'name_with_icon'
:
[[
10
,
10
],
4
]}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_position_and_targets
(
self
):
def
test_position_and_targets
(
self
):
user_input
=
'
{"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}
'
user_input
=
'
[{"1": "t1"}, {"name_with_icon": "t2"}]
'
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
}
correct_answer
=
{
'1'
:
't1'
,
'name_with_icon'
:
't2'
}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_positions_exact
(
self
):
def
test_positions_exact
(
self
):
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer
=
{
'1'
:
[
10
,
10
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[
10
,
10
],
'name_with_icon'
:
[
20
,
20
]}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_positions_false
(
self
):
def
test_positions_false
(
self
):
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer
=
{
'1'
:
[
25
,
25
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[
25
,
25
],
'name_with_icon'
:
[
20
,
20
]}
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_positions_true_in_radius
(
self
):
def
test_positions_true_in_radius
(
self
):
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer
=
{
'1'
:
[
14
,
14
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[
14
,
14
],
'name_with_icon'
:
[
20
,
20
]}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_positions_true_in_manual_radius
(
self
):
def
test_positions_true_in_manual_radius
(
self
):
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer
=
{
'1'
:
[[
40
,
10
],
30
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[[
40
,
10
],
30
],
'name_with_icon'
:
[
20
,
20
]}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_positions_false_in_manual_radius
(
self
):
def
test_positions_false_in_manual_radius
(
self
):
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
correct_answer
=
{
'1'
:
[[
40
,
10
],
29
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[[
40
,
10
],
29
],
'name_with_icon'
:
[
20
,
20
]}
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_correct_answer_not_has_key_from_user_answer
(
self
):
def
test_correct_answer_not_has_key_from_user_answer
(
self
):
user_input
=
'{"draggables": [{"1": "t1"},
\
user_input
=
'[{"1": "t1"}, {"name_with_icon": "t2"}]'
{"name_with_icon": "t2"}]}'
correct_answer
=
{
'3'
:
't3'
,
'name_with_icon'
:
't2'
}
correct_answer
=
{
'3'
:
't3'
,
'name_with_icon'
:
't2'
}
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
...
@@ -131,20 +356,20 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -131,20 +356,20 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
"""Draggables can be places anywhere on base image.
"""Draggables can be places anywhere on base image.
Place grass in the middle of the image and ant in the
Place grass in the middle of the image and ant in the
right upper corner."""
right upper corner."""
user_input
=
'
{"draggables":
\
user_input
=
'
[{"ant":[610.5,57.449951171875]},
\
[{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}
'
{"grass":[322.5,199.449951171875]}]
'
correct_answer
=
{
'grass'
:
[[
300
,
200
],
200
],
'ant'
:
[[
500
,
0
],
200
]}
correct_answer
=
{
'grass'
:
[[
300
,
200
],
200
],
'ant'
:
[[
500
,
0
],
200
]}
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertTrue
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_lcao_correct
(
self
):
def
test_lcao_correct
(
self
):
"""Describe carbon molecule in LCAO-MO"""
"""Describe carbon molecule in LCAO-MO"""
user_input
=
'
{"draggables":
[{"1":"s_left"},
\
user_input
=
'[{"1":"s_left"},
\
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"},
\
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"},
\
{"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"},
\
{"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"},
\
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"},
\
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"},
\
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"},
\
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"},
\
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]
}
'
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer
=
[{
correct_answer
=
[{
'draggables'
:
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
],
'draggables'
:
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
],
...
@@ -178,12 +403,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -178,12 +403,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_lcao_extra_element_incorrect
(
self
):
def
test_lcao_extra_element_incorrect
(
self
):
"""Describe carbon molecule in LCAO-MO"""
"""Describe carbon molecule in LCAO-MO"""
user_input
=
'
{"draggables":
[{"1":"s_left"},
\
user_input
=
'[{"1":"s_left"},
\
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"},
\
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"},
\
{"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"},
\
{"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"},
\
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"},
\
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"},
\
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"},
\
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"},
\
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]
}
'
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer
=
[{
correct_answer
=
[{
'draggables'
:
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
],
'draggables'
:
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
],
...
@@ -217,9 +442,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -217,9 +442,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_reuse_draggable_no_mupliples
(
self
):
def
test_reuse_draggable_no_mupliples
(
self
):
"""Test reusable draggables (no mupltiple draggables per target)"""
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input
=
'
{"draggables":
[{"1":"target1"},
\
user_input
=
'[{"1":"target1"},
\
{"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"},
\
{"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"},
\
{"3":"target6"}]
}
'
{"3":"target6"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'1'
],
'draggables'
:
[
'1'
],
...
@@ -240,9 +465,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -240,9 +465,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_reuse_draggable_with_mupliples
(
self
):
def
test_reuse_draggable_with_mupliples
(
self
):
"""Test reusable draggables with mupltiple draggables per target"""
"""Test reusable draggables with mupltiple draggables per target"""
user_input
=
'
{"draggables":
[{"1":"target1"},
\
user_input
=
'[{"1":"target1"},
\
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"},
\
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"},
\
{"3":"target6"}]
}
'
{"3":"target6"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'1'
],
'draggables'
:
[
'1'
],
...
@@ -263,10 +488,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -263,10 +488,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_reuse_many_draggable_with_mupliples
(
self
):
def
test_reuse_many_draggable_with_mupliples
(
self
):
"""Test reusable draggables with mupltiple draggables per target"""
"""Test reusable draggables with mupltiple draggables per target"""
user_input
=
'
{"draggables":
[{"1":"target1"},
\
user_input
=
'[{"1":"target1"},
\
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"},
\
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"},
\
{"3":"target6"}, {"4": "target3"}, {"5": "target4"},
\
{"3":"target6"}, {"4": "target3"}, {"5": "target4"},
\
{"5": "target5"}, {"6": "target2"}]
}
'
{"5": "target5"}, {"6": "target2"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'1'
,
'4'
],
'draggables'
:
[
'1'
,
'4'
],
...
@@ -292,12 +517,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -292,12 +517,12 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_reuse_many_draggable_with_mupliples_wrong
(
self
):
def
test_reuse_many_draggable_with_mupliples_wrong
(
self
):
"""Test reusable draggables with mupltiple draggables per target"""
"""Test reusable draggables with mupltiple draggables per target"""
user_input
=
'
{"draggables":
[{"1":"target1"},
\
user_input
=
'[{"1":"target1"},
\
{"2":"target2"},{"1":"target1"},
\
{"2":"target2"},{"1":"target1"},
\
{"2":"target3"},
\
{"2":"target3"},
\
{"2":"target4"},
\
{"2":"target4"},
\
{"3":"target6"}, {"4": "target3"}, {"5": "target4"},
\
{"3":"target6"}, {"4": "target3"}, {"5": "target4"},
\
{"5": "target5"}, {"6": "target2"}]
}
'
{"5": "target5"}, {"6": "target2"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'1'
,
'4'
],
'draggables'
:
[
'1'
,
'4'
],
...
@@ -323,10 +548,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -323,10 +548,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_false
(
self
):
def
test_label_10_targets_with_a_b_c_false
(
self
):
"""Test reusable draggables (no mupltiple draggables per target)"""
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"a":"target1"}]
}
'
{"a":"target1"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
],
'draggables'
:
[
'a'
],
...
@@ -347,10 +572,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -347,10 +572,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_
(
self
):
def
test_label_10_targets_with_a_b_c_
(
self
):
"""Test reusable draggables (no mupltiple draggables per target)"""
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"a":"target10"}]
}
'
{"a":"target10"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
],
'draggables'
:
[
'a'
],
...
@@ -371,10 +596,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -371,10 +596,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_multiple
(
self
):
def
test_label_10_targets_with_a_b_c_multiple
(
self
):
"""Test reusable draggables (mupltiple draggables per target)"""
"""Test reusable draggables (mupltiple draggables per target)"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"a":"target1"}]
}
'
{"a":"target1"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
,
'a'
],
'draggables'
:
[
'a'
,
'a'
,
'a'
],
...
@@ -395,10 +620,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -395,10 +620,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_multiple_false
(
self
):
def
test_label_10_targets_with_a_b_c_multiple_false
(
self
):
"""Test reusable draggables (mupltiple draggables per target)"""
"""Test reusable draggables (mupltiple draggables per target)"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"},
\
{"a":"target1"}]
}
'
{"a":"target1"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
,
'a'
],
'draggables'
:
[
'a'
,
'a'
,
'a'
],
...
@@ -419,10 +644,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -419,10 +644,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_reused
(
self
):
def
test_label_10_targets_with_a_b_c_reused
(
self
):
"""Test a b c in 10 labels reused"""
"""Test a b c in 10 labels reused"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"},
\
{"c":"target6"}, {"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"b":"target8"},{"c":"target9"},
\
{"a":"target10"}]
}
'
{"a":"target10"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
],
'draggables'
:
[
'a'
,
'a'
],
...
@@ -443,10 +668,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -443,10 +668,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_label_10_targets_with_a_b_c_reused_false
(
self
):
def
test_label_10_targets_with_a_b_c_reused_false
(
self
):
"""Test a b c in 10 labels reused false"""
"""Test a b c in 10 labels reused false"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},
\
{"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},
\
{"c":"target6"}, {"b":"target8"},{"c":"target9"},
\
{"c":"target6"}, {"b":"target8"},{"c":"target9"},
\
{"a":"target10"}]
}
'
{"a":"target10"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
],
'draggables'
:
[
'a'
,
'a'
],
...
@@ -467,9 +692,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -467,9 +692,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_mixed_reuse_and_not_reuse
(
self
):
def
test_mixed_reuse_and_not_reuse
(
self
):
"""Test reusable draggables """
"""Test reusable draggables """
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"}, {"a":"target4"},
\
{"b":"target2"},{"c":"target3"}, {"a":"target4"},
\
{"a":"target5"}]
}
'
{"a":"target5"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'b'
],
'draggables'
:
[
'a'
,
'b'
],
...
@@ -485,8 +710,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -485,8 +710,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_mixed_reuse_and_not_reuse_number
(
self
):
def
test_mixed_reuse_and_not_reuse_number
(
self
):
"""Test reusable draggables with number """
"""Test reusable draggables with number """
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]
}
'
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
,
'b'
],
'draggables'
:
[
'a'
,
'a'
,
'b'
],
...
@@ -502,8 +727,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -502,8 +727,8 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
def
test_mixed_reuse_and_not_reuse_number_false
(
self
):
def
test_mixed_reuse_and_not_reuse_number_false
(
self
):
"""Test reusable draggables with numbers, but wrong"""
"""Test reusable draggables with numbers, but wrong"""
user_input
=
'
{"draggables":
[{"a":"target1"},
\
user_input
=
'[{"a":"target1"},
\
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]
}
'
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]'
correct_answer
=
[
correct_answer
=
[
{
{
'draggables'
:
[
'a'
,
'a'
,
'b'
],
'draggables'
:
[
'a'
,
'a'
,
'b'
],
...
@@ -518,9 +743,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
...
@@ -518,9 +743,9 @@ class Test_DragAndDrop_Grade(unittest.TestCase):
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
self
.
assertFalse
(
draganddrop
.
grade
(
user_input
,
correct_answer
))
def
test_alternative_correct_answer
(
self
):
def
test_alternative_correct_answer
(
self
):
user_input
=
'
{"draggables":
[{"name_with_icon":"t1"},
\
user_input
=
'[{"name_with_icon":"t1"},
\
{"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"},
\
{"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"},
\
{"name4":"t1"}]
}
'
{"name4":"t1"}]'
correct_answer
=
[
correct_answer
=
[
{
'draggables'
:
[
'name4'
],
'targets'
:
[
't1'
,
't1'
],
'rule'
:
'exact'
},
{
'draggables'
:
[
'name4'
],
'targets'
:
[
't1'
,
't1'
],
'rule'
:
'exact'
},
{
'draggables'
:
[
'name_with_icon'
],
'targets'
:
[
't1'
,
't1'
,
't1'
],
{
'draggables'
:
[
'name_with_icon'
],
'targets'
:
[
't1'
,
't1'
,
't1'
],
...
@@ -533,14 +758,13 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
...
@@ -533,14 +758,13 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
def
test_1
(
self
):
def
test_1
(
self
):
correct_answer
=
{
'1'
:
[[
40
,
10
],
29
],
'name_with_icon'
:
[
20
,
20
]}
correct_answer
=
{
'1'
:
[[
40
,
10
],
29
],
'name_with_icon'
:
[
20
,
20
]}
user_input
=
'{"draggables":
\
user_input
=
'[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
dnd
=
draganddrop
.
DragAndDrop
(
correct_answer
,
user_input
)
dnd
=
draganddrop
.
DragAndDrop
(
correct_answer
,
user_input
)
correct_groups
=
{
'1'
:
[
'name_with_icon'
],
'0'
:
[
'1'
]}
correct_groups
=
[[
'1'
],
[
'name_with_icon'
]]
correct_positions
=
{
'1'
:
{
'exact'
:
[[
20
,
20
]]},
'0'
:
{
'exact'
:
[[[
40
,
10
],
29
]]}}
correct_positions
=
[{
'exact'
:
[[[
40
,
10
],
29
]]},
{
'exact'
:
[[
20
,
20
]]}]
user_groups
=
{
'1'
:
[
u'name_with_icon'
],
'0'
:
[
u'1'
]}
user_groups
=
[[
'1'
],
[
'name_with_icon'
]]
user_positions
=
{
'1'
:
{
'user'
:
[[
20
,
20
]]},
'0'
:
{
'user'
:
[[
10
,
10
]]}}
user_positions
=
[{
'user'
:
[[
10
,
10
]]},
{
'user'
:
[[
20
,
20
]]}]
self
.
assertEqual
(
correct_groups
,
dnd
.
correct_groups
)
self
.
assertEqual
(
correct_groups
,
dnd
.
correct_groups
)
self
.
assertEqual
(
correct_positions
,
dnd
.
correct_positions
)
self
.
assertEqual
(
correct_positions
,
dnd
.
correct_positions
)
...
@@ -551,49 +775,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
...
@@ -551,49 +775,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase):
class
Test_DraAndDrop_Compare_Positions
(
unittest
.
TestCase
):
class
Test_DraAndDrop_Compare_Positions
(
unittest
.
TestCase
):
def
test_1
(
self
):
def
test_1
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
user
=
[[
2
,
3
],
[
1
,
1
]],
user
=
[[
2
,
3
],
[
1
,
1
]],
flag
=
'anyof'
))
flag
=
'anyof'
))
def
test_2a
(
self
):
def
test_2a
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
user
=
[[
2
,
3
],
[
1
,
1
]],
user
=
[[
2
,
3
],
[
1
,
1
]],
flag
=
'exact'
))
flag
=
'exact'
))
def
test_2b
(
self
):
def
test_2b
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[[
1
,
1
],
[
2
,
3
]],
user
=
[[
2
,
13
],
[
1
,
1
]],
user
=
[[
2
,
13
],
[
1
,
1
]],
flag
=
'exact'
))
flag
=
'exact'
))
def
test_3
(
self
):
def
test_3
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
],
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
],
user
=
[
"a"
,
"b"
,
"c"
],
user
=
[
"a"
,
"b"
,
"c"
],
flag
=
'anyof'
))
flag
=
'anyof'
))
def
test_4
(
self
):
def
test_4
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
user
=
[
"a"
,
"b"
],
user
=
[
"a"
,
"b"
],
flag
=
'anyof'
))
flag
=
'anyof'
))
def
test_5
(
self
):
def
test_5
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
user
=
[
"a"
,
"c"
,
"b"
],
user
=
[
"a"
,
"c"
,
"b"
],
flag
=
'exact'
))
flag
=
'exact'
))
def
test_6
(
self
):
def
test_6
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
self
.
assertTrue
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"c"
],
user
=
[
"a"
,
"c"
,
"b"
],
user
=
[
"a"
,
"c"
,
"b"
],
flag
=
'anyof'
))
flag
=
'anyof'
))
def
test_7
(
self
):
def
test_7
(
self
):
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
{"draggables": [{"1": "t1"}]}
'
)
dnd
=
draganddrop
.
DragAndDrop
({
'1'
:
't1'
},
'
[{"1": "t1"}]
'
)
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"b"
],
self
.
assertFalse
(
dnd
.
compare_positions
(
correct
=
[
"a"
,
"b"
,
"b"
],
user
=
[
"a"
,
"c"
,
"b"
],
user
=
[
"a"
,
"c"
,
"b"
],
flag
=
'anyof'
))
flag
=
'anyof'
))
...
...
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
BaseImage
;
return
BaseImage
;
...
@@ -50,10 +45,5 @@ define(['logme'], function (logme) {
...
@@ -50,10 +45,5 @@ define(['logme'], function (logme) {
baseImageElContainer
.
appendTo
(
state
.
containerEl
);
baseImageElContainer
.
appendTo
(
state
.
containerEl
);
});
});
}
}
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
configParser
;
return
configParser
;
...
@@ -16,7 +11,7 @@ define(['logme'], function (logme) {
...
@@ -16,7 +11,7 @@ define(['logme'], function (logme) {
'targetOutline'
:
true
,
'targetOutline'
:
true
,
'labelBgColor'
:
'#d6d6d6'
,
'labelBgColor'
:
'#d6d6d6'
,
'individualTargets'
:
null
,
// Depends on 'targets'.
'individualTargets'
:
null
,
// Depends on 'targets'.
'
errors'
:
0
// Number of errors found while parsing
config.
'
foundErrors'
:
false
// Whether or not we find errors while processing the
config.
};
};
getDraggables
(
state
,
config
);
getDraggables
(
state
,
config
);
...
@@ -28,7 +23,7 @@ define(['logme'], function (logme) {
...
@@ -28,7 +23,7 @@ define(['logme'], function (logme) {
setIndividualTargets
(
state
);
setIndividualTargets
(
state
);
if
(
state
.
config
.
errors
!==
0
)
{
if
(
state
.
config
.
foundErrors
!==
false
)
{
return
false
;
return
false
;
}
}
...
@@ -38,35 +33,34 @@ define(['logme'], function (logme) {
...
@@ -38,35 +33,34 @@ define(['logme'], function (logme) {
function
getDraggables
(
state
,
config
)
{
function
getDraggables
(
state
,
config
)
{
if
(
config
.
hasOwnProperty
(
'draggables'
)
===
false
)
{
if
(
config
.
hasOwnProperty
(
'draggables'
)
===
false
)
{
logme
(
'ERROR: "config" does not have a property "draggables".'
);
logme
(
'ERROR: "config" does not have a property "draggables".'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
else
if
(
$
.
isArray
(
config
.
draggables
)
===
true
)
{
}
else
if
(
$
.
isArray
(
config
.
draggables
)
===
true
)
{
(
function
(
i
)
{
config
.
draggables
.
every
(
function
(
draggable
)
{
while
(
i
<
config
.
draggables
.
length
)
{
if
(
processDraggable
(
state
,
draggable
)
!==
true
)
{
if
(
processDraggable
(
state
,
config
.
draggables
[
i
])
!==
true
)
{
state
.
config
.
foundErrors
=
true
;
state
.
config
.
errors
+=
1
;
}
// Exit immediately from .every() call.
i
+=
1
;
return
false
;
}
}
}(
0
));
}
else
if
(
$
.
isPlainObject
(
config
.
draggables
)
===
true
)
{
// Continue to next .every() call.
if
(
processDraggable
(
state
,
config
.
draggables
)
!==
true
)
{
return
true
;
state
.
config
.
errors
+=
1
;
});
}
}
else
{
}
else
{
logme
(
'ERROR: The type of config.draggables is no supported.'
);
logme
(
'ERROR: The type of config.draggables is no supported.'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
}
function
getBaseImage
(
state
,
config
)
{
function
getBaseImage
(
state
,
config
)
{
if
(
config
.
hasOwnProperty
(
'base_image'
)
===
false
)
{
if
(
config
.
hasOwnProperty
(
'base_image'
)
===
false
)
{
logme
(
'ERROR: "config" does not have a property "base_image".'
);
logme
(
'ERROR: "config" does not have a property "base_image".'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
else
if
(
typeof
config
.
base_image
===
'string'
)
{
}
else
if
(
typeof
config
.
base_image
===
'string'
)
{
state
.
config
.
baseImage
=
config
.
base_image
;
state
.
config
.
baseImage
=
config
.
base_image
;
}
else
{
}
else
{
logme
(
'ERROR: Property config.base_image is not of type "string".'
);
logme
(
'ERROR: Property config.base_image is not of type "string".'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
}
...
@@ -77,28 +71,27 @@ define(['logme'], function (logme) {
...
@@ -77,28 +71,27 @@ define(['logme'], function (logme) {
// Draggables can be positioned anywhere on the image, and the server will
// Draggables can be positioned anywhere on the image, and the server will
// get an answer in the form of (x, y) coordinates for each draggable.
// get an answer in the form of (x, y) coordinates for each draggable.
}
else
if
(
$
.
isArray
(
config
.
targets
)
===
true
)
{
}
else
if
(
$
.
isArray
(
config
.
targets
)
===
true
)
{
(
function
(
i
)
{
config
.
targets
.
every
(
function
(
target
)
{
while
(
i
<
config
.
targets
.
length
)
{
if
(
processTarget
(
state
,
target
)
!==
true
)
{
if
(
processTarget
(
state
,
config
.
targets
[
i
])
!==
true
)
{
state
.
config
.
foundErrors
=
true
;
state
.
config
.
errors
+=
1
;
}
// Exit immediately from .every() call.
i
+=
1
;
return
false
;
}
}
}(
0
));
}
else
if
(
$
.
isPlainObject
(
config
.
targets
)
===
true
)
{
// Continue to next .every() call.
if
(
processTarget
(
state
,
config
.
targets
)
!==
true
)
{
return
true
;
state
.
config
.
errors
+=
1
;
});
}
}
else
{
}
else
{
logme
(
'ERROR: Property config.targets is not of a supported type.'
);
logme
(
'ERROR: Property config.targets is not of a supported type.'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
}
function
getOnePerTarget
(
state
,
config
)
{
function
getOnePerTarget
(
state
,
config
)
{
if
(
config
.
hasOwnProperty
(
'one_per_target'
)
===
false
)
{
if
(
config
.
hasOwnProperty
(
'one_per_target'
)
===
false
)
{
logme
(
'ERROR: "config" does not have a property "one_per_target".'
);
logme
(
'ERROR: "config" does not have a property "one_per_target".'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
else
if
(
typeof
config
.
one_per_target
===
'string'
)
{
}
else
if
(
typeof
config
.
one_per_target
===
'string'
)
{
if
(
config
.
one_per_target
.
toLowerCase
()
===
'true'
)
{
if
(
config
.
one_per_target
.
toLowerCase
()
===
'true'
)
{
state
.
config
.
onePerTarget
=
true
;
state
.
config
.
onePerTarget
=
true
;
...
@@ -106,42 +99,45 @@ define(['logme'], function (logme) {
...
@@ -106,42 +99,45 @@ define(['logme'], function (logme) {
state
.
config
.
onePerTarget
=
false
;
state
.
config
.
onePerTarget
=
false
;
}
else
{
}
else
{
logme
(
'ERROR: Property config.one_per_target can either be "true", or "false".'
);
logme
(
'ERROR: Property config.one_per_target can either be "true", or "false".'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
else
{
}
else
{
logme
(
'ERROR: Property config.one_per_target is not of a supported type.'
);
logme
(
'ERROR: Property config.one_per_target is not of a supported type.'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
}
function
getTargetOutline
(
state
,
config
)
{
function
getTargetOutline
(
state
,
config
)
{
if
(
config
.
hasOwnProperty
(
'target_outline'
)
===
false
)
{
// It is possible that no "target_outline" was specified. This is not an error.
// It is possible that no "target_outline" was specified. This is not an error.
// In this case the default value of 'true' (boolean) will be used.
// In this case the default value of 'true' (boolean) will be used.
}
else
if
(
typeof
config
.
target_outline
===
'string'
)
{
if
(
config
.
hasOwnProperty
(
'target_outline'
)
===
true
)
{
if
(
config
.
target_outline
.
toLowerCase
()
===
'true'
)
{
if
(
typeof
config
.
target_outline
===
'string'
)
{
state
.
config
.
targetOutline
=
true
;
if
(
config
.
target_outline
.
toLowerCase
()
===
'true'
)
{
}
else
if
(
config
.
target_outline
.
toLowerCase
()
===
'false'
)
{
state
.
config
.
targetOutline
=
true
;
state
.
config
.
targetOutline
=
false
;
}
else
if
(
config
.
target_outline
.
toLowerCase
()
===
'false'
)
{
state
.
config
.
targetOutline
=
false
;
}
else
{
logme
(
'ERROR: Property config.target_outline can either be "true", or "false".'
);
state
.
config
.
foundErrors
=
true
;
}
}
else
{
}
else
{
logme
(
'ERROR: Property config.target_outline
can either be "true", or "false"
.'
);
logme
(
'ERROR: Property config.target_outline
is not of a supported type
.'
);
state
.
config
.
errors
+=
1
;
state
.
config
.
foundErrors
=
true
;
}
}
}
else
{
logme
(
'ERROR: Property config.target_outline is not of a supported type.'
);
state
.
config
.
errors
+=
1
;
}
}
}
}
function
getLabelBgColor
(
state
,
config
)
{
function
getLabelBgColor
(
state
,
config
)
{
if
(
config
.
hasOwnProperty
(
'label_bg_color'
)
===
false
)
{
// It is possible that no "label_bg_color" was specified. This is not an error.
// It is possible that no "label_bg_color" was specified. This is not an error.
// In this case the default value of '#d6d6d6' (string) will be used.
// In this case the default value of '#d6d6d6' (string) will be used.
}
else
if
(
typeof
config
.
label_bg_color
===
'string'
)
{
if
(
config
.
hasOwnProperty
(
'label_bg_color'
)
===
true
)
{
state
.
config
.
labelBgColor
=
config
.
label_bg_color
;
if
(
typeof
config
.
label_bg_color
===
'string'
)
{
}
else
{
state
.
config
.
labelBgColor
=
config
.
label_bg_color
;
logme
(
'ERROR: Property config.label_bg_color is not of a supported type.'
);
}
else
{
returnStatus
=
false
;
logme
(
'ERROR: Property config.label_bg_color is not of a supported type.'
);
}
}
}
}
}
...
@@ -159,17 +155,36 @@ define(['logme'], function (logme) {
...
@@ -159,17 +155,36 @@ define(['logme'], function (logme) {
(
attrIsString
(
obj
,
'icon'
)
===
false
)
||
(
attrIsString
(
obj
,
'icon'
)
===
false
)
||
(
attrIsString
(
obj
,
'label'
)
===
false
)
||
(
attrIsString
(
obj
,
'label'
)
===
false
)
||
(
attrIsBoolean
(
obj
,
'can_reuse'
,
false
)
===
false
)
(
attrIsBoolean
(
obj
,
'can_reuse'
,
false
)
===
false
)
||
(
obj
.
hasOwnProperty
(
'target_fields'
)
===
false
)
)
{
)
{
return
false
;
return
false
;
}
}
// Check that all targets in the 'target_fields' property are proper target objects.
// We will be testing the return value from .every() call (it can be 'true' or 'false').
if
(
obj
.
target_fields
.
every
(
function
(
targetObj
)
{
return
processTarget
(
state
,
targetObj
,
false
);
}
)
===
false
)
{
return
false
;
}
state
.
config
.
draggables
.
push
(
obj
);
state
.
config
.
draggables
.
push
(
obj
);
return
true
;
return
true
;
}
}
function
processTarget
(
state
,
obj
)
{
// We need 'pushToState' parameter in order to simply test an object for the fact that it is a
// proper target (without pushing it to the 'state' object). When
//
// pushToState === false
//
// the object being tested is not going to be pushed to 'state'. The function will onyl return
// 'true' or 'false.
function
processTarget
(
state
,
obj
,
pushToState
)
{
if
(
if
(
(
attrIsString
(
obj
,
'id'
)
===
false
)
||
(
attrIsString
(
obj
,
'id'
)
===
false
)
||
...
@@ -182,7 +197,9 @@ define(['logme'], function (logme) {
...
@@ -182,7 +197,9 @@ define(['logme'], function (logme) {
return
false
;
return
false
;
}
}
state
.
config
.
targets
.
push
(
obj
);
if
(
pushToState
!==
false
)
{
state
.
config
.
targets
.
push
(
obj
);
}
return
true
;
return
true
;
}
}
...
@@ -250,10 +267,5 @@ define(['logme'], function (logme) {
...
@@ -250,10 +267,5 @@ define(['logme'], function (logme) {
return
true
;
return
true
;
}
}
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
Container
;
return
Container
;
...
@@ -21,10 +16,5 @@ define(['logme'], function (logme) {
...
@@ -21,10 +16,5 @@ define(['logme'], function (logme) {
$
(
'#inputtype_'
+
state
.
problemId
).
before
(
state
.
containerEl
);
$
(
'#inputtype_'
+
state
.
problemId
).
before
(
state
.
containerEl
);
}
}
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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) {
This diff is collapsed.
Click to expand it.
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) {
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
,
'draggable_events'
,
'draggable_logic'
],
function
(
logme
,
draggableEvents
,
draggableLogic
)
{
define
([
'logme'
,
'update_input'
],
function
(
logme
,
updateInput
)
{
return
{
return
{
'init'
:
init
'init'
:
init
};
};
function
init
(
state
)
{
function
init
(
state
)
{
(
function
(
c1
)
{
state
.
config
.
draggables
.
every
(
function
(
draggable
)
{
while
(
c1
<
state
.
config
.
draggables
.
length
)
{
processDraggable
(
state
,
draggable
);
processDraggable
(
state
,
state
.
config
.
draggables
[
c1
]);
c1
+=
1
return
true
;
}
});
}(
0
));
}
}
function
makeDraggableCopy
(
callbackFunc
)
{
function
makeDraggableCopy
(
callbackFunc
)
{
...
@@ -34,13 +28,18 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -34,13 +28,18 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj
.
stateDraggablesIndex
=
null
;
// Will be set.
draggableObj
.
stateDraggablesIndex
=
null
;
// Will be set.
draggableObj
.
containerEl
=
null
;
// Not needed, since a copy will never return to a container element.
draggableObj
.
containerEl
=
null
;
// Not needed, since a copy will never return to a container element.
draggableObj
.
iconEl
=
null
;
// Will be created.
draggableObj
.
iconEl
=
null
;
// Will be created.
draggableObj
.
iconImgEl
=
null
;
// Will be created.
draggableObj
.
labelEl
=
null
;
// Will be created.
draggableObj
.
labelEl
=
null
;
// Will be created.
draggableObj
.
targetField
=
[];
// Will be populated.
// Create DOM elements and attach events.
// Create DOM elements and attach events.
if
(
draggableObj
.
originalConfigObj
.
icon
.
length
>
0
)
{
if
(
draggableObj
.
originalConfigObj
.
icon
.
length
>
0
)
{
draggableObj
.
iconEl
=
$
(
'<img />'
);
draggableObj
.
iconEl
.
attr
(
'src'
,
draggableObj
.
originalConfigObj
.
icon
);
draggableObj
.
iconEl
=
$
(
'<div></div>'
);
draggableObj
.
iconEl
.
load
(
function
()
{
draggableObj
.
iconImgEl
=
$
(
'<img />'
);
draggableObj
.
iconImgEl
.
attr
(
'src'
,
draggableObj
.
originalConfigObj
.
icon
);
draggableObj
.
iconImgEl
.
load
(
function
()
{
draggableObj
.
iconEl
.
css
({
draggableObj
.
iconEl
.
css
({
'position'
:
'absolute'
,
'position'
:
'absolute'
,
'width'
:
draggableObj
.
iconWidthSmall
,
'width'
:
draggableObj
.
iconWidthSmall
,
...
@@ -48,6 +47,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -48,6 +47,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left'
:
50
-
draggableObj
.
iconWidthSmall
*
0.5
,
'left'
:
50
-
draggableObj
.
iconWidthSmall
*
0.5
,
'top'
:
((
draggableObj
.
originalConfigObj
.
label
.
length
>
0
)
?
5
:
50
-
draggableObj
.
iconHeightSmall
*
0.5
)
'top'
:
((
draggableObj
.
originalConfigObj
.
label
.
length
>
0
)
?
5
:
50
-
draggableObj
.
iconHeightSmall
*
0.5
)
});
});
draggableObj
.
iconImgEl
.
css
({
'position'
:
'absolute'
,
'width'
:
draggableObj
.
iconWidthSmall
,
'height'
:
draggableObj
.
iconHeightSmall
,
'left'
:
0
,
'top'
:
0
});
draggableObj
.
iconImgEl
.
appendTo
(
draggableObj
.
iconEl
);
if
(
draggableObj
.
originalConfigObj
.
label
.
length
>
0
)
{
if
(
draggableObj
.
originalConfigObj
.
label
.
length
>
0
)
{
draggableObj
.
labelEl
=
$
(
draggableObj
.
labelEl
=
$
(
...
@@ -71,7 +78,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -71,7 +78,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj
.
attachMouseEventsTo
(
'iconEl'
);
draggableObj
.
attachMouseEventsTo
(
'iconEl'
);
draggableObj
.
stateDraggablesIndex
=
draggableObj
.
state
.
draggables
.
push
(
draggableObj
);
draggableObj
.
stateDraggablesIndex
=
draggableObj
.
state
.
draggables
.
push
(
draggableObj
)
-
1
;
setTimeout
(
function
()
{
setTimeout
(
function
()
{
callbackFunc
(
draggableObj
);
callbackFunc
(
draggableObj
);
...
@@ -99,7 +106,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -99,7 +106,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj
.
attachMouseEventsTo
(
'iconEl'
);
draggableObj
.
attachMouseEventsTo
(
'iconEl'
);
draggableObj
.
stateDraggablesIndex
=
draggableObj
.
state
.
draggables
.
push
(
draggableObj
);
draggableObj
.
stateDraggablesIndex
=
draggableObj
.
state
.
draggables
.
push
(
draggableObj
)
-
1
;
setTimeout
(
function
()
{
setTimeout
(
function
()
{
callbackFunc
(
draggableObj
);
callbackFunc
(
draggableObj
);
...
@@ -110,115 +117,6 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -110,115 +117,6 @@ define(['logme', 'update_input'], function (logme, updateInput) {
}
}
}
}
function
attachMouseEventsTo
(
element
)
{
var
self
;
self
=
this
;
this
[
element
].
mousedown
(
function
(
event
)
{
self
.
mouseDown
(
event
);
});
this
[
element
].
mouseup
(
function
(
event
)
{
self
.
mouseUp
(
event
);
});
this
[
element
].
mousemove
(
function
(
event
)
{
self
.
mouseMove
(
event
);
});
}
function
moveDraggableTo
(
moveType
,
target
)
{
var
self
,
offset
;
if
(
this
.
hasLoaded
===
false
)
{
self
=
this
;
setTimeout
(
function
()
{
self
.
moveDraggableTo
(
moveType
,
target
);
},
50
);
return
;
}
if
((
this
.
isReusable
===
true
)
&&
(
this
.
isOriginal
===
true
))
{
this
.
makeDraggableCopy
(
function
(
draggableCopy
)
{
draggableCopy
.
moveDraggableTo
(
moveType
,
target
);
});
return
;
}
offset
=
0
;
if
(
this
.
state
.
config
.
targetOutline
===
true
)
{
offset
=
1
;
}
this
.
inContainer
=
false
;
if
(
this
.
isOriginal
===
true
)
{
this
.
containerEl
.
hide
();
this
.
iconEl
.
detach
();
}
this
.
iconEl
.
css
({
'background-color'
:
this
.
iconElBGColor
,
'padding-left'
:
this
.
iconElPadding
,
'padding-right'
:
this
.
iconElPadding
,
'border'
:
this
.
iconElBorder
,
'width'
:
this
.
iconWidth
,
'height'
:
this
.
iconHeight
});
if
(
moveType
===
'target'
)
{
this
.
iconEl
.
css
({
'left'
:
target
.
offset
.
left
+
0.5
*
target
.
w
-
this
.
iconWidth
*
0.5
+
offset
-
this
.
iconElLeftOffset
,
'top'
:
target
.
offset
.
top
+
0.5
*
target
.
h
-
this
.
iconHeight
*
0.5
+
offset
});
}
else
{
this
.
iconEl
.
css
({
'left'
:
target
.
x
-
this
.
iconWidth
*
0.5
+
offset
-
this
.
iconElLeftOffset
,
'top'
:
target
.
y
-
this
.
iconHeight
*
0.5
+
offset
});
}
this
.
iconEl
.
appendTo
(
this
.
state
.
baseImageEl
.
parent
());
if
(
this
.
labelEl
!==
null
)
{
if
(
this
.
isOriginal
===
true
)
{
this
.
labelEl
.
detach
();
}
this
.
labelEl
.
css
({
'background-color'
:
this
.
state
.
config
.
labelBgColor
,
'padding-left'
:
8
,
'padding-right'
:
8
,
'border'
:
'1px solid black'
});
if
(
moveType
===
'target'
)
{
this
.
labelEl
.
css
({
'left'
:
target
.
offset
.
left
+
0.5
*
target
.
w
-
this
.
labelWidth
*
0.5
+
offset
-
9
,
// Account for padding, border.
'top'
:
target
.
offset
.
top
+
0.5
*
target
.
h
+
this
.
iconHeight
*
0.5
+
5
+
offset
});
}
else
{
this
.
labelEl
.
css
({
'left'
:
target
.
x
-
this
.
labelWidth
*
0.5
+
offset
-
9
,
// Account for padding, border.
'top'
:
target
.
y
-
this
.
iconHeight
*
0.5
+
this
.
iconHeight
+
5
+
offset
});
}
this
.
labelEl
.
appendTo
(
this
.
state
.
baseImageEl
.
parent
());
}
if
(
moveType
===
'target'
)
{
target
.
addDraggable
(
this
);
}
else
{
this
.
x
=
target
.
x
;
this
.
y
=
target
.
y
;
}
this
.
zIndex
=
1000
;
this
.
correctZIndexes
();
if
(
this
.
isOriginal
===
true
)
{
this
.
state
.
numDraggablesInSlider
-=
1
;
this
.
state
.
updateArrowOpacity
();
}
}
function
processDraggable
(
state
,
obj
)
{
function
processDraggable
(
state
,
obj
)
{
var
draggableObj
;
var
draggableObj
;
...
@@ -234,6 +132,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -234,6 +132,7 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'zIndex'
:
1
,
'zIndex'
:
1
,
'containerEl'
:
null
,
'containerEl'
:
null
,
'iconEl'
:
null
,
'iconEl'
:
null
,
'iconImgEl'
:
null
,
'iconElBGColor'
:
null
,
'iconElBGColor'
:
null
,
'iconElPadding'
:
null
,
'iconElPadding'
:
null
,
'iconElBorder'
:
null
,
'iconElBorder'
:
null
,
...
@@ -251,17 +150,23 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -251,17 +150,23 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'onTargetIndex'
:
null
,
'onTargetIndex'
:
null
,
'state'
:
state
,
'state'
:
state
,
'mouseDown'
:
mouseDown
,
'mouseDown'
:
draggableEvents
.
mouseDown
,
'mouseUp'
:
mouseUp
,
'mouseUp'
:
draggableEvents
.
mouseUp
,
'mouseMove'
:
mouseMove
,
'mouseMove'
:
draggableEvents
.
mouseMove
,
'checkLandingElement'
:
checkLandingElement
,
'checkIfOnTarget'
:
checkIfOnTarget
,
'checkLandingElement'
:
draggableLogic
.
checkLandingElement
,
'snapToTarget'
:
snapToTarget
,
'checkIfOnTarget'
:
draggableLogic
.
checkIfOnTarget
,
'correctZIndexes'
:
correctZIndexes
,
'snapToTarget'
:
draggableLogic
.
snapToTarget
,
'moveBackToSlider'
:
moveBackToSlider
,
'correctZIndexes'
:
draggableLogic
.
correctZIndexes
,
'moveDraggableTo'
:
moveDraggableTo
,
'moveBackToSlider'
:
draggableLogic
.
moveBackToSlider
,
'moveDraggableTo'
:
draggableLogic
.
moveDraggableTo
,
'makeDraggableCopy'
:
makeDraggableCopy
,
'makeDraggableCopy'
:
makeDraggableCopy
,
'attachMouseEventsTo'
:
attachMouseEventsTo
'attachMouseEventsTo'
:
draggableEvents
.
attachMouseEventsTo
,
'targetField'
:
[],
'numDraggablesOnMe'
:
0
};
};
draggableObj
.
containerEl
=
$
(
draggableObj
.
containerEl
=
$
(
...
@@ -288,9 +193,11 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -288,9 +193,11 @@ define(['logme', 'update_input'], function (logme, updateInput) {
draggableObj
.
iconElBorder
=
'none'
;
draggableObj
.
iconElBorder
=
'none'
;
draggableObj
.
iconElLeftOffset
=
0
;
draggableObj
.
iconElLeftOffset
=
0
;
draggableObj
.
iconEl
=
$
(
'<img />'
);
draggableObj
.
iconEl
=
$
(
'<div></div>'
);
draggableObj
.
iconEl
.
attr
(
'src'
,
obj
.
icon
);
draggableObj
.
iconEl
.
load
(
function
()
{
draggableObj
.
iconImgEl
=
$
(
'<img />'
);
draggableObj
.
iconImgEl
.
attr
(
'src'
,
obj
.
icon
);
draggableObj
.
iconImgEl
.
load
(
function
()
{
draggableObj
.
iconWidth
=
this
.
width
;
draggableObj
.
iconWidth
=
this
.
width
;
draggableObj
.
iconHeight
=
this
.
height
;
draggableObj
.
iconHeight
=
this
.
height
;
...
@@ -309,6 +216,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -309,6 +216,14 @@ define(['logme', 'update_input'], function (logme, updateInput) {
'left'
:
50
-
draggableObj
.
iconWidthSmall
*
0.5
,
'left'
:
50
-
draggableObj
.
iconWidthSmall
*
0.5
,
'top'
:
((
obj
.
label
.
length
>
0
)
?
5
:
50
-
draggableObj
.
iconHeightSmall
*
0.5
)
'top'
:
((
obj
.
label
.
length
>
0
)
?
5
:
50
-
draggableObj
.
iconHeightSmall
*
0.5
)
});
});
draggableObj
.
iconImgEl
.
css
({
'position'
:
'absolute'
,
'width'
:
draggableObj
.
iconWidthSmall
,
'height'
:
draggableObj
.
iconHeightSmall
,
'left'
:
0
,
'top'
:
0
});
draggableObj
.
iconImgEl
.
appendTo
(
draggableObj
.
iconEl
);
draggableObj
.
iconEl
.
appendTo
(
draggableObj
.
containerEl
);
draggableObj
.
iconEl
.
appendTo
(
draggableObj
.
containerEl
);
if
(
obj
.
label
.
length
>
0
)
{
if
(
obj
.
label
.
length
>
0
)
{
...
@@ -384,357 +299,5 @@ define(['logme', 'update_input'], function (logme, updateInput) {
...
@@ -384,357 +299,5 @@ define(['logme', 'update_input'], function (logme, updateInput) {
state
.
numDraggablesInSlider
+=
1
;
state
.
numDraggablesInSlider
+=
1
;
draggableObj
.
stateDraggablesIndex
=
state
.
draggables
.
push
(
draggableObj
)
-
1
;
draggableObj
.
stateDraggablesIndex
=
state
.
draggables
.
push
(
draggableObj
)
-
1
;
}
}
});
// End-of: define(['logme', 'draggable_events', 'draggable_logic'], function (logme, draggableEvents, draggableLogic) {
function
mouseDown
(
event
)
{
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
if
(
this
.
mousePressed
===
false
)
{
// So that the browser does not perform a default drag.
// If we don't do this, each drag operation will
// potentially cause the highlghting of the dragged element.
event
.
preventDefault
();
event
.
stopPropagation
();
// If this draggable is just being dragged out of the
// container, we must perform some additional tasks.
if
(
this
.
inContainer
===
true
)
{
if
((
this
.
isReusable
===
true
)
&&
(
this
.
isOriginal
===
true
))
{
this
.
makeDraggableCopy
(
function
(
draggableCopy
)
{
draggableCopy
.
mouseDown
(
event
);
});
return
;
}
if
(
this
.
isOriginal
===
true
)
{
this
.
containerEl
.
hide
();
this
.
iconEl
.
detach
();
}
this
.
iconEl
.
css
({
'background-color'
:
this
.
iconElBGColor
,
'padding-left'
:
this
.
iconElPadding
,
'padding-right'
:
this
.
iconElPadding
,
'border'
:
this
.
iconElBorder
,
'width'
:
this
.
iconWidth
,
'height'
:
this
.
iconHeight
,
'left'
:
event
.
pageX
-
this
.
state
.
baseImageEl
.
offset
().
left
-
this
.
iconWidth
*
0.5
-
this
.
iconElLeftOffset
,
'top'
:
event
.
pageY
-
this
.
state
.
baseImageEl
.
offset
().
top
-
this
.
iconHeight
*
0.5
});
this
.
iconEl
.
appendTo
(
this
.
state
.
baseImageEl
.
parent
());
if
(
this
.
labelEl
!==
null
)
{
if
(
this
.
isOriginal
===
true
)
{
this
.
labelEl
.
detach
();
}
this
.
labelEl
.
css
({
'background-color'
:
this
.
state
.
config
.
labelBgColor
,
'padding-left'
:
8
,
'padding-right'
:
8
,
'border'
:
'1px solid black'
,
'left'
:
event
.
pageX
-
this
.
state
.
baseImageEl
.
offset
().
left
-
this
.
labelWidth
*
0.5
-
9
,
// Account for padding, border.
'top'
:
event
.
pageY
-
this
.
state
.
baseImageEl
.
offset
().
top
+
this
.
iconHeight
*
0.5
+
5
});
this
.
labelEl
.
appendTo
(
this
.
state
.
baseImageEl
.
parent
());
}
this
.
inContainer
=
false
;
if
(
this
.
isOriginal
===
true
)
{
this
.
state
.
numDraggablesInSlider
-=
1
;
}
}
this
.
zIndex
=
1000
;
this
.
iconEl
.
css
(
'z-index'
,
'1000'
);
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
css
(
'z-index'
,
'1000'
);
}
this
.
mousePressed
=
true
;
this
.
state
.
currentMovingDraggable
=
this
;
}
}
function
mouseUp
()
{
if
(
this
.
mousePressed
===
true
)
{
this
.
state
.
currentMovingDraggable
=
null
;
this
.
checkLandingElement
();
}
}
function
mouseMove
(
event
)
{
if
(
this
.
mousePressed
===
true
)
{
// Because we have also attached a 'mousemove' event to the
// 'document' (that will do the same thing), let's tell the
// browser not to bubble up this event. The attached event
// on the 'document' will only be triggered when the mouse
// pointer leaves the draggable while it is in the middle
// of a drag operation (user moves the mouse very quickly).
event
.
stopPropagation
();
this
.
iconEl
.
css
({
'left'
:
event
.
pageX
-
this
.
state
.
baseImageEl
.
offset
().
left
-
this
.
iconWidth
*
0.5
-
this
.
iconElLeftOffset
,
'top'
:
event
.
pageY
-
this
.
state
.
baseImageEl
.
offset
().
top
-
this
.
iconHeight
*
0.5
});
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
css
({
'left'
:
event
.
pageX
-
this
.
state
.
baseImageEl
.
offset
().
left
-
this
.
labelWidth
*
0.5
-
9
,
// Acoount for padding, border.
'top'
:
event
.
pageY
-
this
.
state
.
baseImageEl
.
offset
().
top
+
this
.
iconHeight
*
0.5
+
5
});
}
}
}
// At this point the mouse was realeased, and we need to check
// where the draggable eneded up. Based on several things, we
// will either move the draggable back to the slider, or update
// the input with the user's answer (X-Y position of the draggable,
// or the ID of the target where it landed.
function
checkLandingElement
()
{
var
positionIE
;
this
.
mousePressed
=
false
;
positionIE
=
this
.
iconEl
.
position
();
if
(
this
.
state
.
config
.
individualTargets
===
true
)
{
if
(
this
.
checkIfOnTarget
(
positionIE
)
===
true
)
{
this
.
correctZIndexes
();
}
else
{
if
(
this
.
onTarget
!==
null
)
{
this
.
onTarget
.
removeDraggable
(
this
);
}
this
.
moveBackToSlider
();
if
(
this
.
isOriginal
===
true
)
{
this
.
state
.
numDraggablesInSlider
+=
1
;
}
}
}
else
{
if
(
(
positionIE
.
left
<
0
)
||
(
positionIE
.
left
+
this
.
iconWidth
>
this
.
state
.
baseImageEl
.
width
())
||
(
positionIE
.
top
<
0
)
||
(
positionIE
.
top
+
this
.
iconHeight
>
this
.
state
.
baseImageEl
.
height
())
)
{
this
.
moveBackToSlider
();
this
.
x
=
-
1
;
this
.
y
=
-
1
;
if
(
this
.
isOriginal
===
true
)
{
this
.
state
.
numDraggablesInSlider
+=
1
;
}
}
else
{
this
.
correctZIndexes
();
this
.
x
=
positionIE
.
left
+
this
.
iconWidth
*
0.5
;
this
.
y
=
positionIE
.
top
+
this
.
iconHeight
*
0.5
;
}
}
if
(
this
.
isOriginal
===
true
)
{
this
.
state
.
updateArrowOpacity
();
}
updateInput
.
update
(
this
.
state
);
}
// Determine if a draggable, after it was relased, ends up on a
// target. We do this by iterating over all of the targets, and
// for each one we check whether the draggable's center is
// within the target's dimensions.
//
// positionIE is the object as returned by
//
// this.iconEl.position()
function
checkIfOnTarget
(
positionIE
)
{
var
c1
,
target
;
for
(
c1
=
0
;
c1
<
this
.
state
.
targets
.
length
;
c1
+=
1
)
{
target
=
this
.
state
.
targets
[
c1
];
// If only one draggable per target is allowed, and
// the current target already has a draggable on it
// (with an ID different from the one we are checking
// against), then go to next target.
if
(
(
this
.
state
.
config
.
onePerTarget
===
true
)
&&
(
target
.
draggableList
.
length
===
1
)
&&
(
target
.
draggableList
[
0
].
uniqueId
!==
this
.
uniqueId
)
)
{
continue
;
}
// Check if the draggable's center coordinate is within
// the target's dimensions. If not, go to next target.
if
(
(
positionIE
.
top
+
this
.
iconHeight
*
0.5
<
target
.
offset
.
top
)
||
(
positionIE
.
top
+
this
.
iconHeight
*
0.5
>
target
.
offset
.
top
+
target
.
h
)
||
(
positionIE
.
left
+
this
.
iconWidth
*
0.5
<
target
.
offset
.
left
)
||
(
positionIE
.
left
+
this
.
iconWidth
*
0.5
>
target
.
offset
.
left
+
target
.
w
)
)
{
continue
;
}
// If the draggable was moved from one target to
// another, then we need to remove it from the
// previous target's draggables list, and add it to the
// new target's draggables list.
if
((
this
.
onTarget
!==
null
)
&&
(
this
.
onTarget
.
id
!==
target
.
id
))
{
this
.
onTarget
.
removeDraggable
(
this
);
target
.
addDraggable
(
this
);
}
// If the draggable was moved from the slider to a
// target, remember the target, and add ID to the
// target's draggables list.
else
if
(
this
.
onTarget
===
null
)
{
target
.
addDraggable
(
this
);
}
// Reposition the draggable so that it's center
// coincides with the center of the target.
this
.
snapToTarget
(
target
);
// Target was found.
return
true
;
}
// Target was not found.
return
false
;
}
function
snapToTarget
(
target
)
{
var
offset
;
offset
=
0
;
if
(
this
.
state
.
config
.
targetOutline
===
true
)
{
offset
=
1
;
}
this
.
iconEl
.
css
({
'left'
:
target
.
offset
.
left
+
0.5
*
target
.
w
-
this
.
iconWidth
*
0.5
+
offset
-
this
.
iconElLeftOffset
,
'top'
:
target
.
offset
.
top
+
0.5
*
target
.
h
-
this
.
iconHeight
*
0.5
+
offset
});
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
css
({
'left'
:
target
.
offset
.
left
+
0.5
*
target
.
w
-
this
.
labelWidth
*
0.5
+
offset
-
9
,
// Acoount for padding, border.
'top'
:
target
.
offset
.
top
+
0.5
*
target
.
h
+
this
.
iconHeight
*
0.5
+
5
+
offset
});
}
}
// Go through all of the draggables subtract 1 from the z-index
// of all whose z-index is higher than the old z-index of the
// current element. After, set the z-index of the current
// element to 1 + N (where N is the number of draggables - i.e.
// the highest z-index possible).
//
// This will make sure that after releasing a draggable, it
// will be on top of all of the other draggables. Also, the
// ordering of the visibility (z-index) of the other draggables
// will not change.
function
correctZIndexes
()
{
var
c1
,
highestZIndex
;
highestZIndex
=
-
10000
;
if
(
this
.
state
.
config
.
individualTargets
===
true
)
{
if
(
this
.
onTarget
.
draggableList
.
length
>
0
)
{
for
(
c1
=
0
;
c1
<
this
.
onTarget
.
draggableList
.
length
;
c1
+=
1
)
{
if
(
(
this
.
onTarget
.
draggableList
[
c1
].
zIndex
>
highestZIndex
)
&&
(
this
.
onTarget
.
draggableList
[
c1
].
zIndex
!==
1000
)
)
{
highestZIndex
=
this
.
onTarget
.
draggableList
[
c1
].
zIndex
;
}
}
}
else
{
highestZIndex
=
0
;
}
}
else
{
for
(
c1
=
0
;
c1
<
this
.
state
.
draggables
.
length
;
c1
++
)
{
if
(
this
.
inContainer
===
false
)
{
if
(
(
this
.
state
.
draggables
[
c1
].
zIndex
>
highestZIndex
)
&&
(
this
.
state
.
draggables
[
c1
].
zIndex
!==
1000
)
)
{
highestZIndex
=
this
.
state
.
draggables
[
c1
].
zIndex
;
}
}
}
}
if
(
highestZIndex
===
-
10000
)
{
highestZIndex
=
0
;
}
this
.
zIndex
=
highestZIndex
+
1
;
this
.
iconEl
.
css
(
'z-index'
,
this
.
zIndex
);
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
css
(
'z-index'
,
this
.
zIndex
);
}
}
// If a draggable was released in a wrong positione, we will
// move it back to the slider, placing it in the same position
// that it was dragged out of.
function
moveBackToSlider
()
{
var
c1
;
if
(
this
.
isOriginal
===
false
)
{
this
.
iconEl
.
remove
();
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
remove
();
}
this
.
state
.
draggables
.
splice
(
this
.
stateDraggablesIndex
,
1
);
for
(
c1
=
0
;
c1
<
this
.
state
.
draggables
;
c1
+=
1
)
{
if
(
this
.
state
.
draggables
[
c1
].
stateDraggablesIndex
>
this
.
stateDraggablesIndex
)
{
this
.
state
.
draggables
[
c1
].
stateDraggablesIndex
-=
1
;
}
}
return
;
}
this
.
containerEl
.
show
();
this
.
zIndex
=
1
;
this
.
iconEl
.
detach
();
this
.
iconEl
.
css
({
'border'
:
'none'
,
'background-color'
:
'transparent'
,
'padding-left'
:
0
,
'padding-right'
:
0
,
'z-index'
:
this
.
zIndex
,
'width'
:
this
.
iconWidthSmall
,
'height'
:
this
.
iconHeightSmall
,
'left'
:
50
-
this
.
iconWidthSmall
*
0.5
,
'top'
:
((
this
.
labelEl
!==
null
)
?
5
:
50
-
this
.
iconHeightSmall
*
0.5
)
});
this
.
iconEl
.
appendTo
(
this
.
containerEl
);
if
(
this
.
labelEl
!==
null
)
{
this
.
labelEl
.
detach
();
this
.
labelEl
.
css
({
'border'
:
'none'
,
'background-color'
:
'transparent'
,
'padding-left'
:
0
,
'padding-right'
:
0
,
'z-index'
:
this
.
zIndex
,
'left'
:
50
-
this
.
labelWidth
*
0.5
,
'top'
:
5
+
this
.
iconHeightSmall
+
5
});
this
.
labelEl
.
appendTo
(
this
.
containerEl
);
}
this
.
inContainer
=
true
;
}
});
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([],
function
()
{
define
([],
function
()
{
var
debugMode
;
var
debugMode
;
...
@@ -27,10 +22,5 @@ define([], function () {
...
@@ -27,10 +22,5 @@ define([], function () {
i
+=
1
;
i
+=
1
;
}
}
}
}
});
});
// End-of: define([], function () {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
(
define
(
[
'logme'
,
'state'
,
'config_parser'
,
'container'
,
'base_image'
,
'scroller'
,
'draggables'
,
'targets'
,
'update_input'
],
[
'logme'
,
'state'
,
'config_parser'
,
'container'
,
'base_image'
,
'scroller'
,
'draggables'
,
'targets'
,
'update_input'
],
function
(
logme
,
State
,
configParser
,
Container
,
BaseImage
,
Scroller
,
Draggables
,
Targets
,
updateInput
)
{
function
(
logme
,
State
,
configParser
,
Container
,
BaseImage
,
Scroller
,
Draggables
,
Targets
,
updateInput
)
{
return
Main
;
return
Main
;
function
Main
()
{
function
Main
()
{
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every
//
// Array.prototype.every is a recent addition to the ECMA-262 standard; as such it may not be present in
// other implementations of the standard.
if
(
!
Array
.
prototype
.
every
)
{
Array
.
prototype
.
every
=
function
(
fun
/*, thisp */
)
{
var
thisp
,
t
,
len
,
i
;
if
(
this
==
null
)
{
throw
new
TypeError
();
}
t
=
Object
(
this
);
len
=
t
.
length
>>>
0
;
if
(
typeof
fun
!=
'function'
)
{
throw
new
TypeError
();
}
thisp
=
arguments
[
1
];
for
(
i
=
0
;
i
<
len
;
i
++
)
{
if
(
i
in
t
&&
!
fun
.
call
(
thisp
,
t
[
i
],
i
,
t
))
{
return
false
;
}
}
return
true
;
};
}
$
(
'.drag_and_drop_problem_div'
).
each
(
processProblem
);
$
(
'.drag_and_drop_problem_div'
).
each
(
processProblem
);
}
}
...
@@ -59,7 +85,7 @@ define(
...
@@ -59,7 +85,7 @@ define(
return
;
return
;
}
}
Targets
(
state
);
Targets
.
initializeBaseTargets
(
state
);
Scroller
(
state
);
Scroller
(
state
);
Draggables
.
init
(
state
);
Draggables
.
init
(
state
);
...
@@ -72,10 +98,5 @@ define(
...
@@ -72,10 +98,5 @@ define(
}
}
}());
}());
}
}
});
});
// End-of: define(
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
Scroller
;
return
Scroller
;
...
@@ -206,10 +201,5 @@ define(['logme'], function (logme) {
...
@@ -206,10 +201,5 @@ define(['logme'], function (logme) {
}
}
}
}
}
// End-of: function Scroller(state)
}
// End-of: function Scroller(state)
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([],
function
()
{
define
([],
function
()
{
return
State
;
return
State
;
...
@@ -96,10 +91,5 @@ define([], function () {
...
@@ -96,10 +91,5 @@ define([], function () {
}
}
}
}
}
}
});
});
// End-of: define([], function () {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
Targets
;
return
{
'initializeBaseTargets'
:
initializeBaseTargets
,
'initializeTargetField'
:
initializeTargetField
,
'destroyTargetField'
:
destroyTargetField
};
function
Targets
(
state
)
{
function
initializeBase
Targets
(
state
)
{
(
function
(
c1
)
{
(
function
(
c1
)
{
while
(
c1
<
state
.
config
.
targets
.
length
)
{
while
(
c1
<
state
.
config
.
targets
.
length
)
{
processTarget
(
state
,
state
.
config
.
targets
[
c1
]);
processTarget
(
state
,
state
.
config
.
targets
[
c1
]);
...
@@ -17,7 +16,58 @@ define(['logme'], function (logme) {
...
@@ -17,7 +16,58 @@ define(['logme'], function (logme) {
}(
0
));
}(
0
));
}
}
function
processTarget
(
state
,
obj
)
{
function
initializeTargetField
(
draggableObj
)
{
var
iconElOffset
;
if
(
draggableObj
.
targetField
.
length
===
0
)
{
draggableObj
.
originalConfigObj
.
target_fields
.
every
(
function
(
targetObj
)
{
processTarget
(
draggableObj
.
state
,
targetObj
,
true
,
draggableObj
);
return
true
;
});
}
else
{
iconElOffset
=
draggableObj
.
iconEl
.
position
();
draggableObj
.
targetField
.
every
(
function
(
targetObj
)
{
targetObj
.
offset
.
top
=
iconElOffset
.
top
+
targetObj
.
y
;
targetObj
.
offset
.
left
=
iconElOffset
.
left
+
targetObj
.
x
;
return
true
;
});
}
}
function
destroyTargetField
(
draggableObj
)
{
var
indexOffset
,
lowestRemovedIndex
;
indexOffset
=
0
;
lowestRemovedIndex
=
draggableObj
.
state
.
targets
.
length
+
1
;
draggableObj
.
targetField
.
every
(
function
(
target
)
{
target
.
el
.
remove
();
if
(
lowestRemovedIndex
>
target
.
indexInStateArray
)
{
lowestRemovedIndex
=
target
.
indexInStateArray
;
}
draggableObj
.
state
.
targets
.
splice
(
target
.
indexInStateArray
-
indexOffset
,
1
);
indexOffset
+=
1
;
return
true
;
});
draggableObj
.
state
.
targets
.
every
(
function
(
target
)
{
if
(
target
.
indexInStateArray
>
lowestRemovedIndex
)
{
target
.
indexInStateArray
-=
indexOffset
;
}
return
true
;
});
draggableObj
.
targetField
=
[];
}
function
processTarget
(
state
,
obj
,
fromTargetField
,
draggableObj
)
{
var
targetEl
,
borderCss
,
numTextEl
,
targetObj
;
var
targetEl
,
borderCss
,
numTextEl
,
targetObj
;
borderCss
=
''
;
borderCss
=
''
;
...
@@ -38,7 +88,13 @@ define(['logme'], function (logme) {
...
@@ -38,7 +88,13 @@ define(['logme'], function (logme) {
'" '
+
'" '
+
'></div>'
'></div>'
);
);
targetEl
.
appendTo
(
state
.
baseImageEl
.
parent
());
if
(
fromTargetField
===
true
)
{
targetEl
.
appendTo
(
draggableObj
.
iconEl
);
}
else
{
targetEl
.
appendTo
(
state
.
baseImageEl
.
parent
());
}
targetEl
.
mousedown
(
function
(
event
)
{
targetEl
.
mousedown
(
function
(
event
)
{
event
.
preventDefault
();
event
.
preventDefault
();
});
});
...
@@ -68,8 +124,13 @@ define(['logme'], function (logme) {
...
@@ -68,8 +124,13 @@ define(['logme'], function (logme) {
}
}
targetObj
=
{
targetObj
=
{
'uniqueId'
:
state
.
getUniqueId
(),
'id'
:
obj
.
id
,
'id'
:
obj
.
id
,
'x'
:
obj
.
x
,
'y'
:
obj
.
y
,
'w'
:
obj
.
w
,
'w'
:
obj
.
w
,
'h'
:
obj
.
h
,
'h'
:
obj
.
h
,
...
@@ -86,9 +147,21 @@ define(['logme'], function (logme) {
...
@@ -86,9 +147,21 @@ define(['logme'], function (logme) {
'updateNumTextEl'
:
updateNumTextEl
,
'updateNumTextEl'
:
updateNumTextEl
,
'removeDraggable'
:
removeDraggable
,
'removeDraggable'
:
removeDraggable
,
'addDraggable'
:
addDraggable
'addDraggable'
:
addDraggable
,
'type'
:
'base'
,
'draggableObj'
:
null
};
};
if
(
fromTargetField
===
true
)
{
targetObj
.
offset
=
draggableObj
.
iconEl
.
position
();
targetObj
.
offset
.
top
+=
obj
.
y
;
targetObj
.
offset
.
left
+=
obj
.
x
;
targetObj
.
type
=
'on_drag'
;
targetObj
.
draggableObj
=
draggableObj
;
}
if
(
state
.
config
.
onePerTarget
===
false
)
{
if
(
state
.
config
.
onePerTarget
===
false
)
{
numTextEl
.
appendTo
(
state
.
baseImageEl
.
parent
());
numTextEl
.
appendTo
(
state
.
baseImageEl
.
parent
());
numTextEl
.
mousedown
(
function
(
event
)
{
numTextEl
.
mousedown
(
function
(
event
)
{
...
@@ -99,7 +172,11 @@ define(['logme'], function (logme) {
...
@@ -99,7 +172,11 @@ define(['logme'], function (logme) {
});
});
}
}
state
.
targets
.
push
(
targetObj
);
targetObj
.
indexInStateArray
=
state
.
targets
.
push
(
targetObj
)
-
1
;
if
(
fromTargetField
===
true
)
{
draggableObj
.
targetField
.
push
(
targetObj
);
}
}
}
function
removeDraggable
(
draggable
)
{
function
removeDraggable
(
draggable
)
{
...
@@ -121,6 +198,10 @@ define(['logme'], function (logme) {
...
@@ -121,6 +198,10 @@ define(['logme'], function (logme) {
draggable
.
onTarget
=
null
;
draggable
.
onTarget
=
null
;
draggable
.
onTargetIndex
=
null
;
draggable
.
onTargetIndex
=
null
;
if
(
this
.
type
===
'on_drag'
)
{
this
.
draggableObj
.
numDraggablesOnMe
-=
1
;
}
this
.
updateNumTextEl
();
this
.
updateNumTextEl
();
}
}
...
@@ -128,6 +209,10 @@ define(['logme'], function (logme) {
...
@@ -128,6 +209,10 @@ define(['logme'], function (logme) {
draggable
.
onTarget
=
this
;
draggable
.
onTarget
=
this
;
draggable
.
onTargetIndex
=
this
.
draggableList
.
push
(
draggable
)
-
1
;
draggable
.
onTargetIndex
=
this
.
draggableList
.
push
(
draggable
)
-
1
;
if
(
this
.
type
===
'on_drag'
)
{
this
.
draggableObj
.
numDraggablesOnMe
+=
1
;
}
this
.
updateNumTextEl
();
this
.
updateNumTextEl
();
}
}
...
@@ -183,10 +268,5 @@ define(['logme'], function (logme) {
...
@@ -183,10 +268,5 @@ define(['logme'], function (logme) {
this
.
numTextEl
.
html
(
this
.
draggableList
.
length
);
this
.
numTextEl
.
html
(
this
.
draggableList
.
length
);
}
}
}
}
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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
)
{
(
function
(
requirejs
,
require
,
define
)
{
define
([
'logme'
],
function
(
logme
)
{
define
([
'logme'
],
function
(
logme
)
{
return
{
return
{
'check'
:
check
,
'check'
:
check
,
...
@@ -37,7 +32,12 @@ define(['logme'], function (logme) {
...
@@ -37,7 +32,12 @@ define(['logme'], function (logme) {
(
function
(
c2
)
{
(
function
(
c2
)
{
while
(
c2
<
state
.
targets
[
c1
].
draggableList
.
length
)
{
while
(
c2
<
state
.
targets
[
c1
].
draggableList
.
length
)
{
tempObj
=
{};
tempObj
=
{};
tempObj
[
state
.
targets
[
c1
].
draggableList
[
c2
].
id
]
=
state
.
targets
[
c1
].
id
;
if
(
state
.
targets
[
c1
].
type
===
'base'
)
{
tempObj
[
state
.
targets
[
c1
].
draggableList
[
c2
].
id
]
=
state
.
targets
[
c1
].
id
;
}
else
{
addTargetRecursively
(
tempObj
,
state
.
targets
[
c1
].
draggableList
[
c2
],
state
.
targets
[
c1
]);
}
draggables
.
push
(
tempObj
);
draggables
.
push
(
tempObj
);
tempObj
=
null
;
tempObj
=
null
;
...
@@ -50,7 +50,18 @@ define(['logme'], function (logme) {
...
@@ -50,7 +50,18 @@ define(['logme'], function (logme) {
}(
0
));
}(
0
));
}
}
$
(
'#input_'
+
state
.
problemId
).
val
(
JSON
.
stringify
({
'draggables'
:
draggables
}));
$
(
'#input_'
+
state
.
problemId
).
val
(
JSON
.
stringify
(
draggables
));
}
function
addTargetRecursively
(
tempObj
,
draggable
,
target
)
{
if
(
target
.
type
===
'base'
)
{
tempObj
[
draggable
.
id
]
=
target
.
id
;
}
else
{
tempObj
[
draggable
.
id
]
=
{};
tempObj
[
draggable
.
id
][
target
.
id
]
=
{};
addTargetRecursively
(
tempObj
[
draggable
.
id
][
target
.
id
],
target
.
draggableObj
,
target
.
draggableObj
.
onTarget
);
}
}
}
// Check if input has an answer from server. If yes, then position
// Check if input has an answer from server. If yes, then position
...
@@ -59,6 +70,7 @@ define(['logme'], function (logme) {
...
@@ -59,6 +70,7 @@ define(['logme'], function (logme) {
var
inputElVal
;
var
inputElVal
;
inputElVal
=
$
(
'#input_'
+
state
.
problemId
).
val
();
inputElVal
=
$
(
'#input_'
+
state
.
problemId
).
val
();
if
(
inputElVal
.
length
===
0
)
{
if
(
inputElVal
.
length
===
0
)
{
return
false
;
return
false
;
}
}
...
@@ -68,95 +80,147 @@ define(['logme'], function (logme) {
...
@@ -68,95 +80,147 @@ define(['logme'], function (logme) {
return
true
;
return
true
;
}
}
function
getUseTargets
(
answer
)
{
function
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
)
{
if
(
$
.
isArray
(
answer
.
draggables
)
===
false
)
{
var
baseDraggableId
,
baseDraggable
,
baseTargetId
,
baseTarget
,
logme
(
'ERROR: answer.draggables is not an array.'
);
layeredDraggableId
,
layeredDraggable
,
layeredTargetId
,
layeredTarget
,
chain
;
if
(
depth
===
0
)
{
// We are at the lowest depth? The end.
return
;
return
;
}
else
if
(
answer
.
draggables
.
length
===
0
)
{
}
if
(
answerSortedByDepth
.
hasOwnProperty
(
depth
)
===
false
)
{
// We have a depth that ts not valid, we decrease the depth by one.
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
-
1
,
0
);
return
;
return
;
}
}
if
(
$
.
isPlainObject
(
answer
.
draggables
[
0
])
===
false
)
{
if
(
answerSortedByDepth
[
depth
].
length
<=
i
)
{
logme
(
'ERROR: answer.draggables array does not contain objects.'
);
// We ran out of answers at this depth, go to the next depth down.
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
-
1
,
0
);
return
;
return
;
}
}
for
(
c1
in
answer
.
draggables
[
0
])
{
chain
=
answerSortedByDepth
[
depth
][
i
];
if
(
answer
.
draggables
[
0
].
hasOwnProperty
(
c1
)
===
false
)
{
continue
;
}
if
(
typeof
answer
.
draggables
[
0
][
c1
]
===
'string'
)
{
baseDraggableId
=
Object
.
keys
(
chain
)[
0
];
// use_targets = true;
return
true
;
// This is a hack. For now we will work with depths 1 and 3.
}
else
if
(
if
(
depth
===
1
)
{
(
$
.
isArray
(
answer
.
draggables
[
0
][
c1
])
===
true
)
&&
baseTargetId
=
chain
[
baseDraggableId
];
(
answer
.
draggables
[
0
][
c1
].
length
===
2
)
)
{
// use_targets = false;
return
false
;
layeredTargetId
=
null
;
}
else
{
layeredDraggableId
=
null
;
logme
(
'ERROR: answer.draggables[0] is inconsidtent.'
);
return
;
// createBaseDraggableOnTarget(state, baseDraggableId, baseTargetId)
;
}
}
else
if
(
depth
===
3
)
{
}
layeredDraggableId
=
baseDraggableId
;
logme
(
'ERROR: answer.draggables[0] is an empty object.'
)
;
layeredTargetId
=
Object
.
keys
(
chain
[
layeredDraggableId
])[
0
]
;
return
;
baseDraggableId
=
Object
.
keys
(
chain
[
layeredDraggableId
][
layeredTargetId
])[
0
];
}
function
processAnswerTargets
(
state
,
answer
)
{
baseTargetId
=
chain
[
layeredDraggableId
][
layeredTargetId
][
baseDraggableId
];
var
draggableId
,
draggable
,
targetId
,
target
;
}
(
function
(
c1
)
{
checkBaseDraggable
();
while
(
c1
<
answer
.
draggables
.
length
)
{
for
(
draggableId
in
answer
.
draggables
[
c1
])
{
if
(
answer
.
draggables
[
c1
].
hasOwnProperty
(
draggableId
)
===
false
)
{
continue
;
}
if
((
draggable
=
getById
(
state
,
'draggables'
,
draggableId
))
===
null
)
{
return
;
logme
(
'ERROR: In answer there exists a '
+
'draggable ID "'
+
draggableId
+
'". No '
+
'draggable with this ID could be found.'
);
continue
;
function
checkBaseDraggable
()
{
if
((
baseDraggable
=
getById
(
state
,
'draggables'
,
baseDraggableId
,
null
,
false
,
baseTargetId
))
===
null
)
{
createBaseDraggableOnTarget
(
state
,
baseDraggableId
,
baseTargetId
,
true
,
function
()
{
if
((
baseDraggable
=
getById
(
state
,
'draggables'
,
baseDraggableId
,
null
,
false
,
baseTargetId
))
===
null
)
{
console
.
log
(
'ERROR: Could not successfully create a base draggable on a base target.'
);
}
else
{
baseTarget
=
baseDraggable
.
onTarget
;
if
((
layeredTargetId
===
null
)
||
(
layeredDraggableId
===
null
))
{
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
+
1
);
}
else
{
checklayeredDraggable
();
}
}
}
});
}
else
{
baseTarget
=
baseDraggable
.
onTarget
;
targetId
=
answer
.
draggables
[
c1
][
draggableId
];
if
((
layeredTargetId
===
null
)
||
(
layeredDraggableId
===
null
))
{
if
((
target
=
getById
(
state
,
'targets'
,
targetId
))
===
null
)
{
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
+
1
);
logme
(
}
else
{
'ERROR: In answer there exists a target '
+
checklayeredDraggable
();
'ID "'
+
targetId
+
'". No target with this '
+
}
'ID could be found.'
}
);
}
continue
;
function
checklayeredDraggable
()
{
if
((
layeredDraggable
=
getById
(
state
,
'draggables'
,
layeredDraggableId
,
null
,
false
,
layeredTargetId
,
baseDraggableId
,
baseTargetId
))
===
null
)
{
layeredDraggable
=
getById
(
state
,
'draggables'
,
layeredDraggableId
);
layeredTarget
=
null
;
baseDraggable
.
targetField
.
every
(
function
(
target
)
{
if
(
target
.
id
===
layeredTargetId
)
{
layeredTarget
=
target
;
}
}
draggable
.
moveDraggableTo
(
'target'
,
target
);
return
true
;
});
if
((
layeredDraggable
!==
null
)
&&
(
layeredTarget
!==
null
))
{
layeredDraggable
.
moveDraggableTo
(
'target'
,
layeredTarget
,
function
()
{
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
+
1
);
});
}
else
{
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
+
1
);
}
}
}
else
{
processAnswerTargets
(
state
,
answerSortedByDepth
,
minDepth
,
maxDepth
,
depth
,
i
+
1
);
}
}
}
c1
+=
1
;
function
createBaseDraggableOnTarget
(
state
,
draggableId
,
targetId
,
reportError
,
funcCallback
)
{
var
draggable
,
target
;
if
((
draggable
=
getById
(
state
,
'draggables'
,
draggableId
))
===
null
)
{
if
(
reportError
!==
false
)
{
logme
(
'ERROR: In answer there exists a '
+
'draggable ID "'
+
draggableId
+
'". No '
+
'draggable with this ID could be found.'
);
}
}
}(
0
));
return
false
;
}
if
((
target
=
getById
(
state
,
'targets'
,
targetId
))
===
null
)
{
if
(
reportError
!==
false
)
{
logme
(
'ERROR: In answer there exists a target '
+
'ID "'
+
targetId
+
'". No target with this '
+
'ID could be found.'
);
}
return
false
;
}
draggable
.
moveDraggableTo
(
'target'
,
target
,
funcCallback
);
return
true
;
}
}
function
processAnswerPositions
(
state
,
answer
)
{
function
processAnswerPositions
(
state
,
answer
)
{
var
draggableId
,
draggable
;
var
draggableId
,
draggable
;
(
function
(
c1
)
{
(
function
(
c1
)
{
while
(
c1
<
answer
.
draggables
.
length
)
{
while
(
c1
<
answer
.
length
)
{
for
(
draggableId
in
answer
.
draggables
[
c1
])
{
for
(
draggableId
in
answer
[
c1
])
{
if
(
answer
.
draggables
[
c1
].
hasOwnProperty
(
draggableId
)
===
false
)
{
if
(
answer
[
c1
].
hasOwnProperty
(
draggableId
)
===
false
)
{
continue
;
continue
;
}
}
...
@@ -171,8 +235,8 @@ define(['logme'], function (logme) {
...
@@ -171,8 +235,8 @@ define(['logme'], function (logme) {
}
}
draggable
.
moveDraggableTo
(
'XY'
,
{
draggable
.
moveDraggableTo
(
'XY'
,
{
'x'
:
answer
.
draggables
[
c1
][
draggableId
][
0
],
'x'
:
answer
[
c1
][
draggableId
][
0
],
'y'
:
answer
.
draggables
[
c1
][
draggableId
][
1
]
'y'
:
answer
[
c1
][
draggableId
][
1
]
});
});
}
}
...
@@ -182,33 +246,110 @@ define(['logme'], function (logme) {
...
@@ -182,33 +246,110 @@ define(['logme'], function (logme) {
}
}
function
repositionDraggables
(
state
,
answer
)
{
function
repositionDraggables
(
state
,
answer
)
{
if
(
answer
.
draggables
.
length
===
0
)
{
var
answerSortedByDepth
,
minDepth
,
maxDepth
;
answerSortedByDepth
=
{};
minDepth
=
1000
;
maxDepth
=
0
;
answer
.
every
(
function
(
chain
)
{
var
depth
;
depth
=
findDepth
(
chain
,
0
);
if
(
depth
<
minDepth
)
{
minDepth
=
depth
;
}
if
(
depth
>
maxDepth
)
{
maxDepth
=
depth
;
}
if
(
answerSortedByDepth
.
hasOwnProperty
(
depth
)
===
false
)
{
answerSortedByDepth
[
depth
]
=
[];
}
answerSortedByDepth
[
depth
].
push
(
chain
);
return
true
;
});
if
(
answer
.
length
===
0
)
{
return
;
return
;
}
}
if
(
state
.
config
.
individualTargets
!==
getUseTargets
(
answer
))
{
// For now we support only one case.
logme
(
'ERROR: JSON config is not consistent with server response.'
);
if
((
minDepth
<
1
)
||
(
maxDepth
>
3
))
{
return
;
return
;
}
}
if
(
state
.
config
.
individualTargets
===
true
)
{
if
(
state
.
config
.
individualTargets
===
true
)
{
processAnswerTargets
(
state
,
answer
);
processAnswerTargets
(
state
,
answer
SortedByDepth
,
minDepth
,
maxDepth
,
maxDepth
,
0
);
}
else
if
(
state
.
config
.
individualTargets
===
false
)
{
}
else
if
(
state
.
config
.
individualTargets
===
false
)
{
processAnswerPositions
(
state
,
answer
);
processAnswerPositions
(
state
,
answer
);
}
}
}
}
function
getById
(
state
,
type
,
id
)
{
function
findDepth
(
tempObj
,
depth
)
{
var
i
;
if
(
$
.
isPlainObject
(
tempObj
)
===
false
)
{
return
depth
;
}
depth
+=
1
;
for
(
i
in
tempObj
)
{
if
(
tempObj
.
hasOwnProperty
(
i
)
===
true
)
{
depth
=
findDepth
(
tempObj
[
i
],
depth
);
}
}
return
depth
;
}
function
getById
(
state
,
type
,
id
,
fromTargetField
,
inContainer
,
targetId
,
baseDraggableId
,
baseTargetId
)
{
return
(
function
(
c1
)
{
return
(
function
(
c1
)
{
while
(
c1
<
state
[
type
].
length
)
{
while
(
c1
<
state
[
type
].
length
)
{
if
(
type
===
'draggables'
)
{
if
(
type
===
'draggables'
)
{
if
((
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
isOriginal
===
true
))
{
if
((
targetId
!==
undefined
)
&&
(
inContainer
===
false
)
&&
(
baseDraggableId
!==
undefined
)
&&
(
baseTargetId
!==
undefined
))
{
return
state
[
type
][
c1
];
if
(
(
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
inContainer
===
false
)
&&
(
state
[
type
][
c1
].
onTarget
.
id
===
targetId
)
&&
(
state
[
type
][
c1
].
onTarget
.
type
===
'on_drag'
)
&&
(
state
[
type
][
c1
].
onTarget
.
draggableObj
.
id
===
baseDraggableId
)
&&
(
state
[
type
][
c1
].
onTarget
.
draggableObj
.
onTarget
.
id
===
baseTargetId
)
)
{
return
state
[
type
][
c1
];
}
}
else
if
((
targetId
!==
undefined
)
&&
(
inContainer
===
false
))
{
if
(
(
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
inContainer
===
false
)
&&
(
state
[
type
][
c1
].
onTarget
.
id
===
targetId
)
)
{
return
state
[
type
][
c1
];
}
}
else
{
if
(
inContainer
===
false
)
{
if
((
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
inContainer
===
false
))
{
return
state
[
type
][
c1
];
}
}
else
{
if
((
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
inContainer
===
true
))
{
return
state
[
type
][
c1
];
}
}
}
}
}
else
{
// 'targets'
}
else
{
// 'targets'
if
(
state
[
type
][
c1
].
id
===
id
)
{
if
(
fromTargetField
===
true
)
{
return
state
[
type
][
c1
];
if
((
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
type
===
'on_drag'
))
{
return
state
[
type
][
c1
];
}
}
else
{
if
((
state
[
type
][
c1
].
id
===
id
)
&&
(
state
[
type
][
c1
].
type
===
'base'
))
{
return
state
[
type
][
c1
];
}
}
}
}
}
...
@@ -218,10 +359,5 @@ define(['logme'], function (logme) {
...
@@ -218,10 +359,5 @@ define(['logme'], function (logme) {
return
null
;
return
null
;
}(
0
));
}(
0
));
}
}
});
});
// End-of: define(['logme'], function (logme) {
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define) {
// End of wrapper for RequireJS. As you can see, we are passing
// namespaced Require JS variables to an anonymous function. Within
// it, you can use the standard requirejs(), require(), and define()
// functions as if they were in the global namespace.
}(
RequireJS
.
requirejs
,
RequireJS
.
require
,
RequireJS
.
define
));
// End-of: (function (requirejs, require, define)
This diff is collapsed.
Click to expand it.
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