From 84efafc896248541f8d5d108bccd2123fba2f41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Fri, 18 Jan 2013 17:00:51 -0500 Subject: [PATCH 001/140] Add missing semicolon at the end of sequence display module javascript --- .../xmodule/js/src/sequence/display/jquery.sequence.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js b/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js index fbefb8a8ce..cfda9e6d50 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js +++ b/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js @@ -1,6 +1,3 @@ - - - var SequenceNav = function($element) { var _this = this; var $element = $element; @@ -44,7 +41,7 @@ var SequenceNav = function($element) { var leftPercent = clamp(-left / padding, 0, 1); $leftShadow.css('opacity', leftPercent); - + var rightPercent = clamp((maxScroll + left) / padding, 0, 1); $rightShadow.css('opacity', rightPercent); }; @@ -95,5 +92,5 @@ var SequenceNav = function($element) { $(window).bind('resize', updateWidths); setTimeout(function() { checkPosition(); - }, 200); -} \ No newline at end of file + }, 200); +}; From 0f035b7692bcbcfde1ac19e0d40759bd3cfa113c Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Fri, 18 Jan 2013 17:03:55 -0500 Subject: [PATCH 002/140] Remove extra RequireJS.js entry. --- lms/envs/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index 330c8fd304..b50fc25917 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -435,7 +435,6 @@ courseware_only_js += [ main_vendor_js = [ 'js/vendor/RequireJS.js', 'js/vendor/json2.js', - 'js/vendor/RequireJS.js', 'js/vendor/jquery.min.js', 'js/vendor/jquery-ui.min.js', 'js/vendor/jquery.cookie.js', From 5200534a4a019acbb075e2f05d263c252c1fe436 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 11:24:38 +0200 Subject: [PATCH 003/140] template for drag_and_drop capaproblem --- common/lib/capa/capa/inputtypes.py | 48 +++++++++++++++++++ common/lib/capa/capa/responsetypes.py | 10 ++-- .../capa/templates/drag_and_drop_input.html | 41 ++++++++++++++++ common/lib/xmodule/xmodule/capa_module.py | 4 +- .../xmodule/js/src/capa/drag_and_drop.js | 1 + 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 common/lib/capa/capa/templates/drag_and_drop_input.html create mode 100644 common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 1d3646fefc..73056bc09e 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -735,3 +735,51 @@ class ChemicalEquationInput(InputTypeBase): registry.register(ChemicalEquationInput) #----------------------------------------------------------------------------- + +class OpenEndedInput(InputTypeBase): + """ + A text area input for code--uses codemirror, does syntax highlighting, special tab handling, + etc. + """ + + template = "openendedinput.html" + tags = ['openendedinput'] + + # pulled out for testing + submitted_msg = ("Feedback not yet available. Reload to check again. " + "Once the problem is graded, this message will be " + "replaced with the grader's feedback") + + @classmethod + def get_attributes(cls): + """ + Convert options to a convenient format. + """ + return [Attribute('rows', '30'), + Attribute('cols', '80'), + Attribute('hidden', ''), + ] + + def setup(self): + """ + Implement special logic: handle queueing state, and default input. + """ + # if no student input yet, then use the default input given by the problem + if not self.value: + self.value = self.xml.text + + # Check if problem has been queued + self.queue_len = 0 + # Flag indicating that the problem has been queued, 'msg' is length of queue + if self.status == 'incomplete': + self.status = 'queued' + self.queue_len = self.msg + self.msg = self.submitted_msg + + def _extra_context(self): + """Defined queue_len, add it """ + return {'queue_len': self.queue_len,} + +registry.register(OpenEndedInput) + +#----------------------------------------------------------------------------- diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 3d97cb0bea..98e36084a1 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -33,7 +33,7 @@ from correctmap import CorrectMap from datetime import datetime from util import * from lxml import etree -from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME? +from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME? import xqueue_interface log = logging.getLogger('mitx.' + __name__) @@ -869,7 +869,9 @@ def sympy_check2(): response_tag = 'customresponse' - allowed_inputfields = ['textline', 'textbox', 'crystallography', 'chemicalequationinput', 'vsepr_input'] + allowed_inputfields = ['textline', 'textbox', 'crystallography', + 'chemicalequationinput', 'vsepr_input', + 'drag_and_drop'] def setup_response(self): xml = self.xml @@ -1044,7 +1046,7 @@ def sympy_check2(): pretty_print=True) #msg = etree.tostring(fromstring_bs(msg),pretty_print=True) msg = msg.replace(' ', '') - #msg = re.sub('(.*)','\\1',msg,flags=re.M|re.DOTALL) # python 2.7 + #msg = re.sub('(.*)','\\1',msg,flags=re.M|re.DOTALL) # python 2.7 msg = re.sub('(?ms)(.*)', '\\1', msg) messages[0] = msg @@ -1763,7 +1765,7 @@ class ImageResponse(LoncapaResponse): def get_score(self, student_answers): correct_map = CorrectMap() expectedset = self.get_answers() - for aid in self.answer_ids: # loop through IDs of + for aid in self.answer_ids: # loop through IDs of # fields in our stanza given = student_answers[aid] # this should be a string of the form '[x,y]' correct_map.set(aid, 'incorrect') diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html new file mode 100644 index 0000000000..8613c97479 --- /dev/null +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -0,0 +1,41 @@ +
+
+ +
+ + % if status == 'unsubmitted': +
+ % elif status == 'correct': +
+ % elif status == 'incorrect': +
+ % elif status == 'incomplete': +
+ % endif + + + + +

+ % if status == 'unsubmitted': + unanswered + % elif status == 'correct': + correct + % elif status == 'incorrect': + incorrect + % elif status == 'incomplete': + incomplete + % endif +

+ +

+ + % if msg: + ${msg|n} + % endif + + % if status in ['unsubmitted', 'correct', 'incorrect', 'incomplete']: +
+ % endif +
diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 1da271072a..00760dc855 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -83,7 +83,9 @@ class CapaModule(XModule): resource_string(__name__, 'js/src/javascript_loader.coffee'), ], 'js': [resource_string(__name__, 'js/src/capa/imageinput.js'), - resource_string(__name__, 'js/src/capa/schematic.js')]} + resource_string(__name__, 'js/src/capa/schematic.js'), + resource_string(__name__, 'js/src/capa/drag_and_drop.js') + ]} js_module_name = "Problem" css = {'scss': [resource_string(__name__, 'css/capa/display.scss')]} diff --git a/common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js b/common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js new file mode 100644 index 0000000000..73c9f606d1 --- /dev/null +++ b/common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js @@ -0,0 +1 @@ +alert('drag-and-drop js is loaded') \ No newline at end of file From ea3b1f93b021af9c0d9d3db383face5d37753dda Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 12:24:54 +0200 Subject: [PATCH 004/140] updated drag and drop files --- common/lib/capa/capa/inputtypes.py | 30 ++++++++++++- .../capa/templates/drag_and_drop_input.html | 6 ++- common/lib/capa/capa/tests/test_inputtypes.py | 42 ++++++++++++++++++- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 73056bc09e..3f01ed58a7 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -13,6 +13,9 @@ Module containing the problem elements which render into input objects - imageinput (for clickable image) - optioninput (for option list) - filesubmission (upload a file) +- crystallography +- vsepr_input +- drag_and_drop These are matched by *.html files templates/*.html which are mako templates with the actual html. @@ -782,4 +785,29 @@ class OpenEndedInput(InputTypeBase): registry.register(OpenEndedInput) -#----------------------------------------------------------------------------- +# ------------------------------------------------------------------------- + + +class DragAndDropInput(InputTypeBase): + """ + Input for molecular geometry--show possible structures, let student + pick structure and label positions with atoms or electron pairs. + """ + + template = 'drag_and_drop.html' + tags = ['drag_and_drop_input'] + + @classmethod + def get_attributes(cls): + """ + Note: height, width are required. + """ + return [Attribute('height'), + Attribute('width'), + Attribute('template'), + Attribute('images'), + ] + +registry.register(DragAndDropInput) + +#-------------------------------------------------------------------------------------------------------------------- diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index 8613c97479..00752ea748 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,6 +1,7 @@
+ style="width:${width};height:${height}" + data-template="${template}" data-images="${images}">
@@ -15,7 +16,8 @@ % endif - +

% if status == 'unsubmitted': diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index dafd31bdc7..743ac7a548 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -9,7 +9,7 @@ TODO: - check rendering -- e.g. msg should appear in the rendered output. If possible, test that templates are escaping things properly. - + - test unicode in values, parameters, etc. - test various html escapes - test funny xml chars -- should never get xml parse error if things are escaped properly. @@ -501,3 +501,43 @@ class ChemicalEquationTest(unittest.TestCase): } self.assertEqual(context, expected) + +class DragAndDropTest(unittest.TestCase): + ''' + Check that drag and drop inputs work + ''' + + def test_rendering(self): + height = '12' + width = '33' + template = "path to template" + images = "path to images" + + xml_str = """""".format(h=height, w=width, t=template, i=images) + + element = etree.fromstring(xml_str) + + value = 'abc' + state = {'value': value, + 'status': 'unsubmitted'} + + the_input = lookup_tag('drag_and_drop')(test_system, element, state) + + context = the_input._get_render_context() + + expected = {'id': 'prob_1_2', + 'value': value, + 'status': 'unsubmitted', + 'msg': '', + 'width': width, + 'height': height, + 'template': template, + 'images': images, + } + + self.assertEqual(context, expected) From cc78ebe329781cbd1a3445800f8d2f697be30ae2 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 12:43:58 +0200 Subject: [PATCH 005/140] rename tag --- common/lib/capa/capa/responsetypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 98e36084a1..429f0cd483 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -871,7 +871,7 @@ def sympy_check2(): allowed_inputfields = ['textline', 'textbox', 'crystallography', 'chemicalequationinput', 'vsepr_input', - 'drag_and_drop'] + 'drag_and_drop_input'] def setup_response(self): xml = self.xml From 0001a67ce1d6f60363d166cdbb815ff60e2c5b06 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 12:54:47 +0200 Subject: [PATCH 006/140] update tag name --- common/lib/capa/capa/inputtypes.py | 8 ++++---- common/lib/capa/capa/tests/test_inputtypes.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 3f01ed58a7..8360e06fbb 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -695,7 +695,7 @@ class VseprInput(InputTypeBase): @classmethod def get_attributes(cls): """ - Note: height, width are required. + Note: height, width, molecules and geometries are required. """ return [Attribute('height'), Attribute('width'), @@ -781,7 +781,7 @@ class OpenEndedInput(InputTypeBase): def _extra_context(self): """Defined queue_len, add it """ - return {'queue_len': self.queue_len,} + return {'queue_len': self.queue_len, } registry.register(OpenEndedInput) @@ -794,13 +794,13 @@ class DragAndDropInput(InputTypeBase): pick structure and label positions with atoms or electron pairs. """ - template = 'drag_and_drop.html' + template = 'drag_and_drop_input.html' tags = ['drag_and_drop_input'] @classmethod def get_attributes(cls): """ - Note: height, width are required. + Note: height, width, template and images are required. """ return [Attribute('height'), Attribute('width'), diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 743ac7a548..505b536ab6 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -511,7 +511,7 @@ class DragAndDropTest(unittest.TestCase): height = '12' width = '33' template = "path to template" - images = "path to images" + images = "list of pathes to images" xml_str = """ Date: Fri, 21 Dec 2012 16:10:56 +0200 Subject: [PATCH 007/140] added new tag --- common/lib/capa/capa/inputtypes.py | 6 +++--- .../lib/capa/capa/templates/drag_and_drop_input.html | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 8360e06fbb..2162388371 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -800,12 +800,12 @@ class DragAndDropInput(InputTypeBase): @classmethod def get_attributes(cls): """ - Note: height, width, template and images are required. + Note: height, width, images_directory_path are required. """ + images_directory_path = Attribute('images_directory_path') return [Attribute('height'), Attribute('width'), - Attribute('template'), - Attribute('images'), + images_directory_path, ] registry.register(DragAndDropInput) diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index 00752ea748..bf9a58ea3e 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,7 +1,14 @@

+ style="width:${width};height:${height}"> + % if item +
    + % for item in images_directory_path: +
  • ${item}
  • + % endfor +
+ % endif +
From a394419641ce08f5a9c8b686af6a6b104398123c Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 17:43:09 +0200 Subject: [PATCH 008/140] updates --- common/lib/capa/capa/inputtypes.py | 16 ++++++++++++++-- .../capa/capa/templates/drag_and_drop_input.html | 5 +++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 2162388371..bf84d3f78c 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -44,6 +44,7 @@ from lxml import etree import re import shlex # for splitting quoted strings import sys +import os from registry import TagRegistry @@ -802,12 +803,23 @@ class DragAndDropInput(InputTypeBase): """ Note: height, width, images_directory_path are required. """ - images_directory_path = Attribute('images_directory_path') + # import ipdb; ipdb.set_trace() return [Attribute('height'), Attribute('width'), - images_directory_path, + Attribute('images_directory_path'), ] + def setup(self): + # import ipdb; ipdb.set_trace() + imagepath = self.loaded_attributes['images_directory_path'] + # import ipdb; ipdb.set_trace() + try: + images_list = self.system.filestore.listdir(os.path.join('static', + 'images', imagepath)) + except: + images_list = [] + self.loaded_attributes['images_list'] = images_list + registry.register(DragAndDropInput) #-------------------------------------------------------------------------------------------------------------------- diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index bf9a58ea3e..dc48862b4f 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,9 +1,10 @@
- % if item +

${images_list}

+ % if images_list:
    - % for item in images_directory_path: + % for item in images_list:
  • ${item}
  • % endfor
From ccb8e96acb8d96e74c896001563df4152e6f804b Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 17:47:51 +0200 Subject: [PATCH 009/140] fix --- common/lib/capa/capa/templates/drag_and_drop_input.html | 1 - 1 file changed, 1 deletion(-) diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index dc48862b4f..405e66f75c 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,7 +1,6 @@
-

${images_list}

% if images_list:
    % for item in images_list: From 313055fe960a9ed99a6d6605d5aa7451a8e4cbca Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 21 Dec 2012 18:17:27 +0200 Subject: [PATCH 010/140] transfer images list to html --- common/lib/capa/capa/inputtypes.py | 2 ++ common/lib/capa/capa/templates/drag_and_drop_input.html | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index bf84d3f78c..ec00a64833 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -816,9 +816,11 @@ class DragAndDropInput(InputTypeBase): try: images_list = self.system.filestore.listdir(os.path.join('static', 'images', imagepath)) + images_list = ['/static/images/' + img for img in images_list] except: images_list = [] self.loaded_attributes['images_list'] = images_list + self.to_render.add('images_list') registry.register(DragAndDropInput) diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index 405e66f75c..58fed37c84 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -4,7 +4,7 @@ % if images_list:
      % for item in images_list: -
    • ${item}
    • +
    • % endfor
    % endif From 757f955c5ebcc07debe132aed32605f030991088 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Mon, 24 Dec 2012 10:01:38 +0200 Subject: [PATCH 011/140] Moved JS for drag-and-drop from global capa. Now it will load only when required, and can be specified from any course. --- common/lib/xmodule/xmodule/capa_module.py | 3 +-- .../xmodule/xmodule/js/src => static/js}/capa/drag_and_drop.js | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename common/{lib/xmodule/xmodule/js/src => static/js}/capa/drag_and_drop.js (100%) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 00760dc855..d3b2c5c6eb 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -83,8 +83,7 @@ class CapaModule(XModule): resource_string(__name__, 'js/src/javascript_loader.coffee'), ], 'js': [resource_string(__name__, 'js/src/capa/imageinput.js'), - resource_string(__name__, 'js/src/capa/schematic.js'), - resource_string(__name__, 'js/src/capa/drag_and_drop.js') + resource_string(__name__, 'js/src/capa/schematic.js') ]} js_module_name = "Problem" diff --git a/common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js b/common/static/js/capa/drag_and_drop.js similarity index 100% rename from common/lib/xmodule/xmodule/js/src/capa/drag_and_drop.js rename to common/static/js/capa/drag_and_drop.js From 61c68b740ce2151e5bb61419ee436e4da5822c5d Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Mon, 24 Dec 2012 10:41:07 +0200 Subject: [PATCH 012/140] Set up of basic RequireJS skeleton structure. --- common/static/js/capa/drag_and_drop.js | 28 ++++++++++++++++++++- common/static/js/capa/drag_and_drop/main.js | 19 ++++++++++++++ lms/envs/common.py | 2 ++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 common/static/js/capa/drag_and_drop/main.js diff --git a/common/static/js/capa/drag_and_drop.js b/common/static/js/capa/drag_and_drop.js index 73c9f606d1..a2d8b8d37b 100644 --- a/common/static/js/capa/drag_and_drop.js +++ b/common/static/js/capa/drag_and_drop.js @@ -1 +1,27 @@ -alert('drag-and-drop js is loaded') \ No newline at end of file +// 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) { + +requirejs.config({ + 'baseUrl': '/static/js/capa/drag_and_drop/', + + 'paths': { + + }, + + 'shim': { + + } +}); + +requirejs(['main'], function (Main) { + Main(); +}); + +// 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) diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js new file mode 100644 index 0000000000..7ea7b103ad --- /dev/null +++ b/common/static/js/capa/drag_and_drop/main.js @@ -0,0 +1,19 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + return Main; + + function Main() { + alert('This is a drag-and-drop demo.'); + } +}); + +// 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) diff --git a/lms/envs/common.py b/lms/envs/common.py index b50fc25917..570d26ae2a 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -432,6 +432,8 @@ courseware_only_js += [ in glob2.glob(PROJECT_ROOT / 'static/coffee/src/modules/**/*.coffee') ] +# 'js/vendor/RequireJS.js' - Require JS wrapper. +# See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system main_vendor_js = [ 'js/vendor/RequireJS.js', 'js/vendor/json2.js', From 95060f39301f21242eebc835df569f33e57a7a2b Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 24 Dec 2012 14:39:52 +0200 Subject: [PATCH 013/140] updated python part to sync with new interface --- common/lib/capa/capa/inputtypes.py | 60 ++++++++++++------- .../capa/templates/drag_and_drop_input.html | 14 ++--- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index ec00a64833..46c23fb3a0 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -45,6 +45,7 @@ import re import shlex # for splitting quoted strings import sys import os +import unidecode from registry import TagRegistry @@ -798,29 +799,44 @@ class DragAndDropInput(InputTypeBase): template = 'drag_and_drop_input.html' tags = ['drag_and_drop_input'] - @classmethod - def get_attributes(cls): - """ - Note: height, width, images_directory_path are required. - """ - # import ipdb; ipdb.set_trace() - return [Attribute('height'), - Attribute('width'), - Attribute('images_directory_path'), - ] - def setup(self): - # import ipdb; ipdb.set_trace() - imagepath = self.loaded_attributes['images_directory_path'] - # import ipdb; ipdb.set_trace() - try: - images_list = self.system.filestore.listdir(os.path.join('static', - 'images', imagepath)) - images_list = ['/static/images/' + img for img in images_list] - except: - images_list = [] - self.loaded_attributes['images_list'] = images_list - self.to_render.add('images_list') + + def slugify(s): + """Makes slug from string""" + s = unidecode.unidecode(s).lower() + return re.sub(r'\W+', '-', s) + + def parse(x): + """Parses xml element to dictionary. + + Args: + xml etree element with optional + name or label or icon attributes. At least one + attribute must be presented. + + Returns: + dict{'name': smth, 'label': smth, 'icon': smth}. + """ + dic = dict() + for attr_name in ('name', 'label', 'icon'): + try: + dic[attr_name] = Attribute(attr_name).parse_from_xml(x) + except ValueError: + dic[attr_name] = None + dic['name'] = dic['name'] or slugify(dic['label']) + dic['label'] = dic['label'] or dic['name'] + + return dic + + to_js = dict() + to_js['target'] = Attribute('img').parse_from_xml(self.xml) + to_js['draggable'] = [parse(draggable) for draggable in + self.xml.getchildren()] + + self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) + self.to_render.add('drag_and_drop_json') + self.loaded_attributes['course_folder'] = self.system.course_id.split('/')[1] + self.to_render.add('course_folder') registry.register(DragAndDropInput) diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index 58fed37c84..f939dd58eb 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,16 +1,12 @@
    - % if images_list: -
      - % for item in images_list: -
    • - % endfor -
    - % endif + data-plain-id="${id}" data-course-folder="${course_folder}">
    -
    + + +
    % if status == 'unsubmitted':
    From 8d6904974679218c6ad626fd62829fe4097f7e63 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Mon, 24 Dec 2012 15:01:49 +0200 Subject: [PATCH 014/140] Client-side Drag and Drop: work in progress. Part 1. --- common/static/js/capa/drag_and_drop.js | 14 +++---- .../js/capa/drag_and_drop/config_parser.js | 19 ++++++++++ .../js/capa/drag_and_drop/draggables.js | 19 ++++++++++ common/static/js/capa/drag_and_drop/logme.js | 35 +++++++++++++++++ common/static/js/capa/drag_and_drop/main.js | 38 ++++++++++++++++++- common/static/js/capa/drag_and_drop/state.js | 21 ++++++++++ common/static/js/capa/drag_and_drop/target.js | 19 ++++++++++ 7 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 common/static/js/capa/drag_and_drop/config_parser.js create mode 100644 common/static/js/capa/drag_and_drop/draggables.js create mode 100644 common/static/js/capa/drag_and_drop/logme.js create mode 100644 common/static/js/capa/drag_and_drop/state.js create mode 100644 common/static/js/capa/drag_and_drop/target.js diff --git a/common/static/js/capa/drag_and_drop.js b/common/static/js/capa/drag_and_drop.js index a2d8b8d37b..ce6f4c4559 100644 --- a/common/static/js/capa/drag_and_drop.js +++ b/common/static/js/capa/drag_and_drop.js @@ -6,16 +6,14 @@ requirejs.config({ 'baseUrl': '/static/js/capa/drag_and_drop/', - - 'paths': { - - }, - - 'shim': { - - } }); +// The current JS file will be loaded and run each time. It will require a +// single dependency which will be loaded and stored by RequireJS. On +// subsequent runs, RequireJS will return the dependency from memory, rather +// than loading it again from the server. For that reason, it is a good idea to +// keep the current JS file as small as possible, and move everything else into +// RequireJS module dependencies. requirejs(['main'], function (Main) { Main(); }); diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js new file mode 100644 index 0000000000..ce120674f7 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -0,0 +1,19 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + return configParser; + + function configParser(config, state) { + state.config = 1; + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js new file mode 100644 index 0000000000..c291422fee --- /dev/null +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -0,0 +1,19 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + return Draggables; + + function Draggables(state) { + state.draggables = 1; + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) diff --git a/common/static/js/capa/drag_and_drop/logme.js b/common/static/js/capa/drag_and_drop/logme.js new file mode 100644 index 0000000000..1ab2eebac6 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/logme.js @@ -0,0 +1,35 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + var debugMode; + + debugMode = true; + + return logme; + + function logme() { + if ( + (debugMode !== true) || + (typeof window.console === 'undefined') + ) { + return; + } + + (function (i) { + while (i < arguments.length) { + window.console.log(arguments[i]); + i += 1; + } + }(0); + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 7ea7b103ad..142e1041ce 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -4,11 +4,45 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define([], function () { +define( + ['logme', 'state', 'config_parser', 'target', 'draggables'], + function (logme, State, configParser, Target, raggables) { return Main; function Main() { - alert('This is a drag-and-drop demo.'); + $('.drag_and_drop_problem').each(processProblem); + } + + // $(value) - get the element of the entire problem + function processProblem(index, value) { + var problemId, config, state; + + problemId = $(value).attr('data-plain-id'); + if (typeof problemId !== 'string') { + logme('ERROR: Could not find a problem DOM element ID.'); + + return; + } + + try { + config = JSON.parse($(value).html()); + } catch (err) { + logme('ERROR: Could not parse the JSON configuration options.'); + logme('Error message: "' + err.message + '".'); + + return; + } + + state = State(problemId); + + configParser(config, state); + + // Container(state); + Target(state); + // Scroller(state); + Draggables(state); + + logme(state); } }); diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js new file mode 100644 index 0000000000..7ebf89d023 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/state.js @@ -0,0 +1,21 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + return State; + + function State(problemId) { + return { + 'problemId': problemId + }; + } +}); + +// 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) diff --git a/common/static/js/capa/drag_and_drop/target.js b/common/static/js/capa/drag_and_drop/target.js new file mode 100644 index 0000000000..eaf4ffa731 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/target.js @@ -0,0 +1,19 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define([], function () { + return Target; + + function Target(state) { + state.target = 1; + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) From f65924773954c0bb905a5715262a2e196c8567bf Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Mon, 24 Dec 2012 16:24:25 +0200 Subject: [PATCH 015/140] Client-side Drag and Drop: work in progress. Part 2. --- common/lib/capa/capa/inputtypes.py | 2 +- .../capa/templates/drag_and_drop_input.html | 4 +- .../js/capa/drag_and_drop/config_parser.js | 61 ++++++++++++++++++- .../static/js/capa/drag_and_drop/container.js | 19 ++++++ common/static/js/capa/drag_and_drop/logme.js | 13 ++-- common/static/js/capa/drag_and_drop/main.js | 25 +++++--- 6 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 common/static/js/capa/drag_and_drop/container.js diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 46c23fb3a0..654a212b60 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -822,7 +822,7 @@ class DragAndDropInput(InputTypeBase): try: dic[attr_name] = Attribute(attr_name).parse_from_xml(x) except ValueError: - dic[attr_name] = None + dic[attr_name] = "" dic['name'] = dic['name'] or slugify(dic['label']) dic['label'] = dic['label'] or dic['name'] diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index f939dd58eb..161ecd1af1 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,9 +1,9 @@
    -
    -
    diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index ce120674f7..7c5e5ded73 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -4,11 +4,66 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define([], function () { +define(['logme'], function (logme) { return configParser; - function configParser(config, state) { - state.config = 1; + function configParser(config, imageDir, state) { + var returnStatus; + + returnStatus = true; + + state.config = { + 'imageDir': '/static/' + imageDir + '/images/', + 'draggable': [], + 'target': '' + }; + + if ($.isArray(config.draggable) === true) { + (function (i) { + while (i < config.draggable.length) { + if (processDraggable(config.draggable[i]) !== true) { + returnStatus = false; + } + i += 1; + } + }(0)); + } else if ($.isPlainObject(config.draggable) === true) { + if (processDraggable(config.draggable) !== true) { + returnStatus = false; + } + } else { + logme('ERROR: The type of config.draggable is no supported.'); + returnStatus = false; + } + + if (typeof config.target === 'string') { + state.config.target = config.target; + } else { + logme('ERROR: Property config.target is not of type "string".'); + returnStatus = false; + } + + return true; + + function processDraggable(obj) { + if (typeof obj.icon !== 'string') { + logme('ERROR: Attribute "obj.icon" is not a string.'); + + return false; + } else if (typeof obj.label !== 'string') { + logme('ERROR: Attribute "obj.label" is not a string.'); + + return false; + } else if (typeof obj.name !== 'string') { + logme('ERROR: Attribute "obj.name" is not a string.'); + + return false; + } + + state.config.draggable.push(obj); + + true; + } } }); diff --git a/common/static/js/capa/drag_and_drop/container.js b/common/static/js/capa/drag_and_drop/container.js new file mode 100644 index 0000000000..d9e7f51fc8 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/container.js @@ -0,0 +1,19 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define(['logme'], function (logme) { + return Container; + + function Container(state) { + + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) diff --git a/common/static/js/capa/drag_and_drop/logme.js b/common/static/js/capa/drag_and_drop/logme.js index 1ab2eebac6..21f73bf2a5 100644 --- a/common/static/js/capa/drag_and_drop/logme.js +++ b/common/static/js/capa/drag_and_drop/logme.js @@ -12,6 +12,8 @@ define([], function () { return logme; function logme() { + var i; + if ( (debugMode !== true) || (typeof window.console === 'undefined') @@ -19,12 +21,11 @@ define([], function () { return; } - (function (i) { - while (i < arguments.length) { - window.console.log(arguments[i]); - i += 1; - } - }(0); + i = 0; + while (i < arguments.length) { + window.console.log(arguments[i]); + i += 1; + } } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 142e1041ce..2745374950 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -6,26 +6,33 @@ define( ['logme', 'state', 'config_parser', 'target', 'draggables'], - function (logme, State, configParser, Target, raggables) { + function (logme, State, configParser, Target, Draggables) { return Main; function Main() { - $('.drag_and_drop_problem').each(processProblem); + $('.drag_and_drop_problem_div').each(processProblem); } // $(value) - get the element of the entire problem function processProblem(index, value) { - var problemId, config, state; + var problemId, imageDir, config, state; problemId = $(value).attr('data-plain-id'); if (typeof problemId !== 'string') { - logme('ERROR: Could not find a problem DOM element ID.'); + logme('ERROR: Could not find the ID of the problem DOM element.'); + + return; + } + + imageDir = $(value).attr('data-plain-id'); + if (typeof imageDir !== 'string') { + logme('ERROR: Could not find the name of the image directory.'); return; } try { - config = JSON.parse($(value).html()); + config = JSON.parse($('#drag_and_drop_json_' + problemId).html()); } catch (err) { logme('ERROR: Could not parse the JSON configuration options.'); logme('Error message: "' + err.message + '".'); @@ -35,9 +42,13 @@ define( state = State(problemId); - configParser(config, state); + if (configParser(config, imageDir, state) !== true) { + logme('ERROR: Could not make sense of the JSON configuration options.'); - // Container(state); + return; + } + + Container(state); Target(state); // Scroller(state); Draggables(state); From 06fd823881ed807de567385f5eca10649295b428 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 08:55:13 +0200 Subject: [PATCH 016/140] Client side drag and drop. Work in progress. Part 3. --- common/static/images/arrow-left.png | Bin 0 -> 2467 bytes common/static/images/arrow-right.png | Bin 0 -> 2457 bytes .../js/capa/drag_and_drop/config_parser.js | 2 +- .../static/js/capa/drag_and_drop/container.js | 10 ++ .../js/capa/drag_and_drop/draggables.js | 35 ++++++- common/static/js/capa/drag_and_drop/main.js | 16 ++- .../static/js/capa/drag_and_drop/scroller.js | 99 ++++++++++++++++++ common/static/js/capa/drag_and_drop/target.js | 43 +++++++- rakefile | 2 +- 9 files changed, 197 insertions(+), 10 deletions(-) create mode 100644 common/static/images/arrow-left.png create mode 100644 common/static/images/arrow-right.png create mode 100644 common/static/js/capa/drag_and_drop/scroller.js diff --git a/common/static/images/arrow-left.png b/common/static/images/arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..94ed6404aa15923eaf3fe722d56ec06dffd9efb2 GIT binary patch literal 2467 zcmV;U30(GxP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_000R#NklWQ4t%ftRl8p zf(Nl7MMdQx7GPmf+NuZ&E@feMr@i;fA8&RROjJB4_nbL*X5M}0_xXK)<=zViF(yB^ zlwUe}kDqf;>*S{%9zXGEKAbu*|GVv|naAx-rZ36*!D;EN->E3-#uQ;+GVR-zsWHMk zC2{ZNnukWr&x1^u!;0H4`{e-Ludk%)p1}Z=4w*5gKAE|{-;r(2(9^op?eH>EDkRcb z%?1I55l38sZMA!O^4+z(u2Xi*#=uyMxl zXOH;H(`%4H5XEGQL7O!zZ^5)F|@3QjlD>;7R z)9vchndwWW4A5g9cwbr+MKQ*JF^I7cu@EESEa89I0J^p)VfB>$1j!5{0>%mnRiP-x z1yf&=D}Q^Gc4<|5QHRkLm#n*Q2))M5-rtY&fq6s*-23gSJ1;wI{F1G@Wlw|Dr!v&+ zOd$LZ$pa;w&CBi-==T)i4~YAOiI%_En7H&|wczKWD7`AOv5~ zeoVzhCvTrZ|69G39v(rUT% zlyVZO%s7De{&6YRm+!pDpy{s_>`El>9&+vp`oWibLdvp%CrUDD5cp7jGdl*agI z?5XTcWe|aqn9HR3tGI36Dx8?RUvcG;R~P^B z*v>5ba*q^5Y&_c{>R5QLO<6pGib2GXRiS;e0^T2U61y8R1PXjpxke={i-+G?BUknQ{;d$K2Iyd>^&^Mdu=@!rxAAOPf8b8TwtvwWY(qN)T6 z$!tKsPR+u&Hde>}aX+A0nFF3721|@+piP}H7IF6MO(cohp;6BR#RNyJqKXU zyyXP|QW^iv1uHj8Nz}o!Eh4r>)HXzI6NVw56E?*$m+RiBVMl!yfMh11OIe%+!~2lP z@S_l9l>t3l%e&QE>NY(v?6dM~E3q~$4gq>!FsSO~LNXgL<@_V{r6nt6=f30(yOz&e zzGK-dVF>z6c#02h9r)R{U5SOWmsClI5>Fjl@_Eo0ibzg@FhOmKqb|c<*}#ta3s-y3*p)egqY+dxq)0;cJ}f(Q+1rDpP396j+F zy0vN68^E=t2>{^e@lPQl+ND)VuWr{r$w{qT3L~~Uw#g}BH6>=wcr-T`c@7mTzN2}e zm)m^8Q&E7B$UyC_=V;#>pBBe9BR`#X`4{EaR ze)y_KJ74n{YwsPLKa`sNPsN{1yPWo;E7I;yv12J(H?6)pA*nLM@3p7!h1s z@)hE_c37fr*uq`Q%5B{iPY>v!&tG`Bv>ExZ>`bHveldOMgrojCoAuL&{rttG*SOhy zI_+`*3a^;{TJxg#Irm@Dm(zN6Wck*#e!8t*HtcF(Zz@Y&(q~UHL);aPEQ{$Mx-}z~ z+%2Ok7xUrzZ#SIPtKErnZ}|Q84r40VG-LR|U(Jr2IEQ6Z!#iE~>nhuA*}3mQRepD2 z?+*0q-j?z+MSd;C)e hz4p92UOwox{{bsE`%YJ7@d^L{002ovPDHLkV1n7dwAKIs literal 0 HcmV?d00001 diff --git a/common/static/images/arrow-right.png b/common/static/images/arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..333796a5f345d1cc5df8a54a01b036bcbdfe5291 GIT binary patch literal 2457 zcmV;K31;?*P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_000RrNklnwJp?bRTqE7QNawrA5O!Xodk& z5;IMTip$t~;1nP2`4%DgThok`XAZoPdlI0>^bLG5Z}9a4v>dmBZ}Ubl;I%(D*j#w> zpSQPal(i(cC$V51x#?gj`%7XXnvkRib<%<=sR50wx*@tW4$Iq`7-C1^aVD(Y$@z-t zCzWf*_xtgJt_je2@;bhLbtuioEPEnm*>7)uaS(^CfE=~y8=-3|^&&A6E?t5+6{ua-y;KY&j$LuA*ye{-zN6p?^NK+{MI>7)jdBdC&^gh&ghq!=100hLrkA)u5*_8;t7XJ)5f$NOxweBTrArqZo8O{i)p)MCaV`Zi&EEDqdve7c(cw0M9 zUfcPr1w*;D)-F-1-CDFTmo8J*wYD8Ef9LEcWxW}nh=>jf66v`}DUnigzS3kww}w=h zHgVe_ZhOS5VQd%E@vt11xZ`3w9+j3OK0A}(;OkFu_{^n3DW&-Gl_6IGBpeCA!Q#?A zU(S1i%sHD083Lpq9O;4QQW3MrdoYV39qY4tU>28RiOazq2h;H(;#y{{&Er;#>)Ec^o6WOslCwTJBtnJ`LLd|sAq2K3m~u}` zCf;!qr7;^t2rfiz#(aF7`jG%>L7kLWu?gMNvlOhN}$WX z<~;c|fYNr-Y5MA9Il^Kwom6>;0&4YqGWvz8i` z?UC>#-;}!y=$6HeBl8E;n9y?Eo?Tlu;P|N$-}TbHHbg*h-^$&5QxYYhDM%@45Z0J; zZ*wY4+Yg*bD&hEPi90Tq?Qycq6umn%BWl~dY5*M9Yhy|UghYCtKYk9v5cFNO$A{1r zB#;r(c&TS|-xIa#LvmMGjz>w%A^XO9xUOtl1CSOp(k#~lYMbx+AnsqahrMTiKvM)l zO40)gf9Tnqc*3bkhna+^Rw6YLBxc#^H2^{pxYCnI??-{I4S|&4xokaFN>Ec!1PCFx zA)-+)tPx8%wRVN;o6+_V2&D9Dj|Na4HDe)74dc4W4=#8h6cy=tmkYM1fRg;Cs#L`+ zp6k_`7kg)MF`7UTf{3A#vwAP-VV%aQK}_34R|J|Z&=r9zUE-ESO3<(?s}%_9+8ILt zw&Sl>%4;iuM=WYGrB@a&^=U<=X`?8D^q@xHf-gB$9^-6zGg0$0id zwNvnqv!#`UG9vgYTpa>D%qo*qE?TX_jWNv*--zp}?*hfYhL|am~&m#@yXb;=17dcL=1!sxq10uO0LIckov(piW5V zj=aw}QCfv2BvN_^=}{83FssezxKvpVJ#I>uAMQFz@Al2#uK_rnKWfW|yN)n5r;{LN z0rIC1(#4LN%y_UPbNhFs!u0z=-H<`|g*!NYv5Lz@%x_@ju|$mt;|U)kQF%$6FOMBv zKfYhp-Lu|9xpUUL07L@%f_FbH;(=dfOI-OA2-mh5+dG@t{W}r2T~r~c6E^6uU^B-` zE76ePd9_-%OWby_5)N_Oq1;T6dutQ<{Q6I6P$xVUVCPGZU3nr34_+iMtQlY?d~dxosS!K~{MAoEmnrK2Xp|m#JZH{_6wV(ksfhW(WwjC#p(}Kl`2h#cmeY3b zUpR8U0_|$OLrRby(S%J&mC3u0ci^r+Y@};ugB}3W-nd7#b20uC>FnvC4MO+)U? z9@x8EmV9X59|h@QKOOi=C_=%r90&%`RIO&ULZ}F#T%m-Az(Y_xGey-dH+y)w|?mXz3I}?Ida+NLV=?Ac9jw-1VU3$ zlITK+TE|=A4Mw`ZKrA3g2naVt5p_%~M^%az(MhZj9fdCcUN39GocdfdSC#uLvz+M_j3-PM-H^-@VlsI+XUDGCuo69FMP z_+1IBb{t^q{!?B!s81*>7?%go{<+m0cs=)*UrxKduz@}EFW>1h$1d-GDQXV#BzHTm zmzfgu&qNjRM9k3CW16b$NDUhAomx8bi|T)FpZ+$V&wG;p>*+Ri!!Q14#(SS#*S!A# X;ZM1LG>q9a00000NkvXXu0mjfzEPn$ literal 0 HcmV?d00001 diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 7c5e5ded73..6b4c2939f5 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -13,7 +13,7 @@ define(['logme'], function (logme) { returnStatus = true; state.config = { - 'imageDir': '/static/' + imageDir + '/images/', + 'imageDir': '/static/' + imageDir + '/images', 'draggable': [], 'target': '' }; diff --git a/common/static/js/capa/drag_and_drop/container.js b/common/static/js/capa/drag_and_drop/container.js index d9e7f51fc8..7fa922ba43 100644 --- a/common/static/js/capa/drag_and_drop/container.js +++ b/common/static/js/capa/drag_and_drop/container.js @@ -8,7 +8,17 @@ define(['logme'], function (logme) { return Container; function Container(state) { + state.containerEl = $( + '
    ' + ); + $('#inputtype_' + state.problemId).before(state.containerEl); } }); diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index c291422fee..daf9c0ee02 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -4,11 +4,42 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define([], function () { +define(['logme'], function (logme) { return Draggables; function Draggables(state) { - state.draggables = 1; + (function (i) { + while (i < state.config.draggable.length) { + processDraggable(state.config.draggable[i]); + i += 1; + } + }(0)); + + function processDraggable(obj) { + var draggableContainerEl; + + logme(obj); + + draggableContainerEl = $( + '
    ' + ); + + if (obj.icon.length > 0) { + draggableContainerEl.append( + $('') + ); + } + + draggableContainerEl.appendTo(state.sliderEl); + } + } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 2745374950..2d4fa07cd0 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -5,8 +5,8 @@ (function (requirejs, require, define) { define( - ['logme', 'state', 'config_parser', 'target', 'draggables'], - function (logme, State, configParser, Target, Draggables) { + ['logme', 'state', 'config_parser', 'container', 'target', 'scroller', 'draggables'], + function (logme, State, configParser, Container, Target, Scroller, Draggables) { return Main; function Main() { @@ -17,6 +17,14 @@ define( function processProblem(index, value) { var problemId, imageDir, config, state; + if ($(value).attr('data-problem-processed') === 'true') { + // This problem was already processed by us before, so we will + // skip it. + + return; + } + $(value).attr('data-problem-processed', 'true'); + problemId = $(value).attr('data-plain-id'); if (typeof problemId !== 'string') { logme('ERROR: Could not find the ID of the problem DOM element.'); @@ -24,7 +32,7 @@ define( return; } - imageDir = $(value).attr('data-plain-id'); + imageDir = $(value).attr('data-course-folder'); if (typeof imageDir !== 'string') { logme('ERROR: Could not find the name of the image directory.'); @@ -50,7 +58,7 @@ define( Container(state); Target(state); - // Scroller(state); + Scroller(state); Draggables(state); logme(state); diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js new file mode 100644 index 0000000000..451d15907c --- /dev/null +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -0,0 +1,99 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define(['logme'], function (logme) { + return Scroller; + + function Scroller(state) { + var parentEl, moveLeftEl, showEl, moveRightEL, + moveRightEl_leftMargin; + + parentEl = $( + '
    ' + ); + + moveLeftEl = $( + '
    ' + ); + moveLeftEl.appendTo(parentEl); + + moveLeftEl.click(function () { + state.showElLeftMargin -= 100; + state.sliderEl.animate({ + 'margin-left': state.showElLeftMargin + 'px' + }); + }); + + showEl = $( + '
    ' + ); + showEl.appendTo(parentEl); + + state.showElLeftMargin = 0; + + state.sliderEl = $( + '
    ' + ); + state.sliderEl.appendTo(showEl); + + moveRightEl = $( + '
    ' + ); + moveRightEl.appendTo(parentEl); + + moveRightEl.click(function () { + state.showElLeftMargin += 100; + state.sliderEl.animate({ + 'margin-left': state.showElLeftMargin + 'px' + }); + }); + + parentEl.appendTo(state.containerEl); + } +}); + +// End of wrapper for RequireJS. As you can see, we are passing +// namespaced Require JS variables to an anonymous function. Within +// it, you can use the standard requirejs(), require(), and define() +// functions as if they were in the global namespace. +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) diff --git a/common/static/js/capa/drag_and_drop/target.js b/common/static/js/capa/drag_and_drop/target.js index eaf4ffa731..486a6264e7 100644 --- a/common/static/js/capa/drag_and_drop/target.js +++ b/common/static/js/capa/drag_and_drop/target.js @@ -4,11 +4,50 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define([], function () { +define(['logme'], function (logme) { return Target; function Target(state) { - state.target = 1; + var targetImgSrc, targetElContainer, mouseMoveDiv; + + targetImgSrc = state.config.imageDir + '/' + state.config.target; + + targetElContainer = $( + '
    ' + ); + + state.targetEl = $( + '' + ); + state.targetEl.appendTo(targetElContainer); + + state.targetEl.mousemove( + function (event) { + mouseMoveDiv.html( + '[' + event.offsetX + ', ' + event.offsetY + ']' + ); + } + ); + + mouseMoveDiv = $( + '
    [0, 0]
    ' + ); + mouseMoveDiv.appendTo(targetElContainer); + + targetElContainer.appendTo(state.containerEl); } }); diff --git a/rakefile b/rakefile index 53d7381dd7..fa0293b900 100644 --- a/rakefile +++ b/rakefile @@ -53,7 +53,7 @@ default_options = { task :predjango do sh("find . -type f -name *.pyc -delete") - sh('pip install -q --upgrade --no-deps -r local-requirements.txt') + # sh('pip install -q --upgrade --no-deps -r local-requirements.txt') end task :clean_test_files do From de954a41bec6ad64a7ee09d3839ac19fa9435178 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 08:58:43 +0200 Subject: [PATCH 017/140] Changed back rakefile. --- rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rakefile b/rakefile index fa0293b900..53d7381dd7 100644 --- a/rakefile +++ b/rakefile @@ -53,7 +53,7 @@ default_options = { task :predjango do sh("find . -type f -name *.pyc -delete") - # sh('pip install -q --upgrade --no-deps -r local-requirements.txt') + sh('pip install -q --upgrade --no-deps -r local-requirements.txt') end task :clean_test_files do From 4594986b3846a78567042f9a4c87469308ab5163 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 11:10:26 +0200 Subject: [PATCH 018/140] Client side drag and drop. Work in progress. Part 4. --- .../static/js/capa/drag_and_drop/container.js | 2 +- .../js/capa/drag_and_drop/draggables.js | 107 +++++++++++++++++- .../static/js/capa/drag_and_drop/scroller.js | 4 +- common/static/js/capa/drag_and_drop/target.js | 2 + 4 files changed, 106 insertions(+), 9 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/container.js b/common/static/js/capa/drag_and_drop/container.js index 7fa922ba43..9b04d01e4a 100644 --- a/common/static/js/capa/drag_and_drop/container.js +++ b/common/static/js/capa/drag_and_drop/container.js @@ -12,7 +12,7 @@ define(['logme'], function (logme) { '
    ' diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index daf9c0ee02..c7108927c3 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -10,15 +10,14 @@ define(['logme'], function (logme) { function Draggables(state) { (function (i) { while (i < state.config.draggable.length) { - processDraggable(state.config.draggable[i]); + processDraggable(state.config.draggable[i], i + 1); i += 1; } }(0)); - function processDraggable(obj) { - var draggableContainerEl; - - logme(obj); + function processDraggable(obj, index) { + var draggableContainerEl, imgEl, inContainer, + mouseDown; draggableContainerEl = $( '
    ' ); if (obj.icon.length > 0) { + imgEl = $( + '' + ); + + draggableContainerEl.append(imgEl); + } + + if (obj.label.length > 0) { draggableContainerEl.append( - $('') + $('
    ' + obj.label + '
    ') ); } draggableContainerEl.appendTo(state.sliderEl); + + inContainer = true; + mouseDown = false; + + draggableContainerEl.mousedown(function (event) { + if (mouseDown === false) { + if (inContainer === true) { + draggableContainerEl.detach(); + draggableContainerEl.css('position', 'absolute'); + draggableContainerEl.css('left', (event.pageX - 50)); + draggableContainerEl.css('top', (event.pageY - 50)); + draggableContainerEl.appendTo(state.containerEl); + + inContainer = false; + } + + mouseDown = true; + event.preventDefault(); + } + }); + + draggableContainerEl.mouseup(function (event) { + if (mouseDown === true) { + mouseDown = false; + checkLandingElement(event); + event.preventDefault(); + } + }); + + draggableContainerEl.mousemove(function (event) { + if (mouseDown === true) { + draggableContainerEl.css('left', (event.pageX - 50)); + draggableContainerEl.css('top', (event.pageY - 50)); + event.preventDefault(); + } + }); + + draggableContainerEl.mouseleave(function (event) { + if (mouseDown === true) { + mouseDown = false; + checkLandingElement(event); + event.preventDefault(); + } + }); + + return; + + function checkLandingElement(event) { + var offsetDE, children; + + offsetDE = draggableContainerEl.offset(); + + if ( + (offsetDE.left < state.targetEl.offset().left) || + (offsetDE.left + 100 > state.targetEl.offset().left + state.targetEl.width()) || + (offsetDE.top < state.targetEl.offset().top) || + (offsetDE.top + 100 > state.targetEl.offset().top + state.targetEl.height()) + ) { + draggableContainerEl.detach(); + draggableContainerEl.css('position', 'static'); + + children = state.sliderEl.children(); + + if (children.length === 0) { + draggableContainerEl.appendTo(state.sliderEl); + } else { + state.sliderEl.children().each(function (index, value) { + if (inContainer === false) { + if (parseInt($(value).attr('data-draggable-position-index'), 10) + 1 === parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10)) { + $(value).after(draggableContainerEl); + inContainer = true; + } else if (parseInt($(value).attr('data-draggable-position-index'), 10) - 1 === parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10)) { + $(value).before(draggableContainerEl); + inContainer = true; + } + } + }); + } + + if (inContainer === false) { + draggableContainerEl.appendTo(state.sliderEl); + } + } + } } } diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 451d15907c..d6d945e329 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -37,7 +37,7 @@ define(['logme'], function (logme) { moveLeftEl.appendTo(parentEl); moveLeftEl.click(function () { - state.showElLeftMargin -= 100; + state.showElLeftMargin += 100; state.sliderEl.animate({ 'margin-left': state.showElLeftMargin + 'px' }); @@ -82,7 +82,7 @@ define(['logme'], function (logme) { moveRightEl.appendTo(parentEl); moveRightEl.click(function () { - state.showElLeftMargin += 100; + state.showElLeftMargin -= 100; state.sliderEl.animate({ 'margin-left': state.showElLeftMargin + 'px' }); diff --git a/common/static/js/capa/drag_and_drop/target.js b/common/static/js/capa/drag_and_drop/target.js index 486a6264e7..b3396ad466 100644 --- a/common/static/js/capa/drag_and_drop/target.js +++ b/common/static/js/capa/drag_and_drop/target.js @@ -48,6 +48,8 @@ define(['logme'], function (logme) { mouseMoveDiv.appendTo(targetElContainer); targetElContainer.appendTo(state.containerEl); + + state.targetElOffset = state.targetEl.offset(); } }); From 57bec4083ebc5e144762f23138d256710fa557db Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Tue, 25 Dec 2012 12:21:03 +0200 Subject: [PATCH 019/140] updated inpyttype to new req --- common/lib/capa/capa/inputtypes.py | 59 ++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 654a212b60..958cf229e9 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -801,37 +801,56 @@ class DragAndDropInput(InputTypeBase): def setup(self): - def slugify(s): - """Makes slug from string""" - s = unidecode.unidecode(s).lower() - return re.sub(r'\W+', '-', s) - - def parse(x): - """Parses xml element to dictionary. + def parse(tag, tag_type): + """Parses xml element to dictionary. Args: - xml etree element with optional - name or label or icon attributes. At least one - attribute must be presented. + tag: xml etree element with attributes + + tag_type: type of tag: 'draggable' or 'target'. + + If tag_type is 'draggable' : all attributes (name or label or + icon) are optional, but at least one attribute must be + presented. + + If tag_type is 'target' all attributes (name, x, y, w, h) + are requred. (x, y) - coordinates of center of target, + w, h - weight and height of target. Returns: + Dictionary of vaues of attributes: dict{'name': smth, 'label': smth, 'icon': smth}. """ + tag_attrs = dict() + tag_attrs['draggable'] = {'id': Attribute._sentinel, + 'label': "", 'icon': ""} + tag_attrs['target'] = {'id': Attribute._sentinel, + 'x': Attribute._sentinel, + 'y': Attribute._sentinel, + 'w': Attribute._sentinel, + 'h': Attribute._sentinel} + dic = dict() - for attr_name in ('name', 'label', 'icon'): - try: - dic[attr_name] = Attribute(attr_name).parse_from_xml(x) - except ValueError: - dic[attr_name] = "" - dic['name'] = dic['name'] or slugify(dic['label']) - dic['label'] = dic['label'] or dic['name'] + + for attr_name in tag_attrs[tag_type].keys(): + dic[attr_name] = Attribute(attr_name, + default=tag_attrs[tag_type][attr_name]).parse_from_xml(tag) + + if tag_type == 'draggable': + dic['label'] = dic['label'] or dic['id'] return dic to_js = dict() - to_js['target'] = Attribute('img').parse_from_xml(self.xml) - to_js['draggable'] = [parse(draggable) for draggable in - self.xml.getchildren()] + to_js['target_container'] = Attribute('img').parse_from_xml(self.xml) + to_js['target_outline'] = Attribute('target_outline', + default="False").parse_from_xml(self.xml) + to_js['one_per_garget'] = Attribute('one_per_target', + default="True").parse_from_xml(self.xml) + to_js['draggable'] = [parse(draggable, 'draggable') for draggable in + self.xml.iterchildren('draggable')] + to_js['targets'] = [parse(target, 'target') for target in + self.xml.iterchildren('target')] self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.to_render.add('drag_and_drop_json') From b1a73a529323eb7daa2277f01cefebcf09c01419 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 12:51:56 +0200 Subject: [PATCH 020/140] Client side drag and drop. Work in progress. Part 5. --- .../js/capa/drag_and_drop/draggables.js | 119 +++++++++++------- common/static/js/capa/drag_and_drop/main.js | 3 +- 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index c7108927c3..747ddefc54 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -8,6 +8,10 @@ define(['logme'], function (logme) { return Draggables; function Draggables(state) { + var _draggables; + + _draggables = []; + (function (i) { while (i < state.config.draggable.length) { processDraggable(state.config.draggable[i], i + 1); @@ -15,9 +19,10 @@ define(['logme'], function (logme) { } }(0)); + return; + function processDraggable(obj, index) { - var draggableContainerEl, imgEl, inContainer, - mouseDown; + var draggableContainerEl, imgEl, inContainer, ousePressed; draggableContainerEl = $( '
    ' @@ -49,89 +55,114 @@ define(['logme'], function (logme) { } draggableContainerEl.appendTo(state.sliderEl); + _draggables.push(draggableContainerEl); inContainer = true; - mouseDown = false; + mousePressed = false; - draggableContainerEl.mousedown(function (event) { - if (mouseDown === false) { + draggableContainerEl.mousedown(mouseDown); + draggableContainerEl.mouseup(mouseUp); + draggableContainerEl.mousemove(mouseMove); + draggableContainerEl.mouseleave(mouseLeave); + + return; + + function mouseDown(event) { + if (mousePressed === false) { if (inContainer === true) { draggableContainerEl.detach(); draggableContainerEl.css('position', 'absolute'); - draggableContainerEl.css('left', (event.pageX - 50)); - draggableContainerEl.css('top', (event.pageY - 50)); + draggableContainerEl.css('left', event.pageX - 50); + draggableContainerEl.css('top', event.pageY - 50); draggableContainerEl.appendTo(state.containerEl); inContainer = false; } - mouseDown = true; + draggableContainerEl.attr('data-old-z-index', draggableContainerEl.css('z-index')); + draggableContainerEl.css('z-index', '1000'); + + mousePressed = true; event.preventDefault(); } - }); + } - draggableContainerEl.mouseup(function (event) { - if (mouseDown === true) { - mouseDown = false; + function mouseUp(event) { + if (mousePressed === true) { checkLandingElement(event); - event.preventDefault(); } - }); + } - draggableContainerEl.mousemove(function (event) { - if (mouseDown === true) { + function mouseMove(event) { + if (mousePressed === true) { draggableContainerEl.css('left', (event.pageX - 50)); draggableContainerEl.css('top', (event.pageY - 50)); event.preventDefault(); } - }); + } - draggableContainerEl.mouseleave(function (event) { - if (mouseDown === true) { - mouseDown = false; + function mouseLeave(event) { + if (mousePressed === true) { checkLandingElement(event); - event.preventDefault(); } - }); - - return; + } function checkLandingElement(event) { - var offsetDE, children; + var offsetDE, offsetTE, indexes, DEindex; + + mousePressed = false; offsetDE = draggableContainerEl.offset(); + offsetTE = state.targetEl.offset(); if ( - (offsetDE.left < state.targetEl.offset().left) || - (offsetDE.left + 100 > state.targetEl.offset().left + state.targetEl.width()) || - (offsetDE.top < state.targetEl.offset().top) || - (offsetDE.top + 100 > state.targetEl.offset().top + state.targetEl.height()) + (offsetDE.left < offsetTE.left) || + (offsetDE.left + 100 > offsetTE.left + state.targetEl.width()) || + (offsetDE.top < offsetTE.top) || + (offsetDE.top + 100 > offsetTE.top + state.targetEl.height()) ) { draggableContainerEl.detach(); draggableContainerEl.css('position', 'static'); - children = state.sliderEl.children(); + indexes = []; + DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10); - if (children.length === 0) { - draggableContainerEl.appendTo(state.sliderEl); - } else { - state.sliderEl.children().each(function (index, value) { - if (inContainer === false) { - if (parseInt($(value).attr('data-draggable-position-index'), 10) + 1 === parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10)) { - $(value).after(draggableContainerEl); - inContainer = true; - } else if (parseInt($(value).attr('data-draggable-position-index'), 10) - 1 === parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10)) { - $(value).before(draggableContainerEl); - inContainer = true; - } - } + state.sliderEl.children().each(function (index, value) { + indexes.push({ + 'index': parseInt($(value).attr('data-draggable-position-index'), 10), + 'el': $(value) }); - } + }); + + (function (c1) { + while (c1 < indexes.length) { + if ((inContainer === false) && (indexes[c1].index > DEindex)) { + indexes[c1].el.before(draggableContainerEl); + inContainer = true; + } + + c1 += 1; + } + }(0)); if (inContainer === false) { draggableContainerEl.appendTo(state.sliderEl); + inContainer = true; } } + + (function (c1) { + while (c1 < _draggables.length) { + if (draggableContainerEl.attr('data-old-z-index') < _draggables[c1].css('z-index')) { + _draggables[c1].css('z-index', _draggables[c1].css('z-index') - 1); + } + c1 += 1; + } + + draggableContainerEl.css('z-index', c1); + }(0)); + + event.preventDefault(); } } diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 2d4fa07cd0..f2349f6006 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -61,7 +61,8 @@ define( Scroller(state); Draggables(state); - logme(state); + logme('config', config); + logme('state', state); } }); From f0850f4c4bbb54a6b4b21ea29ad0f6e2d66a1263 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 13:02:08 +0200 Subject: [PATCH 021/140] Updated JS to use latest JSON def. --- common/static/js/capa/drag_and_drop/config_parser.js | 8 ++++---- common/static/js/capa/drag_and_drop/target.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 6b4c2939f5..501808bd53 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -36,8 +36,8 @@ define(['logme'], function (logme) { returnStatus = false; } - if (typeof config.target === 'string') { - state.config.target = config.target; + if (typeof config.target_container === 'string') { + state.config.target_container = config.target_container; } else { logme('ERROR: Property config.target is not of type "string".'); returnStatus = false; @@ -54,8 +54,8 @@ define(['logme'], function (logme) { logme('ERROR: Attribute "obj.label" is not a string.'); return false; - } else if (typeof obj.name !== 'string') { - logme('ERROR: Attribute "obj.name" is not a string.'); + } else if (typeof obj.id !== 'string') { + logme('ERROR: Attribute "obj.id" is not a string.'); return false; } diff --git a/common/static/js/capa/drag_and_drop/target.js b/common/static/js/capa/drag_and_drop/target.js index b3396ad466..b1358a992f 100644 --- a/common/static/js/capa/drag_and_drop/target.js +++ b/common/static/js/capa/drag_and_drop/target.js @@ -10,7 +10,7 @@ define(['logme'], function (logme) { function Target(state) { var targetImgSrc, targetElContainer, mouseMoveDiv; - targetImgSrc = state.config.imageDir + '/' + state.config.target; + targetImgSrc = state.config.imageDir + '/' + state.config.target_container; targetElContainer = $( '
    Date: Tue, 25 Dec 2012 13:26:34 +0200 Subject: [PATCH 022/140] Fix. Now the z index stack is consistent. --- common/static/js/capa/drag_and_drop/draggables.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 747ddefc54..0eaf918748 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -153,8 +153,8 @@ define(['logme'], function (logme) { (function (c1) { while (c1 < _draggables.length) { - if (draggableContainerEl.attr('data-old-z-index') < _draggables[c1].css('z-index')) { - _draggables[c1].css('z-index', _draggables[c1].css('z-index') - 1); + if (parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < parseInt(_draggables[c1].css('z-index'), 10)) { + _draggables[c1].css('z-index', parseInt(_draggables[c1].css('z-index'), 10) - 1); } c1 += 1; } From e842415194d13f30562e0add2d3893ace26d9f41 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 25 Dec 2012 16:06:08 +0200 Subject: [PATCH 023/140] Added ability to define targets on the target container. Work in progress. Part 6. --- common/lib/capa/capa/inputtypes.py | 2 +- .../js/capa/drag_and_drop/config_parser.js | 107 +++++++++++++++--- common/static/js/capa/drag_and_drop/main.js | 7 +- .../{target.js => target_container.js} | 18 ++- .../static/js/capa/drag_and_drop/targets.js | 57 ++++++++++ 5 files changed, 169 insertions(+), 22 deletions(-) rename common/static/js/capa/drag_and_drop/{target.js => target_container.js} (79%) create mode 100644 common/static/js/capa/drag_and_drop/targets.js diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 958cf229e9..c240b8b237 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -845,7 +845,7 @@ class DragAndDropInput(InputTypeBase): to_js['target_container'] = Attribute('img').parse_from_xml(self.xml) to_js['target_outline'] = Attribute('target_outline', default="False").parse_from_xml(self.xml) - to_js['one_per_garget'] = Attribute('one_per_target', + to_js['one_per_target'] = Attribute('one_per_target', default="True").parse_from_xml(self.xml) to_js['draggable'] = [parse(draggable, 'draggable') for draggable in self.xml.iterchildren('draggable')] diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 501808bd53..8718ba7f23 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -15,7 +15,8 @@ define(['logme'], function (logme) { state.config = { 'imageDir': '/static/' + imageDir + '/images', 'draggable': [], - 'target': '' + 'targets': [], + 'target_container': '' }; if ($.isArray(config.draggable) === true) { @@ -39,31 +40,109 @@ define(['logme'], function (logme) { if (typeof config.target_container === 'string') { state.config.target_container = config.target_container; } else { - logme('ERROR: Property config.target is not of type "string".'); + logme('ERROR: Property config.target_container is not of type "string".'); + returnStatus = false; + } + + if ($.isArray(config.targets) === true) { + (function (i) { + while (i < config.targets.length) { + if (processTarget(config.targets[i]) !== true) { + returnStatus = false; + } + i += 1; + } + }(0)); + } else if ($.isPlainObject(config.targets) === true) { + if (processTarget(config.targets) !== true) { + returnStatus = false; + } + } else if (typeof config.targets !== 'undefined') { + logme('ERROR: Property config.targets is not of a supported type.'); + returnStatus = false; + } + + if (typeof config.one_per_target === 'string') { + if (config.one_per_target.toLowerCase() === 'true') { + state.config.one_per_target = true; + } else if (config.one_per_target.toLowerCase() === 'false') { + state.config.one_per_target = false; + } else { + logme('ERROR: Property config.one_per_target can either be "true", or "false".'); + returnStatus = false; + } + } else if (typeof config.one_per_target !== 'undefined') { + logme('ERROR: Property config.one_per_target is not of a supported type.'); + returnStatus = false; + } + + if (typeof config.target_outline === 'string') { + if (config.target_outline.toLowerCase() === 'true') { + state.config.target_outline = true; + } else if (config.target_outline.toLowerCase() === 'false') { + state.config.target_outline = false; + } else { + logme('ERROR: Property config.target_outline can either be "true", or "false".'); + returnStatus = false; + } + } else if (typeof config.target_outline !== 'undefined') { + logme('ERROR: Property config.target_outline is not of a supported type.'); returnStatus = false; } return true; function processDraggable(obj) { - if (typeof obj.icon !== 'string') { - logme('ERROR: Attribute "obj.icon" is not a string.'); + if (!attrIsString(obj, 'id')) { return false; } - return false; - } else if (typeof obj.label !== 'string') { - logme('ERROR: Attribute "obj.label" is not a string.'); - - return false; - } else if (typeof obj.id !== 'string') { - logme('ERROR: Attribute "obj.id" is not a string.'); - - return false; - } + if (!attrIsString(obj, 'icon')) { return false; } + if (!attrIsString(obj, 'label')) { return false; } state.config.draggable.push(obj); true; } + + function processTarget(obj) { + if (!attrIsString(obj, 'id')) { return false; } + + if (!attrIsInteger(obj, 'w')) { return false; } + if (!attrIsInteger(obj, 'h')) { return false; } + + if (!attrIsInteger(obj, 'x')) { return false; } + if (!attrIsInteger(obj, 'y')) { return false; } + + state.config.targets.push(obj); + + true; + + } + + function attrIsString(obj, attr) { + if (typeof obj[attr] !== 'string') { + logme('ERROR: Attribute "obj.' + attr + '" is not a string.'); + + return false; + } + + return true; + } + + function attrIsInteger(obj, attr) { + var tempInt; + + tempInt = parseInt(obj[attr], 10); + + if (isFinite(tempInt) === false) { + logme('ERROR: Attribute "obj.' + attr + '" is not an integer.'); + + return false; + } + + obj[attr] = tempInt; + + return true; + } } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index f2349f6006..103a149c6a 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -5,8 +5,8 @@ (function (requirejs, require, define) { define( - ['logme', 'state', 'config_parser', 'container', 'target', 'scroller', 'draggables'], - function (logme, State, configParser, Container, Target, Scroller, Draggables) { + ['logme', 'state', 'config_parser', 'container', 'target_container', 'scroller', 'draggables', 'targets'], + function (logme, State, configParser, Container, TargetContainer, Scroller, Draggables, Targets) { return Main; function Main() { @@ -57,9 +57,10 @@ define( } Container(state); - Target(state); + TargetContainer(state); Scroller(state); Draggables(state); + Targets(state); logme('config', config); logme('state', state); diff --git a/common/static/js/capa/drag_and_drop/target.js b/common/static/js/capa/drag_and_drop/target_container.js similarity index 79% rename from common/static/js/capa/drag_and_drop/target.js rename to common/static/js/capa/drag_and_drop/target_container.js index b1358a992f..62562b5039 100644 --- a/common/static/js/capa/drag_and_drop/target.js +++ b/common/static/js/capa/drag_and_drop/target_container.js @@ -5,9 +5,9 @@ (function (requirejs, require, define) { define(['logme'], function (logme) { - return Target; + return TargetContainer; - function Target(state) { + function TargetContainer(state) { var targetImgSrc, targetElContainer, mouseMoveDiv; targetImgSrc = state.config.imageDir + '/' + state.config.target_container; @@ -15,6 +15,7 @@ define(['logme'], function (logme) { targetElContainer = $( '
    ' @@ -27,6 +28,17 @@ define(['logme'], function (logme) { ); state.targetEl.appendTo(targetElContainer); + state.targetEl_loaded = false; + + $("") // Make in memory copy of image to avoid css issues. + .attr("src", state.targetEl.attr("src")) + .load(function() { + state.targetEl_height = this.height; + state.targetEl_width = this.width; + + state.targetEl_loaded = true; + }); + state.targetEl.mousemove( function (event) { mouseMoveDiv.html( @@ -48,8 +60,6 @@ define(['logme'], function (logme) { mouseMoveDiv.appendTo(targetElContainer); targetElContainer.appendTo(state.containerEl); - - state.targetElOffset = state.targetEl.offset(); } }); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js new file mode 100644 index 0000000000..b41123cc69 --- /dev/null +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -0,0 +1,57 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define(['logme'], function (logme) { + return Targets; + + function Targets(state) { + (function (c1) { + while (c1 < state.config.targets.length) { + processTarget(state.config.targets[c1]); + c1 += 1; + } + }(0)); + + return; + + function processTarget(obj) { + var targetElOffset, tEl, left, top; + + if (state.targetEl_loaded === false) { + window.setTimeout(function () { + processTarget(obj); + }, 50); + return; + } + + left = obj.x + 0.5 * (state.targetEl.parent().width() - state.targetEl_width); + top = obj.y + + tEl = $( + '
    ' + ); + + tEl.appendTo(state.targetEl.parent()); + } + } +}); + +// 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) From c34fbbe19fe314dc73b87806ec125853db208ce7 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 07:52:28 +0200 Subject: [PATCH 024/140] Added support for individual targets. Work in progress. Part 7. --- .../js/capa/drag_and_drop/draggables.js | 173 ++++++++++++++---- .../static/js/capa/drag_and_drop/scroller.js | 13 +- .../js/capa/drag_and_drop/target_container.js | 14 +- .../static/js/capa/drag_and_drop/targets.js | 34 +++- 4 files changed, 175 insertions(+), 59 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 0eaf918748..17c68ae22d 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -22,7 +22,8 @@ define(['logme'], function (logme) { return; function processDraggable(obj, index) { - var draggableContainerEl, imgEl, inContainer, ousePressed; + var draggableContainerEl, imgEl, inContainer, ousePressed, + onTarget; draggableContainerEl = $( '
    offsetTE.left + state.targetEl.width()) || - (offsetDE.top < offsetTE.top) || - (offsetDE.top + 100 > offsetTE.top + state.targetEl.height()) - ) { + if (state.individualTargets === false) { + offsetTE = state.targetEl.offset(); + + if ( + (offsetDE.left < offsetTE.left) || + (offsetDE.left + 100 > offsetTE.left + state.targetEl.width()) || + (offsetDE.top < offsetTE.top) || + (offsetDE.top + 100 > offsetTE.top + state.targetEl.height()) + ) { + moveBackToSlider(); + } else { + correctZIndexes(); + } + } else if (state.individualTargets === true) { + targetFound = false; + + checkIfOnTarget(); + + if (targetFound === true) { + correctZIndexes(); + } else { + moveBackToSlider(); + removeObjIdFromTarget(); + } + } + + return; + + function removeObjIdFromTarget() { + var c1; + + if (onTarget !== null) { + c1 = 0; + + while (c1 < onTarget.draggable.length) { + if (onTarget.draggable[c1] === obj.id) { + onTarget.draggable.splice(c1, 1); + + break; + } + c1 += 1; + } + + onTarget = null; + } + } + + function checkIfOnTarget() { + var c1, c2, target; + + c1 = 0; + + while (c1 < state.targets.length) { + target = state.targets[c1]; + + if (offsetDE.top + 50 < target.offset.top) { + c1 += 1; + continue; + } + if (offsetDE.top + 50 > target.offset.top + target.h) { + c1 += 1; + continue; + } + if (offsetDE.left + 50 < target.offset.left) { + c1 += 1; + continue; + } + if (offsetDE.left + 50 > target.offset.left + target.w) { + c1 += 1; + continue; + } + + if (state.config.one_per_target === true) { + if ( + (target.draggable.length === 1) && + (target.draggable[0] !== obj.id) + ) { + c1 += 1; + continue; + } + } + + targetFound = true; + + removeObjIdFromTarget(); + onTarget = target; + + target.draggable.push(obj.id); + snapToTarget(target); + + break; + } + } + + function snapToTarget(target) { + draggableContainerEl.css('left', (target.offset.left + 0.5 * target.w - 50)); + draggableContainerEl.css('top', (target.offset.top + 0.5 * target.h - 50)); + } + + function correctZIndexes() { + var c1; + + c1 = 0; + + while (c1 < _draggables.length) { + if (parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < parseInt(_draggables[c1].css('z-index'), 10)) { + _draggables[c1].css('z-index', parseInt(_draggables[c1].css('z-index'), 10) - 1); + } + c1 += 1; + } + + draggableContainerEl.css('z-index', c1); + } + + function moveBackToSlider() { + var c1; + draggableContainerEl.detach(); draggableContainerEl.css('position', 'static'); @@ -134,35 +246,22 @@ define(['logme'], function (logme) { }); }); - (function (c1) { - while (c1 < indexes.length) { - if ((inContainer === false) && (indexes[c1].index > DEindex)) { - indexes[c1].el.before(draggableContainerEl); - inContainer = true; - } + c1 = 0; - c1 += 1; + while (c1 < indexes.length) { + if ((inContainer === false) && (indexes[c1].index > DEindex)) { + indexes[c1].el.before(draggableContainerEl); + inContainer = true; } - }(0)); + + c1 += 1; + } if (inContainer === false) { draggableContainerEl.appendTo(state.sliderEl); inContainer = true; } } - - (function (c1) { - while (c1 < _draggables.length) { - if (parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < parseInt(_draggables[c1].css('z-index'), 10)) { - _draggables[c1].css('z-index', parseInt(_draggables[c1].css('z-index'), 10) - 1); - } - c1 += 1; - } - - draggableContainerEl.css('z-index', c1); - }(0)); - - event.preventDefault(); } } diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index d6d945e329..4048b50c94 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -8,8 +8,7 @@ define(['logme'], function (logme) { return Scroller; function Scroller(state) { - var parentEl, moveLeftEl, showEl, moveRightEL, - moveRightEl_leftMargin; + var parentEl, moveLeftEl, showEl, moveRightEL, showElLeftMargin; parentEl = $( '
    ") // Make in memory copy of image to avoid css issues. - .attr("src", state.targetEl.attr("src")) - .load(function() { - state.targetEl_height = this.height; - state.targetEl_width = this.width; - - state.targetEl_loaded = true; + state.targetElWidth = null; + $('') // Make in memory copy of image to avoid css issues. + .attr('src', state.targetEl.attr('src')) + .load(function () { + state.targetElWidth = this.width; }); state.targetEl.mousemove( diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index b41123cc69..44431becba 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -8,6 +8,15 @@ define(['logme'], function (logme) { return Targets; function Targets(state) { + if (state.config.targets.length === 0) { + state.individualTargets = false; + + return; + } + + state.individualTargets = true; + state.targets = []; + (function (c1) { while (c1 < state.config.targets.length) { processTarget(state.config.targets[c1]); @@ -18,17 +27,21 @@ define(['logme'], function (logme) { return; function processTarget(obj) { - var targetElOffset, tEl, left, top; + var targetElOffset, tEl, left, borderCss; - if (state.targetEl_loaded === false) { + if (state.targetElWidth === null) { window.setTimeout(function () { processTarget(obj); }, 50); return; } - left = obj.x + 0.5 * (state.targetEl.parent().width() - state.targetEl_width); - top = obj.y + left = obj.x + 0.5 * (state.targetEl.parent().width() - state.targetElWidth); + + borderCss = ''; + if (state.config.target_outline === true) { + borderCss = 'border: 1px solid black; '; + } tEl = $( '
    ' ); tEl.appendTo(state.targetEl.parent()); + + state.targets.push({ + 'id': obj.id, + 'offset': tEl.offset(), + 'w': obj.w, + 'h': obj.h, + 'el': tEl, + 'draggable': [] + }); } } }); From 30488537490812feee7d9062675edc8a8c3ca083 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 09:45:15 +0200 Subject: [PATCH 025/140] Added ability to communicate with server via input element. Work in progress. Part 8. --- .../js/capa/drag_and_drop/config_parser.js | 6 +++ .../js/capa/drag_and_drop/draggables.js | 24 +++++++++- .../static/js/capa/drag_and_drop/targets.js | 13 ++--- .../js/capa/drag_and_drop/update_input.js | 47 +++++++++++++++++++ 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 common/static/js/capa/drag_and_drop/update_input.js diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 8718ba7f23..fd38081de1 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -90,6 +90,12 @@ define(['logme'], function (logme) { returnStatus = false; } + if (state.config.targets.length === 0) { + state.individualTargets = false; + } else { + state.individualTargets = true; + } + return true; function processDraggable(obj) { diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 17c68ae22d..f31b4dcdfb 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -4,13 +4,14 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define(['logme'], function (logme) { +define(['logme', 'update_input'], function (logme, updateInput) { return Draggables; function Draggables(state) { var _draggables; _draggables = []; + state.draggables = []; (function (i) { while (i < state.config.draggable.length) { @@ -23,7 +24,7 @@ define(['logme'], function (logme) { function processDraggable(obj, index) { var draggableContainerEl, imgEl, inContainer, ousePressed, - onTarget; + onTarget, draggableObj; draggableContainerEl = $( '
    offsetTE.top + state.targetEl.height()) ) { moveBackToSlider(); + + draggableObj.x = -1; + draggableObj.y = -1; } else { correctZIndexes(); + + draggableObj.x = offsetDE.left + 50 - offsetTE.left; + draggableObj.y = offsetDE.top + 50 - offsetTE.top; } } else if (state.individualTargets === true) { targetFound = false; @@ -142,6 +160,8 @@ define(['logme'], function (logme) { } } + updateInput(state); + return; function removeObjIdFromTarget() { diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index 44431becba..18c07f6620 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -4,17 +4,10 @@ // See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system (function (requirejs, require, define) { -define(['logme'], function (logme) { +define(['logme', 'update_input'], function (logme, updateInput) { return Targets; function Targets(state) { - if (state.config.targets.length === 0) { - state.individualTargets = false; - - return; - } - - state.individualTargets = true; state.targets = []; (function (c1) { @@ -68,6 +61,10 @@ define(['logme'], function (logme) { 'el': tEl, 'draggable': [] }); + + if (state.individualTargets === true) { + updateInput(state); + } } } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js new file mode 100644 index 0000000000..225834c36c --- /dev/null +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -0,0 +1,47 @@ +// Wrapper for RequireJS. It will make the standard requirejs(), require(), and +// define() functions from Require JS available inside the anonymous function. +// +// See https://edx-wiki.atlassian.net/wiki/display/LMS/Integration+of+Require+JS+into+the+system +(function (requirejs, require, define) { + +define(['logme'], function (logme) { + return updateInput; + + function updateInput(state) { + var inputEl, stateStr, targets; + + if (state.individualTargets === false) { + stateStr = JSON.stringify({ + 'individualTargets': false, + 'draggables': state.draggables + }); + } else { + targets = []; + (function (c1) { + while (c1 < state.targets.length) { + targets.push({ + 'id': state.targets[c1].id, + 'draggables': state.targets[c1].draggable + }); + + c1 += 1; + } + }(0)); + + stateStr = JSON.stringify({ + 'individualTargets': true, + 'targets': targets + }); + } + + inputEl = $('#input_' + state.problemId); + inputEl.val(stateStr); + } + +}); + +// 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) From 545b1bea24800f6f00e1f42a70a83cb21e9590c2 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 14:46:34 +0200 Subject: [PATCH 026/140] Slight changes to UI design. --- common/static/images/arrow-left.png | Bin 2467 -> 1062 bytes common/static/images/arrow-right.png | Bin 2457 -> 1079 bytes .../static/js/capa/drag_and_drop/container.js | 2 - .../js/capa/drag_and_drop/draggables.js | 1 + .../static/js/capa/drag_and_drop/scroller.js | 110 +++++++++++++++--- .../static/js/capa/drag_and_drop/targets.js | 2 +- 6 files changed, 95 insertions(+), 20 deletions(-) diff --git a/common/static/images/arrow-left.png b/common/static/images/arrow-left.png index 94ed6404aa15923eaf3fe722d56ec06dffd9efb2..33277d6af446ff0efd14fdba9b22216d99fc02c8 100644 GIT binary patch literal 1062 zcmaJ=%}*0S6yE|tf)uJ41!Ks#c#wqdN57y;D`K~mCN9C!Xpe4pr*uR2gWbU{T%Zz@ ziisBqnrMt%@L*!Nm=HY>6%+jfjK+90dN674NJ1Hn*;?FT-F_0bGoAuk)?f+5Z~0IDB}vzc;^OYww2Mfi{p76=~iAhKx`reP70!=XpW<=f)E5QsaZ(WB$(u*B<7$LMdn8Mfk4pDGJGr$ zVA(hu4h#gt{%|0WNcjBmP^^aKb!kS08meLCJuF)n>xe-$aAqEw%4H}gOkD+)ra7f< zElj*FUll9Yt%XU{#nQMK+8OO%qprEIejIaGxcIOueW+o*n^@TMpWO}kQQS`QBWe46 zc6ntb2M%VK1FNI$F?BU4LU}>YJ{BX6Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_000R#NklWQ4t%ftRl8p zf(Nl7MMdQx7GPmf+NuZ&E@feMr@i;fA8&RROjJB4_nbL*X5M}0_xXK)<=zViF(yB^ zlwUe}kDqf;>*S{%9zXGEKAbu*|GVv|naAx-rZ36*!D;EN->E3-#uQ;+GVR-zsWHMk zC2{ZNnukWr&x1^u!;0H4`{e-Ludk%)p1}Z=4w*5gKAE|{-;r(2(9^op?eH>EDkRcb z%?1I55l38sZMA!O^4+z(u2Xi*#=uyMxl zXOH;H(`%4H5XEGQL7O!zZ^5)F|@3QjlD>;7R z)9vchndwWW4A5g9cwbr+MKQ*JF^I7cu@EESEa89I0J^p)VfB>$1j!5{0>%mnRiP-x z1yf&=D}Q^Gc4<|5QHRkLm#n*Q2))M5-rtY&fq6s*-23gSJ1;wI{F1G@Wlw|Dr!v&+ zOd$LZ$pa;w&CBi-==T)i4~YAOiI%_En7H&|wczKWD7`AOv5~ zeoVzhCvTrZ|69G39v(rUT% zlyVZO%s7De{&6YRm+!pDpy{s_>`El>9&+vp`oWibLdvp%CrUDD5cp7jGdl*agI z?5XTcWe|aqn9HR3tGI36Dx8?RUvcG;R~P^B z*v>5ba*q^5Y&_c{>R5QLO<6pGib2GXRiS;e0^T2U61y8R1PXjpxke={i-+G?BUknQ{;d$K2Iyd>^&^Mdu=@!rxAAOPf8b8TwtvwWY(qN)T6 z$!tKsPR+u&Hde>}aX+A0nFF3721|@+piP}H7IF6MO(cohp;6BR#RNyJqKXU zyyXP|QW^iv1uHj8Nz}o!Eh4r>)HXzI6NVw56E?*$m+RiBVMl!yfMh11OIe%+!~2lP z@S_l9l>t3l%e&QE>NY(v?6dM~E3q~$4gq>!FsSO~LNXgL<@_V{r6nt6=f30(yOz&e zzGK-dVF>z6c#02h9r)R{U5SOWmsClI5>Fjl@_Eo0ibzg@FhOmKqb|c<*}#ta3s-y3*p)egqY+dxq)0;cJ}f(Q+1rDpP396j+F zy0vN68^E=t2>{^e@lPQl+ND)VuWr{r$w{qT3L~~Uw#g}BH6>=wcr-T`c@7mTzN2}e zm)m^8Q&E7B$UyC_=V;#>pBBe9BR`#X`4{EaR ze)y_KJ74n{YwsPLKa`sNPsN{1yPWo;E7I;yv12J(H?6)pA*nLM@3p7!h1s z@)hE_c37fr*uq`Q%5B{iPY>v!&tG`Bv>ExZ>`bHveldOMgrojCoAuL&{rttG*SOhy zI_+`*3a^;{TJxg#Irm@Dm(zN6Wck*#e!8t*HtcF(Zz@Y&(q~UHL);aPEQ{$Mx-}z~ z+%2Ok7xUrzZ#SIPtKErnZ}|Q84r40VG-LR|U(Jr2IEQ6Z!#iE~>nhuA*}3mQRepD2 z?+*0q-j?z+MSd;C)e hz4p92UOwox{{bsE`%YJ7@d^L{002ovPDHLkV1n7dwAKIs diff --git a/common/static/images/arrow-right.png b/common/static/images/arrow-right.png index 333796a5f345d1cc5df8a54a01b036bcbdfe5291..b54f8566de1f90db9872940a9e345040be5f204f 100644 GIT binary patch literal 1079 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 X;ZM1LG>q9a00000NkvXXu0mjfzEPn$ diff --git a/common/static/js/capa/drag_and_drop/container.js b/common/static/js/capa/drag_and_drop/container.js index 9b04d01e4a..766a020802 100644 --- a/common/static/js/capa/drag_and_drop/container.js +++ b/common/static/js/capa/drag_and_drop/container.js @@ -11,8 +11,6 @@ define(['logme'], function (logme) { state.containerEl = $( '
    ' diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index f31b4dcdfb..cb48cb3480 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -35,6 +35,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'float: left; ' + 'overflow: hidden; ' + 'z-index: ' + index + '; ' + + 'border: 1px solid gray; ' + '" ' + 'data-draggable-position-index="' + index + '" ' + '>
    ' diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 4048b50c94..41109abcbc 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -13,9 +13,8 @@ define(['logme'], function (logme) { parentEl = $( '
    ' + '>' + + '
    ' + + '
    ' ); moveLeftEl.appendTo(parentEl); +/* +position: absolute; +margin-bottom: 0; +height: 44px; +width: 70px; +border: 1px solid #CCC; +background-color: #EEE; +background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #EEE), color-stop(100%, #DDD)); +background-image: -webkit-linear-gradient(top, #EEE, #DDD); +background-image: -moz-linear-gradient(top, #EEE, #DDD); +background-image: -ms-linear-gradient(top, #EEE, #DDD); +background-image: -o-linear-gradient(top, #EEE, #DDD); +background-image: linear-gradient(top, #EEE, #DDD); +-webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; +-moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; +box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; +*/ + moveLeftEl.click(function () { - showElLeftMargin += 100; + if (showElLeftMargin > -102) { + return; + } + + showElLeftMargin += 102; state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' - }); + }, 100); }); showEl = $( '
    ' ); @@ -71,20 +117,50 @@ define(['logme'], function (logme) { '
    ' + '>' + + '
    ' + + '
    ' ); moveRightEl.appendTo(parentEl); moveRightEl.click(function () { - showElLeftMargin -= 100; + if (showElLeftMargin < -102 * (state.sliderEl.children().length - 8)) { + return; + } + + showElLeftMargin -= 102; state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' - }); + }, 100); }); parentEl.appendTo(state.containerEl); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index 18c07f6620..eddc51f440 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -33,7 +33,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { borderCss = ''; if (state.config.target_outline === true) { - borderCss = 'border: 1px solid black; '; + borderCss = 'border: 1px dashed gray; '; } tEl = $( From c5d4f54aa2ef053780d60346830472bf7a4dac56 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 14:52:22 +0200 Subject: [PATCH 027/140] Slight changes to UI design. Part 2. --- .../js/capa/drag_and_drop/draggables.js | 10 ++++++++-- .../static/js/capa/drag_and_drop/scroller.js | 19 ------------------- .../js/capa/drag_and_drop/target_container.js | 16 ++++++++-------- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index cb48cb3480..8aafb200b2 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -24,7 +24,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { function processDraggable(obj, index) { var draggableContainerEl, imgEl, inContainer, ousePressed, - onTarget, draggableObj; + onTarget, draggableObj, marginCss; draggableContainerEl = $( '
    0) { + marginCss = ''; + + if (obj.icon.length === 0) { + marginCss = 'margin-top: 38px;'; + } + draggableContainerEl.append( - $('
    ' + obj.label + '
    ') + $('
    ' + obj.label + '
    ') ); } diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 41109abcbc..fc11740611 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -59,24 +59,6 @@ define(['logme'], function (logme) { ); moveLeftEl.appendTo(parentEl); -/* -position: absolute; -margin-bottom: 0; -height: 44px; -width: 70px; -border: 1px solid #CCC; -background-color: #EEE; -background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #EEE), color-stop(100%, #DDD)); -background-image: -webkit-linear-gradient(top, #EEE, #DDD); -background-image: -moz-linear-gradient(top, #EEE, #DDD); -background-image: -ms-linear-gradient(top, #EEE, #DDD); -background-image: -o-linear-gradient(top, #EEE, #DDD); -background-image: linear-gradient(top, #EEE, #DDD); --webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; --moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; -*/ - moveLeftEl.click(function () { if (showElLeftMargin > -102) { return; @@ -120,7 +102,6 @@ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7) inset; 'height: 102px; ' + 'display: inline; ' + 'float: left; ' + - // 'background: url(\'/static/images/arrow-right.png\') center center no-repeat; ' + '" ' + '>' + '
    [0, 0]
    ' + '>
    ' ); mouseMoveDiv.appendTo(targetElContainer); From 9efe1c0a4b5d5fa2a72b9c1d69db973d739997fa Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 26 Dec 2012 15:33:58 +0200 Subject: [PATCH 028/140] added parameters and rename --- common/lib/capa/capa/inputtypes.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index c240b8b237..147251329b 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -824,6 +824,7 @@ class DragAndDropInput(InputTypeBase): tag_attrs = dict() tag_attrs['draggable'] = {'id': Attribute._sentinel, 'label': "", 'icon': ""} + tag_attrs['target'] = {'id': Attribute._sentinel, 'x': Attribute._sentinel, 'y': Attribute._sentinel, @@ -842,13 +843,24 @@ class DragAndDropInput(InputTypeBase): return dic to_js = dict() - to_js['target_container'] = Attribute('img').parse_from_xml(self.xml) + + # image drag and drop onto + to_js['base_image'] = Attribute('img').parse_from_xml(self.xml) + + # problem subtype: targets or positions + to_js['use_targets'] = Attribute('targets', + default="True").parse_from_xml(self.xml) + + # outline places on image where to drag adn drop to_js['target_outline'] = Attribute('target_outline', default="False").parse_from_xml(self.xml) + # one draggable per target? to_js['one_per_target'] = Attribute('one_per_target', default="True").parse_from_xml(self.xml) - to_js['draggable'] = [parse(draggable, 'draggable') for draggable in + # list of draggables + to_js['draggables'] = [parse(draggable, 'draggable') for draggable in self.xml.iterchildren('draggable')] + # list of targets to_js['targets'] = [parse(target, 'target') for target in self.xml.iterchildren('target')] From 36b1d4f1d0e20721bb337de62607a6d366298132 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 26 Dec 2012 15:34:15 +0200 Subject: [PATCH 029/140] graders for draganddrop --- common/lib/capa/capa/graders/__init__.py | 0 common/lib/capa/capa/graders/draganddrop.py | 98 +++++++++++++++++++++ common/lib/capa/capa/graders/tests.py | 23 +++++ 3 files changed, 121 insertions(+) create mode 100644 common/lib/capa/capa/graders/__init__.py create mode 100644 common/lib/capa/capa/graders/draganddrop.py create mode 100644 common/lib/capa/capa/graders/tests.py diff --git a/common/lib/capa/capa/graders/__init__.py b/common/lib/capa/capa/graders/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py new file mode 100644 index 0000000000..d8618820cf --- /dev/null +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -0,0 +1,98 @@ +""" Grader of drag and drop input. + +Client side behavior: user can drag and drop images from list on base image. +Parameter 'use_targets' in xml can control two use cases. + +if use_targets is true (defaut), then json returned from client is: + { + "use_targets": true, + "draggable": [ + { "image1": "t1" }, + { "ant": "t2" }, + { "molecule": "t3" }, + ] +} +values are target names. + +If use_targets is false: + { + "use_targets": false, + "draggable": [ + { "image1": "(10, 20)" }, + { "ant": "(30, 40)" }, + { "molecule": "(100, 200)" }, + ] +} +values are (x,y) coordinates of centers of dragged images. + +""" + +import json +import math + + +def grade(user_input, correct_answer): + ''' + Grade drag and drop problem. + If use_targets is True - checks if image placed on proper target. + If use_targets is False - checks if image placed on proper coordinates, + with setted radius of forgiveness (default is 10) + + Args: + user_input, correct_answer: json. Format: + + user_input: see module docstring + + correct_answer: + if use_targets is True: + {'1': 't1', 'name_with_icon': 't2'} + else: + {'1': '(10, 10)', 'name_with_icon': '[(10, 10), 20]'} + + Returns: + True or False. + ''' + + user_answer = json.loads(user_input) + + if len(correct_answer.keys()) != len(user_answer['draggables']): + return False + + key = ['position'] + + def is_equal(user_answer, correct_answer): + """ Checks if user_answer is equal to correct_answer inside radius + of forgiveness (default 10 px). + + Args: + user_answer: (x, y) tuple of floats; + correct_answer is one of variants: + - (x, y) tuple of floats + - [(x, y), r], r - radius of forgiveness; + + Returns: bool. + """ + r = 10 + if type(correct_answer) is type(()): # (x, y) case + corr_x = correct_answer[0] + corr_y = correct_answer[1] + else: # [(x, y), r] case + r = correct_answer[1] + corr_x = correct_answer[0][0] + corr_y = correct_answer[0][1] + + if ((user_answer[0] - corr_x) ** 2 + + (user_answer[1] - corr_y) ** 2) > r * r: + return False + + return True + + if user_answer["use_targets"]: + key = ['target_id'] + is_equal = lambda x, y: x == y + + for draggable in user_answer['draggables']: + if not is_equal(draggable[key], correct_answer[key]): + return False + + return True diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py new file mode 100644 index 0000000000..1e0da9411e --- /dev/null +++ b/common/lib/capa/capa/graders/tests.py @@ -0,0 +1,23 @@ +import unittest + +import draganddrop + + +class Test_DragAndDrop(unittest.TestCase): + + def test_1(self): + user_input = '{"laice": "bcc", "points": [["0.00", "1.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}' + correct_answer = {} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + +def suite(): + + testcases = [Test_DragAndDrop] + suites = [] + for testcase in testcases: + suites.append(unittest.TestLoader().loadTestsFromTestCase(testcase)) + return unittest.TestSuite(suites) + +if __name__ == "__main__": + unittest.TextTestRunner(verbosity=2).run(suite()) From 4268f040698271a64300f9eac84dc72f2d27dfdf Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 15:38:48 +0200 Subject: [PATCH 030/140] Updated JS part to use new XML format. --- .../js/capa/drag_and_drop/update_input.js | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 225834c36c..72db2b7849 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -8,34 +8,46 @@ define(['logme'], function (logme) { return updateInput; function updateInput(state) { - var inputEl, stateStr, targets; + var inputEl, stateStr, targets, draggables, c1, c2, tempObj; + + draggables = []; if (state.individualTargets === false) { + for (c1 = 0; c1 < state.draggables.length; c1++) { + if (state.draggables[c1].x !== -1) { + tempObj = {}; + tempObj[state.draggables[c1].id] = { + 'x': state.draggables[c1].x, + 'y': state.draggables[c1].y + }; + + draggables.push(tempObj); + } + } + stateStr = JSON.stringify({ - 'individualTargets': false, - 'draggables': state.draggables + 'targets': false, + 'draggables': draggables }); } else { - targets = []; - (function (c1) { - while (c1 < state.targets.length) { - targets.push({ - 'id': state.targets[c1].id, - 'draggables': state.targets[c1].draggable - }); + for (c1 = 0; c1 < state.targets.length; c1++) { + for (c2 = 0; c2 < state.targets[c1].draggable.length; c2++) { + tempObj = {}; + tempObj[state.targets[c1].draggable[c2]] = state.draggables[c1].id; - c1 += 1; + draggables.push(tempObj); } - }(0)); + } stateStr = JSON.stringify({ - 'individualTargets': true, - 'targets': targets + 'targets': true, + 'draggables': draggables }); } inputEl = $('#input_' + state.problemId); inputEl.val(stateStr); + logme(inputEl.val()); } }); From f9169613fc80f176394edf22db422aa34253150f Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 26 Dec 2012 15:54:15 +0200 Subject: [PATCH 031/140] Making JS work with the new XML format. --- .../{target_container.js => base_image.js} | 6 ++--- .../js/capa/drag_and_drop/config_parser.js | 24 +++++++++---------- .../js/capa/drag_and_drop/draggables.js | 4 ++-- common/static/js/capa/drag_and_drop/main.js | 6 ++--- .../js/capa/drag_and_drop/update_input.js | 1 - 5 files changed, 20 insertions(+), 21 deletions(-) rename common/static/js/capa/drag_and_drop/{target_container.js => base_image.js} (96%) diff --git a/common/static/js/capa/drag_and_drop/target_container.js b/common/static/js/capa/drag_and_drop/base_image.js similarity index 96% rename from common/static/js/capa/drag_and_drop/target_container.js rename to common/static/js/capa/drag_and_drop/base_image.js index b3294a2574..9fa5ef6f52 100644 --- a/common/static/js/capa/drag_and_drop/target_container.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -5,12 +5,12 @@ (function (requirejs, require, define) { define(['logme'], function (logme) { - return TargetContainer; + return BaseImage; - function TargetContainer(state) { + function BaseImage(state) { var targetImgSrc, targetElContainer, mouseMoveDiv; - targetImgSrc = state.config.imageDir + '/' + state.config.target_container; + targetImgSrc = state.config.imageDir + '/' + state.config.base_image; targetElContainer = $( '
    Date: Wed, 26 Dec 2012 15:57:48 +0200 Subject: [PATCH 032/140] started tests and add graders to capa_problem --- common/lib/capa/capa/capa_problem.py | 5 ++++- common/lib/capa/capa/graders/tests.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 2eaa0e4286..96a334ef97 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -34,6 +34,8 @@ import chem import chem.chemcalc import chem.chemtools import chem.miller +import graders +import graders.draganddrop import calc from correctmap import CorrectMap @@ -69,7 +71,8 @@ global_context = {'random': random, 'eia': eia, 'chemcalc': chem.chemcalc, 'chemtools': chem.chemtools, - 'miller': chem.miller} + 'miller': chem.miller, + 'draganddrop': graders.draganddrop} # These should be removed from HTML output, including all subelements html_problem_semantics = ["codeparam", "responseparam", "answer", "script", "hintgroup", "openendedparam","openendedrubric"] diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 1e0da9411e..be37c49679 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -5,9 +5,16 @@ import draganddrop class Test_DragAndDrop(unittest.TestCase): - def test_1(self): - user_input = '{"laice": "bcc", "points": [["0.00", "1.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}' - correct_answer = {} + def test_targets_true(self): + user_input = '{"use_targets": "true", "draggables": \ + ["1": "t1", "name_with_icon": "t2"]}' + correct_answer = {'1': 't1', 'name_with_icon': 't2'} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_targets_true(self): + user_input = '{"use_targets": "true", "draggables": \ + ["1": "t1", "name_with_icon": "t2"]}' + correct_answer = {'1': 't1', 'name_with_icon': 't2'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) From dd9cdc1c2f49c0c45338546fb8856f477c510d5a Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 26 Dec 2012 17:17:12 +0200 Subject: [PATCH 033/140] tests for drag and drop --- common/lib/capa/capa/graders/tests.py | 66 ++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index be37c49679..70208ab327 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -6,17 +6,71 @@ import draganddrop class Test_DragAndDrop(unittest.TestCase): def test_targets_true(self): - user_input = '{"use_targets": "true", "draggables": \ - ["1": "t1", "name_with_icon": "t2"]}' + user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + {"name_with_icon": "t2"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't2'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) - def test_targets_true(self): - user_input = '{"use_targets": "true", "draggables": \ - ["1": "t1", "name_with_icon": "t2"]}' - correct_answer = {'1': 't1', 'name_with_icon': 't2'} + def test_targets_false(self): + user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + {"name_with_icon": "t2"}]}' + correct_answer = {'1': 't3', 'name_with_icon': 't2'} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_multiple_images_per_target_true(self): + user_input = '{"use_targets": true, \ + "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' + correct_answer = {'1': 't1', 'name_with_icon': 't1'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) + def test_multiple_images_per_target_false(self): + user_input = '{"use_targets": true, \ + "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' + correct_answer = {'1': 't2', 'name_with_icon': 't1'} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_targets_and_positions(self): + user_input = '{"use_targets": true, "draggables": [{"1": [10,10]}, \ + {"name_with_icon": [[10,10],4]}]}' + correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_position_and_targets(self): + user_input = '{"use_targets": false, \ + "draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' + correct_answer = {'1': 't1', 'name_with_icon': 't2'} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_positions_exact(self): + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_positions_false(self): + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_positions_true_in_radius(self): + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_positions_true_in_manual_radius(self): + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_positions_false_in_manual_radius(self): + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def suite(): From 3844476314977f16fb6b52ed76803141e4670b6a Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 26 Dec 2012 17:17:37 +0200 Subject: [PATCH 034/140] fixes in algorithm --- common/lib/capa/capa/graders/draganddrop.py | 29 +++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index d8618820cf..07c8f60541 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -28,7 +28,6 @@ values are (x,y) coordinates of centers of dragged images. """ import json -import math def grade(user_input, correct_answer): @@ -58,28 +57,29 @@ def grade(user_input, correct_answer): if len(correct_answer.keys()) != len(user_answer['draggables']): return False - key = ['position'] - def is_equal(user_answer, correct_answer): """ Checks if user_answer is equal to correct_answer inside radius of forgiveness (default 10 px). Args: - user_answer: (x, y) tuple of floats; - correct_answer is one of variants: - - (x, y) tuple of floats - - [(x, y), r], r - radius of forgiveness; + user_answer: [x, y] - list of floats; + correct_answer: [x, y] or [[x, y], r], where + r is radius of forgiveness; Returns: bool. """ + if not isinstance(correct_answer, list) or \ + not isinstance(user_answer, list): + return False + r = 10 - if type(correct_answer) is type(()): # (x, y) case - corr_x = correct_answer[0] - corr_y = correct_answer[1] - else: # [(x, y), r] case + if isinstance(correct_answer[0], list): # [(x, y), r] case r = correct_answer[1] corr_x = correct_answer[0][0] corr_y = correct_answer[0][1] + else: # (x, y) case + corr_x = correct_answer[0] + corr_y = correct_answer[1] if ((user_answer[0] - corr_x) ** 2 + (user_answer[1] - corr_y) ** 2) > r * r: @@ -88,11 +88,12 @@ def grade(user_input, correct_answer): return True if user_answer["use_targets"]: - key = ['target_id'] - is_equal = lambda x, y: x == y + is_equal = lambda user, correct: user == correct if ( + isinstance(user, unicode) and isinstance(correct, str)) else False for draggable in user_answer['draggables']: - if not is_equal(draggable[key], correct_answer[key]): + if not is_equal(draggable.values()[0], + correct_answer[draggable.keys()[0]]): return False return True From d3264dd877bb681506b3db225a2ba38fdbbff6b5 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 26 Dec 2012 18:10:42 +0200 Subject: [PATCH 035/140] rename tag --- common/lib/capa/capa/tests/test_inputtypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 505b536ab6..4a4c6ee5bc 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -513,7 +513,7 @@ class DragAndDropTest(unittest.TestCase): template = "path to template" images = "list of pathes to images" - xml_str = """ Date: Wed, 26 Dec 2012 18:11:09 +0200 Subject: [PATCH 036/140] update format --- .../static/js/capa/drag_and_drop/update_input.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 9898d3b3c9..60057e2674 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -16,31 +16,31 @@ define(['logme'], function (logme) { for (c1 = 0; c1 < state.draggables.length; c1++) { if (state.draggables[c1].x !== -1) { tempObj = {}; - tempObj[state.draggables[c1].id] = { - 'x': state.draggables[c1].x, - 'y': state.draggables[c1].y - }; + tempObj[state.draggables[c1].id] = [ + state.draggables[c1].x, + state.draggables[c1].y + ]; draggables.push(tempObj); } } stateStr = JSON.stringify({ - 'targets': false, + 'use_targets': false, 'draggables': draggables }); } else { for (c1 = 0; c1 < state.targets.length; c1++) { for (c2 = 0; c2 < state.targets[c1].draggable.length; c2++) { tempObj = {}; - tempObj[state.targets[c1].draggable[c2]] = state.draggables[c1].id; + tempObj[state.targets[c1].draggable[c2]] = state.targets[c1].id; draggables.push(tempObj); } } stateStr = JSON.stringify({ - 'targets': true, + 'use_targets': true, 'draggables': draggables }); } From 86d133664aadc4ce2f0948d5c4331953b3831310 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 07:43:49 +0200 Subject: [PATCH 037/140] Fixing bugs. Now resize of window does not mess up targets. Dragging is much smoother on touch pad. Removed border from draggable when it is not in slider. The whole applet fits into min-width of main part of page. --- .../js/capa/drag_and_drop/base_image.js | 22 +++--- .../static/js/capa/drag_and_drop/container.js | 3 + .../js/capa/drag_and_drop/draggables.js | 79 +++++++++++++------ common/static/js/capa/drag_and_drop/main.js | 14 ++-- .../static/js/capa/drag_and_drop/scroller.js | 18 +++-- .../static/js/capa/drag_and_drop/targets.js | 10 +-- 6 files changed, 90 insertions(+), 56 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 9fa5ef6f52..185325512c 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -8,34 +8,34 @@ define(['logme'], function (logme) { return BaseImage; function BaseImage(state) { - var targetImgSrc, targetElContainer, mouseMoveDiv; + var targetImgSrc, baseImageElContainer, mouseMoveDiv; targetImgSrc = state.config.imageDir + '/' + state.config.base_image; - targetElContainer = $( + baseImageElContainer = $( '
    ' ); - state.targetEl = $( + state.baseImageEl = $( '' ); - state.targetEl.appendTo(targetElContainer); + state.baseImageEl.appendTo(baseImageElContainer); - state.targetElWidth = null; + state.baseImageElWidth = null; $('') // Make in memory copy of image to avoid css issues. - .attr('src', state.targetEl.attr('src')) + .attr('src', state.baseImageEl.attr('src')) .load(function () { - state.targetElWidth = this.width; + state.baseImageElWidth = this.width; }); - // state.targetEl.mousemove( + // state.baseImageEl.mousemove( // function (event) { // mouseMoveDiv.html( // '[' + event.offsetX + ', ' + event.offsetY + ']' @@ -53,9 +53,9 @@ define(['logme'], function (logme) { '" ' + '>
    ' ); - mouseMoveDiv.appendTo(targetElContainer); + mouseMoveDiv.appendTo(baseImageElContainer); - targetElContainer.appendTo(state.containerEl); + baseImageElContainer.appendTo(state.containerEl); } }); diff --git a/common/static/js/capa/drag_and_drop/container.js b/common/static/js/capa/drag_and_drop/container.js index 766a020802..e5a7de447f 100644 --- a/common/static/js/capa/drag_and_drop/container.js +++ b/common/static/js/capa/drag_and_drop/container.js @@ -12,6 +12,9 @@ define(['logme'], function (logme) { '
    ' ); diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index bb6b52a7a4..9186c0d837 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -20,6 +20,17 @@ define(['logme', 'update_input'], function (logme, updateInput) { } }(0)); + state.currentMovingDraggable = null; + + $(document).mousemove(function (event) { + normalizeEvent(event); + + if (state.currentMovingDraggable !== null) { + state.currentMovingDraggable.css('left', event.pageX - state.baseImageEl.offset().left - 50); + state.currentMovingDraggable.css('top', event.pageY - state.baseImageEl.offset().top - 50); + } + }); + return; function processDraggable(obj, index) { @@ -81,7 +92,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableContainerEl.mousedown(mouseDown); draggableContainerEl.mouseup(mouseUp); draggableContainerEl.mousemove(mouseMove); - draggableContainerEl.mouseleave(mouseLeave); + // draggableContainerEl.mouseleave(mouseLeave); if (state.individualTargets === false) { updateInput(state); @@ -91,12 +102,16 @@ define(['logme', 'update_input'], function (logme, updateInput) { function mouseDown(event) { if (mousePressed === false) { + state.currentMovingDraggable = draggableContainerEl; + normalizeEvent(event); + if (inContainer === true) { draggableContainerEl.detach(); + draggableContainerEl.css('border', 'none'); draggableContainerEl.css('position', 'absolute'); - draggableContainerEl.css('left', event.pageX - 50); - draggableContainerEl.css('top', event.pageY - 50); - draggableContainerEl.appendTo(state.containerEl); + draggableContainerEl.css('left', event.pageX - state.baseImageEl.offset().left - 50); + draggableContainerEl.css('top', event.pageY - state.baseImageEl.offset().top - 50); + draggableContainerEl.appendTo(state.baseImageEl.parent()); inContainer = false; } @@ -109,40 +124,43 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - function mouseUp() { + function mouseUp(event) { if (mousePressed === true) { - checkLandingElement(); + state.currentMovingDraggable = null; + normalizeEvent(event); + + checkLandingElement(event); } } function mouseMove() { if (mousePressed === true) { - draggableContainerEl.css('left', (event.pageX - 50)); - draggableContainerEl.css('top', (event.pageY - 50)); + draggableContainerEl.css('left', event.pageX - state.baseImageEl.offset().left - 50); + draggableContainerEl.css('top', event.pageY - state.baseImageEl.offset().top - 50); } } - function mouseLeave() { - if (mousePressed === true) { - checkLandingElement(); - } - } + // function mouseLeave(event) { + // if (mousePressed === true) { + // normalizeEvent(event); + // + // checkLandingElement(event); + // } + // } - function checkLandingElement() { - var offsetDE, offsetTE, indexes, DEindex, targetFound; + function checkLandingElement(event) { + var offsetDE, indexes, DEindex, targetFound; mousePressed = false; - offsetDE = draggableContainerEl.offset(); + offsetDE = draggableContainerEl.position(); if (state.individualTargets === false) { - offsetTE = state.targetEl.offset(); - if ( - (offsetDE.left < offsetTE.left) || - (offsetDE.left + 100 > offsetTE.left + state.targetEl.width()) || - (offsetDE.top < offsetTE.top) || - (offsetDE.top + 100 > offsetTE.top + state.targetEl.height()) + (offsetDE.left < 0) || + (offsetDE.left + 100 > state.baseImageEl.width()) || + (offsetDE.top < 0) || + (offsetDE.top + 100 > state.baseImageEl.height()) ) { moveBackToSlider(); @@ -151,8 +169,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } else { correctZIndexes(); - draggableObj.x = offsetDE.left + 50 - offsetTE.left; - draggableObj.y = offsetDE.top + 50 - offsetTE.top; + draggableObj.x = offsetDE.left + 50; + draggableObj.y = offsetDE.top + 50; } } else if (state.individualTargets === true) { targetFound = false; @@ -238,8 +256,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function snapToTarget(target) { - draggableContainerEl.css('left', (target.offset.left + 0.5 * target.w - 50)); - draggableContainerEl.css('top', (target.offset.top + 0.5 * target.h - 50)); + draggableContainerEl.css('left', target.offset.left + 0.5 * target.w - 50); + draggableContainerEl.css('top', target.offset.top + 0.5 * target.h - 50); } function correctZIndexes() { @@ -288,10 +306,19 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableContainerEl.appendTo(state.sliderEl); inContainer = true; } + + draggableContainerEl.css('border', '1px solid gray'); } } } + function normalizeEvent(event) { + if(!event.offsetX) { + event.offsetX = (event.pageX - $(event.target).offset().left); + event.offsetY = (event.pageY - $(event.target).offset().top); + } + return event; + } } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 7a18eacd93..9027f55d01 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -32,13 +32,6 @@ define( return; } - imageDir = $(value).attr('data-course-folder'); - if (typeof imageDir !== 'string') { - logme('ERROR: Could not find the name of the image directory.'); - - return; - } - try { config = JSON.parse($('#drag_and_drop_json_' + problemId).html()); } catch (err) { @@ -48,6 +41,13 @@ define( return; } + imageDir = $(value).attr('data-course-folder'); + if (typeof imageDir !== 'string') { + logme('ERROR: Could not find the name of the image directory.'); + + return; + } + state = State(problemId); if (configParser(config, imageDir, state) !== true) { diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index fc11740611..6c2950e458 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -13,7 +13,7 @@ define(['logme'], function (logme) { parentEl = $( '
    ' ); - tEl.appendTo(state.targetEl.parent()); + tEl.appendTo(state.baseImageEl.parent()); state.targets.push({ 'id': obj.id, - 'offset': tEl.offset(), + 'offset': tEl.position(), 'w': obj.w, 'h': obj.h, 'el': tEl, From 3bbe0759ea4c09398b6e946736ef51b377fa3e74 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 10:36:11 +0200 Subject: [PATCH 038/140] Added support for reading answer from input element (as set by server). --- .../js/capa/drag_and_drop/draggables.js | 34 ++--- .../static/js/capa/drag_and_drop/scroller.js | 22 ++- common/static/js/capa/drag_and_drop/state.js | 5 +- .../static/js/capa/drag_and_drop/targets.js | 33 +++-- .../js/capa/drag_and_drop/update_input.js | 136 +++++++++++++++++- 5 files changed, 196 insertions(+), 34 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 9186c0d837..01fc612c5c 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -8,16 +8,21 @@ define(['logme', 'update_input'], function (logme, updateInput) { return Draggables; function Draggables(state) { - var _draggables; + var _draggables, numDraggables; + numDraggables = state.config.draggables.length; _draggables = []; state.draggables = []; (function (i) { - while (i < state.config.draggables.length) { + while (i < numDraggables) { processDraggable(state.config.draggables[i], i + 1); i += 1; } + + if (state.individualTargets === false) { + updateInput(state, true); + } }(0)); state.currentMovingDraggable = null; @@ -33,7 +38,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; - function processDraggable(obj, index) { + function processDraggable(obj, objIndex) { var draggableContainerEl, imgEl, inContainer, ousePressed, onTarget, draggableObj, marginCss; @@ -45,10 +50,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'display: inline; ' + 'float: left; ' + 'overflow: hidden; ' + - 'z-index: ' + index + '; ' + + 'z-index: ' + objIndex + '; ' + 'border: 1px solid gray; ' + '" ' + - 'data-draggable-position-index="' + index + '" ' + + 'data-draggable-position-index="' + objIndex + '" ' + '>
    ' ); @@ -84,18 +89,21 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj = { 'id': obj.id, + 'el': draggableContainerEl, 'x': -1, - 'y': -1 + 'y': -1, + + 'setInContainer': function (val) { inContainer = val; }, + 'setOnTarget': function (val) { onTarget = val; }, }; state.draggables.push(draggableObj); draggableContainerEl.mousedown(mouseDown); draggableContainerEl.mouseup(mouseUp); draggableContainerEl.mousemove(mouseMove); - // draggableContainerEl.mouseleave(mouseLeave); - if (state.individualTargets === false) { - updateInput(state); + if (objIndex + 1 === numDraggables) { + state.draggablesLoaded = true; } return; @@ -140,14 +148,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - // function mouseLeave(event) { - // if (mousePressed === true) { - // normalizeEvent(event); - // - // checkLandingElement(event); - // } - // } - function checkLandingElement(event) { var offsetDE, indexes, DEindex, targetFound; diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 6c2950e458..fcd68613cb 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -59,7 +59,16 @@ define(['logme'], function (logme) { ); moveLeftEl.appendTo(parentEl); - moveLeftEl.click(function () { + // The below is necessary to prevent the browser thinking that we want + // to perform a drag operation, or a highlight operation. If we don't + // do this, the browser will then highlight with a gray shade the + // element. + moveLeftEl.mousemove(function (event) { event.preventDefault(); }); + moveLeftEl.mousedown(function (event) { event.preventDefault(); }); + + moveLeftEl.mouseup(function (event) { + event.preventDefault(); + if (showElLeftMargin > -102) { return; } @@ -135,7 +144,16 @@ define(['logme'], function (logme) { ); moveRightEl.appendTo(parentEl); - moveRightEl.click(function () { + // The below is necessary to prevent the browser thinking that we want + // to perform a drag operation, or a highlight operation. If we don't + // do this, the browser will then highlight with a gray shade the + // element. + moveRightEl.mousemove(function (event) { event.preventDefault(); }); + moveRightEl.mousedown(function (event) { event.preventDefault(); }); + + moveRightEl.mouseup(function (event) { + event.preventDefault(); + if (showElLeftMargin < -102 * (state.sliderEl.children().length - 6)) { return; } diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index 7ebf89d023..eb693347d7 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -9,7 +9,10 @@ define([], function () { function State(problemId) { return { - 'problemId': problemId + 'problemId': problemId, + + 'targetsLoaded': false, + 'draggablesLoaded': false }; } }); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index c1a5b5e265..8a98cee473 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -8,28 +8,37 @@ define(['logme', 'update_input'], function (logme, updateInput) { return Targets; function Targets(state) { + var numTargets; + + numTargets = state.config.targets.length; state.targets = []; (function (c1) { - while (c1 < state.config.targets.length) { - processTarget(state.config.targets[c1]); + while (c1 < numTargets) { + processTarget(state.config.targets[c1], c1); c1 += 1; } + + if (state.individualTargets === true) { + updateInput(state, true); + } }(0)); return; - function processTarget(obj) { + function processTarget(obj, objIndex) { var baseImageElOffset, tEl, left, borderCss; - if (state.baseImageElWidth === null) { - window.setTimeout(function () { - processTarget(obj); - }, 50); - return; - } + // if (state.baseImageElWidth === null) { + // window.setTimeout(function () { + // processTarget(obj); + // }, 50); + // + // return; + // } - left = obj.x + 0.5 * (state.baseImageEl.parent().width() - state.baseImageElWidth); + // left = obj.x + 0.5 * (state.baseImageEl.parent().width() - state.baseImageElWidth); + left = obj.x; borderCss = ''; if (state.config.target_outline === true) { @@ -62,8 +71,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'draggable': [] }); - if (state.individualTargets === true) { - updateInput(state); + if (objIndex + 1 === numTargets) { + state.targetsLoaded = true; } } } diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 60057e2674..ed49f3dd85 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -7,9 +7,15 @@ define(['logme'], function (logme) { return updateInput; - function updateInput(state) { + function updateInput(state, checkFirst) { var inputEl, stateStr, targets, draggables, c1, c2, tempObj; + if (checkFirst === true) { + if (checkIfHasAnswer() === true) { + return; + } + } + draggables = []; if (state.individualTargets === false) { @@ -47,8 +53,134 @@ define(['logme'], function (logme) { inputEl = $('#input_' + state.problemId); inputEl.val(stateStr); - } + return; + + // Check if input has an answer from server. If yes, then position + // all draggables according to answer. + function checkIfHasAnswer() { + var inputElVal; + + inputElVal = $('#input_' + state.problemId).val(); + if (inputElVal.length === 0) { + return false; + } + + repositionDraggables(JSON.parse(inputElVal)); + + return true; + } + + function repositionDraggables(answer) { + var draggableId, draggable, targetId, target, draggablePosition, + c1; + + if ( + ((state.individualTargets === true) && (state.targetsLoaded === false)) || + (state.draggablesLoaded === false) + ) { + window.setTimeout(function () { + repositionDraggables(answer); + }, 50); + + return; + } + + if ( + ((typeof answer.use_targets === 'boolean') && (answer.use_targets === true)) || + ((typeof answer.use_targets === 'string') && (answer.use_targets === 'true')) + ) { + for (c1 = 0; c1 < answer.draggables.length; c1++) { + for (draggableId in answer.draggables[c1]) { + if ((draggable = getDraggableById(draggableId)) === null) { + logme('ERROR: In answer there exists a draggable ID "' + draggableId + '". No draggable with this ID could be found.'); + + continue; + } + + targetId = answer.draggables[c1][draggableId]; + if ((target = getTargetById(targetId)) === null) { + logme('ERROR: In answer there exists a target ID "' + targetId + '". No target with this ID could be found.'); + + continue; + } + + + draggable.setInContainer(false); + + draggable.el.detach(); + draggable.el.css('border', 'none'); + draggable.el.css('position', 'absolute'); + draggable.el.css('left', answer.draggables[c1][draggableId][0] - 50); + draggable.el.css('top', answer.draggables[c1][draggableId][1] - 50); + + draggable.el.css('left', target.offset.left + 0.5 * target.w - 50); + draggable.el.css('top', target.offset.top + 0.5 * target.h - 50); + + draggable.el.appendTo(state.baseImageEl.parent()); + + draggable.setOnTarget(target); + target.draggable.push(draggableId); + } + } + } else if ( + ((typeof answer.use_targets === 'boolean') && (answer.use_targets === false)) || + ((typeof answer.use_targets === 'string') && (answer.use_targets === 'false')) + ) { + for (c1 = 0; c1 < answer.draggables.length; c1++) { + for (draggableId in answer.draggables[c1]) { + if ((draggable = getDraggableById(draggableId)) === null) { + logme('ERROR: In answer there exists a draggable ID "' + draggableId + '". No draggable with this ID could be found.'); + + continue; + } + + draggable.setInContainer(false); + + draggable.el.detach(); + draggable.el.css('border', 'none'); + draggable.el.css('position', 'absolute'); + draggable.el.css('left', answer.draggables[c1][draggableId][0] - 50); + draggable.el.css('top', answer.draggables[c1][draggableId][1] - 50); + draggable.el.appendTo(state.baseImageEl.parent()); + + draggable.x = answer.draggables[c1][draggableId][0]; + draggable.y = answer.draggables[c1][draggableId][1]; + } + } + } else { + logme('ERROR: The type of answer.targets is not supported. answer.targets = ', answer.targets); + + return; + } + } + + return; + + function getDraggableById(id) { + var c1; + + for (c1 = 0; c1 < state.draggables.length; c1 += 1) { + if (state.draggables[c1].id === id) { + return state.draggables[c1]; + } + } + + return null; + } + + function getTargetById(id) { + var c1; + + for (c1 = 0; c1 < state.targets.length; c1 += 1) { + if (state.targets[c1].id === id) { + return state.targets[c1]; + } + } + + return null; + } + } }); // End of wrapper for RequireJS. As you can see, we are passing From 9ef0c563ef12bd122d5a3859699e51cea60ecdbc Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 12:11:33 +0200 Subject: [PATCH 039/140] Slight changes in UI styling. --- .../js/capa/drag_and_drop/draggables.js | 4 ++ .../static/js/capa/drag_and_drop/scroller.js | 48 ++++++++++++------- .../js/capa/drag_and_drop/update_input.js | 2 + 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 01fc612c5c..acabe50034 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -104,6 +104,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { if (objIndex + 1 === numDraggables) { state.draggablesLoaded = true; + + state.updateArrowOpacity(); } return; @@ -185,6 +187,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } + state.updateArrowOpacity(); + updateInput(state); return; diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index fcd68613cb..d9a43e7735 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -8,7 +8,7 @@ define(['logme'], function (logme) { return Scroller; function Scroller(state) { - var parentEl, moveLeftEl, showEl, moveRightEL, showElLeftMargin; + var parentEl, moveLeftEl, showEl, moveRightEl, showElLeftMargin; parentEl = $( '
    ' + '
    ' + '
    ' @@ -77,7 +72,10 @@ define(['logme'], function (logme) { state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' }, 100, function () { - // Check if at the end, and make arrow less visible. + // Check if at the end, and make arrow less visibl. + logme('showElLeftMargin = ' + showElLeftMargin); + + updateArrowOpacity(); }); }); @@ -117,11 +115,8 @@ define(['logme'], function (logme) { '>' + '
    ' + '
    ' @@ -159,15 +152,36 @@ define(['logme'], function (logme) { } showElLeftMargin -= 102; + state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' }, 100, function () { // Check if at the end, and make arrow less visible. + logme('showElLeftMargin = ' + showElLeftMargin); + logme('-102 * (state.sliderEl.children().length - 6) = ' + (-102 * (state.sliderEl.children().length - 6))); + + updateArrowOpacity(); }); }); parentEl.appendTo(state.containerEl); - } + + state.updateArrowOpacity = updateArrowOpacity; + + return; + + function updateArrowOpacity() { + moveLeftEl.children('div').css('opacity', '1'); + moveRightEl.children('div').css('opacity', '1'); + + if (showElLeftMargin < -102 * (state.sliderEl.children().length - 6)) { + moveRightEl.children('div').css('opacity', '.4'); + } + if (showElLeftMargin > -102) { + moveLeftEl.children('div').css('opacity', '.4'); + } + } + } // End-of: function Scroller(state) }); // End of wrapper for RequireJS. As you can see, we are passing diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index ed49f3dd85..7b5cd6acfa 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -153,6 +153,8 @@ define(['logme'], function (logme) { return; } + + state.updateArrowOpacity(); } return; From 3abdd1801d176e9b1a93b0106caf8b7621ec2379 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 12:18:51 +0200 Subject: [PATCH 040/140] Removed debug comments. --- common/static/js/capa/drag_and_drop/scroller.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index d9a43e7735..46af42bb1b 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -72,9 +72,6 @@ define(['logme'], function (logme) { state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' }, 100, function () { - // Check if at the end, and make arrow less visibl. - logme('showElLeftMargin = ' + showElLeftMargin); - updateArrowOpacity(); }); }); @@ -156,10 +153,6 @@ define(['logme'], function (logme) { state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' }, 100, function () { - // Check if at the end, and make arrow less visible. - logme('showElLeftMargin = ' + showElLeftMargin); - logme('-102 * (state.sliderEl.children().length - 6) = ' + (-102 * (state.sliderEl.children().length - 6))); - updateArrowOpacity(); }); }); From 13d53539f6c29bb37aab6baf0f1551bde3ea93ce Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 27 Dec 2012 12:39:37 +0200 Subject: [PATCH 041/140] bug fix: exception when correct answer not contain some user answer --- common/lib/capa/capa/graders/draganddrop.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 07c8f60541..ddd94f13fc 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -92,8 +92,11 @@ def grade(user_input, correct_answer): isinstance(user, unicode) and isinstance(correct, str)) else False for draggable in user_answer['draggables']: - if not is_equal(draggable.values()[0], - correct_answer[draggable.keys()[0]]): + user_img_location = draggable.values()[0] + corr_img_location = correct_answer.get(draggable.keys()[0], None) + if not corr_img_location: + return False + if not is_equal(user_img_location, corr_img_location): return False return True From 58483319a30706a22686e3d0b5b40fe4ad846321 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 12:40:05 +0200 Subject: [PATCH 042/140] Refactoring. --- .../js/capa/drag_and_drop/draggables.js | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index acabe50034..714fe19597 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -213,38 +213,30 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function checkIfOnTarget() { - var c1, c2, target; + var c1, target; - c1 = 0; - - while (c1 < state.targets.length) { + for (c1 = 0; c1 < state.targets.length; c1++) { target = state.targets[c1]; if (offsetDE.top + 50 < target.offset.top) { - c1 += 1; continue; } if (offsetDE.top + 50 > target.offset.top + target.h) { - c1 += 1; continue; } if (offsetDE.left + 50 < target.offset.left) { - c1 += 1; continue; } if (offsetDE.left + 50 > target.offset.left + target.w) { - c1 += 1; continue; } - if (state.config.one_per_target === true) { - if ( - (target.draggable.length === 1) && - (target.draggable[0] !== obj.id) - ) { - c1 += 1; - continue; - } + if ( + (state.config.one_per_target === true) && + (target.draggable.length === 1) && + (target.draggable[0] !== obj.id) + ) { + continue; } targetFound = true; From d7801fdabe521ecece64eb6245930a820900e29e Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 27 Dec 2012 12:41:09 +0200 Subject: [PATCH 043/140] add test for different keys in correct and iser answer --- common/lib/capa/capa/graders/tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 70208ab327..7b0b719a6a 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -71,6 +71,12 @@ class Test_DragAndDrop(unittest.TestCase): correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def test_correct_answer_not_has_key_from_user_answer(self): + user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + {"name_with_icon": "t2"}]}' + correct_answer = {'3': 't3', 'name_with_icon': 't2'} + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def suite(): From f2e930a395932dd4e6e4dc5a4404838daaa703ba Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 27 Dec 2012 13:27:55 +0200 Subject: [PATCH 044/140] small improvements --- common/lib/capa/capa/graders/draganddrop.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index ddd94f13fc..1e847b105b 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -46,7 +46,7 @@ def grade(user_input, correct_answer): if use_targets is True: {'1': 't1', 'name_with_icon': 't2'} else: - {'1': '(10, 10)', 'name_with_icon': '[(10, 10), 20]'} + {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} Returns: True or False. @@ -68,8 +68,7 @@ def grade(user_input, correct_answer): Returns: bool. """ - if not isinstance(correct_answer, list) or \ - not isinstance(user_answer, list): + if not isinstance(correct_answer, list) or isinstance(user_answer, list): return False r = 10 From 108b0fc384c1982a6b8704aaedc76873d8be7730 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 18:03:28 +0200 Subject: [PATCH 045/140] Refactoring and code documentation. --- .../js/capa/drag_and_drop/base_image.js | 34 +--- .../js/capa/drag_and_drop/config_parser.js | 4 +- .../js/capa/drag_and_drop/draggables.js | 156 ++++++++++-------- common/static/js/capa/drag_and_drop/main.js | 13 +- .../static/js/capa/drag_and_drop/scroller.js | 25 +++ common/static/js/capa/drag_and_drop/state.js | 2 + .../static/js/capa/drag_and_drop/targets.js | 52 ++---- .../js/capa/drag_and_drop/update_input.js | 13 +- 8 files changed, 149 insertions(+), 150 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 185325512c..9cee42feb2 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -8,53 +8,25 @@ define(['logme'], function (logme) { return BaseImage; function BaseImage(state) { - var targetImgSrc, baseImageElContainer, mouseMoveDiv; - - targetImgSrc = state.config.imageDir + '/' + state.config.base_image; + var baseImageElContainer; baseImageElContainer = $( '
    ' ); state.baseImageEl = $( '' ); state.baseImageEl.appendTo(baseImageElContainer); - state.baseImageElWidth = null; - $('') // Make in memory copy of image to avoid css issues. - .attr('src', state.baseImageEl.attr('src')) - .load(function () { - state.baseImageElWidth = this.width; - }); - - // state.baseImageEl.mousemove( - // function (event) { - // mouseMoveDiv.html( - // '[' + event.offsetX + ', ' + event.offsetY + ']' - // ); - // } - // ); - - mouseMoveDiv = $( - '
    ' - ); - mouseMoveDiv.appendTo(baseImageElContainer); - baseImageElContainer.appendTo(state.containerEl); } }); diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 58f6114009..664324e0b9 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -78,9 +78,9 @@ define(['logme'], function (logme) { if (typeof config.target_outline === 'string') { if (config.target_outline.toLowerCase() === 'true') { - state.config.target_outline = true; + state.config.targetOutline = true; } else if (config.target_outline.toLowerCase() === 'false') { - state.config.target_outline = false; + state.config.targetOutline = false; } else { logme('ERROR: Property config.target_outline can either be "true", or "false".'); returnStatus = false; diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 714fe19597..faf0f7b3f3 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -8,22 +8,13 @@ define(['logme', 'update_input'], function (logme, updateInput) { return Draggables; function Draggables(state) { - var _draggables, numDraggables; + var c1; - numDraggables = state.config.draggables.length; - _draggables = []; state.draggables = []; - (function (i) { - while (i < numDraggables) { - processDraggable(state.config.draggables[i], i + 1); - i += 1; - } - - if (state.individualTargets === false) { - updateInput(state, true); - } - }(0)); + for (c1 = 0; c1 < state.config.draggables.length; c1 += 1) { + processDraggable(state.config.draggables[c1], c1 + 1); + } state.currentMovingDraggable = null; @@ -39,8 +30,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; function processDraggable(obj, objIndex) { - var draggableContainerEl, imgEl, inContainer, ousePressed, - onTarget, draggableObj, marginCss; + var draggableContainerEl, inContainer, mousePressed, onTarget, + draggableObj, marginCss; draggableContainerEl = $( '
    ' ); if (obj.icon.length > 0) { - imgEl = $( - '' + draggableContainerEl.append( + $('') ); - - draggableContainerEl.append(imgEl); } if (obj.label.length > 0) { @@ -80,7 +67,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { } draggableContainerEl.appendTo(state.sliderEl); - _draggables.push(draggableContainerEl); inContainer = true; mousePressed = false; @@ -102,12 +88,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableContainerEl.mouseup(mouseUp); draggableContainerEl.mousemove(mouseMove); - if (objIndex + 1 === numDraggables) { - state.draggablesLoaded = true; - - state.updateArrowOpacity(); - } - return; function mouseDown(event) { @@ -134,10 +114,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - function mouseUp(event) { + function mouseUp() { if (mousePressed === true) { state.currentMovingDraggable = null; - normalizeEvent(event); checkLandingElement(event); } @@ -150,14 +129,30 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - function checkLandingElement(event) { + // 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 offsetDE, indexes, DEindex, targetFound; mousePressed = false; offsetDE = draggableContainerEl.position(); - if (state.individualTargets === false) { + if (state.individualTargets === true) { + targetFound = false; + + checkIfOnTarget(); + + if (targetFound === true) { + correctZIndexes(); + } else { + moveBackToSlider(); + removeObjIdFromTarget(); + } + } else { if ( (offsetDE.left < 0) || (offsetDE.left + 100 > state.baseImageEl.width()) || @@ -174,21 +169,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.x = offsetDE.left + 50; draggableObj.y = offsetDE.top + 50; } - } else if (state.individualTargets === true) { - targetFound = false; - - checkIfOnTarget(); - - if (targetFound === true) { - correctZIndexes(); - } else { - moveBackToSlider(); - removeObjIdFromTarget(); - } } state.updateArrowOpacity(); - updateInput(state); return; @@ -197,25 +180,26 @@ define(['logme', 'update_input'], function (logme, updateInput) { var c1; if (onTarget !== null) { - c1 = 0; - - while (c1 < onTarget.draggable.length) { + for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) { if (onTarget.draggable[c1] === obj.id) { onTarget.draggable.splice(c1, 1); break; } - c1 += 1; } onTarget = null; } } + // 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. function checkIfOnTarget() { var c1, target; - for (c1 = 0; c1 < state.targets.length; c1++) { + for (c1 = 0; c1 < state.targets.length; c1 += 1) { target = state.targets[c1]; if (offsetDE.top + 50 < target.offset.top) { @@ -241,10 +225,21 @@ define(['logme', 'update_input'], function (logme, updateInput) { targetFound = true; - removeObjIdFromTarget(); - onTarget = target; + // If the draggable was moved from one target to + // another, then we need to remove it's ID from the + // previous target's draggables list, and add it to the + // new target's draggables list. + if ((onTarget !== null) && (onTarget.id !== target.id)) { + removeObjIdFromTarget(); + onTarget = target; + target.draggable.push(obj.id); + } else if (onTarget === null) { + onTarget = target; + target.draggable.push(obj.id); + } - target.draggable.push(obj.id); + // Reposition the draggable so that it's center + // coincides with the center of the target. snapToTarget(target); break; @@ -256,30 +251,47 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableContainerEl.css('top', target.offset.top + 0.5 * target.h - 50); } + // 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; - c1 = 0; - - while (c1 < _draggables.length) { - if (parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < parseInt(_draggables[c1].css('z-index'), 10)) { - _draggables[c1].css('z-index', parseInt(_draggables[c1].css('z-index'), 10) - 1); + for (c1 = 0; c1 < state.draggables.length; c1++) { + if ( + parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < + parseInt(state.draggables[c1].el.css('z-index'), 10) + ) { + state.draggables[c1].el.css( + 'z-index', + parseInt(state.draggables[c1].el.css('z-index'), 10) - 1 + ); } - c1 += 1; } draggableContainerEl.css('z-index', c1); } + // 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; draggableContainerEl.detach(); draggableContainerEl.css('position', 'static'); + // Get the position indexes of all draggables that are + // currently in the slider, along with the corresponding + // jQuery element. indexes = []; - DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10); - state.sliderEl.children().each(function (index, value) { indexes.push({ 'index': parseInt($(value).attr('data-draggable-position-index'), 10), @@ -287,27 +299,39 @@ define(['logme', 'update_input'], function (logme, updateInput) { }); }); - c1 = 0; + // Get the position index of the element that we are + // inserting back into the slider. + DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10); - while (c1 < indexes.length) { + // Starting from the first position index that we + // retrieved, and going up, if we find a position index + // that is more than 'DEindex', we know that we must insert + // the current element before the element with the greater + // position index. + for (c1 = 0; c1 < indexes.length; c1 += 1) { if ((inContainer === false) && (indexes[c1].index > DEindex)) { indexes[c1].el.before(draggableContainerEl); inContainer = true; } - - c1 += 1; } + // If we did not find a greater postion index, then either + // there are no elements in the slider, or all of them + // have a lesser position index. In both cases we add the + // current draggable to the end. if (inContainer === false) { draggableContainerEl.appendTo(state.sliderEl); - inContainer = true; } + inContainer = true; + draggableContainerEl.css('border', '1px solid gray'); } } } + // In firefox the event does not have a proper pageX and pageY + // coordinates. function normalizeEvent(event) { if(!event.offsetX) { event.offsetX = (event.pageX - $(event.target).offset().left); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 9027f55d01..0ccfb47051 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -5,8 +5,10 @@ (function (requirejs, require, define) { define( - ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets'], - function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets) { + ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', + 'draggables', 'targets', 'update_input'], + function (logme, State, configParser, Container, BaseImage, Scroller, + Draggables, Targets, updateInput) { return Main; function Main() { @@ -58,12 +60,13 @@ define( Container(state); BaseImage(state); + Targets(state); Scroller(state); Draggables(state); - Targets(state); - logme('config', config); - logme('state', state); + // Update the input element, checking first that it is not filled with + // an answer from the server. + updateInput(state, true); } }); diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 46af42bb1b..d3a4d23eab 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -61,14 +61,20 @@ define(['logme'], function (logme) { moveLeftEl.mousemove(function (event) { event.preventDefault(); }); moveLeftEl.mousedown(function (event) { event.preventDefault(); }); + // This event will be responsible for moving the scroller left. + // Hidden draggables will be shown. moveLeftEl.mouseup(function (event) { event.preventDefault(); + // When there are no more hidden draggables, prevent from + // scrolling infinitely. if (showElLeftMargin > -102) { return; } showElLeftMargin += 102; + + // We scroll by changing the 'margin-left' CSS property smoothly. state.sliderEl.animate({ 'margin-left': showElLeftMargin + 'px' }, 100, function () { @@ -91,6 +97,10 @@ define(['logme'], function (logme) { showElLeftMargin = 0; + // Element where the draggables will be contained. It is very long + // so that any SANE number of draggables will fit in a single row. It + // will be contained in a parent element whose 'overflow' CSS value + // will be hidden, preventing the long row from fully being visible. state.sliderEl = $( '
    ' ); - tEl.appendTo(state.baseImageEl.parent()); + targetEl.appendTo(state.baseImageEl.parent()); state.targets.push({ 'id': obj.id, - 'offset': tEl.position(), + 'w': obj.w, 'h': obj.h, - 'el': tEl, + + 'el': targetEl, + 'offset': targetEl.position(), + 'draggable': [] }); - - if (objIndex + 1 === numTargets) { - state.targetsLoaded = true; - } } } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 7b5cd6acfa..6ebf62ef11 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -54,6 +54,8 @@ define(['logme'], function (logme) { inputEl = $('#input_' + state.problemId); inputEl.val(stateStr); + logme(inputEl.val()); + return; // Check if input has an answer from server. If yes, then position @@ -75,16 +77,7 @@ define(['logme'], function (logme) { var draggableId, draggable, targetId, target, draggablePosition, c1; - if ( - ((state.individualTargets === true) && (state.targetsLoaded === false)) || - (state.draggablesLoaded === false) - ) { - window.setTimeout(function () { - repositionDraggables(answer); - }, 50); - - return; - } + logme(answer); if ( ((typeof answer.use_targets === 'boolean') && (answer.use_targets === true)) || From 505fefb802444a54b84211e4241ce7401ffd4006 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 27 Dec 2012 18:18:53 +0200 Subject: [PATCH 046/140] Initiated DOCS for Drag and Drop. --- docs/source/drag_and_drop_input.rst | 352 ++++++++++++++++++++++++++++ docs/source/xml_formats.rst | 3 +- 2 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 docs/source/drag_and_drop_input.rst diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst new file mode 100644 index 0000000000..d977270269 --- /dev/null +++ b/docs/source/drag_and_drop_input.rst @@ -0,0 +1,352 @@ +********************************************** +Xml format of drag and drop input [inputtypes] +********************************************** + +.. module:: drag_and_drop_input + +Format description +================== + +Graphical slider tool (GST) main tag is:: + + BODY + +``graphical_slider_tool`` tag must have two children tags: ``render`` +and ``configuration``. + +Render tag +---------- + +Render tag can contain usual html tags mixed with some GST specific tags:: + + - represents jQuery slider + - represents input + - represents Flot plot + +Also GST will track all elements inside ```` where ``id`` +attribute is set. + +The contents of the section will be shown to the user after +all occurrences of:: + + + + + +have been converted to actual sliders, text inputs, and a plot graph. +Everything in square brackets is optional. + +``{parameter name}`` specifies the parameter to which the slider or text +input will be attached to. + +There is a one-to-one relationship between a slider and a parameter. +I.e. for one parameter you can put only one ```` section. However, you don't have to specify a slider - they +are optional. + +There is a many-to-one relationship between text inputs and a +parameter. I.e. for one parameter you can put many '`` section. However, you don't have to specify a text +input - they are optional. + +You can put only one ```` section. It is not +required. + +NOTE: MathJax, independently of this module, will render all TeX code +within the ```` section into nice mathematical formulas. Just +remember to wrap it in one of:: + + \( and \) - for inline formulas (formulas surrounded by + standard text) + \[ and \] - if you want the formula to be a separate line + +Slider tag +.......... + +Slider tag must have ``var`` attribute and optional ``style`` attribute:: + + + +After processing slider tags will be replaced by jQuery UI sliders with applied +``style`` attribute. + + +``Var`` attribute must correspond to parameter in one of ``function`` tags in +``functions`` tag. By moving slider, value of parameter ``a`` will change, and so +result of function, that depends on parameter ``a``, will change. + + +Textbox tag +........... + + +Texbox tag must have ``var`` attribute and optional ``style`` and ``readonly`` +attributes:: + + + +After processing tetbox tags will be replaced by html inputs with applied +``style`` attribute. If ``readonly`` is set to ``true`` input will be +not-editable, default is ``false``. + +``Var`` attribute must correspond to parameter in one of ``function`` tags in +``functions`` tag. By entering value in input, value of parameter ``b`` will change, and so +result of function, that depends on parameter ``b``, will change. + +Plot tag +........ + +Plot tag may have optional ``style`` attribute:: + + + +After processing plot tags will be replaced by Flot plot with applied +``style`` attribute. + +HTML tags with id +................. + +Any html tag with id, e.g. ```` will be counted as +place where result of function can be rendered. To render function result to +element, element id must be included in ``function`` tag as ``el_id`` attribute +and ``output`` value must be ``"element"``:: + + + function add(a, b, precision) { + var x = Math.pow(10, precision || 2); + return (Math.round(a * x) + Math.round(b * x)) / x; + } + + return add(a, b, 5); + + + + +Configuration tag +----------------- + +The configuration tag contain sparameter settings, graph +settings, and function definitions which are to be plotted on the +graph and that use specified parameters. + +Configuration tag contains two mandatory tag ``functions`` and ``parameters`` and +may contain another ``plot`` tag. + +Parameters tag +.............. + +``Parameters`` tag contains ``parameter`` tags. Each ``parameter`` tag must have +``var``, ``max``, ``min``, ``step`` and ``initial`` attributes:: + + + + + + +``Var`` attribute links min, max, step and initial values to parameter name. +``Min`` attribute is minimal value that parameter can take. Slider and input +values can not go below it. + +``Max`` attribute is maximal value that parameter can take. Slider and input +values can not go over it. + +``Step`` attribute is value of slider step. When a slider increase or decreases +the specified parameter, it will do so by the amount specified with 'step' + +``Initial`` attribute is the initial value that the specified parameter should be +set to. + +The parameter's name is specified by the 'var' property. All occurrences +of sliders and/or text inputs that specify a 'var' property, will be +connected to this parameter - i.e. they will reflect the current +value of the parameter and will be updated when the parameter +changes. + + +If at lest one of these attributes is not set, then the parameter +will not be used, slider's and/or text input elements that specify +this parameter will not be activated, and the specified functions +which use this parameter will return a non valid value. This means +that neglecting to specify at least one of the attributes for some +parameter will have the result of the whole GST instance not working +properly. + + +Functions tag +............. + + +For the GST to do something, you must defined at least one +function, which can use any of the specified parameter values. The +function expects to take the ``x`` value, do some calculations, and +return the ``y`` value. I.e. this is a 2D plot in Cartesian +coordinates. + +Each function must be defined inside ``function`` tag in ``functions`` tag:: + + + + function add(a, b, precision) { + var x = Math.pow(10, precision || 2); + return (Math.round(a * x) + Math.round(b * x)) / x; + } + + return add(a, b, 5); + + + +The parameter names (along with their values, as provided from text +inputs and/or sliders), will be available inside all defined +functions. A defined function body string will be parsed internally +by the browser's JavaScript engine and converted to a true JS +function. + +The function's parameter list will automatically be created and +populated, and will include the ``x``, and all of the specified +parameter values (from sliders and text inputs). This means that +each of the defined functions will have access to all of the +parameter values. You don't have to use them, but they will be +there. + +Examples:: + + + return x; + + + + return (x + a) * Math.sin(x * b); + + + + function helperFunc(c1) { + return c1 * c1 - a; + } + return helperFunc(x + 10 * a * b) + Math.sin(a - x); + + +Required parameters:: + + function body: + + A string composing a normal JavaScript function + except that there is no function declaration + (along with parameters), and no closing bracket. + + So if you normally would have written your + JavaScript function like this: + + function myFunc(x, a, b) { + return x * a + b; + } + + here you must specify just the function body + (everything that goes between '{' and '}'). So, + you would specify the above function like so (the + bare-bone minimum): + + + return x * a + b; + + +Optional parameters:: + + + color: Color name (red, green, etc.) or in the form of + '#FFFF00'. If not specified, a default color (different + one for each function) will be given by Flot; + line: A string - 'true' or 'false'. Should the data points be + connected by a line on the graph? Default is 'true'. + dot: A string - 'true' or 'false'. Should points be shown for + each data point on the graph? Default is 'false'. + label: A string. If provided, will be shown in the legend, along + with the color that was used to plot the function. + output: "element" or "plot". If not defined, function will be plotted. + If defined, function will not be plotted, but rendered to element + with id set in 'el_id' attribute. + el_id: Id of html element, defined in 'render' section. Value of + function will be rendered to content of this element. + +With ``output`` and ``el_id`` set together you can update html elements with +function value, also function will not be plotted. + +[note on MathJax and labels]: + +It is possible to define a label in standard TeX notation. The JS +library MathJax will work on these labels also because they are +inserted on top of the plot as standard HTML (text within a DIV). + +Plot tag +........ + +``Plot`` tag inside ``configuration`` tag defines settings for plot output. + +Required parameters:: + + xrange: 2 functions that must return value. Value is constant (3.1415) + or depend on parameter from parameters section: + + return 0; + return 30; + + or + + return -a; + return a; + + + All functions will be calculated over domain between xrange:min + and xrange:max. Xrange depending on parameter is extremely + useful when domain(s) of your function(s) depends on parameter + (like circle, when parameter is radius and you want to allow + to change it). + +Optional parameters:: + + num_points: Number of data points to generated for the plot. If + this is not set, the number of points will be + calculated as width / 5. + xticks, + yticks: 3 floating point numbers separated by commas. This + specifies how many ticks are created, what number they + start at, and what number they end at. This is different + from the 'xrange' setting in that it has nothing to do + with the data points - it control what area of the + Cartesian space you will see. The first number is the + first tick's value, the second number is the step + between each tick, the third number is the value of the + last tick. If these configurations are not specified, + Flot will chose them for you based on the data points + set that he is currently plotting. Usually, this results + in a nice graph, however, sometimes you need to fine + grain the controls. For example, when you want to show + a fixed area of the Cartesian space, even when the data + set changes. On it's own, Flot will recalculate the + ticks, which will result in a different graph each time. + By specifying the xticks, yticks configurations, only + the plotted data will change - the axes (ticks) will + remain as you have defined them. + + xunits, + yunits: Units values to be set on axes. Use MathJax. Example: + \(cm\) + \(m\) + +Example +======= + +Plotting, sliders and inputs +---------------------------- + +.. literalinclude:: gst_example_with_documentation.xml + +Update of html elements, no plotting +------------------------------------ + +.. literalinclude:: gst_example_html_element_output.xml + + +Circle with dynamic radius +-------------------------- + +.. literalinclude:: gst_example_dynamic_range.xml diff --git a/docs/source/xml_formats.rst b/docs/source/xml_formats.rst index b76ee11642..7c92546a5e 100644 --- a/docs/source/xml_formats.rst +++ b/docs/source/xml_formats.rst @@ -5,4 +5,5 @@ Contents: .. toctree:: :maxdepth: 2 - graphical_slider_tool.rst \ No newline at end of file + graphical_slider_tool.rst + drag_and_drop_input.rst From f1f3b2ea8ba5ad7c14c0108109f97f13fc5496df Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 28 Dec 2012 09:52:26 +0200 Subject: [PATCH 047/140] Slight UI design change. Draggables are now positioned in the center of their container DIV. --- common/static/js/capa/drag_and_drop/draggables.js | 1 + 1 file changed, 1 insertion(+) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index faf0f7b3f3..1c4a6cad8b 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -43,6 +43,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'overflow: hidden; ' + 'z-index: ' + objIndex + '; ' + 'border: 1px solid #CCC; ' + + 'text-align: center; ' + '" ' + 'data-draggable-position-index="' + objIndex + '" ' + '>
' From 744b7947e60ea10a5dc78fe7e79ff76bd91ba56a Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 28 Dec 2012 11:00:55 +0200 Subject: [PATCH 048/140] Added initial docs for Drag and Drop. --- docs/source/drag-n-drop-demo.xml | 188 ++++++++++++++ docs/source/drag_and_drop_input.rst | 376 ++++------------------------ 2 files changed, 243 insertions(+), 321 deletions(-) create mode 100644 docs/source/drag-n-drop-demo.xml diff --git a/docs/source/drag-n-drop-demo.xml b/docs/source/drag-n-drop-demo.xml new file mode 100644 index 0000000000..574a188c33 --- /dev/null +++ b/docs/source/drag-n-drop-demo.xml @@ -0,0 +1,188 @@ + + + +

+ Drag and drop pictures to proper positions. +

+

+ Drag images to the cow)). +

+
+ + + +

Individual targets with outlines. One draggable per target.

+

Drag ant to first and star to 3rd.

+
+
+ + + + + + + + + + + + + + + + + + +
+ + + +

+ Individual targets WITHOUT outlines. Move Star to the + volcano opening, and Label3 on to the right ear of the cow. + One draggable per target. +

+
+
+ + + + + + + + + + + + + + + + + +
+ + + +

Many draggable per target. Move star and ant to most left target + and lable 3 and label 2 to most right target.

+
+
+ + + + + + + + + + + + + + + + + + +
+ + + +

Draggables can be places anywhere on base image. + Place grass in the middle of the image and ant in the + right upper corner.

+
+
+ + + + + + + + + + + + + + +
+ + + +

Please identify the Carbon and Oxygen atoms in the molecule.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index d977270269..75e77b97e2 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -7,346 +7,80 @@ Xml format of drag and drop input [inputtypes] Format description ================== -Graphical slider tool (GST) main tag is:: +The main tag of Drag and Drop (DnD) input is:: - BODY + ... -``graphical_slider_tool`` tag must have two children tags: ``render`` -and ``configuration``. +``drag_and_drop_input`` can include any number of the following 2 tags: +``draggable`` and ``target``. -Render tag ----------- +drag_and_drop_input tag +----------------------- -Render tag can contain usual html tags mixed with some GST specific tags:: +The main container for a single instance of DnD. The following attributes can +be specified for this tag:: - - represents jQuery slider - - represents input - - represents Flot plot + img - Relative path to an image that will be the base image. All draggables + can be dragged onto it. + target_outline - Specify whether an outline (gray dashed line) should be + drawn around targets (if they are specified). It can be either + 'true' or 'false'. If not specified, the default value is + 'false'. + one_per_target - Specify whether to allow more than one draggable to be + placed onto a single target. It can be either 'true' or 'false'. If + not specified, the default value is 'true'. -Also GST will track all elements inside ```` where ``id`` -attribute is set. +draggable tag +------------- -The contents of the section will be shown to the user after -all occurrences of:: - - - - - -have been converted to actual sliders, text inputs, and a plot graph. -Everything in square brackets is optional. - -``{parameter name}`` specifies the parameter to which the slider or text -input will be attached to. - -There is a one-to-one relationship between a slider and a parameter. -I.e. for one parameter you can put only one ```` section. However, you don't have to specify a slider - they -are optional. - -There is a many-to-one relationship between text inputs and a -parameter. I.e. for one parameter you can put many '`` section. However, you don't have to specify a text -input - they are optional. - -You can put only one ```` section. It is not -required. - -NOTE: MathJax, independently of this module, will render all TeX code -within the ```` section into nice mathematical formulas. Just -remember to wrap it in one of:: - - \( and \) - for inline formulas (formulas surrounded by - standard text) - \[ and \] - if you want the formula to be a separate line - -Slider tag -.......... - -Slider tag must have ``var`` attribute and optional ``style`` attribute:: - - - -After processing slider tags will be replaced by jQuery UI sliders with applied -``style`` attribute. - - -``Var`` attribute must correspond to parameter in one of ``function`` tags in -``functions`` tag. By moving slider, value of parameter ``a`` will change, and so -result of function, that depends on parameter ``a``, will change. - - -Textbox tag -........... - - -Texbox tag must have ``var`` attribute and optional ``style`` and ``readonly`` +Draggable tag specifies a single draggable object which has the following attributes:: - + id - Unique identifier of the draggable object. + label - Human readable label that will be shown to the user. + icon - Relative path to an image that will be shown to the user. -After processing tetbox tags will be replaced by html inputs with applied -``style`` attribute. If ``readonly`` is set to ``true`` input will be -not-editable, default is ``false``. +A draggable is what the user must drag out of the slider and place onto the +base image. After a drag operation, if the center of the draggable ends up +outside the rectangular dimensions of the image, it will be returned back +to the slider. -``Var`` attribute must correspond to parameter in one of ``function`` tags in -``functions`` tag. By entering value in input, value of parameter ``b`` will change, and so -result of function, that depends on parameter ``b``, will change. +In order for the grader to work, it is essential that a unique ID +is provided. Otherwise, there will be no way to tell which draggable is at what +coordinate, or over what target. Label and icon attributes are optional. If +they are provided they will be used, otherwise, you can have an empty +draggable. The path is relative to 'course_folder/static/images/' folder. -Plot tag -........ +target tag +---------- -Plot tag may have optional ``style`` attribute:: +Target tag specifies a single target object which has the following required +attributes:: - + id - Unique identifier of the target object. + x - X-coordinate on the base image where the top left corner of the target + will be positioned. + y - Y-coordinate on the base image where the top left corner of the target + will be positioned. + w - Width of the target. + h - Height of the target. -After processing plot tags will be replaced by Flot plot with applied -``style`` attribute. +A target specifies a place on the base image where a draggable can be +positioned. By design, if the center of a draggable lies within the target +(i.e. in the rectangle defined by [[x, y], [x + w, y + h]], then it is within +the target. Otherwise, it is outside. -HTML tags with id -................. +If at lest one target is provided, the behavior of the client side logic +changes. If a draggable is not dragged on to a target, it is returned back to +the slider. -Any html tag with id, e.g. ```` will be counted as -place where result of function can be rendered. To render function result to -element, element id must be included in ``function`` tag as ``el_id`` attribute -and ``output`` value must be ``"element"``:: - - - function add(a, b, precision) { - var x = Math.pow(10, precision || 2); - return (Math.round(a * x) + Math.round(b * x)) / x; - } - - return add(a, b, 5); - - - - -Configuration tag ------------------ - -The configuration tag contain sparameter settings, graph -settings, and function definitions which are to be plotted on the -graph and that use specified parameters. - -Configuration tag contains two mandatory tag ``functions`` and ``parameters`` and -may contain another ``plot`` tag. - -Parameters tag -.............. - -``Parameters`` tag contains ``parameter`` tags. Each ``parameter`` tag must have -``var``, ``max``, ``min``, ``step`` and ``initial`` attributes:: - - - - - - -``Var`` attribute links min, max, step and initial values to parameter name. -``Min`` attribute is minimal value that parameter can take. Slider and input -values can not go below it. - -``Max`` attribute is maximal value that parameter can take. Slider and input -values can not go over it. - -``Step`` attribute is value of slider step. When a slider increase or decreases -the specified parameter, it will do so by the amount specified with 'step' - -``Initial`` attribute is the initial value that the specified parameter should be -set to. - -The parameter's name is specified by the 'var' property. All occurrences -of sliders and/or text inputs that specify a 'var' property, will be -connected to this parameter - i.e. they will reflect the current -value of the parameter and will be updated when the parameter -changes. - - -If at lest one of these attributes is not set, then the parameter -will not be used, slider's and/or text input elements that specify -this parameter will not be activated, and the specified functions -which use this parameter will return a non valid value. This means -that neglecting to specify at least one of the attributes for some -parameter will have the result of the whole GST instance not working -properly. - - -Functions tag -............. - - -For the GST to do something, you must defined at least one -function, which can use any of the specified parameter values. The -function expects to take the ``x`` value, do some calculations, and -return the ``y`` value. I.e. this is a 2D plot in Cartesian -coordinates. - -Each function must be defined inside ``function`` tag in ``functions`` tag:: - - - - function add(a, b, precision) { - var x = Math.pow(10, precision || 2); - return (Math.round(a * x) + Math.round(b * x)) / x; - } - - return add(a, b, 5); - - - -The parameter names (along with their values, as provided from text -inputs and/or sliders), will be available inside all defined -functions. A defined function body string will be parsed internally -by the browser's JavaScript engine and converted to a true JS -function. - -The function's parameter list will automatically be created and -populated, and will include the ``x``, and all of the specified -parameter values (from sliders and text inputs). This means that -each of the defined functions will have access to all of the -parameter values. You don't have to use them, but they will be -there. - -Examples:: - - - return x; - - - - return (x + a) * Math.sin(x * b); - - - - function helperFunc(c1) { - return c1 * c1 - a; - } - return helperFunc(x + 10 * a * b) + Math.sin(a - x); - - -Required parameters:: - - function body: - - A string composing a normal JavaScript function - except that there is no function declaration - (along with parameters), and no closing bracket. - - So if you normally would have written your - JavaScript function like this: - - function myFunc(x, a, b) { - return x * a + b; - } - - here you must specify just the function body - (everything that goes between '{' and '}'). So, - you would specify the above function like so (the - bare-bone minimum): - - - return x * a + b; - - -Optional parameters:: - - - color: Color name (red, green, etc.) or in the form of - '#FFFF00'. If not specified, a default color (different - one for each function) will be given by Flot; - line: A string - 'true' or 'false'. Should the data points be - connected by a line on the graph? Default is 'true'. - dot: A string - 'true' or 'false'. Should points be shown for - each data point on the graph? Default is 'false'. - label: A string. If provided, will be shown in the legend, along - with the color that was used to plot the function. - output: "element" or "plot". If not defined, function will be plotted. - If defined, function will not be plotted, but rendered to element - with id set in 'el_id' attribute. - el_id: Id of html element, defined in 'render' section. Value of - function will be rendered to content of this element. - -With ``output`` and ``el_id`` set together you can update html elements with -function value, also function will not be plotted. - -[note on MathJax and labels]: - -It is possible to define a label in standard TeX notation. The JS -library MathJax will work on these labels also because they are -inserted on top of the plot as standard HTML (text within a DIV). - -Plot tag -........ - -``Plot`` tag inside ``configuration`` tag defines settings for plot output. - -Required parameters:: - - xrange: 2 functions that must return value. Value is constant (3.1415) - or depend on parameter from parameters section: - - return 0; - return 30; - - or - - return -a; - return a; - - - All functions will be calculated over domain between xrange:min - and xrange:max. Xrange depending on parameter is extremely - useful when domain(s) of your function(s) depends on parameter - (like circle, when parameter is radius and you want to allow - to change it). - -Optional parameters:: - - num_points: Number of data points to generated for the plot. If - this is not set, the number of points will be - calculated as width / 5. - xticks, - yticks: 3 floating point numbers separated by commas. This - specifies how many ticks are created, what number they - start at, and what number they end at. This is different - from the 'xrange' setting in that it has nothing to do - with the data points - it control what area of the - Cartesian space you will see. The first number is the - first tick's value, the second number is the step - between each tick, the third number is the value of the - last tick. If these configurations are not specified, - Flot will chose them for you based on the data points - set that he is currently plotting. Usually, this results - in a nice graph, however, sometimes you need to fine - grain the controls. For example, when you want to show - a fixed area of the Cartesian space, even when the data - set changes. On it's own, Flot will recalculate the - ticks, which will result in a different graph each time. - By specifying the xticks, yticks configurations, only - the plotted data will change - the axes (ticks) will - remain as you have defined them. - - xunits, - yunits: Units values to be set on axes. Use MathJax. Example: - \(cm\) - \(m\) +If no targets are provided, then a draggable can be dragged and placed anywhere +on the base image. Example ======= -Plotting, sliders and inputs ----------------------------- +Various configuration of DnD on one page +---------------------------------------- -.. literalinclude:: gst_example_with_documentation.xml - -Update of html elements, no plotting ------------------------------------- - -.. literalinclude:: gst_example_html_element_output.xml - - -Circle with dynamic radius --------------------------- - -.. literalinclude:: gst_example_dynamic_range.xml +.. literalinclude:: drag-n-drop-demo.xml From 7ab1325647f2a4f9e2fcbb3257aeffbcac317a4d Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 13:34:14 +0200 Subject: [PATCH 049/140] extended grader --- common/lib/capa/capa/graders/draganddrop.py | 232 ++++++++++++++++---- 1 file changed, 184 insertions(+), 48 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 1e847b105b..eaee4bc58e 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -28,74 +28,210 @@ values are (x,y) coordinates of centers of dragged images. """ import json +from collections import OrderedDict -def grade(user_input, correct_answer): - ''' - Grade drag and drop problem. - If use_targets is True - checks if image placed on proper target. - If use_targets is False - checks if image placed on proper coordinates, - with setted radius of forgiveness (default is 10) +class PositionsCompare(list): + """Inputs are: "abc" - target + [10, 20] - list of integers + [[10,20], 200] list of list and integer - Args: - user_input, correct_answer: json. Format: + """ + def __eq__(self, other): + # Default lists behaviour is convers "abc" to ["a", "b", "c"]. + # We will use that. + # import ipdb; ipdb.set_trace() - user_input: see module docstring + #check if self or other is not empty list (empty lists = false) + if not self or not other: + return False - correct_answer: - if use_targets is True: - {'1': 't1', 'name_with_icon': 't2'} - else: - {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} + # check correct input types + if (not isinstance(self[0], (str, unicode, list, int)) or + not isinstance(other[0], (str, unicode, list, int))): + print 'Incorrect input type' + return False - Returns: - True or False. - ''' + if (isinstance(self[0], (list, int)) and + isinstance(other[0], (list, int))): + print 'Numerical position compare' + return self.coordinate_positions_compare(other) + elif (isinstance(self[0], (unicode, str)) and + isinstance(other[0], (unicode, str))): + print 'Targets compare' + return ''.join(self) == ''.join(other) + else: + # we do not have ints or lists of lists or two string/unicode lists + # on both sides + print type(self[0]), type(other[0]), "not correct" + return False - user_answer = json.loads(user_input) + def __ne__(self, other): + return not self.__eq__(other) - if len(correct_answer.keys()) != len(user_answer['draggables']): - return False - - def is_equal(user_answer, correct_answer): - """ Checks if user_answer is equal to correct_answer inside radius + def coordinate_positions_compare(self, other, r=10): + """ Checks if pos1 is equal to pos2 inside radius of forgiveness (default 10 px). Args: - user_answer: [x, y] - list of floats; - correct_answer: [x, y] or [[x, y], r], where - r is radius of forgiveness; + self, other: [x, y] or [[x, y], r], where + r is radius of forgiveness; + x, y, r: int Returns: bool. """ - if not isinstance(correct_answer, list) or isinstance(user_answer, list): - return False + print 'I am called', self, other - r = 10 - if isinstance(correct_answer[0], list): # [(x, y), r] case - r = correct_answer[1] - corr_x = correct_answer[0][0] - corr_y = correct_answer[0][1] - else: # (x, y) case - corr_x = correct_answer[0] - corr_y = correct_answer[1] + # get max radius of forgiveness + if isinstance(self[0], list): # [(x, y), r] case + r = max(self[1], r) + x1, y1 = self[0] + else: + x1, y1 = self - if ((user_answer[0] - corr_x) ** 2 + - (user_answer[1] - corr_y) ** 2) > r * r: + if isinstance(other[0], list): # [(x, y), r] case + r = max(other[1], r) + x2, y2 = other[0] + else: + x2, y2 = other + + if (x2 - x1) ** 2 + (y2 - y1) ** 2 > r * r: return False return True - if user_answer["use_targets"]: - is_equal = lambda user, correct: user == correct if ( - isinstance(user, unicode) and isinstance(correct, str)) else False - for draggable in user_answer['draggables']: - user_img_location = draggable.values()[0] - corr_img_location = correct_answer.get(draggable.keys()[0], None) - if not corr_img_location: - return False - if not is_equal(user_img_location, corr_img_location): +class DragAndDrop(object): + + def __init__(self): + self.correct_groups = OrderedDict() # groups + self.correct_positions = OrderedDict() # positions of comparing + self.user_groups = OrderedDict() + self.user_positions = OrderedDict() + + def grade(self): + ''' + Grade drag and drop problem. + If use_targets is True - checks if image placed on proper target. + If use_targets is False - checks if image placed on proper coordinates, + with setted radius of forgiveness (default is 10) + + Args: + user_input, correct_answer: json. Format: + + user_input: see module docstring + + correct_answer: + if use_targets is True: + {'1': 't1', 'name_with_icon': 't2'} + else: + {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} + + Returns: + True or False. + ''' + if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()): return False - return True + for groupname, draggable_ids in self.correct_groups.items(): + if sorted(draggable_ids) != sorted(self.user_groups[groupname]): + return False + + # from now self.groups and self.user_groups are equal + assert self.correct_groups == self.user_groups + + # Check fo every group that positions of every group element are equal + # with positions + + # 'denied' rule + # import ipdb; ipdb.set_trace() + denied_positions = [self.correct_positions[g].get('denied', []) + for g in self.correct_groups.keys()] + all_user_positions = [self.user_positions[g]['user'] + for g in self.correct_groups.keys()] + if not self.compare_positions(denied_positions, + all_user_positions, flag='denied'): + return False + + no_exact, no_allowed = False, False + # 'exact' rule + for groupname in self.correct_groups: + if self.correct_positions[groupname].get('exact', []): + if not self.compare_positions( + self.correct_positions[groupname]['exact'], + self.user_positions[groupname]['user'], flag='exact'): + return False + else: + no_exact = True + + # 'allowed' rule + for groupname in self.correct_groups: + if self.correct_positions[groupname].get('allowed', []): + if not self.compare_positions( + self.correct_positions[groupname]['allowed'], + self.user_positions[groupname]['user'], flag='allowed'): + return False + else: + no_allowed = True + + if no_allowed and no_exact: + return False + + return True + + def compare_positions(self, list1, list2, flag): + # import ipdb; ipdb.set_trace() + if flag == 'denied': + for el1 in list1: + for el2 in list2: + if PositionsCompare(el1) == PositionsCompare(el2): + return False + + if flag == 'allowed': + for el1, el2 in zip(sorted(list1), sorted(list2)): + if PositionsCompare(el1) != PositionsCompare(el2): + return False + + if flag == 'exact': + for el1, el2 in zip(list1, list2): + if PositionsCompare(el1) != PositionsCompare(el2): + return False + + return True + + def populate(self, correct_answer, user_answer): + """ """ + + if isinstance(correct_answer, dict): + for key, value in correct_answer.items(): + self.correct_groups[key] = [key] + self.correct_positions[key] = {'exact': [value]} + + user_answer = json.loads(user_answer) + self.use_targets = user_answer.get('use_targets') + + # create identical data structures + # user groups must mirror correct_groups + # and positions must reflect order in groups + + for groupname in self.correct_groups: + self.user_groups[groupname] = [] + self.user_positions[groupname] = {'user': []} + for draggable_dict in user_answer['draggables']: + # draggable_dict is 1-to-1 {draggable_name: position} + draggable_name = draggable_dict.keys()[0] + if draggable_name in self.correct_groups[groupname]: + self.user_groups[groupname].append(draggable_name) + self.user_positions[groupname]['user'].append( + draggable_dict[draggable_name]) + + # import ipdb; ipdb.set_trace() + + +def grade(user_input, correct_answer): + """ Support 2 interfaces""" + if isinstance(correct_answer, dict): + dnd = DragAndDrop() + + dnd.populate(correct_answer=correct_answer, user_answer=user_input) + return dnd.grade() From ff782fff6c7a28e7e57569738dfd235ff57e1745 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 13:34:29 +0200 Subject: [PATCH 050/140] test for drag and drop --- common/lib/capa/capa/graders/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 7b0b719a6a..a8616d808b 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -33,13 +33,13 @@ class Test_DragAndDrop(unittest.TestCase): user_input = '{"use_targets": true, "draggables": [{"1": [10,10]}, \ {"name_with_icon": [[10,10],4]}]}' correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} - self.assertFalse(draganddrop.grade(user_input, correct_answer)) + self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_position_and_targets(self): user_input = '{"use_targets": false, \ "draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't2'} - self.assertFalse(draganddrop.grade(user_input, correct_answer)) + self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_positions_exact(self): user_input = '{"use_targets": false, "draggables": \ From 37e3181c3634040f9e3305cac940d102bc25921d Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 13:34:38 +0200 Subject: [PATCH 051/140] removed unidecode --- common/lib/capa/capa/inputtypes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 147251329b..d1e298564a 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -45,7 +45,6 @@ import re import shlex # for splitting quoted strings import sys import os -import unidecode from registry import TagRegistry From 604c42eb07c13f1551efdc39f19186025beac9b5 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 16:07:49 +0200 Subject: [PATCH 052/140] added anyof flag --- common/lib/capa/capa/graders/draganddrop.py | 51 +++++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index eaee4bc58e..0d0750d970 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -47,13 +47,13 @@ class PositionsCompare(list): return False # check correct input types - if (not isinstance(self[0], (str, unicode, list, int)) or - not isinstance(other[0], (str, unicode, list, int))): + if (not isinstance(self[0], (str, unicode, list, int, float)) or + not isinstance(other[0], (str, unicode, list, int, float))): print 'Incorrect input type' return False - if (isinstance(self[0], (list, int)) and - isinstance(other[0], (list, int))): + if (isinstance(self[0], (list, int, float)) and + isinstance(other[0], (list, int, float))): print 'Numerical position compare' return self.coordinate_positions_compare(other) elif (isinstance(self[0], (unicode, str)) and @@ -81,7 +81,6 @@ class PositionsCompare(list): Returns: bool. """ print 'I am called', self, other - # get max radius of forgiveness if isinstance(self[0], list): # [(x, y), r] case r = max(self[1], r) @@ -136,9 +135,8 @@ class DragAndDrop(object): for groupname, draggable_ids in self.correct_groups.items(): if sorted(draggable_ids) != sorted(self.user_groups[groupname]): return False - - # from now self.groups and self.user_groups are equal - assert self.correct_groups == self.user_groups + # from now self.correct_groups and self.user_groups are equal if + # order is ignored # Check fo every group that positions of every group element are equal # with positions @@ -153,7 +151,7 @@ class DragAndDrop(object): all_user_positions, flag='denied'): return False - no_exact, no_allowed = False, False + no_exact, no_allowed, no_anyof = False, False, False # 'exact' rule for groupname in self.correct_groups: if self.correct_positions[groupname].get('exact', []): @@ -174,29 +172,50 @@ class DragAndDrop(object): else: no_allowed = True - if no_allowed and no_exact: + # 'anyof' rule + for groupname in self.correct_groups: + if self.correct_positions[groupname].get('anyof', []): + if not self.compare_positions( + self.correct_positions[groupname]['anyof'], + self.user_positions[groupname]['user'], flag='anyof'): + return False + else: + no_anyof = True + + if no_allowed and no_exact and no_anyof: return False return True - def compare_positions(self, list1, list2, flag): + def compare_positions(self, correct, user, flag): + """ order of correct/user is matter only in anyof_flag""" # import ipdb; ipdb.set_trace() if flag == 'denied': - for el1 in list1: - for el2 in list2: + for el1 in correct: + for el2 in user: if PositionsCompare(el1) == PositionsCompare(el2): return False if flag == 'allowed': - for el1, el2 in zip(sorted(list1), sorted(list2)): + for el1, el2 in zip(sorted(correct), sorted(user)): if PositionsCompare(el1) != PositionsCompare(el2): return False if flag == 'exact': - for el1, el2 in zip(list1, list2): + for el1, el2 in zip(correct, user): if PositionsCompare(el1) != PositionsCompare(el2): return False + if flag == 'anyof': + count = 0 + for u_el in user: + for c_el in correct: + if PositionsCompare(u_el) == PositionsCompare(c_el): + count += 1 + continue + if count != len(user): + return False + return True def populate(self, correct_answer, user_answer): @@ -232,6 +251,8 @@ def grade(user_input, correct_answer): """ Support 2 interfaces""" if isinstance(correct_answer, dict): dnd = DragAndDrop() + else: + dnd = correct_answer dnd.populate(correct_answer=correct_answer, user_answer=user_input) return dnd.grade() From 961285a4d10a369511e123e3c59dcebc3b1b43c4 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 16:08:01 +0200 Subject: [PATCH 053/140] added no_labels flag --- common/lib/capa/capa/inputtypes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index d1e298564a..4cb75c9ce3 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -836,11 +836,15 @@ class DragAndDropInput(InputTypeBase): dic[attr_name] = Attribute(attr_name, default=tag_attrs[tag_type][attr_name]).parse_from_xml(tag) - if tag_type == 'draggable': + if tag_type == 'draggable' and not self.no_labels: dic['label'] = dic['label'] or dic['id'] return dic + # add labels to images: + self.no_labels = Attribute('no_labels', + default="False").parse_from_xml(self.xml) + to_js = dict() # image drag and drop onto From 590e63639e971dab123faffa61a146b0d8f51d01 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 16:08:47 +0200 Subject: [PATCH 054/140] added anywhere test --- common/lib/capa/capa/graders/tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index a8616d808b..4ffee39aaa 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -77,6 +77,15 @@ class Test_DragAndDrop(unittest.TestCase): correct_answer = {'3': 't3', 'name_with_icon': 't2'} self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def test_anywhere(self): + """Draggables can be places anywhere on base image. + Place grass in the middle of the image and ant in the + right upper corner.""" + user_input = '{"use_targets": false, "draggables": \ + [{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}' + correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + def suite(): From a0f99ac3ce8dd6783f2f5dd3d15e2d9bc40c01b3 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 2 Jan 2013 17:16:37 +0200 Subject: [PATCH 055/140] added case for extra draggables --- common/lib/capa/capa/graders/draganddrop.py | 28 ++++++--- common/lib/capa/capa/graders/tests.py | 66 +++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 0d0750d970..e02304aef0 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -107,6 +107,7 @@ class DragAndDrop(object): self.correct_positions = OrderedDict() # positions of comparing self.user_groups = OrderedDict() self.user_positions = OrderedDict() + self.incorrect = False def grade(self): ''' @@ -129,9 +130,12 @@ class DragAndDrop(object): Returns: True or False. ''' - if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()): + + if self.incorrect: return False + if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()): + return False for groupname, draggable_ids in self.correct_groups.items(): if sorted(draggable_ids) != sorted(self.user_groups[groupname]): return False @@ -142,11 +146,10 @@ class DragAndDrop(object): # with positions # 'denied' rule - # import ipdb; ipdb.set_trace() - denied_positions = [self.correct_positions[g].get('denied', []) - for g in self.correct_groups.keys()] - all_user_positions = [self.user_positions[g]['user'] - for g in self.correct_groups.keys()] + denied_positions = [item for g in self.correct_groups.keys() + for item in self.correct_positions[g].get('denied', [])] + all_user_positions = [item for g in self.correct_groups.keys() + for item in self.user_positions[g]['user']] if not self.compare_positions(denied_positions, all_user_positions, flag='denied'): return False @@ -229,10 +232,12 @@ class DragAndDrop(object): user_answer = json.loads(user_answer) self.use_targets = user_answer.get('use_targets') + # check if we have draggables that are not in correct answer: + check_extra_draggables = {} + # create identical data structures # user groups must mirror correct_groups - # and positions must reflect order in groups - + # and positions must reflect order in group for groupname in self.correct_groups: self.user_groups[groupname] = [] self.user_positions[groupname] = {'user': []} @@ -243,7 +248,14 @@ class DragAndDrop(object): self.user_groups[groupname].append(draggable_name) self.user_positions[groupname]['user'].append( draggable_dict[draggable_name]) + check_extra_draggables[draggable_name] = True + else: + check_extra_draggables[draggable_name] = \ + check_extra_draggables.get(draggable_name, False) + for draggable in check_extra_draggables: + if not check_extra_draggables[draggable]: + self.incorrect = True # import ipdb; ipdb.set_trace() diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 4ffee39aaa..0e95b810a9 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -86,6 +86,72 @@ class Test_DragAndDrop(unittest.TestCase): correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) + def test_lcao_correct(self): + """Describe carbon molecule in LCAO-MO""" + user_input = '{"use_targets":true,"draggables":[{"1":"s_left"}, \ + {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ + {"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \ + {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ + {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ + {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' + correct_answer = draganddrop.DragAndDrop() + correct_answer.correct_groups['filled_levels'] = ['1', '2', '3', '4', '5', '6'] + correct_answer.correct_positions['filled_levels'] = {'allowed': [ + 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2']} + + correct_answer.correct_groups['spin_up'] = ['7', '8', '9', '10'] + correct_answer.correct_positions['spin_up'] = {'allowed': + ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2']} + + correct_answer.correct_groups['sigma'] = ['11', '12'] + correct_answer.correct_positions['sigma'] = {'allowed': + ['s_sigma_name', 'p_sigma_name']} + + correct_answer.correct_groups['sigma_star'] = ['13', '14'] + correct_answer.correct_positions['sigma_star'] = {'allowed': + ['s_sigma_star_name', 'p_sigma_star_name']} + + correct_answer.correct_groups['pi'] = ['15'] + correct_answer.correct_positions['pi'] = {'allowed': ['p_pi_name']} + + correct_answer.correct_groups['pi_star'] = ['16'] + correct_answer.correct_positions['pi_star'] = {'allowed': ['p_pi_star_name']} + + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_lcao_extra_element_incorrect(self): + """Describe carbon molecule in LCAO-MO""" + user_input = '{"use_targets":true,"draggables":[{"1":"s_left"}, \ + {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ + {"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \ + {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ + {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ + {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' + correct_answer = draganddrop.DragAndDrop() + correct_answer.correct_groups['filled_levels'] = ['1', '2', '3', '4', '5', '6'] + correct_answer.correct_positions['filled_levels'] = {'allowed': [ + 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2']} + + correct_answer.correct_groups['spin_up'] = ['7', '8', '9', '10'] + correct_answer.correct_positions['spin_up'] = {'allowed': + ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2']} + + correct_answer.correct_groups['sigma'] = ['11', '12'] + correct_answer.correct_positions['sigma'] = {'allowed': + ['s_sigma_name', 'p_sigma_name']} + + correct_answer.correct_groups['sigma_star'] = ['13', '14'] + correct_answer.correct_positions['sigma_star'] = {'allowed': + ['s_sigma_star_name', 'p_sigma_star_name']} + + correct_answer.correct_groups['pi'] = ['15'] + correct_answer.correct_positions['pi'] = {'allowed': ['p_pi_name']} + + correct_answer.correct_groups['pi_star'] = ['16'] + correct_answer.correct_positions['pi_star'] = {'allowed': ['p_pi_star_name']} + + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def suite(): From 2e08ece7ffd73ee573222b1eac2888b16e91de34 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 07:45:49 +0200 Subject: [PATCH 056/140] Fixed drag behavior. Now drag of icon itself rather than container element. This allows for finer selection (no overlap of transparent areas), and positioning of icon precisely onto a target. This breaks labels - TODO fix. --- .../js/capa/drag_and_drop/draggables.js | 207 +++++++++--------- 1 file changed, 104 insertions(+), 103 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 1c4a6cad8b..f70ae108b4 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -22,18 +22,31 @@ define(['logme', 'update_input'], function (logme, updateInput) { normalizeEvent(event); if (state.currentMovingDraggable !== null) { - state.currentMovingDraggable.css('left', event.pageX - state.baseImageEl.offset().left - 50); - state.currentMovingDraggable.css('top', event.pageY - state.baseImageEl.offset().top - 50); + state.currentMovingDraggable.iconEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.iconWidth * 0.5 + ); + state.currentMovingDraggable.iconEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top - + state.currentMovingDraggable.iconHeight * 0.5 + ); } }); return; function processDraggable(obj, objIndex) { - var draggableContainerEl, inContainer, mousePressed, onTarget, - draggableObj, marginCss; + var inContainer, mousePressed, onTarget, draggableObj, marginCss; - draggableContainerEl = $( + draggableObj = {}; + + draggableObj.zIndex = objIndex; + + draggableObj.containerEl = $( '
' ); + draggableObj.containerEl.appendTo(state.sliderEl); if (obj.icon.length > 0) { - draggableContainerEl.append( - $('') + draggableObj.iconEl = $(''); + draggableObj.iconEl.attr( + 'src', + state.config.imageDir + '/' + obj.icon ); + draggableObj.iconEl.load(function () { + draggableObj.iconWidth = this.width; + draggableObj.iconHeight = this.height; + + draggableObj.iconEl.css('position', 'absolute'); + draggableObj.iconEl.css('top', (50 - this.height * 0.5) + 'px'); + draggableObj.iconEl.css('left', (50 - this.width * 0.5) + 'px'); + + draggableObj.iconEl.appendTo(draggableObj.containerEl); + }); + + draggableObj.iconEl.mousedown(mouseDown); + draggableObj.iconEl.mouseup(mouseUp); + draggableObj.iconEl.mousemove(mouseMove); + } else { + // Must fix - add +label support, and just label support. + return; } - if (obj.label.length > 0) { - marginCss = ''; + // if (obj.label.length > 0) { + // marginCss = ''; + // + // if (obj.icon.length === 0) { + // marginCss = 'margin-top: 38px;'; + // } - if (obj.icon.length === 0) { - marginCss = 'margin-top: 38px;'; - } - - draggableContainerEl.append( - $('
' + obj.label + '
') - ); - } - - draggableContainerEl.appendTo(state.sliderEl); + // draggableContainerEl.append( + // $('
' + obj.label + '
') + // ); + // } inContainer = true; mousePressed = false; onTarget = null; - draggableObj = { - 'id': obj.id, - 'el': draggableContainerEl, - 'x': -1, - 'y': -1, + draggableObj.id = obj.id; + draggableObj.x = -1; + draggableObj.y = -1; + + draggableObj.setInContainer = function (val) { inContainer = val; }; + draggableObj.setOnTarget = function (val) { onTarget = val; }; - 'setInContainer': function (val) { inContainer = val; }, - 'setOnTarget': function (val) { onTarget = val; }, - }; state.draggables.push(draggableObj); - draggableContainerEl.mousedown(mouseDown); - draggableContainerEl.mouseup(mouseUp); - draggableContainerEl.mousemove(mouseMove); - return; function mouseDown(event) { if (mousePressed === false) { - state.currentMovingDraggable = draggableContainerEl; + state.currentMovingDraggable = draggableObj; normalizeEvent(event); if (inContainer === true) { - draggableContainerEl.detach(); - draggableContainerEl.css('border', 'none'); - draggableContainerEl.css('position', 'absolute'); - draggableContainerEl.css('left', event.pageX - state.baseImageEl.offset().left - 50); - draggableContainerEl.css('top', event.pageY - state.baseImageEl.offset().top - 50); - draggableContainerEl.appendTo(state.baseImageEl.parent()); + draggableObj.containerEl.hide(); + + draggableObj.iconEl.detach(); + draggableObj.iconEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.iconWidth * 0.5); + draggableObj.iconEl.css('top', event.pageY - state.baseImageEl.offset().top - draggableObj.iconHeight * 0.5); + draggableObj.iconEl.appendTo(state.baseImageEl.parent()); inContainer = false; } - draggableContainerEl.attr('data-old-z-index', draggableContainerEl.css('z-index')); - draggableContainerEl.css('z-index', '1000'); + draggableObj.oldZIndex = draggableObj.zIndex; + draggableObj.zIndex = 1000; + draggableObj.iconEl.css('z-index', '1000'); mousePressed = true; event.preventDefault(); @@ -119,14 +145,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { if (mousePressed === true) { state.currentMovingDraggable = null; - checkLandingElement(event); + checkLandingElement(); } } function mouseMove() { if (mousePressed === true) { - draggableContainerEl.css('left', event.pageX - state.baseImageEl.offset().left - 50); - draggableContainerEl.css('top', event.pageY - state.baseImageEl.offset().top - 50); + draggableObj.iconEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.iconWidth * 0.5); + draggableObj.iconEl.css('top', event.pageY - state.baseImageEl.offset().top - draggableObj.iconHeight * 0.5); } } @@ -136,11 +162,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { // 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 offsetDE, indexes, DEindex, targetFound; + var offsetIE, targetFound; mousePressed = false; - offsetDE = draggableContainerEl.position(); + offsetIE = draggableObj.iconEl.position(); if (state.individualTargets === true) { targetFound = false; @@ -155,10 +181,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } else { if ( - (offsetDE.left < 0) || - (offsetDE.left + 100 > state.baseImageEl.width()) || - (offsetDE.top < 0) || - (offsetDE.top + 100 > state.baseImageEl.height()) + (offsetIE.left < 0) || + (offsetIE.left + draggableObj.iconWidth > state.baseImageEl.width()) || + (offsetIE.top < 0) || + (offsetIE.top + draggableObj.iconHeight > state.baseImageEl.height()) ) { moveBackToSlider(); @@ -182,7 +208,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { if (onTarget !== null) { for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) { - if (onTarget.draggable[c1] === obj.id) { + if (onTarget.draggable[c1] === draggableObj.id) { onTarget.draggable.splice(c1, 1); break; @@ -203,23 +229,23 @@ define(['logme', 'update_input'], function (logme, updateInput) { for (c1 = 0; c1 < state.targets.length; c1 += 1) { target = state.targets[c1]; - if (offsetDE.top + 50 < target.offset.top) { + if (offsetIE.top + draggableObj.iconHeight * 0.5 < target.offset.top) { continue; } - if (offsetDE.top + 50 > target.offset.top + target.h) { + if (offsetIE.top + draggableObj.iconHeight * 0.5 > target.offset.top + target.h) { continue; } - if (offsetDE.left + 50 < target.offset.left) { + if (offsetIE.left + draggableObj.iconWidth * 0.5 < target.offset.left) { continue; } - if (offsetDE.left + 50 > target.offset.left + target.w) { + if (offsetIE.left + draggableObj.iconWidth * 0.5 > target.offset.left + target.w) { continue; } if ( (state.config.one_per_target === true) && (target.draggable.length === 1) && - (target.draggable[0] !== obj.id) + (target.draggable[0] !== draggableObj.id) ) { continue; } @@ -233,10 +259,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { if ((onTarget !== null) && (onTarget.id !== target.id)) { removeObjIdFromTarget(); onTarget = target; - target.draggable.push(obj.id); + target.draggable.push(draggableObj.id); } else if (onTarget === null) { onTarget = target; - target.draggable.push(obj.id); + target.draggable.push(draggableObj.id); } // Reposition the draggable so that it's center @@ -248,8 +274,15 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function snapToTarget(target) { - draggableContainerEl.css('left', target.offset.left + 0.5 * target.w - 50); - draggableContainerEl.css('top', target.offset.top + 0.5 * target.h - 50); + var offset; + + offset = 0; + if (state.config.targetOutline === true) { + offset = 1; + } + + draggableObj.iconEl.css('left', target.offset.left + 0.5 * target.w - draggableObj.iconWidth * 0.5 + offset); + draggableObj.iconEl.css('top', target.offset.top + 0.5 * target.h - draggableObj.iconHeight * 0.5 + offset); } // Go through all of the draggables subtract 1 from the z-index @@ -266,18 +299,17 @@ define(['logme', 'update_input'], function (logme, updateInput) { var c1; for (c1 = 0; c1 < state.draggables.length; c1++) { - if ( - parseInt(draggableContainerEl.attr('data-old-z-index'), 10) < - parseInt(state.draggables[c1].el.css('z-index'), 10) - ) { - state.draggables[c1].el.css( + if (draggableObj.oldZIndex < state.draggables[c1].zIndex) { + state.draggables[c1].zIndex -= 1; + state.draggables[c1].iconEl.css( 'z-index', - parseInt(state.draggables[c1].el.css('z-index'), 10) - 1 + state.draggables[c1].zIndex ); } } - draggableContainerEl.css('z-index', c1); + draggableObj.zIndex = c1; + draggableObj.iconEl.css('z-index', c1); } // If a draggable was released in a wrong positione, we will @@ -286,47 +318,16 @@ define(['logme', 'update_input'], function (logme, updateInput) { function moveBackToSlider() { var c1; - draggableContainerEl.detach(); - draggableContainerEl.css('position', 'static'); + draggableObj.containerEl.show(); - // Get the position indexes of all draggables that are - // currently in the slider, along with the corresponding - // jQuery element. - indexes = []; - state.sliderEl.children().each(function (index, value) { - indexes.push({ - 'index': parseInt($(value).attr('data-draggable-position-index'), 10), - 'el': $(value) - }); - }); + draggableObj.iconEl.detach(); - // Get the position index of the element that we are - // inserting back into the slider. - DEindex = parseInt(draggableContainerEl.attr('data-draggable-position-index'), 10); + draggableObj.iconEl.css('top', (50 - draggableObj.iconHeight * 0.5) + 'px'); + draggableObj.iconEl.css('left', (50 - draggableObj.iconWidth * 0.5) + 'px'); - // Starting from the first position index that we - // retrieved, and going up, if we find a position index - // that is more than 'DEindex', we know that we must insert - // the current element before the element with the greater - // position index. - for (c1 = 0; c1 < indexes.length; c1 += 1) { - if ((inContainer === false) && (indexes[c1].index > DEindex)) { - indexes[c1].el.before(draggableContainerEl); - inContainer = true; - } - } - - // If we did not find a greater postion index, then either - // there are no elements in the slider, or all of them - // have a lesser position index. In both cases we add the - // current draggable to the end. - if (inContainer === false) { - draggableContainerEl.appendTo(state.sliderEl); - } + draggableObj.iconEl.appendTo(draggableObj.containerEl); inContainer = true; - - draggableContainerEl.css('border', '1px solid gray'); } } } From 1cc23e3a4d0622198f191085409ea8c21e5fa7b1 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 10:05:30 +0200 Subject: [PATCH 057/140] Bugfixing. --- common/static/js/capa/drag_and_drop/draggables.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index f70ae108b4..2123e7650a 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -193,8 +193,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } else { correctZIndexes(); - draggableObj.x = offsetDE.left + 50; - draggableObj.y = offsetDE.top + 50; + draggableObj.x = offsetIE.left + draggableObj.iconWidth * 0.5; + draggableObj.y = offsetIE.top + draggableObj.iconHeight * 0.5; } } From 5a6e587a55312a2cab47a2874c60d1311fabcd59 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:10:29 +0200 Subject: [PATCH 058/140] added case for mocking --- common/lib/capa/capa/inputtypes.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 4cb75c9ce3..7bfc093a4d 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -869,7 +869,12 @@ class DragAndDropInput(InputTypeBase): self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.to_render.add('drag_and_drop_json') - self.loaded_attributes['course_folder'] = self.system.course_id.split('/')[1] + # import ipdb; ipdb.set_trace() + try: # for tests and mock up, work normally not in tets + self.loaded_attributes['course_folder'] = \ + self.system.course_id.split('/')[1] + except: + self.loaded_attributes['course_folder'] = 'mock' self.to_render.add('course_folder') registry.register(DragAndDropInput) From cac564de7e35ab96a47bb8b9c532bee8b7b97be8 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:10:42 +0200 Subject: [PATCH 059/140] added test for inputtypes --- common/lib/capa/capa/tests/test_inputtypes.py | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 4a4c6ee5bc..58b0bb2f9f 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -508,17 +508,24 @@ class DragAndDropTest(unittest.TestCase): ''' def test_rendering(self): - height = '12' - width = '33' - template = "path to template" - images = "list of pathes to images" + path_to_images = '/static/images/' - xml_str = """""".format(h=height, w=width, t=template, i=images) + xml_str = """ + + + + + + + + + + + + + + + """.format(path=path_to_images) element = etree.fromstring(xml_str) @@ -529,15 +536,26 @@ class DragAndDropTest(unittest.TestCase): the_input = lookup_tag('drag_and_drop_input')(test_system, element, state) context = the_input._get_render_context() - expected = {'id': 'prob_1_2', 'value': value, 'status': 'unsubmitted', + 'course_folder': 'mock', 'msg': '', - 'width': width, - 'height': height, - 'template': template, - 'images': images, - } - + 'drag_and_drop_json': '{"use_targets": "True", \ +"target_outline": "false", "one_per_target": "True", \ +"draggables": [{"label": "Label 1", "id": "1", "icon": ""}, \ +{"label": "cc", "id": "name_with_icon", "icon": \ +"/static/images/cc.jpg"}, {"label": "arrow-left", "id": \ +"with_icon", "icon": "/static/images/arrow-left.png"}, \ +{"label": "Label2", "id": "5", "icon": ""}, {"label": \ +"Mute", "id": "2", "icon": "/static/images/mute.png"}, \ +{"label": "spinner", "id": "name_label_icon3", "icon": \ +"/static/images/spinner.gif"}, {"label": "Star", "id": \ +"name4", "icon": "/static/images/volume.png"}, {"label": \ +"Label3", "id": "7", "icon": ""}], "base_image": \ +"/static/images/about_1.png", "targets": \ +[{"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"}, \ +{"y": "160", "x": "370", "id": "t2", "w": "90", "h": "90"}]}', + } + # import ipdb; ipdb.set_trace() self.assertEqual(context, expected) From 6ed8598f53e4244555ededf38f2240010cf7ccf0 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:10:54 +0200 Subject: [PATCH 060/140] added soem tests --- common/lib/capa/capa/graders/tests.py | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 0e95b810a9..0207b14b97 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -1,6 +1,40 @@ import unittest import draganddrop +from draganddrop import PositionsCompare + + +class Test_PositionsCompare(unittest.TestCase): + + def test_nested_list_and_list1(self): + self.assertEqual(PositionsCompare([[1, 2], 40]), PositionsCompare([1, 3])) + + def test_nested_list_and_list2(self): + self.assertNotEqual(PositionsCompare([1, 12]), PositionsCompare([1, 1])) + + def test_list_and_list1(self): + self.assertNotEqual(PositionsCompare([[1, 2], 12]), PositionsCompare([1, 15])) + + def test_list_and_list2(self): + self.assertEqual(PositionsCompare([1, 11]), PositionsCompare([1, 1])) + + def test_numerical_list_and_string_list(self): + self.assertNotEqual(PositionsCompare([1, 2]), PositionsCompare(["1"])) + + def test_string_and_string_list1(self): + self.assertEqual(PositionsCompare("1"), PositionsCompare(["1"])) + + def test_string_and_string_list2(self): + self.assertEqual(PositionsCompare("abc"), PositionsCompare("abc")) + + def test_string_and_string_list3(self): + self.assertNotEqual(PositionsCompare("abd"), PositionsCompare("abe")) + + def test_float_and_string(self): + self.assertNotEqual(PositionsCompare([3.5, 5.7]), PositionsCompare(["1"])) + + def test_floats_and_ints(self): + self.assertEqual(PositionsCompare([3.5, 4.5]), PositionsCompare([5, 7])) class Test_DragAndDrop(unittest.TestCase): From b436e6d4d1277956c807bf602d32134ca19630e0 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:11:41 +0200 Subject: [PATCH 061/140] removed debugger call --- common/lib/capa/capa/tests/test_inputtypes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 58b0bb2f9f..0b3eeea0c6 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -557,5 +557,4 @@ class DragAndDropTest(unittest.TestCase): [{"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"}, \ {"y": "160", "x": "370", "id": "t2", "w": "90", "h": "90"}]}', } - # import ipdb; ipdb.set_trace() self.assertEqual(context, expected) From 792cd34ce98d863c722d23d63a6846aeb761e8ca Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:18:11 +0200 Subject: [PATCH 062/140] documentation for drag and drop inputtype python subclass --- common/lib/capa/capa/inputtypes.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 7bfc093a4d..5cc5d8117b 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -791,8 +791,8 @@ registry.register(OpenEndedInput) class DragAndDropInput(InputTypeBase): """ - Input for molecular geometry--show possible structures, let student - pick structure and label positions with atoms or electron pairs. + Input for drag and drop problems. Allows student to drag and drop images and + labels to base image. """ template = 'drag_and_drop_input.html' @@ -801,12 +801,14 @@ class DragAndDropInput(InputTypeBase): def setup(self): def parse(tag, tag_type): - """Parses xml element to dictionary. + """Parses xml element to dictionary. Stores + 'draggable' and 'target' tags with attributes to dictionary and + returns last. Args: tag: xml etree element with attributes - tag_type: type of tag: 'draggable' or 'target'. + tag_type: 'draggable' or 'target'. If tag_type is 'draggable' : all attributes (name or label or icon) are optional, but at least one attribute must be @@ -841,7 +843,7 @@ class DragAndDropInput(InputTypeBase): return dic - # add labels to images: + # add labels to images?: self.no_labels = Attribute('no_labels', default="False").parse_from_xml(self.xml) @@ -869,7 +871,6 @@ class DragAndDropInput(InputTypeBase): self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.to_render.add('drag_and_drop_json') - # import ipdb; ipdb.set_trace() try: # for tests and mock up, work normally not in tets self.loaded_attributes['course_folder'] = \ self.system.course_id.split('/')[1] From 586a47a2ad2d4210f02c08d22bc679196c6553e6 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 14:25:53 +0200 Subject: [PATCH 063/140] Rewrote functionality to include labels as separate from the container. Fixed bugs. Refactoring. --- .../js/capa/drag_and_drop/config_parser.js | 2 + .../js/capa/drag_and_drop/draggables.js | 178 +++++++++++++++--- .../static/js/capa/drag_and_drop/scroller.js | 8 +- common/static/js/capa/drag_and_drop/state.js | 4 +- .../js/capa/drag_and_drop/update_input.js | 103 +++++++--- 5 files changed, 242 insertions(+), 53 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 664324e0b9..6800cb7c97 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -12,6 +12,8 @@ define(['logme'], function (logme) { returnStatus = true; + logme('imageDir', imageDir); + state.config = { 'imageDir': '/static/' + imageDir + '/images', 'draggables': [], diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 2123e7650a..c4c5a94ec0 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -11,11 +11,13 @@ define(['logme', 'update_input'], function (logme, updateInput) { var c1; state.draggables = []; + state.numDraggablesInSlider = 0; for (c1 = 0; c1 < state.config.draggables.length; c1 += 1) { processDraggable(state.config.draggables[c1], c1 + 1); } + state.updateArrowOpacity(); state.currentMovingDraggable = null; $(document).mousemove(function (event) { @@ -34,17 +36,35 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.baseImageEl.offset().top - state.currentMovingDraggable.iconHeight * 0.5 ); + + if (state.currentMovingDraggable.labelEl !== null) { + state.currentMovingDraggable.labelEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top + + state.currentMovingDraggable.iconHeight * 0.5 + + 5 + ); + state.currentMovingDraggable.labelEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.labelWidth * 0.5 + ); + } } }); return; function processDraggable(obj, objIndex) { - var inContainer, mousePressed, onTarget, draggableObj, marginCss; + var inContainer, mousePressed, onTarget, draggableObj; - draggableObj = {}; - - draggableObj.zIndex = objIndex; + draggableObj = { + 'zIndex': objIndex, + 'labelEl': null, + 'hasLoaded': false + }; draggableObj.containerEl = $( '
' ); + draggableObj.containerEl.appendTo(state.sliderEl); if (obj.icon.length > 0) { + draggableObj.hasIcon = true; + draggableObj.iconEl = $(''); draggableObj.iconEl.attr( 'src', @@ -73,32 +97,90 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconWidth = this.width; draggableObj.iconHeight = this.height; + if (draggableObj.iconWidth >= draggableObj.iconHeight) { + draggableObj.iconWidthSmall = 60; + draggableObj.iconHeightSmall = draggableObj.iconWidthSmall * (draggableObj.iconHeight / draggableObj.iconWidth); + } else { + draggableObj.iconHeightSmall = 60; + draggableObj.iconWidthSmall = draggableObj.iconHeightSmall * (draggableObj.iconWidth / draggableObj.iconHeight); + } + draggableObj.iconEl.css('position', 'absolute'); - draggableObj.iconEl.css('top', (50 - this.height * 0.5) + 'px'); - draggableObj.iconEl.css('left', (50 - this.width * 0.5) + 'px'); + + draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); + draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + + if (obj.label.length > 0) { + draggableObj.iconEl.css('top', 5); + } else { + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + } draggableObj.iconEl.appendTo(draggableObj.containerEl); - }); - draggableObj.iconEl.mousedown(mouseDown); - draggableObj.iconEl.mouseup(mouseUp); - draggableObj.iconEl.mousemove(mouseMove); + if (obj.label.length > 0) { + draggableObj.labelEl = $( + '
' + + obj.label + + '
' + ); + + draggableObj.labelEl.appendTo(draggableObj.containerEl); + + draggableObj.labelWidth = draggableObj.labelEl.width(); + + draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); + draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); + + draggableObj.labelEl.mousedown(mouseDown); + draggableObj.labelEl.mouseup(mouseUp); + draggableObj.labelEl.mousemove(mouseMove); + } + + draggableObj.hasLoaded = true; + }); } else { - // Must fix - add +label support, and just label support. - return; + // To make life easier, if there is no icon, but there is a + // label, we will create a label and store it as if it was an + // icon. All the existing code will work, and the user will + // see a label instead of an icon. + if (obj.label.length > 0) { + logme('We have a label length > 0.'); + + draggableObj.iconEl = $( + '
' + + obj.label + + '
' + ); + + draggableObj.iconEl.appendTo(draggableObj.containerEl); + + draggableObj.iconWidth = draggableObj.iconEl.width(); + draggableObj.iconHeight = draggableObj.iconEl.height(); + draggableObj.iconWidthSmall = draggableObj.iconWidth; + draggableObj.iconHeightSmall = draggableObj.iconHeight; + + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + } else { + // If no icon and no label, don't create a draggable. + return; + } } - // if (obj.label.length > 0) { - // marginCss = ''; - // - // if (obj.icon.length === 0) { - // marginCss = 'margin-top: 38px;'; - // } - - // draggableContainerEl.append( - // $('
' + obj.label + '
') - // ); - // } + draggableObj.iconEl.mousedown(mouseDown); + draggableObj.iconEl.mouseup(mouseUp); + draggableObj.iconEl.mousemove(mouseMove); inContainer = true; mousePressed = false; @@ -114,6 +196,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.draggables.push(draggableObj); + state.numDraggablesInSlider += 1; + + if (obj.icon.length === 0) { + draggableObj.hasLoaded = true; + } + return; function mouseDown(event) { @@ -125,11 +213,23 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.containerEl.hide(); draggableObj.iconEl.detach(); + + draggableObj.iconEl.css('width', draggableObj.iconWidth); + draggableObj.iconEl.css('height', draggableObj.iconHeight); + draggableObj.iconEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.iconWidth * 0.5); draggableObj.iconEl.css('top', event.pageY - state.baseImageEl.offset().top - draggableObj.iconHeight * 0.5); draggableObj.iconEl.appendTo(state.baseImageEl.parent()); + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.detach(); + draggableObj.labelEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.labelWidth * 0.5); + draggableObj.labelEl.css('top', event.pageY - state.baseImageEl.offset().top + draggableObj.iconHeight * 0.5 + 5); + draggableObj.labelEl.appendTo(state.baseImageEl.parent()); + } + inContainer = false; + state.numDraggablesInSlider -= 1; } draggableObj.oldZIndex = draggableObj.zIndex; @@ -178,6 +278,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } else { moveBackToSlider(); removeObjIdFromTarget(); + + state.numDraggablesInSlider += 1; } } else { if ( @@ -190,6 +292,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.x = -1; draggableObj.y = -1; + + state.numDraggablesInSlider += 1; } else { correctZIndexes(); @@ -283,6 +387,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconEl.css('left', target.offset.left + 0.5 * target.w - draggableObj.iconWidth * 0.5 + offset); draggableObj.iconEl.css('top', target.offset.top + 0.5 * target.h - draggableObj.iconHeight * 0.5 + offset); + + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.css('left', target.offset.left + 0.5 * target.w - draggableObj.labelWidth * 0.5 + offset); + draggableObj.labelEl.css('top', target.offset.top + 0.5 * target.h + draggableObj.iconHeight * 0.5 + 5 + offset); + } } // Go through all of the draggables subtract 1 from the z-index @@ -316,17 +425,30 @@ define(['logme', 'update_input'], function (logme, updateInput) { // move it back to the slider, placing it in the same position // that it was dragged out of. function moveBackToSlider() { - var c1; - draggableObj.containerEl.show(); draggableObj.iconEl.detach(); - draggableObj.iconEl.css('top', (50 - draggableObj.iconHeight * 0.5) + 'px'); - draggableObj.iconEl.css('left', (50 - draggableObj.iconWidth * 0.5) + 'px'); + draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); + draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + + if (draggableObj.labelEl !== null) { + draggableObj.iconEl.css('top', 5); + } else { + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + } draggableObj.iconEl.appendTo(draggableObj.containerEl); + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.detach(); + draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); + draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); + draggableObj.labelEl.appendTo(draggableObj.containerEl); + } + inContainer = true; } } diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index d3a4d23eab..505785086d 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -105,7 +105,9 @@ define(['logme'], function (logme) { '
' ); @@ -158,7 +160,7 @@ define(['logme'], function (logme) { // When there are no more hidden draggables, prevent from // scrolling infinitely. - if (showElLeftMargin < -102 * (state.sliderEl.children().length - 6)) { + if (showElLeftMargin < -102 * (state.numDraggablesInSlider - 6)) { return; } @@ -192,7 +194,7 @@ define(['logme'], function (logme) { moveLeftEl.children('div').css('opacity', '1'); moveRightEl.children('div').css('opacity', '1'); - if (showElLeftMargin < -102 * (state.sliderEl.children().length - 6)) { + if (showElLeftMargin < -102 * (state.numDraggablesInSlider - 6)) { moveRightEl.children('div').css('opacity', '.4'); } if (showElLeftMargin > -102) { diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index 5bf957eff5..c3a4d3c4ac 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -14,7 +14,9 @@ define([], function () { // Will indicate when all targetsand draggables have been loaded, // processed, and postioned intially. 'targetsLoaded': false, - 'draggablesLoaded': false + 'draggablesLoaded': false, + + 'numDraggablesInSlider': 0 }; } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 6ebf62ef11..0ae95e6a30 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -75,7 +75,12 @@ define(['logme'], function (logme) { function repositionDraggables(answer) { var draggableId, draggable, targetId, target, draggablePosition, - c1; + c1, offset; + + offset = 0; + if (state.config.targetOutline === true) { + offset = 1; + } logme(answer); @@ -98,22 +103,47 @@ define(['logme'], function (logme) { continue; } + (function (c1, draggableId, draggable) { + moveDraggableToBaseImage(); + return; - draggable.setInContainer(false); + function moveDraggableToBaseImage() { + if (draggable.hasLoaded === false) { + setTimeout(moveDraggableToBaseImage, 50); + return; + } - draggable.el.detach(); - draggable.el.css('border', 'none'); - draggable.el.css('position', 'absolute'); - draggable.el.css('left', answer.draggables[c1][draggableId][0] - 50); - draggable.el.css('top', answer.draggables[c1][draggableId][1] - 50); + draggable.setInContainer(false); + draggable.containerEl.hide(); - draggable.el.css('left', target.offset.left + 0.5 * target.w - 50); - draggable.el.css('top', target.offset.top + 0.5 * target.h - 50); + draggable.iconEl.detach(); - draggable.el.appendTo(state.baseImageEl.parent()); + draggable.iconEl.css('width', draggable.iconWidth); + draggable.iconEl.css('height', draggable.iconHeight); + draggable.iconEl.css( + 'left', + target.offset.left + 0.5 * target.w - draggable.iconWidth * 0.5 + offset + ); + draggable.iconEl.css( + 'top', + target.offset.top + 0.5 * target.h - draggable.iconHeight * 0.5 + offset + ); - draggable.setOnTarget(target); - target.draggable.push(draggableId); + draggable.iconEl.appendTo(state.baseImageEl.parent()); + + if (draggable.labelEl !== null) { + draggable.labelEl.detach(); + + draggable.labelEl.css('left', target.offset.left + 0.5 * target.w - draggable.labelWidth * 0.5 + offset); + draggable.labelEl.css('top', target.offset.top + 0.5 * target.h + draggable.iconHeight * 0.5 + 5 + offset); + + draggable.labelEl.appendTo(state.baseImageEl.parent()); + } + + draggable.setOnTarget(target); + target.draggable.push(draggableId); + } + }(c1, draggableId, draggable)); } } } else if ( @@ -128,17 +158,47 @@ define(['logme'], function (logme) { continue; } - draggable.setInContainer(false); + (function (c1, draggableId, draggable) { + moveDraggableToBaseImage(); + return; - draggable.el.detach(); - draggable.el.css('border', 'none'); - draggable.el.css('position', 'absolute'); - draggable.el.css('left', answer.draggables[c1][draggableId][0] - 50); - draggable.el.css('top', answer.draggables[c1][draggableId][1] - 50); - draggable.el.appendTo(state.baseImageEl.parent()); + function moveDraggableToBaseImage() { + if (draggable.hasLoaded === false) { + setTimeout(moveDraggableToBaseImage, 50); + return; + } - draggable.x = answer.draggables[c1][draggableId][0]; - draggable.y = answer.draggables[c1][draggableId][1]; + draggable.setInContainer(false); + draggable.containerEl.hide(); + + draggable.iconEl.detach(); + + draggable.iconEl.css('width', draggable.iconWidth); + draggable.iconEl.css('height', draggable.iconHeight); + draggable.iconEl.css( + 'left', + answer.draggables[c1][draggableId][0] - draggable.iconWidth * 0.5 + offset + ); + draggable.iconEl.css( + 'top', + answer.draggables[c1][draggableId][0] - draggable.iconHeight * 0.5 + offset + ); + + draggable.iconEl.appendTo(state.baseImageEl.parent()); + + if (draggable.labelEl !== null) { + draggable.labelEl.detach(); + + draggable.labelEl.css('left', answer.draggables[c1][draggableId][0] - draggable.iconWidth * 0.5 - draggable.labelWidth * 0.5 + offset); + draggable.labelEl.css('top', answer.draggables[c1][draggableId][0] - draggable.iconHeight * 0.5 + draggable.iconHeight + 5 + offset); + + draggable.labelEl.appendTo(state.baseImageEl.parent()); + } + + draggable.x = answer.draggables[c1][draggableId][0]; + draggable.y = answer.draggables[c1][draggableId][1]; + } + }(c1, draggableId, draggable)); } } } else { @@ -147,6 +207,7 @@ define(['logme'], function (logme) { return; } + state.numDraggablesInSlider -= 1; state.updateArrowOpacity(); } From 28f7a68e4238c57db001333d35f313f94983418b Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 14:26:14 +0200 Subject: [PATCH 064/140] fix grammar --- common/lib/capa/capa/inputtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 5cc5d8117b..c968130fdc 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -815,7 +815,7 @@ class DragAndDropInput(InputTypeBase): presented. If tag_type is 'target' all attributes (name, x, y, w, h) - are requred. (x, y) - coordinates of center of target, + are required. (x, y) - coordinates of center of target, w, h - weight and height of target. Returns: From 17b01071d075d0126126814c63f63ada555ff96a Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 15:11:37 +0200 Subject: [PATCH 065/140] Fixed bug. --- common/static/js/capa/drag_and_drop/update_input.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 0ae95e6a30..31e2295edb 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -82,6 +82,7 @@ define(['logme'], function (logme) { offset = 1; } + logme('!!!!!!!!!!!!!!!! answer'); logme(answer); if ( @@ -103,7 +104,7 @@ define(['logme'], function (logme) { continue; } - (function (c1, draggableId, draggable) { + (function (c1, draggableId, draggable, targetId, target) { moveDraggableToBaseImage(); return; @@ -113,6 +114,8 @@ define(['logme'], function (logme) { return; } + logme('use_targets = false; draggableId: ' + draggableId); + draggable.setInContainer(false); draggable.containerEl.hide(); @@ -143,7 +146,7 @@ define(['logme'], function (logme) { draggable.setOnTarget(target); target.draggable.push(draggableId); } - }(c1, draggableId, draggable)); + }(c1, draggableId, draggable, targetId, target)); } } } else if ( @@ -164,10 +167,13 @@ define(['logme'], function (logme) { function moveDraggableToBaseImage() { if (draggable.hasLoaded === false) { + logme('draggable.hasLoaded === false'); setTimeout(moveDraggableToBaseImage, 50); return; } + logme('use_targets = false; draggableId: ' + draggableId); + draggable.setInContainer(false); draggable.containerEl.hide(); From ab95193252581086e3e3313554f3ad9d3e655ee6 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 15:21:18 +0200 Subject: [PATCH 066/140] Bugfixing. --- common/static/js/capa/drag_and_drop/update_input.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 31e2295edb..87f82dd146 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -74,8 +74,7 @@ define(['logme'], function (logme) { } function repositionDraggables(answer) { - var draggableId, draggable, targetId, target, draggablePosition, - c1, offset; + var draggableId, draggable, targetId, target, c1, offset; offset = 0; if (state.config.targetOutline === true) { @@ -104,7 +103,7 @@ define(['logme'], function (logme) { continue; } - (function (c1, draggableId, draggable, targetId, target) { + (function (draggableId, draggable, targetId, target) { moveDraggableToBaseImage(); return; @@ -146,7 +145,7 @@ define(['logme'], function (logme) { draggable.setOnTarget(target); target.draggable.push(draggableId); } - }(c1, draggableId, draggable, targetId, target)); + }(draggableId, draggable, targetId, target)); } } } else if ( @@ -187,7 +186,7 @@ define(['logme'], function (logme) { ); draggable.iconEl.css( 'top', - answer.draggables[c1][draggableId][0] - draggable.iconHeight * 0.5 + offset + answer.draggables[c1][draggableId][1] - draggable.iconHeight * 0.5 + offset ); draggable.iconEl.appendTo(state.baseImageEl.parent()); @@ -196,7 +195,7 @@ define(['logme'], function (logme) { draggable.labelEl.detach(); draggable.labelEl.css('left', answer.draggables[c1][draggableId][0] - draggable.iconWidth * 0.5 - draggable.labelWidth * 0.5 + offset); - draggable.labelEl.css('top', answer.draggables[c1][draggableId][0] - draggable.iconHeight * 0.5 + draggable.iconHeight + 5 + offset); + draggable.labelEl.css('top', answer.draggables[c1][draggableId][1] - draggable.iconHeight * 0.5 + draggable.iconHeight + 5 + offset); draggable.labelEl.appendTo(state.baseImageEl.parent()); } From 75ff3aaaa044d5009dc4f3040a910ebc4dc83781 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 15:24:13 +0200 Subject: [PATCH 067/140] docs and denied rule refactores --- common/lib/capa/capa/graders/draganddrop.py | 129 ++++++++++---------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index e02304aef0..414cef91a5 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -18,13 +18,12 @@ If use_targets is false: { "use_targets": false, "draggable": [ - { "image1": "(10, 20)" }, - { "ant": "(30, 40)" }, - { "molecule": "(100, 200)" }, + { "image1": "[10, 20]" }, + { "ant": "[30, 40]" }, + { "molecule": "[100, 200]" }, ] } values are (x,y) coordinates of centers of dragged images. - """ import json @@ -32,55 +31,58 @@ from collections import OrderedDict class PositionsCompare(list): - """Inputs are: "abc" - target + """ Class for comparing positions. + + Args: + list or string:: + "abc" - target [10, 20] - list of integers [[10,20], 200] list of list and integer """ def __eq__(self, other): - # Default lists behaviour is convers "abc" to ["a", "b", "c"]. - # We will use that. - # import ipdb; ipdb.set_trace() + """ Compares two arguments. - #check if self or other is not empty list (empty lists = false) + Default lists behavior is conversion of string "abc" to list + ["a", "b", "c"]. We will use that. + + If self or other is empty - returns False. + + Args: + self, other: str, unicode, list, int, float + + Returns: bool + """ + # checks if self or other is not empty list (empty lists = false) if not self or not other: return False - # check correct input types - if (not isinstance(self[0], (str, unicode, list, int, float)) or - not isinstance(other[0], (str, unicode, list, int, float))): - print 'Incorrect input type' - return False - if (isinstance(self[0], (list, int, float)) and isinstance(other[0], (list, int, float))): - print 'Numerical position compare' return self.coordinate_positions_compare(other) + elif (isinstance(self[0], (unicode, str)) and isinstance(other[0], (unicode, str))): - print 'Targets compare' return ''.join(self) == ''.join(other) - else: - # we do not have ints or lists of lists or two string/unicode lists - # on both sides - print type(self[0]), type(other[0]), "not correct" + else: # improper argument types + # Now we have no (float / int or lists of list and float / int pair) + # or two string / unicode lists pair return False def __ne__(self, other): return not self.__eq__(other) def coordinate_positions_compare(self, other, r=10): - """ Checks if pos1 is equal to pos2 inside radius - of forgiveness (default 10 px). + """ Checks if self is equal to other inside radius of forgiveness + (default 10 px). Args: - self, other: [x, y] or [[x, y], r], where - r is radius of forgiveness; + self, other: [x, y] or [[x, y], r], where r is radius of + forgiveness; x, y, r: int Returns: bool. """ - print 'I am called', self, other # get max radius of forgiveness if isinstance(self[0], list): # [(x, y), r] case r = max(self[1], r) @@ -101,62 +103,56 @@ class PositionsCompare(list): class DragAndDrop(object): + """ Grader for drag and drop inputtype. + """ def __init__(self): - self.correct_groups = OrderedDict() # groups - self.correct_positions = OrderedDict() # positions of comparing - self.user_groups = OrderedDict() - self.user_positions = OrderedDict() + self.correct_groups = OrderedDict() # correct groups from xml + self.correct_positions = OrderedDict() # correct positions for comparing + self.user_groups = OrderedDict() # will be populated from user answer + self.user_positions = OrderedDict() # will be populated from user answer + + # flag to check if user answer has more draggables than correct answer self.incorrect = False def grade(self): - ''' - Grade drag and drop problem. - If use_targets is True - checks if image placed on proper target. - If use_targets is False - checks if image placed on proper coordinates, - with setted radius of forgiveness (default is 10) + ''' Grader user answer. - Args: - user_input, correct_answer: json. Format: + If use_targets is True - checks if every draggable isplaced on proper + target. - user_input: see module docstring + If use_targets is False - checks if every draggable is placed on proper + coordinates within radius of forgiveness (default is 10). - correct_answer: - if use_targets is True: - {'1': 't1', 'name_with_icon': 't2'} - else: - {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} - - Returns: - True or False. + Returns: bool. ''' - if self.incorrect: + if self.incorrect: # user answer has more draggables than correct answer return False + # checks if we have same groups of draggables if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()): return False + + # checks if for every groups draggables names are same for groupname, draggable_ids in self.correct_groups.items(): if sorted(draggable_ids) != sorted(self.user_groups[groupname]): return False + # from now self.correct_groups and self.user_groups are equal if # order is ignored - # Check fo every group that positions of every group element are equal - # with positions + # Checks in every group that user positions of every element are equal + # with correct positions for every rule - # 'denied' rule - denied_positions = [item for g in self.correct_groups.keys() - for item in self.correct_positions[g].get('denied', [])] - all_user_positions = [item for g in self.correct_groups.keys() - for item in self.user_positions[g]['user']] - if not self.compare_positions(denied_positions, - all_user_positions, flag='denied'): - return False + for group in self.correct_groups: # 'denied' rule + if not self.compare_positions(self.correct_positions[group].get( + 'denied', []), self.user_positions[group]['user'], flag='denied'): + return False no_exact, no_allowed, no_anyof = False, False, False - # 'exact' rule - for groupname in self.correct_groups: + + for groupname in self.correct_groups: # 'exact' rule if self.correct_positions[groupname].get('exact', []): if not self.compare_positions( self.correct_positions[groupname]['exact'], @@ -165,8 +161,7 @@ class DragAndDrop(object): else: no_exact = True - # 'allowed' rule - for groupname in self.correct_groups: + for groupname in self.correct_groups: # 'allowed' rule if self.correct_positions[groupname].get('allowed', []): if not self.compare_positions( self.correct_positions[groupname]['allowed'], @@ -260,7 +255,17 @@ class DragAndDrop(object): def grade(user_input, correct_answer): - """ Support 2 interfaces""" + """Args: + user_input, correct_answer: json. Format: + + user_input: see module docstring + + correct_answer: + if use_targets is True: + {'1': 't1', 'name_with_icon': 't2'} + else: + {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} + Support 2 interfaces""" if isinstance(correct_answer, dict): dnd = DragAndDrop() else: From 315137876890d69538d1cd4379ed8f7493331b39 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 15:50:47 +0200 Subject: [PATCH 068/140] removed denied and allowed rule --- common/lib/capa/capa/graders/draganddrop.py | 71 ++++++++------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 414cef91a5..1eb1a1b495 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -145,42 +145,25 @@ class DragAndDrop(object): # Checks in every group that user positions of every element are equal # with correct positions for every rule - for group in self.correct_groups: # 'denied' rule - if not self.compare_positions(self.correct_positions[group].get( - 'denied', []), self.user_positions[group]['user'], flag='denied'): - return False + # for group in self.correct_groups: # 'denied' rule + # if not self.compare_positions(self.correct_positions[group].get( + # 'denied', []), self.user_positions[group]['user'], flag='denied'): + # return False - no_exact, no_allowed, no_anyof = False, False, False + passed_rules = dict() + for rule in ('exact', 'anyof'): + passed_rules[rule] = True + for groupname in self.correct_groups: + if self.correct_positions[groupname].get(rule, []): + if not self.compare_positions( + self.correct_positions[groupname][rule], + self.user_positions[groupname]['user'], flag=rule): + return False + else: + passed_rules[rule] = False - for groupname in self.correct_groups: # 'exact' rule - if self.correct_positions[groupname].get('exact', []): - if not self.compare_positions( - self.correct_positions[groupname]['exact'], - self.user_positions[groupname]['user'], flag='exact'): - return False - else: - no_exact = True - - for groupname in self.correct_groups: # 'allowed' rule - if self.correct_positions[groupname].get('allowed', []): - if not self.compare_positions( - self.correct_positions[groupname]['allowed'], - self.user_positions[groupname]['user'], flag='allowed'): - return False - else: - no_allowed = True - - # 'anyof' rule - for groupname in self.correct_groups: - if self.correct_positions[groupname].get('anyof', []): - if not self.compare_positions( - self.correct_positions[groupname]['anyof'], - self.user_positions[groupname]['user'], flag='anyof'): - return False - else: - no_anyof = True - - if no_allowed and no_exact and no_anyof: + # if all passed rules are false + if not reduce(lambda x, y: x or y, passed_rules): return False return True @@ -188,16 +171,16 @@ class DragAndDrop(object): def compare_positions(self, correct, user, flag): """ order of correct/user is matter only in anyof_flag""" # import ipdb; ipdb.set_trace() - if flag == 'denied': - for el1 in correct: - for el2 in user: - if PositionsCompare(el1) == PositionsCompare(el2): - return False + # if flag == 'denied': + # for el1 in correct: + # for el2 in user: + # if PositionsCompare(el1) == PositionsCompare(el2): + # return False - if flag == 'allowed': - for el1, el2 in zip(sorted(correct), sorted(user)): - if PositionsCompare(el1) != PositionsCompare(el2): - return False + # if flag == 'allowed': + # for el1, el2 in zip(sorted(correct), sorted(user)): + # if PositionsCompare(el1) != PositionsCompare(el2): + # return False if flag == 'exact': for el1, el2 in zip(correct, user): @@ -210,7 +193,7 @@ class DragAndDrop(object): for c_el in correct: if PositionsCompare(u_el) == PositionsCompare(c_el): count += 1 - continue + break if count != len(user): return False From 16ecdadfc67284188ce89fc4ff309638c9158be4 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 16:15:32 +0200 Subject: [PATCH 069/140] changed interface --- common/lib/capa/capa/graders/draganddrop.py | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 1eb1a1b495..594f311b81 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -201,11 +201,15 @@ class DragAndDrop(object): def populate(self, correct_answer, user_answer): """ """ - + # convert from dict format to list format + tmp = [] if isinstance(correct_answer, dict): for key, value in correct_answer.items(): - self.correct_groups[key] = [key] - self.correct_positions[key] = {'exact': [value]} + tmp_dict = {'draggables': [], 'targets': [], 'rule': 'exact'} + tmp_dict['draggables'].append(key) + tmp_dict['targets'].append(value) + tmp.append(tmp_dict) + correct_answer = tmp user_answer = json.loads(user_answer) self.use_targets = user_answer.get('use_targets') @@ -216,7 +220,12 @@ class DragAndDrop(object): # create identical data structures # user groups must mirror correct_groups # and positions must reflect order in group - for groupname in self.correct_groups: + for i in xrange(0, len(correct_answer)): + groupname = str(i) + self.correct_groups = OrderedDict() + self.correct_groups[groupname] = correct_answer[i]['draggables'] + self.correct_positions = OrderedDict() + self.correct_positions[groupname] = {correct_answer[i]['rule']: correct_answer[i]['targets']} self.user_groups[groupname] = [] self.user_positions[groupname] = {'user': []} for draggable_dict in user_answer['draggables']: @@ -249,10 +258,6 @@ def grade(user_input, correct_answer): else: {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} Support 2 interfaces""" - if isinstance(correct_answer, dict): - dnd = DragAndDrop() - else: - dnd = correct_answer - + dnd = DragAndDrop() dnd.populate(correct_answer=correct_answer, user_answer=user_input) return dnd.grade() From efdc4c7b7cfdffd8d5920186e4298da68b3aa7a2 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 16:17:55 +0200 Subject: [PATCH 070/140] Updated tests for new XML format. --- common/lib/capa/capa/graders/tests.py | 96 +++++++++++++++------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 0207b14b97..a7876dd6a9 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -128,28 +128,34 @@ class Test_DragAndDrop(unittest.TestCase): {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' - correct_answer = draganddrop.DragAndDrop() - correct_answer.correct_groups['filled_levels'] = ['1', '2', '3', '4', '5', '6'] - correct_answer.correct_positions['filled_levels'] = {'allowed': [ - 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2']} - correct_answer.correct_groups['spin_up'] = ['7', '8', '9', '10'] - correct_answer.correct_positions['spin_up'] = {'allowed': - ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2']} - - correct_answer.correct_groups['sigma'] = ['11', '12'] - correct_answer.correct_positions['sigma'] = {'allowed': - ['s_sigma_name', 'p_sigma_name']} - - correct_answer.correct_groups['sigma_star'] = ['13', '14'] - correct_answer.correct_positions['sigma_star'] = {'allowed': - ['s_sigma_star_name', 'p_sigma_star_name']} - - correct_answer.correct_groups['pi'] = ['15'] - correct_answer.correct_positions['pi'] = {'allowed': ['p_pi_name']} - - correct_answer.correct_groups['pi_star'] = ['16'] - correct_answer.correct_positions['pi_star'] = {'allowed': ['p_pi_star_name']} + correct_answers = [{ + 'draggables': ['1', '2', '3', '4', '5', '6'], + 'targets': [ + 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' + ], + 'rule': 'anyof' + }, { + 'draggables': ['7','8', '9', '10'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'rule': 'anyof' + }, { + 'draggables': ['11', '12'], + 'targets': ['s_sigma_name', 'p_sigma_name'], + 'rule': 'anyof' + }, { + 'draggables': ['13', '14'], + 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], + 'rule': 'anyof' + }, { + 'draggables': ['15'], + 'targets': ['p_pi_name'], + 'rule': 'anyof' + }, { + 'draggables': ['16'], + 'targets': ['p_pi_star_name'], + 'rule': 'anyof' + }] self.assertTrue(draganddrop.grade(user_input, correct_answer)) @@ -161,28 +167,34 @@ class Test_DragAndDrop(unittest.TestCase): {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' - correct_answer = draganddrop.DragAndDrop() - correct_answer.correct_groups['filled_levels'] = ['1', '2', '3', '4', '5', '6'] - correct_answer.correct_positions['filled_levels'] = {'allowed': [ - 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2']} - correct_answer.correct_groups['spin_up'] = ['7', '8', '9', '10'] - correct_answer.correct_positions['spin_up'] = {'allowed': - ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2']} - - correct_answer.correct_groups['sigma'] = ['11', '12'] - correct_answer.correct_positions['sigma'] = {'allowed': - ['s_sigma_name', 'p_sigma_name']} - - correct_answer.correct_groups['sigma_star'] = ['13', '14'] - correct_answer.correct_positions['sigma_star'] = {'allowed': - ['s_sigma_star_name', 'p_sigma_star_name']} - - correct_answer.correct_groups['pi'] = ['15'] - correct_answer.correct_positions['pi'] = {'allowed': ['p_pi_name']} - - correct_answer.correct_groups['pi_star'] = ['16'] - correct_answer.correct_positions['pi_star'] = {'allowed': ['p_pi_star_name']} + correct_answers = [{ + 'draggables': ['1', '2', '3', '4', '5', '6'], + 'targets': [ + 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' + ], + 'rule': 'anyof' + }, { + 'draggables': ['7','8', '9', '10'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'rule': 'anyof' + }, { + 'draggables': ['11', '12'], + 'targets': ['s_sigma_name', 'p_sigma_name'], + 'rule': 'anyof' + }, { + 'draggables': ['13', '14'], + 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], + 'rule': 'anyof' + }, { + 'draggables': ['15'], + 'targets': ['p_pi_name'], + 'rule': 'anyof' + }, { + 'draggables': ['16'], + 'targets': ['p_pi_star_name'], + 'rule': 'anyof' + }] self.assertFalse(draganddrop.grade(user_input, correct_answer)) From 17f11cef077e9972ef5f2705cd54a22cd899933b Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 16:18:11 +0200 Subject: [PATCH 071/140] added comments --- common/lib/capa/capa/graders/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index a7876dd6a9..d5a65c7596 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -199,6 +199,11 @@ class Test_DragAndDrop(unittest.TestCase): self.assertFalse(draganddrop.grade(user_input, correct_answer)) + #test for every function in DND + + # test for different denied-allowed positions inised + # different groups + def suite(): testcases = [Test_DragAndDrop] From 6aaad83abc7e4c3a1eece6deabb64d82eea4e5f8 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 16:33:34 +0200 Subject: [PATCH 072/140] finished interface change --- common/lib/capa/capa/graders/draganddrop.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 594f311b81..980977d949 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -202,14 +202,14 @@ class DragAndDrop(object): def populate(self, correct_answer, user_answer): """ """ # convert from dict format to list format - tmp = [] if isinstance(correct_answer, dict): + tmp = [] for key, value in correct_answer.items(): tmp_dict = {'draggables': [], 'targets': [], 'rule': 'exact'} tmp_dict['draggables'].append(key) tmp_dict['targets'].append(value) tmp.append(tmp_dict) - correct_answer = tmp + correct_answer = tmp user_answer = json.loads(user_answer) self.use_targets = user_answer.get('use_targets') @@ -222,9 +222,7 @@ class DragAndDrop(object): # and positions must reflect order in group for i in xrange(0, len(correct_answer)): groupname = str(i) - self.correct_groups = OrderedDict() self.correct_groups[groupname] = correct_answer[i]['draggables'] - self.correct_positions = OrderedDict() self.correct_positions[groupname] = {correct_answer[i]['rule']: correct_answer[i]['targets']} self.user_groups[groupname] = [] self.user_positions[groupname] = {'user': []} @@ -258,6 +256,8 @@ def grade(user_input, correct_answer): else: {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} Support 2 interfaces""" + # import ipdb; ipdb.set_trace() dnd = DragAndDrop() + # import ipdb; ipdb.set_trace() dnd.populate(correct_answer=correct_answer, user_answer=user_input) return dnd.grade() From 1afaec6f6102bef33dadb288a661d8c856080003 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 16:34:19 +0200 Subject: [PATCH 073/140] update in tests --- common/lib/capa/capa/graders/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index d5a65c7596..409b22fe05 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -129,7 +129,7 @@ class Test_DragAndDrop(unittest.TestCase): {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' - correct_answers = [{ + correct_answer = [{ 'draggables': ['1', '2', '3', '4', '5', '6'], 'targets': [ 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' @@ -168,7 +168,7 @@ class Test_DragAndDrop(unittest.TestCase): {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]}' - correct_answers = [{ + correct_answer = [{ 'draggables': ['1', '2', '3', '4', '5', '6'], 'targets': [ 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' From f283df9022394d475c784333cc260bc9a8f372ab Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 3 Jan 2013 16:51:23 +0200 Subject: [PATCH 074/140] Cleaning up code. --- .../js/capa/drag_and_drop/config_parser.js | 2 -- .../js/capa/drag_and_drop/draggables.js | 2 -- .../js/capa/drag_and_drop/update_input.js | 19 ++++++------------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 6800cb7c97..664324e0b9 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -12,8 +12,6 @@ define(['logme'], function (logme) { returnStatus = true; - logme('imageDir', imageDir); - state.config = { 'imageDir': '/static/' + imageDir + '/images', 'draggables': [], diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index c4c5a94ec0..0800ce4c6b 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -151,8 +151,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { // icon. All the existing code will work, and the user will // see a label instead of an icon. if (obj.label.length > 0) { - logme('We have a label length > 0.'); - draggableObj.iconEl = $( '
Date: Thu, 3 Jan 2013 17:52:37 +0200 Subject: [PATCH 077/140] fixed dirname --- common/lib/capa/capa/inputtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 86454d4fdb..48ee45e8ac 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -873,7 +873,7 @@ class DragAndDropInput(InputTypeBase): self.to_render.add('drag_and_drop_json') try: # for tests and mock up, work normally not in tets self.loaded_attributes['course_folder'] = \ - self.system.location.split('/')[3] + self.system.filestore.root_path.split('/')[-1] except: self.loaded_attributes['course_folder'] = 'mock' self.to_render.add('course_folder') From bdcf94dd406cda8a0e761dcec4e6b5b9699b183e Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Thu, 3 Jan 2013 18:17:13 +0200 Subject: [PATCH 078/140] comments and pepification --- common/lib/capa/capa/graders/draganddrop.py | 64 +++++++++++++++------ 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 0b02181982..38f4dc2fb3 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -163,19 +163,14 @@ class DragAndDrop(object): return True def compare_positions(self, correct, user, flag): - """ order of correct/user is matter only in anyof_flag""" - # import ipdb; ipdb.set_trace() - # if flag == 'denied': - # for el1 in correct: - # for el2 in user: - # if PositionsCompare(el1) == PositionsCompare(el2): - # return False + """ Compares two lists of positions with flag rules. Order of + correct/user arguments is matter only in 'anyof' flag. - # if flag == 'allowed': - # for el1, el2 in zip(sorted(correct), sorted(user)): - # if PositionsCompare(el1) != PositionsCompare(el2): - # return False + Args: + correst, user: lists of positions + Returns: True if within rule lists are equal, otherwise False. + """ if flag == 'exact': for el1, el2 in zip(correct, user): if PositionsCompare(el1) != PositionsCompare(el2): @@ -194,7 +189,44 @@ class DragAndDrop(object): return True def populate(self, correct_answer, user_answer): - """ """ + """ Populates DragAndDrop variables from user_answer and correct_answer. + If correct_answer is dict, converts it to list. + Correct answer in dict form is simpe structure for fast and simple + grading. Example of corrrect answer dict example:: + + correct_answer = {'name4': 't1', + 'name_with_icon': 't1', + '5': 't2', + '7':'t2'} + + It is draggable_name: dragable_position mapping + + Correct answer in list form is designed for complex cases:: + + correct_answers = [ + { + 'draggables': ['1', '2', '3', '4', '5', '6'], + 'targets': [ + 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2'], + 'rule': 'anyof'}, + { + 'draggables': ['7', '8', '9', '10'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], + 'rule': 'anyof' + } + ] + Correct answer in list form is list of dicts, and every dict must have + 3 keys: 'draggables', 'targets' and 'rule'. 'Draggables' value is + list of draggables ids, 'targes' values are list of targets ids, 'rule' + value is 'exact' or 'anyof'. + + Args: + user_answer: json + correct_answer: dict or list + + Returns: None + + """ # convert from dict answer format to list format if isinstance(correct_answer, dict): tmp = [] @@ -211,13 +243,12 @@ class DragAndDrop(object): # check if we have draggables that are not in correct answer: check_extra_draggables = {} - # create identical data structures - # user groups must mirror correct_groups - # and positions must reflect order in group + # create identical data structures from user answer and correct answer for i in xrange(0, len(correct_answer)): groupname = str(i) self.correct_groups[groupname] = correct_answer[i]['draggables'] - self.correct_positions[groupname] = {correct_answer[i]['rule']: correct_answer[i]['targets']} + self.correct_positions[groupname] = {correct_answer[i]['rule']: + correct_answer[i]['targets']} self.user_groups[groupname] = [] self.user_positions[groupname] = {'user': []} for draggable_dict in user_answer['draggables']: @@ -235,7 +266,6 @@ class DragAndDrop(object): for draggable in check_extra_draggables: if not check_extra_draggables[draggable]: self.incorrect = True - # import ipdb; ipdb.set_trace() def grade(user_input, correct_answer): From e21048206a58ae4fac2c99b2bdb16524743374ee Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 11:04:04 +0200 Subject: [PATCH 079/140] comments and refactoring --- common/lib/capa/capa/graders/draganddrop.py | 115 +++++++++++--------- 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index 38f4dc2fb3..af28435bb6 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -64,9 +64,8 @@ class PositionsCompare(list): elif (isinstance(self[0], (unicode, str)) and isinstance(other[0], (unicode, str))): return ''.join(self) == ''.join(other) - else: # improper argument types - # Now we have no (float / int or lists of list and float / int pair) - # or two string / unicode lists pair + else: # improper argument types: no (float / int or lists of list + #and float / int pair) or two string / unicode lists pair return False def __ne__(self, other): @@ -103,18 +102,14 @@ class PositionsCompare(list): class DragAndDrop(object): - """ Grader for drag and drop inputtype. + """ Grader class for drag and drop inputtype. """ - def __init__(self): self.correct_groups = OrderedDict() # correct groups from xml self.correct_positions = OrderedDict() # correct positions for comparing self.user_groups = OrderedDict() # will be populated from user answer self.user_positions = OrderedDict() # will be populated from user answer - # flag to check if user answer has more draggables than correct answer - self.incorrect = False - def grade(self): ''' Grader user answer. @@ -126,39 +121,30 @@ class DragAndDrop(object): Returns: bool. ''' + for draggable in self.excess_draggables: + if not self.excess_draggables[draggable]: + return False # user answer has more draggables than correct answer - if self.incorrect: # user answer has more draggables than correct answer - return False - - # checks if we have same groups of draggables - if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()): - return False - - # checks if for every groups draggables names are same + # Number of draggables in user_groups may be smaller that in + # correct_groups, that is incorrect. for groupname, draggable_ids in self.correct_groups.items(): if sorted(draggable_ids) != sorted(self.user_groups[groupname]): return False - # from now self.correct_groups and self.user_groups are equal if - # order is ignored - - # Next check in every group that user positions of every element are equal - # with correct positions for every rule - - passed_rules = dict() - for rule in ('exact', 'anyof'): - passed_rules[rule] = 0 - for groupname in self.correct_groups: + # Check that in every group, for rule of that group, user positions of + #every element are equal with correct positions + for groupname in self.correct_groups: + rules_executed = 0 + for rule in ('exact', 'anyof'): # every group has only one rule if self.correct_positions[groupname].get(rule, []): + rules_executed += 1 if not self.compare_positions( self.correct_positions[groupname][rule], self.user_positions[groupname]['user'], flag=rule): return False - passed_rules[rule] += 1 - - # if no rule was executed for all groups - if sum(passed_rules.values()) == 0: - return False + if not rules_executed: # no correct rules for current group + # probably xml content mistake - wrong rules names + return False return True @@ -199,7 +185,7 @@ class DragAndDrop(object): '5': 't2', '7':'t2'} - It is draggable_name: dragable_position mapping + It is draggable_name: dragable_position mapping. Correct answer in list form is designed for complex cases:: @@ -215,6 +201,7 @@ class DragAndDrop(object): 'rule': 'anyof' } ] + Correct answer in list form is list of dicts, and every dict must have 3 keys: 'draggables', 'targets' and 'rule'. 'Draggables' value is list of draggables ids, 'targes' values are list of targets ids, 'rule' @@ -241,7 +228,7 @@ class DragAndDrop(object): self.use_targets = user_answer.get('use_targets') # check if we have draggables that are not in correct answer: - check_extra_draggables = {} + self.excess_draggables = {} # create identical data structures from user answer and correct answer for i in xrange(0, len(correct_answer)): @@ -258,30 +245,56 @@ class DragAndDrop(object): self.user_groups[groupname].append(draggable_name) self.user_positions[groupname]['user'].append( draggable_dict[draggable_name]) - check_extra_draggables[draggable_name] = True + self.excess_draggables[draggable_name] = True else: - check_extra_draggables[draggable_name] = \ - check_extra_draggables.get(draggable_name, False) - - for draggable in check_extra_draggables: - if not check_extra_draggables[draggable]: - self.incorrect = True + self.excess_draggables[draggable_name] = \ + self.excess_draggables.get(draggable_name, False) def grade(user_input, correct_answer): - """Args: - user_input, correct_answer: json. Format: + """ Populates DragAndDrop instance from user_input and correct_answer and + calls DragAndDrop.drade for grading. - user_input: see module docstring + Supports two interfaces for correct_answer: dict and list. - correct_answer: - if use_targets is True: - {'1': 't1', 'name_with_icon': 't2'} - else: - {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} - Support 2 interfaces""" - # import ipdb; ipdb.set_trace() + Args: + user_input: json. Format:: + + {"use_targets": false, "draggables": + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + + or + + {"use_targets": true, "draggables": [{"1": "t1"}, \ + {"name_with_icon": "t2"}]} + + correct_answer: dict or list. + + Dict form:: + + {'1': 't1', 'name_with_icon': 't2'} + + or + + {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} + + List form:: + + correct_answer = [ + { + 'draggables': ['l3_o', 'l10_o'], + 'targets': ['t1_o', 't9_o'], + 'rule': 'anyof' + }, + { + 'draggables': ['l1_c','l8_c'], + 'targets': ['t5_c','t6_c'], + 'rule': 'anyof' + } + ] + + Returns: bool + """ dnd = DragAndDrop() - # import ipdb; ipdb.set_trace() dnd.populate(correct_answer=correct_answer, user_answer=user_input) return dnd.grade() From ad0a7d308c7d243e6284e7037f8c0c6e586e68a8 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 11:04:17 +0200 Subject: [PATCH 080/140] tests and test plan --- common/lib/capa/capa/graders/tests.py | 121 +++++++++++++++----------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index 409b22fe05..a38fbce3a4 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -37,7 +37,7 @@ class Test_PositionsCompare(unittest.TestCase): self.assertEqual(PositionsCompare([3.5, 4.5]), PositionsCompare([5, 7])) -class Test_DragAndDrop(unittest.TestCase): +class Test_DragAndDrop_Grade(unittest.TestCase): def test_targets_true(self): user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ @@ -134,28 +134,28 @@ class Test_DragAndDrop(unittest.TestCase): 'targets': [ 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' ], - 'rule': 'anyof' - }, { - 'draggables': ['7','8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], - 'rule': 'anyof' - }, { - 'draggables': ['11', '12'], - 'targets': ['s_sigma_name', 'p_sigma_name'], - 'rule': 'anyof' - }, { - 'draggables': ['13', '14'], - 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], - 'rule': 'anyof' - }, { - 'draggables': ['15'], - 'targets': ['p_pi_name'], - 'rule': 'anyof' - }, { - 'draggables': ['16'], - 'targets': ['p_pi_star_name'], - 'rule': 'anyof' - }] + 'rule': 'anyof' + }, { + 'draggables': ['7', '8', '9', '10'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'rule': 'anyof' + }, { + 'draggables': ['11', '12'], + 'targets': ['s_sigma_name', 'p_sigma_name'], + 'rule': 'anyof' + }, { + 'draggables': ['13', '14'], + 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], + 'rule': 'anyof' + }, { + 'draggables': ['15'], + 'targets': ['p_pi_name'], + 'rule': 'anyof' + }, { + 'draggables': ['16'], + 'targets': ['p_pi_star_name'], + 'rule': 'anyof' + }] self.assertTrue(draganddrop.grade(user_input, correct_answer)) @@ -172,41 +172,64 @@ class Test_DragAndDrop(unittest.TestCase): 'draggables': ['1', '2', '3', '4', '5', '6'], 'targets': [ 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' - ], - 'rule': 'anyof' - }, { - 'draggables': ['7','8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], - 'rule': 'anyof' - }, { - 'draggables': ['11', '12'], - 'targets': ['s_sigma_name', 'p_sigma_name'], - 'rule': 'anyof' - }, { - 'draggables': ['13', '14'], - 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], - 'rule': 'anyof' - }, { - 'draggables': ['15'], - 'targets': ['p_pi_name'], - 'rule': 'anyof' - }, { - 'draggables': ['16'], - 'targets': ['p_pi_star_name'], - 'rule': 'anyof' - }] + ], + 'rule': 'anyof' + }, { + 'draggables': ['7', '8', '9', '10'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'rule': 'anyof' + }, { + 'draggables': ['11', '12'], + 'targets': ['s_sigma_name', 'p_sigma_name'], + 'rule': 'anyof' + }, { + 'draggables': ['13', '14'], + 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], + 'rule': 'anyof' + }, { + 'draggables': ['15'], + 'targets': ['p_pi_name'], + 'rule': 'anyof' + }, { + 'draggables': ['16'], + 'targets': ['p_pi_star_name'], + 'rule': 'anyof' + }] self.assertFalse(draganddrop.grade(user_input, correct_answer)) +class Test_DragAndDrop_Populate(unittest.TestCase): #test for every function in DND - # test for different denied-allowed positions inised - # different groups + def test1(self): + self.assertTrue(1) + + +class Test_DraAndDrop_Compare_Positions(unittest.TestCase): + + def test_exact_1(self): + self.assertTrue(1) + + def test_exact_2(self): + self.assertTrue(1) + + def test_anyof_1(self): + self.assertTrue(1) + + def test_anyof_2(self): + self.assertTrue(1) + + def test_5(self): + self.assertTrue(1) + def suite(): - testcases = [Test_DragAndDrop] + testcases = [Test_PositionsCompare, + Test_DragAndDrop_Populate, + Test_DragAndDrop_Grade, + Test_DraAndDrop_Compare_Positions] suites = [] for testcase in testcases: suites.append(unittest.TestLoader().loadTestsFromTestCase(testcase)) From 9817253850b457cbc6311f13bdfc13319f397db4 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 4 Jan 2013 11:13:54 +0200 Subject: [PATCH 081/140] Fixed problem with smaller and larger base images. Refactoring. Cleaning of code. --- .../js/capa/drag_and_drop/base_image.js | 20 +- .../js/capa/drag_and_drop/draggables.js | 289 ++++++++++++++---- common/static/js/capa/drag_and_drop/main.js | 21 +- common/static/js/capa/drag_and_drop/state.js | 5 +- .../js/capa/drag_and_drop/update_input.js | 144 ++++++--- 5 files changed, 359 insertions(+), 120 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 9cee42feb2..9c7c217214 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -16,18 +16,26 @@ define(['logme'], function (logme) { 'style=" ' + 'position: relative; ' + 'margin-bottom: 25px; ' + + 'margin-left: auto; ' + + 'margin-right: auto; ' + '" ' + '>
' ); - state.baseImageEl = $( - '' + state.baseImageEl = $(''); + state.baseImageEl.attr( + 'src', + state.config.imageDir + '/' + state.config.base_image ); - state.baseImageEl.appendTo(baseImageElContainer); + state.baseImageEl.load(function () { + baseImageElContainer.css('width', this.width); + baseImageElContainer.css('height', this.height); - baseImageElContainer.appendTo(state.containerEl); + state.baseImageEl.appendTo(baseImageElContainer); + baseImageElContainer.appendTo(state.containerEl); + + state.baseImageLoaded = true; + }); } }); diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 0800ce4c6b..78fc47bf75 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -21,8 +21,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.currentMovingDraggable = null; $(document).mousemove(function (event) { - normalizeEvent(event); - if (state.currentMovingDraggable !== null) { state.currentMovingDraggable.iconEl.css( 'left', @@ -38,6 +36,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { ); if (state.currentMovingDraggable.labelEl !== null) { + state.currentMovingDraggable.labelEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.labelWidth * 0.5 + ); state.currentMovingDraggable.labelEl.css( 'top', event.pageY - @@ -45,12 +49,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.currentMovingDraggable.iconHeight * 0.5 + 5 ); - state.currentMovingDraggable.labelEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - state.currentMovingDraggable.labelWidth * 0.5 - ); } } }); @@ -99,23 +97,39 @@ define(['logme', 'update_input'], function (logme, updateInput) { if (draggableObj.iconWidth >= draggableObj.iconHeight) { draggableObj.iconWidthSmall = 60; - draggableObj.iconHeightSmall = draggableObj.iconWidthSmall * (draggableObj.iconHeight / draggableObj.iconWidth); + draggableObj.iconHeightSmall = + draggableObj.iconWidthSmall * + (draggableObj.iconHeight / draggableObj.iconWidth); } else { draggableObj.iconHeightSmall = 60; - draggableObj.iconWidthSmall = draggableObj.iconHeightSmall * (draggableObj.iconWidth / draggableObj.iconHeight); + draggableObj.iconWidthSmall = + draggableObj.iconHeightSmall * + (draggableObj.iconWidth / draggableObj.iconHeight); } draggableObj.iconEl.css('position', 'absolute'); - draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); - draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + draggableObj.iconEl.css( + 'width', + draggableObj.iconWidthSmall + ); + draggableObj.iconEl.css( + 'height', + draggableObj.iconHeightSmall + ); - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css( + 'left', + 50 - draggableObj.iconWidthSmall * 0.5 + ); if (obj.label.length > 0) { draggableObj.iconEl.css('top', 5); } else { - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + draggableObj.iconEl.css( + 'top', + 50 - draggableObj.iconHeightSmall * 0.5 + ); } draggableObj.iconEl.appendTo(draggableObj.containerEl); @@ -131,12 +145,20 @@ define(['logme', 'update_input'], function (logme, updateInput) { '
' ); - draggableObj.labelEl.appendTo(draggableObj.containerEl); + draggableObj.labelEl.appendTo( + draggableObj.containerEl + ); draggableObj.labelWidth = draggableObj.labelEl.width(); - draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); - draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); + draggableObj.labelEl.css( + 'left', + 50 - draggableObj.labelWidth * 0.5 + ); + draggableObj.labelEl.css( + 'top', + 5 + draggableObj.iconHeightSmall + 5 + ); draggableObj.labelEl.mousedown(mouseDown); draggableObj.labelEl.mouseup(mouseUp); @@ -168,8 +190,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconWidthSmall = draggableObj.iconWidth; draggableObj.iconHeightSmall = draggableObj.iconHeight; - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + draggableObj.iconEl.css( + 'left', + 50 - draggableObj.iconWidthSmall * 0.5 + ); + draggableObj.iconEl.css( + 'top', + 50 - draggableObj.iconHeightSmall * 0.5 + ); } else { // If no icon and no label, don't create a draggable. return; @@ -189,8 +217,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.x = -1; draggableObj.y = -1; - draggableObj.setInContainer = function (val) { inContainer = val; }; - draggableObj.setOnTarget = function (val) { onTarget = val; }; + draggableObj.setInContainer = function (val) { + inContainer = val; + }; + draggableObj.setOnTarget = function (val) { + onTarget = val; + }; state.draggables.push(draggableObj); @@ -204,26 +236,58 @@ define(['logme', 'update_input'], function (logme, updateInput) { function mouseDown(event) { if (mousePressed === false) { - state.currentMovingDraggable = draggableObj; - normalizeEvent(event); + // 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(); + // If this draggable is just being dragged out of the + // container, we must perform some additional tasks. if (inContainer === true) { draggableObj.containerEl.hide(); draggableObj.iconEl.detach(); - - draggableObj.iconEl.css('width', draggableObj.iconWidth); - draggableObj.iconEl.css('height', draggableObj.iconHeight); - - draggableObj.iconEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.iconWidth * 0.5); - draggableObj.iconEl.css('top', event.pageY - state.baseImageEl.offset().top - draggableObj.iconHeight * 0.5); - draggableObj.iconEl.appendTo(state.baseImageEl.parent()); + draggableObj.iconEl.css( + 'width', + draggableObj.iconWidth + ); + draggableObj.iconEl.css( + 'height', + draggableObj.iconHeight + ); + draggableObj.iconEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + draggableObj.iconWidth * 0.5 + ); + draggableObj.iconEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top - + draggableObj.iconHeight * 0.5 + ); + draggableObj.iconEl.appendTo( + state.baseImageEl.parent() + ); if (draggableObj.labelEl !== null) { draggableObj.labelEl.detach(); - draggableObj.labelEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.labelWidth * 0.5); - draggableObj.labelEl.css('top', event.pageY - state.baseImageEl.offset().top + draggableObj.iconHeight * 0.5 + 5); - draggableObj.labelEl.appendTo(state.baseImageEl.parent()); + draggableObj.labelEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + draggableObj.labelWidth * 0.5 + ); + draggableObj.labelEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top + + draggableObj.iconHeight * 0.5 + 5 + ); + draggableObj.labelEl.appendTo( + state.baseImageEl.parent() + ); } inContainer = false; @@ -235,7 +299,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconEl.css('z-index', '1000'); mousePressed = true; - event.preventDefault(); + state.currentMovingDraggable = draggableObj; } } @@ -249,8 +313,42 @@ define(['logme', 'update_input'], function (logme, updateInput) { function mouseMove() { if (mousePressed === true) { - draggableObj.iconEl.css('left', event.pageX - state.baseImageEl.offset().left - draggableObj.iconWidth * 0.5); - draggableObj.iconEl.css('top', event.pageY - state.baseImageEl.offset().top - draggableObj.iconHeight * 0.5); + // 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(); + + draggableObj.iconEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + draggableObj.iconWidth * 0.5 + ); + draggableObj.iconEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top - + draggableObj.iconHeight * 0.5 + ); + + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + draggableObj.labelWidth * 0.5 + ); + draggableObj.labelEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top + + draggableObj.iconHeight * 0.5 + + 5 + ); + } } } @@ -280,11 +378,22 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.numDraggablesInSlider += 1; } } else { + logme( + 'baseImageEl.width = ' + state.baseImageEl.width() + '; ' + + 'baseImageEl.height = ' + state.baseImageEl.height() + ); + if ( (offsetIE.left < 0) || - (offsetIE.left + draggableObj.iconWidth > state.baseImageEl.width()) || + ( + offsetIE.left + draggableObj.iconWidth > + state.baseImageEl.width() + ) || (offsetIE.top < 0) || - (offsetIE.top + draggableObj.iconHeight > state.baseImageEl.height()) + ( + offsetIE.top + draggableObj.iconHeight > + state.baseImageEl.height() + ) ) { moveBackToSlider(); @@ -295,8 +404,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { } else { correctZIndexes(); - draggableObj.x = offsetIE.left + draggableObj.iconWidth * 0.5; - draggableObj.y = offsetIE.top + draggableObj.iconHeight * 0.5; + draggableObj.x = + offsetIE.left + draggableObj.iconWidth * 0.5; + draggableObj.y = + offsetIE.top + draggableObj.iconHeight * 0.5; } } @@ -331,16 +442,28 @@ define(['logme', 'update_input'], function (logme, updateInput) { for (c1 = 0; c1 < state.targets.length; c1 += 1) { target = state.targets[c1]; - if (offsetIE.top + draggableObj.iconHeight * 0.5 < target.offset.top) { + if ( + offsetIE.top + draggableObj.iconHeight * 0.5 < + target.offset.top + ) { continue; } - if (offsetIE.top + draggableObj.iconHeight * 0.5 > target.offset.top + target.h) { + if ( + offsetIE.top + draggableObj.iconHeight * 0.5 > + target.offset.top + target.h + ) { continue; } - if (offsetIE.left + draggableObj.iconWidth * 0.5 < target.offset.left) { + if ( + offsetIE.left + draggableObj.iconWidth * 0.5 < + target.offset.left + ) { continue; } - if (offsetIE.left + draggableObj.iconWidth * 0.5 > target.offset.left + target.w) { + if ( + offsetIE.left + draggableObj.iconWidth * 0.5 > + target.offset.left + target.w + ) { continue; } @@ -358,7 +481,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { // another, then we need to remove it's ID from the // previous target's draggables list, and add it to the // new target's draggables list. - if ((onTarget !== null) && (onTarget.id !== target.id)) { + if ( + (onTarget !== null) && + (onTarget.id !== target.id) + ) { removeObjIdFromTarget(); onTarget = target; target.draggable.push(draggableObj.id); @@ -383,12 +509,28 @@ define(['logme', 'update_input'], function (logme, updateInput) { offset = 1; } - draggableObj.iconEl.css('left', target.offset.left + 0.5 * target.w - draggableObj.iconWidth * 0.5 + offset); - draggableObj.iconEl.css('top', target.offset.top + 0.5 * target.h - draggableObj.iconHeight * 0.5 + offset); + draggableObj.iconEl.css( + 'left', + target.offset.left + 0.5 * target.w - + draggableObj.iconWidth * 0.5 + offset + ); + draggableObj.iconEl.css( + 'top', + target.offset.top + 0.5 * target.h - + draggableObj.iconHeight * 0.5 + offset + ); if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css('left', target.offset.left + 0.5 * target.w - draggableObj.labelWidth * 0.5 + offset); - draggableObj.labelEl.css('top', target.offset.top + 0.5 * target.h + draggableObj.iconHeight * 0.5 + 5 + offset); + draggableObj.labelEl.css( + 'left', + target.offset.left + 0.5 * target.w - + draggableObj.labelWidth * 0.5 + offset + ); + draggableObj.labelEl.css( + 'top', + target.offset.top + 0.5 * target.h + + draggableObj.iconHeight * 0.5 + 5 + offset + ); } } @@ -406,7 +548,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { var c1; for (c1 = 0; c1 < state.draggables.length; c1++) { - if (draggableObj.oldZIndex < state.draggables[c1].zIndex) { + if ( + draggableObj.oldZIndex < + state.draggables[c1].zIndex + ) { state.draggables[c1].zIndex -= 1; state.draggables[c1].iconEl.css( 'z-index', @@ -427,40 +572,50 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconEl.detach(); - draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); - draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + draggableObj.iconEl.css( + 'width', + draggableObj.iconWidthSmall + ); + draggableObj.iconEl.css( + 'height', + draggableObj.iconHeightSmall + ); - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css( + 'left', + 50 - draggableObj.iconWidthSmall * 0.5 + ); if (draggableObj.labelEl !== null) { draggableObj.iconEl.css('top', 5); } else { - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + draggableObj.iconEl.css( + 'top', + 50 - draggableObj.iconHeightSmall * 0.5 + ); } draggableObj.iconEl.appendTo(draggableObj.containerEl); if (draggableObj.labelEl !== null) { draggableObj.labelEl.detach(); - draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); - draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); - draggableObj.labelEl.appendTo(draggableObj.containerEl); + draggableObj.labelEl.css( + 'left', + 50 - draggableObj.labelWidth * 0.5 + ); + draggableObj.labelEl.css( + 'top', + 5 + draggableObj.iconHeightSmall + 5 + ); + draggableObj.labelEl.appendTo( + draggableObj.containerEl + ); } inContainer = true; } } } - - // In firefox the event does not have a proper pageX and pageY - // coordinates. - function normalizeEvent(event) { - if(!event.offsetX) { - event.offsetX = (event.pageX - $(event.target).offset().left); - event.offsetY = (event.pageY - $(event.target).offset().top); - } - return event; - } } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 0ccfb47051..ec186066c1 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -60,13 +60,22 @@ define( Container(state); BaseImage(state); - Targets(state); - Scroller(state); - Draggables(state); - // Update the input element, checking first that it is not filled with - // an answer from the server. - updateInput(state, true); + (function addContent() { + if (state.baseImageLoaded !== true) { + setTimeout(addContent, 50); + + return; + } + + Targets(state); + Scroller(state); + Draggables(state); + + // Update the input element, checking first that it is not filled with + // an answer from the server. + updateInput(state, true); + }()); } }); diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index c3a4d3c4ac..99dd7dc8e4 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -11,10 +11,7 @@ define([], function () { return { 'problemId': problemId, - // Will indicate when all targetsand draggables have been loaded, - // processed, and postioned intially. - 'targetsLoaded': false, - 'draggablesLoaded': false, + 'baseImageLoaded': false, 'numDraggablesInSlider': 0 }; diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index df7c5edb2e..2c6a5ee364 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -39,7 +39,8 @@ define(['logme'], function (logme) { for (c1 = 0; c1 < state.targets.length; c1++) { for (c2 = 0; c2 < state.targets[c1].draggable.length; c2++) { tempObj = {}; - tempObj[state.targets[c1].draggable[c2]] = state.targets[c1].id; + tempObj[state.targets[c1].draggable[c2]] = + state.targets[c1].id; draggables.push(tempObj); } @@ -80,20 +81,37 @@ define(['logme'], function (logme) { } if ( - ((typeof answer.use_targets === 'boolean') && (answer.use_targets === true)) || - ((typeof answer.use_targets === 'string') && (answer.use_targets === 'true')) + ( + (typeof answer.use_targets === 'boolean') && + (answer.use_targets === true) + ) || + ( + (typeof answer.use_targets === 'string') && + (answer.use_targets === 'true') + ) ) { for (c1 = 0; c1 < answer.draggables.length; c1++) { for (draggableId in answer.draggables[c1]) { - if ((draggable = getDraggableById(draggableId)) === null) { - logme('ERROR: In answer there exists a draggable ID "' + draggableId + '". No draggable with this ID could be found.'); + if ( + (draggable = getDraggableById(draggableId)) === + null + ) { + logme( + 'ERROR: In answer there exists a ' + + 'draggable ID "' + draggableId + '". No ' + + 'draggable with this ID could be found.' + ); continue; } targetId = answer.draggables[c1][draggableId]; if ((target = getTargetById(targetId)) === null) { - logme('ERROR: In answer there exists a target ID "' + targetId + '". No target with this ID could be found.'); + logme( + 'ERROR: In answer there exists a target ' + + 'ID "' + targetId + '". No target with this ' + + 'ID could be found.' + ); continue; } @@ -112,27 +130,44 @@ define(['logme'], function (logme) { draggable.containerEl.hide(); draggable.iconEl.detach(); - - draggable.iconEl.css('width', draggable.iconWidth); - draggable.iconEl.css('height', draggable.iconHeight); + draggable.iconEl.css( + 'width', + draggable.iconWidth + ); + draggable.iconEl.css( + 'height', + draggable.iconHeight + ); draggable.iconEl.css( 'left', - target.offset.left + 0.5 * target.w - draggable.iconWidth * 0.5 + offset + target.offset.left + 0.5 * target.w - + draggable.iconWidth * 0.5 + offset ); draggable.iconEl.css( 'top', - target.offset.top + 0.5 * target.h - draggable.iconHeight * 0.5 + offset + target.offset.top + 0.5 * target.h - + draggable.iconHeight * 0.5 + offset + ); + draggable.iconEl.appendTo( + state.baseImageEl.parent() ); - - draggable.iconEl.appendTo(state.baseImageEl.parent()); if (draggable.labelEl !== null) { draggable.labelEl.detach(); - - draggable.labelEl.css('left', target.offset.left + 0.5 * target.w - draggable.labelWidth * 0.5 + offset); - draggable.labelEl.css('top', target.offset.top + 0.5 * target.h + draggable.iconHeight * 0.5 + 5 + offset); - - draggable.labelEl.appendTo(state.baseImageEl.parent()); + draggable.labelEl.css( + 'left', + target.offset.left + 0.5 * target.w - + draggable.labelWidth * 0.5 + offset + ); + draggable.labelEl.css( + 'top', + target.offset.top + 0.5 * target.h + + draggable.iconHeight * 0.5 + 5 + + offset + ); + draggable.labelEl.appendTo( + state.baseImageEl.parent() + ); } draggable.setOnTarget(target); @@ -145,13 +180,26 @@ define(['logme'], function (logme) { } } } else if ( - ((typeof answer.use_targets === 'boolean') && (answer.use_targets === false)) || - ((typeof answer.use_targets === 'string') && (answer.use_targets === 'false')) + ( + (typeof answer.use_targets === 'boolean') && + (answer.use_targets === false) + ) || + ( + (typeof answer.use_targets === 'string') && + (answer.use_targets === 'false') + ) ) { for (c1 = 0; c1 < answer.draggables.length; c1++) { for (draggableId in answer.draggables[c1]) { - if ((draggable = getDraggableById(draggableId)) === null) { - logme('ERROR: In answer there exists a draggable ID "' + draggableId + '". No draggable with this ID could be found.'); + if ( + (draggable = getDraggableById(draggableId)) === + null + ) { + logme( + 'ERROR: In answer there exists a ' + + 'draggable ID "' + draggableId + '". No ' + + 'draggable with this ID could be found.' + ); continue; } @@ -170,31 +218,50 @@ define(['logme'], function (logme) { draggable.containerEl.hide(); draggable.iconEl.detach(); - - draggable.iconEl.css('width', draggable.iconWidth); - draggable.iconEl.css('height', draggable.iconHeight); + draggable.iconEl.css( + 'width', + draggable.iconWidth + ); + draggable.iconEl.css( + 'height', + draggable.iconHeight + ); draggable.iconEl.css( 'left', - answer.draggables[c1][draggableId][0] - draggable.iconWidth * 0.5 + offset + answer.draggables[c1][draggableId][0] - + draggable.iconWidth * 0.5 + offset ); draggable.iconEl.css( 'top', - answer.draggables[c1][draggableId][1] - draggable.iconHeight * 0.5 + offset + answer.draggables[c1][draggableId][1] - + draggable.iconHeight * 0.5 + offset + ); + draggable.iconEl.appendTo( + state.baseImageEl.parent() ); - - draggable.iconEl.appendTo(state.baseImageEl.parent()); if (draggable.labelEl !== null) { draggable.labelEl.detach(); - - draggable.labelEl.css('left', answer.draggables[c1][draggableId][0] - draggable.iconWidth * 0.5 - draggable.labelWidth * 0.5 + offset); - draggable.labelEl.css('top', answer.draggables[c1][draggableId][1] - draggable.iconHeight * 0.5 + draggable.iconHeight + 5 + offset); - - draggable.labelEl.appendTo(state.baseImageEl.parent()); + draggable.labelEl.css( + 'left', + answer.draggables[c1][draggableId][0] - + draggable.labelWidth * 0.5 + offset + ); + draggable.labelEl.css( + 'top', + answer.draggables[c1][draggableId][1] - + draggable.iconHeight * 0.5 + + draggable.iconHeight + 5 + offset + ); + draggable.labelEl.appendTo( + state.baseImageEl.parent() + ); } - draggable.x = answer.draggables[c1][draggableId][0]; - draggable.y = answer.draggables[c1][draggableId][1]; + draggable.x = + answer.draggables[c1][draggableId][0]; + draggable.y = + answer.draggables[c1][draggableId][1]; state.numDraggablesInSlider -= 1; state.updateArrowOpacity(); @@ -203,7 +270,10 @@ define(['logme'], function (logme) { } } } else { - logme('ERROR: The type of answer.targets is not supported. answer.targets = ', answer.targets); + logme( + 'ERROR: The type of answer.targets is not supported. ' + + 'answer.targets = ', answer.targets + ); return; } From a9e7890d6b76527a908e4d867dfd674ac728d4ef Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 4 Jan 2013 11:35:22 +0200 Subject: [PATCH 082/140] Added correct handling of invalid (missing) base images. Now error is reported, and the damage is reduced to a minimum. --- common/static/js/capa/drag_and_drop/base_image.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 9c7c217214..85f6006918 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -23,6 +23,7 @@ define(['logme'], function (logme) { ); state.baseImageEl = $(''); + state.baseImageEl.attr( 'src', state.config.imageDir + '/' + state.config.base_image @@ -36,6 +37,19 @@ define(['logme'], function (logme) { state.baseImageLoaded = true; }); + state.baseImageEl.error(function () { + logme( + 'ERROR: Image "' + state.config.imageDir + '/' + + state.config.base_image + '" was not found!' + ); + baseImageElContainer.html( + '' + + 'ERROR: Image "' + state.config.imageDir + '/' + + state.config.base_image + '" was not found!' + + '' + ); + baseImageElContainer.appendTo(state.containerEl); + }); } }); From 533a1693b3951e38b10a3ab1ca1c4a6fbc9ec2f7 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 4 Jan 2013 11:57:07 +0200 Subject: [PATCH 083/140] Updated XML example in docs for Drag and Drop. --- docs/source/drag-n-drop-demo.xml | 363 ++++++++++++++++++++++++++++++- 1 file changed, 355 insertions(+), 8 deletions(-) diff --git a/docs/source/drag-n-drop-demo.xml b/docs/source/drag-n-drop-demo.xml index 574a188c33..6f82db24a2 100644 --- a/docs/source/drag-n-drop-demo.xml +++ b/docs/source/drag-n-drop-demo.xml @@ -1,5 +1,260 @@ + + +

Please set hydrogen atoms to LEFT carbon atom

+
+
+ + + + + + + + + + + + + + + + + + + +
+ + + +

Describe carbon molecule in LCAO-MO

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

Describe oxygen molecule in LCAO-MO

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Drag and drop pictures to proper positions. @@ -44,14 +299,14 @@ else:

- Individual targets WITHOUT outlines. Move Star to the + SMALL IMAGE. Individual targets WITHOUT outlines. Move Star to the volcano opening, and Label3 on to the right ear of the cow. One draggable per target.


- + @@ -61,8 +316,8 @@ else: - - + + @@ -173,11 +428,103 @@ else: + - 'l1_c': 't5_c', - 'l8_c': 't6_c'} + + +

If the element appears in this molecule, + drag the label onto it

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

+ SMALL IMAGE. Individual targets WITHOUT outlines. Move Star to the + volcano opening, and Label3 on to the right ear of the cow. + One draggable per target. +

+
+
+ + + + + + + + + + + + + + + + + Date: Fri, 4 Jan 2013 13:05:28 +0200 Subject: [PATCH 084/140] Removed reliance on imageDir config setting. Now we expect the correct path to tbe set in XML. --- .../js/capa/drag_and_drop/base_image.js | 8 +- .../js/capa/drag_and_drop/config_parser.js | 3 +- .../js/capa/drag_and_drop/draggables.js | 94 +++++++++++-------- common/static/js/capa/drag_and_drop/main.js | 11 +-- 4 files changed, 59 insertions(+), 57 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 85f6006918..0c9c15fd2e 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -26,7 +26,7 @@ define(['logme'], function (logme) { state.baseImageEl.attr( 'src', - state.config.imageDir + '/' + state.config.base_image + state.config.base_image ); state.baseImageEl.load(function () { baseImageElContainer.css('width', this.width); @@ -39,13 +39,11 @@ define(['logme'], function (logme) { }); state.baseImageEl.error(function () { logme( - 'ERROR: Image "' + state.config.imageDir + '/' + - state.config.base_image + '" was not found!' + 'ERROR: Image "' + state.config.base_image + '" was not found!' ); baseImageElContainer.html( '' + - 'ERROR: Image "' + state.config.imageDir + '/' + - state.config.base_image + '" was not found!' + + 'ERROR: Image "' + state.config.base_image + '" was not found!' + '' ); baseImageElContainer.appendTo(state.containerEl); diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 664324e0b9..2b58bc42f8 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -7,13 +7,12 @@ define(['logme'], function (logme) { return configParser; - function configParser(config, imageDir, state) { + function configParser(config, state) { var returnStatus; returnStatus = true; state.config = { - 'imageDir': '/static/' + imageDir + '/images', 'draggables': [], 'targets': [], 'base_image': '' diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 78fc47bf75..36ff7238d9 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -89,7 +89,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconEl = $(''); draggableObj.iconEl.attr( 'src', - state.config.imageDir + '/' + obj.icon + obj.icon ); draggableObj.iconEl.load(function () { draggableObj.iconWidth = this.width; @@ -358,11 +358,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { // 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 offsetIE, targetFound; + var positionIE, targetFound; mousePressed = false; - - offsetIE = draggableObj.iconEl.position(); + positionIE = draggableObj.iconEl.position(); if (state.individualTargets === true) { targetFound = false; @@ -378,20 +377,15 @@ define(['logme', 'update_input'], function (logme, updateInput) { state.numDraggablesInSlider += 1; } } else { - logme( - 'baseImageEl.width = ' + state.baseImageEl.width() + '; ' + - 'baseImageEl.height = ' + state.baseImageEl.height() - ); - if ( - (offsetIE.left < 0) || + (positionIE.left < 0) || ( - offsetIE.left + draggableObj.iconWidth > + positionIE.left + draggableObj.iconWidth > state.baseImageEl.width() ) || - (offsetIE.top < 0) || + (positionIE.top < 0) || ( - offsetIE.top + draggableObj.iconHeight > + positionIE.top + draggableObj.iconHeight > state.baseImageEl.height() ) ) { @@ -405,9 +399,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { correctZIndexes(); draggableObj.x = - offsetIE.left + draggableObj.iconWidth * 0.5; + positionIE.left + draggableObj.iconWidth * 0.5; draggableObj.y = - offsetIE.top + draggableObj.iconHeight * 0.5; + positionIE.top + draggableObj.iconHeight * 0.5; } } @@ -432,41 +426,26 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } + // // 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 + // + // draggableObj.iconEl.position() + // function checkIfOnTarget() { var c1, target; for (c1 = 0; c1 < state.targets.length; c1 += 1) { target = state.targets[c1]; - if ( - offsetIE.top + draggableObj.iconHeight * 0.5 < - target.offset.top - ) { - continue; - } - if ( - offsetIE.top + draggableObj.iconHeight * 0.5 > - target.offset.top + target.h - ) { - continue; - } - if ( - offsetIE.left + draggableObj.iconWidth * 0.5 < - target.offset.left - ) { - continue; - } - if ( - offsetIE.left + draggableObj.iconWidth * 0.5 > - target.offset.left + target.w - ) { - continue; - } - + // 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 ( (state.config.one_per_target === true) && (target.draggable.length === 1) && @@ -475,6 +454,35 @@ define(['logme', 'update_input'], function (logme, updateInput) { continue; } + // Check if the draggable's center coordinate is within + // the target's dimensions. If not, go to next target. + if ( + positionIE.top + draggableObj.iconHeight * 0.5 < + target.offset.top + ) { + continue; + } + if ( + positionIE.top + draggableObj.iconHeight * 0.5 > + target.offset.top + target.h + ) { + continue; + } + if ( + positionIE.left + draggableObj.iconWidth * 0.5 < + target.offset.left + ) { + continue; + } + if ( + positionIE.left + draggableObj.iconWidth * 0.5 > + target.offset.left + target.w + ) { + continue; + } + + // If we got here, then our draggable is on top of a + // target. targetFound = true; // If the draggable was moved from one target to @@ -488,7 +496,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { removeObjIdFromTarget(); onTarget = target; target.draggable.push(draggableObj.id); - } else if (onTarget === null) { + } + // 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 (onTarget === null) { onTarget = target; target.draggable.push(draggableObj.id); } diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index ec186066c1..24cc8cbdcb 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -17,7 +17,7 @@ define( // $(value) - get the element of the entire problem function processProblem(index, value) { - var problemId, imageDir, config, state; + var problemId, config, state; if ($(value).attr('data-problem-processed') === 'true') { // This problem was already processed by us before, so we will @@ -43,16 +43,9 @@ define( return; } - imageDir = $(value).attr('data-course-folder'); - if (typeof imageDir !== 'string') { - logme('ERROR: Could not find the name of the image directory.'); - - return; - } - state = State(problemId); - if (configParser(config, imageDir, state) !== true) { + if (configParser(config, state) !== true) { logme('ERROR: Could not make sense of the JSON configuration options.'); return; From f02621876d86ac6696d460f79cad5105f4bbedca Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 13:13:05 +0200 Subject: [PATCH 085/140] removed course id --- common/lib/capa/capa/inputtypes.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 48ee45e8ac..e0c90bd8ee 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -871,12 +871,6 @@ class DragAndDropInput(InputTypeBase): self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.to_render.add('drag_and_drop_json') - try: # for tests and mock up, work normally not in tets - self.loaded_attributes['course_folder'] = \ - self.system.filestore.root_path.split('/')[-1] - except: - self.loaded_attributes['course_folder'] = 'mock' - self.to_render.add('course_folder') registry.register(DragAndDropInput) From 4c23860e0f9311b81f1013daf9b62d0b7ba8f1e7 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 13:15:26 +0200 Subject: [PATCH 086/140] updated inputtype test without course_id ransferring --- common/lib/capa/capa/tests/test_inputtypes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 0b3eeea0c6..a61d47a243 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -539,7 +539,6 @@ class DragAndDropTest(unittest.TestCase): expected = {'id': 'prob_1_2', 'value': value, 'status': 'unsubmitted', - 'course_folder': 'mock', 'msg': '', 'drag_and_drop_json': '{"use_targets": "True", \ "target_outline": "false", "one_per_target": "True", \ From 933f94a51430faa0da26ee6e287c2370381877fa Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 13:17:23 +0200 Subject: [PATCH 087/140] comment fix --- common/lib/capa/capa/graders/draganddrop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index af28435bb6..ff37ef2b3e 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -132,7 +132,7 @@ class DragAndDrop(object): return False # 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: rules_executed = 0 for rule in ('exact', 'anyof'): # every group has only one rule From 87c58be7421cd35f112d229847df68a88bf2b0d3 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 13:31:54 +0200 Subject: [PATCH 088/140] removed course folder var from mako template --- common/lib/capa/capa/templates/drag_and_drop_input.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/templates/drag_and_drop_input.html b/common/lib/capa/capa/templates/drag_and_drop_input.html index 161ecd1af1..c186281796 100644 --- a/common/lib/capa/capa/templates/drag_and_drop_input.html +++ b/common/lib/capa/capa/templates/drag_and_drop_input.html @@ -1,6 +1,6 @@
+ data-plain-id="${id}">
Date: Fri, 4 Jan 2013 14:10:50 +0200 Subject: [PATCH 089/140] replaced ordereddict with dict --- common/lib/capa/capa/graders/draganddrop.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/graders/draganddrop.py index ff37ef2b3e..4fef58bf7b 100644 --- a/common/lib/capa/capa/graders/draganddrop.py +++ b/common/lib/capa/capa/graders/draganddrop.py @@ -27,7 +27,6 @@ values are (x,y) coordinates of centers of dragged images. """ import json -from collections import OrderedDict class PositionsCompare(list): @@ -105,10 +104,10 @@ class DragAndDrop(object): """ Grader class for drag and drop inputtype. """ def __init__(self): - self.correct_groups = OrderedDict() # correct groups from xml - self.correct_positions = OrderedDict() # correct positions for comparing - self.user_groups = OrderedDict() # will be populated from user answer - self.user_positions = OrderedDict() # will be populated from user answer + self.correct_groups = dict() # correct groups from xml + self.correct_positions = dict() # correct positions for comparing + self.user_groups = dict() # will be populated from user answer + self.user_positions = dict() # will be populated from user answer def grade(self): ''' Grader user answer. From 01e4bdb831f91046bc318b2df5df69685e28c21a Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 14:11:04 +0200 Subject: [PATCH 090/140] added test for populate --- common/lib/capa/capa/graders/tests.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index a38fbce3a4..bd71c673ee 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -5,6 +5,7 @@ from draganddrop import PositionsCompare class Test_PositionsCompare(unittest.TestCase): + """ describe""" def test_nested_list_and_list1(self): self.assertEqual(PositionsCompare([[1, 2], 40]), PositionsCompare([1, 3])) @@ -137,7 +138,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): 'rule': 'anyof' }, { 'draggables': ['7', '8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], 'rule': 'anyof' }, { 'draggables': ['11', '12'], @@ -200,10 +201,23 @@ class Test_DragAndDrop_Grade(unittest.TestCase): class Test_DragAndDrop_Populate(unittest.TestCase): - #test for every function in DND - def test1(self): - self.assertTrue(1) + def test_1(self): + dnd = draganddrop.DragAndDrop() + correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} + user_input = '{"use_targets": false, "draggables": \ + [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' + dnd.populate(correct_answer, user_input) + + correct_groups = {'1': ['name_with_icon'], '0': ['1']} + correct_positions = {'1': {'exact': [[20, 20]]}, '0': {'exact': [[[40, 10], 29]]}} + user_groups = {'1': [u'name_with_icon'], '0': [u'1']} + user_positions = {'1': {'user': [[20, 20]]}, '0': {'user': [[10, 10]]}} + + self.assertEqual(correct_groups, dnd.correct_groups) + self.assertEqual(correct_positions, dnd.correct_positions) + self.assertEqual(user_groups, dnd.user_groups) + self.assertEqual(user_positions, dnd.user_positions) class Test_DraAndDrop_Compare_Positions(unittest.TestCase): From 5c58881222ea4a226dce2165da74cf8e6719be2a Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 14:48:55 +0200 Subject: [PATCH 091/140] tests for drag and drop --- common/lib/capa/capa/graders/tests.py | 51 ++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/graders/tests.py index bd71c673ee..90a8a9a98a 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/graders/tests.py @@ -222,20 +222,53 @@ class Test_DragAndDrop_Populate(unittest.TestCase): class Test_DraAndDrop_Compare_Positions(unittest.TestCase): - def test_exact_1(self): - self.assertTrue(1) + def test_1(self): + dnd = draganddrop.DragAndDrop() + self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], + user=[[2, 3], [1, 1]], + flag='anyof')) - def test_exact_2(self): - self.assertTrue(1) + def test_2a(self): + dnd = draganddrop.DragAndDrop() + self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], + user=[[2, 3], [1, 1]], + flag='exact')) - def test_anyof_1(self): - self.assertTrue(1) + def test_2b(self): + dnd = draganddrop.DragAndDrop() + self.assertFalse(dnd.compare_positions(correct=[[1, 1], [2, 3]], + user=[[2, 13], [1, 1]], + flag='exact')) - def test_anyof_2(self): - self.assertTrue(1) + def test_3(self): + dnd = draganddrop.DragAndDrop() + self.assertFalse(dnd.compare_positions(correct=["a", "b"], + user=["a", "b", "c"], + flag='anyof')) + + def test_4(self): + dnd = draganddrop.DragAndDrop() + self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], + user=["a", "b"], + flag='anyof')) def test_5(self): - self.assertTrue(1) + dnd = draganddrop.DragAndDrop() + self.assertFalse(dnd.compare_positions(correct=["a", "b", "c"], + user=["a", "c", "b"], + flag='exact')) + + def test_6(self): + dnd = draganddrop.DragAndDrop() + self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], + user=["a", "c", "b"], + flag='anyof')) + + def test_7(self): + dnd = draganddrop.DragAndDrop() + self.assertFalse(dnd.compare_positions(correct=["a", "b", "b"], + user=["a", "c", "b"], + flag='anyof')) def suite(): From b7b25369e0df1c9c7e640efcf6133bb4e414671f Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 4 Jan 2013 15:10:26 +0200 Subject: [PATCH 092/140] Added mouse event handlers to container of draggable. Now it is easier to initiate a dragging operation. --- common/static/js/capa/drag_and_drop/draggables.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 36ff7238d9..ea100e072f 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -208,6 +208,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconEl.mouseup(mouseUp); draggableObj.iconEl.mousemove(mouseMove); + draggableObj.containerEl.mousedown(mouseDown); + draggableObj.containerEl.mouseup(mouseUp); + draggableObj.containerEl.mousemove(mouseMove); + inContainer = true; mousePressed = false; From b540ca492aeea2dbee233a3950d1df5ffadea805 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 4 Jan 2013 15:13:51 +0200 Subject: [PATCH 093/140] removed old flag use_targets --- common/lib/capa/capa/inputtypes.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index e0c90bd8ee..4d2e2ed142 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -852,10 +852,6 @@ class DragAndDropInput(InputTypeBase): # image drag and drop onto to_js['base_image'] = Attribute('img').parse_from_xml(self.xml) - # problem subtype: targets or positions - to_js['use_targets'] = Attribute('targets', - default="True").parse_from_xml(self.xml) - # outline places on image where to drag adn drop to_js['target_outline'] = Attribute('target_outline', default="False").parse_from_xml(self.xml) From cd6e220eada8a0ef4952a4fe4de8dd473fd0ef16 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 4 Jan 2013 15:20:41 +0200 Subject: [PATCH 094/140] Fixed annoying bug whre if you dragged any part of the base image or other parts of the Drag and Drop problem, then the browser would start highlighting the elements. --- common/static/js/capa/drag_and_drop/base_image.js | 4 ++++ common/static/js/capa/drag_and_drop/scroller.js | 4 ++++ common/static/js/capa/drag_and_drop/targets.js | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 0c9c15fd2e..0b64a7fd65 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -35,6 +35,10 @@ define(['logme'], function (logme) { state.baseImageEl.appendTo(baseImageElContainer); baseImageElContainer.appendTo(state.containerEl); + state.baseImageEl.mousedown(function (event) { + event.preventDefault(); + }); + state.baseImageLoaded = true; }); state.baseImageEl.error(function () { diff --git a/common/static/js/capa/drag_and_drop/scroller.js b/common/static/js/capa/drag_and_drop/scroller.js index 505785086d..c1fe867006 100644 --- a/common/static/js/capa/drag_and_drop/scroller.js +++ b/common/static/js/capa/drag_and_drop/scroller.js @@ -113,6 +113,10 @@ define(['logme'], function (logme) { ); state.sliderEl.appendTo(showEl); + state.sliderEl.mousedown(function (event) { + event.preventDefault(); + }); + moveRightEl = $( '
Date: Wed, 9 Jan 2013 11:34:03 +0200 Subject: [PATCH 096/140] New feature: when the draggable is out of the slider container, if it has a label, the label will get a background. This way it is much easier to discern the label against the base image. --- .../js/capa/drag_and_drop/draggables.js | 53 +++++++++++++++++-- .../js/capa/drag_and_drop/update_input.js | 52 ++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index ea100e072f..1aafa0591c 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -27,6 +27,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { event.pageX - state.baseImageEl.offset().left - state.currentMovingDraggable.iconWidth * 0.5 + - state.currentMovingDraggable.iconElLeftOffset ); state.currentMovingDraggable.iconEl.css( 'top', @@ -41,6 +42,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { event.pageX - state.baseImageEl.offset().left - state.currentMovingDraggable.labelWidth * 0.5 + - 9 // Account for padding, border. ); state.currentMovingDraggable.labelEl.css( 'top', @@ -84,7 +86,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.containerEl.appendTo(state.sliderEl); if (obj.icon.length > 0) { - draggableObj.hasIcon = true; + draggableObj.iconElBGColor = 'transparent'; + draggableObj.iconElPadding = 0; + draggableObj.iconElBorder = 'none'; + draggableObj.iconElLeftOffset = 0; draggableObj.iconEl = $(''); draggableObj.iconEl.attr( @@ -173,6 +178,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { // icon. All the existing code will work, and the user will // see a label instead of an icon. if (obj.label.length > 0) { + draggableObj.iconElBGColor = 'yellow'; + draggableObj.iconElPadding = 8; + draggableObj.iconElBorder = '1px solid black'; + draggableObj.iconElLeftOffset = 9; + draggableObj.iconEl = $( '
' + obj.label + @@ -187,6 +189,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { '
' + obj.label + @@ -261,18 +265,18 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.containerEl.hide(); draggableObj.iconEl.detach(); - draggableObj.iconEl.css( - 'background-color', draggableObj.iconElBGColor - ); - draggableObj.iconEl.css( - 'padding-left', draggableObj.iconElPadding - ); - draggableObj.iconEl.css( - 'padding-right', draggableObj.iconElPadding - ); - draggableObj.iconEl.css( - 'border', draggableObj.iconElBorder - ); + draggableObj.iconEl.css( + 'background-color', draggableObj.iconElBGColor + ); + draggableObj.iconEl.css( + 'padding-left', draggableObj.iconElPadding + ); + draggableObj.iconEl.css( + 'padding-right', draggableObj.iconElPadding + ); + draggableObj.iconEl.css( + 'border', draggableObj.iconElBorder + ); draggableObj.iconEl.css( 'width', draggableObj.iconWidth From c2dec3fb9d8d10c7e6db3b73ef2d31adca26a6fe Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 9 Jan 2013 14:03:03 +0200 Subject: [PATCH 099/140] label bg color --- common/lib/capa/capa/inputtypes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 4d2e2ed142..5a69f6de66 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -865,6 +865,11 @@ class DragAndDropInput(InputTypeBase): to_js['targets'] = [parse(target, 'target') for target in self.xml.iterchildren('target')] + # custom background color for labels: + label_bg_color = Attribute('label_bg_color').parse_from_xml(self.xml) + if label_bg_color: + to_js['label_bg_color'] = label_bg_color + self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.to_render.add('drag_and_drop_json') From f157702548ed3eeb1b10b463ccccbb6db9e91624 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 9 Jan 2013 14:06:23 +0200 Subject: [PATCH 100/140] fix --- common/lib/capa/capa/inputtypes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 5a69f6de66..cf5bee3b0b 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -866,7 +866,8 @@ class DragAndDropInput(InputTypeBase): self.xml.iterchildren('target')] # custom background color for labels: - label_bg_color = Attribute('label_bg_color').parse_from_xml(self.xml) + label_bg_color = Attribute('label_bg_color', + default=None).parse_from_xml(self.xml) if label_bg_color: to_js['label_bg_color'] = label_bg_color From 22014f7f58ca44d33efe143ecaded10c5732f1bb Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 9 Jan 2013 18:07:44 +0200 Subject: [PATCH 101/140] New feature: Clicking on number for multiple icons on target will cycle all icons on target. Fixed bug with z-order ordering. --- .../js/capa/drag_and_drop/draggables.js | 136 ++++++++++++++++-- .../static/js/capa/drag_and_drop/targets.js | 110 +++++++++++++- .../js/capa/drag_and_drop/update_input.js | 4 + 3 files changed, 232 insertions(+), 18 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 7d12e0b4fa..d95b0e706f 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -62,6 +62,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj = { 'zIndex': objIndex, + 'oldZIndex': objIndex, 'labelEl': null, 'hasLoaded': false }; @@ -146,6 +147,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'position: absolute; ' + 'color: black; ' + 'font-size: 0.95em; ' + + 'z-index: ' + objIndex + '; ' + '" ' + '>' + obj.label + @@ -191,6 +193,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'position: absolute; ' + 'color: black; ' + 'font-size: 0.95em; ' + + 'z-index: ' + objIndex + '; ' + '" ' + '>' + obj.label + @@ -341,6 +344,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.oldZIndex = draggableObj.zIndex; draggableObj.zIndex = 1000; draggableObj.iconEl.css('z-index', '1000'); + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.css('z-index', '1000'); + } mousePressed = true; state.currentMovingDraggable = draggableObj; @@ -468,6 +474,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } + if (onTarget.numTextEl !== null) { + onTarget.updateNumTextEl(); + } + onTarget = null; } } @@ -551,6 +561,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { target.draggable.push(draggableObj.id); } + if (target.numTextEl !== null) { + target.updateNumTextEl(); + } + // Reposition the draggable so that it's center // coincides with the center of the target. snapToTarget(target); @@ -605,23 +619,117 @@ define(['logme', 'update_input'], function (logme, updateInput) { // ordering of the visibility (z-index) of the other draggables // will not change. function correctZIndexes() { - var c1; + var draggablesInMe, c1, c2, highestZIndex; - for (c1 = 0; c1 < state.draggables.length; c1++) { - if ( - draggableObj.oldZIndex < - state.draggables[c1].zIndex - ) { - state.draggables[c1].zIndex -= 1; - state.draggables[c1].iconEl.css( + if (state.individualTargets === true) { + + logme('In correctZIndexes.'); + + if (onTarget.draggable.length > 0) { + logme('onTarget.draggable.length > 0'); + + draggablesInMe = []; + + for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) { + for (c2 = 0; c2 < state.draggables.length; c2 += 1) { + if ( + onTarget.draggable[c1] === + state.draggables[c2].id + ) { + draggablesInMe.push(state.draggables[c2]); + } + } + } + + highestZIndex = -10000; + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + if ( + (draggablesInMe[c1].zIndex > highestZIndex) && + (draggablesInMe[c1].zIndex !== 1000) + ) { + highestZIndex = draggablesInMe[c1].zIndex; + } + } + + if (highestZIndex === -10000) { + highestZIndex = onTarget.draggable.length; + } else if (highestZIndex < draggableObj.oldZIndex) { + highestZIndex = draggableObj.oldZIndex; + } else { + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + logme('draggablesInMe[' + c1 + '].id = ' + draggablesInMe[c1].id); + logme('draggablesInMe[' + c1 + '].zIndex = ' + draggablesInMe[c1].zIndex); + logme('draggablesInMe[' + c1 + '].oldZIndex = ' + draggablesInMe[c1].oldZIndex); + } + + logme('----------------'); + logme('highestZIndex = ' + highestZIndex); + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + draggablesInMe[c1].zIndex -= 1; + draggablesInMe[c1].oldZIndex -= 1; + + draggablesInMe[c1].iconEl.css( + 'z-index', + draggablesInMe[c1].zIndex + ); + if (draggablesInMe[c1].labelEl !== null) { + draggablesInMe[c1].labelEl.css( + 'z-index', + draggablesInMe[c1].zIndex + ); + } + } + } + } else { + logme('highestZIndex = onTarget.draggable.length'); + highestZIndex = onTarget.draggable.length; + } + + draggableObj.zIndex = highestZIndex; + draggableObj.oldZIndex = highestZIndex; + + draggableObj.iconEl.css( + 'z-index', + draggableObj.zIndex + ); + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.css( 'z-index', - state.draggables[c1].zIndex + draggableObj.zIndex ); } - } + } else { + for (c1 = 0; c1 < state.draggables.length; c1++) { + if ( + draggableObj.oldZIndex < + state.draggables[c1].zIndex + ) { + state.draggables[c1].zIndex -= 1; + state.draggables[c1].oldZIndex = state.draggables[c1].zIndex; + state.draggables[c1].iconEl.css( + 'z-index', + state.draggables[c1].zIndex + ); - draggableObj.zIndex = c1; - draggableObj.iconEl.css('z-index', c1); + if (state.draggables[c1].labelEl !== null) { + state.draggables[c1].labelEl.css( + 'z-index', + state.draggables[c1].zIndex + ); + } + } + } + + draggableObj.zIndex = c1; + draggableObj.oldZIndex = c1; + draggableObj.iconEl.css('z-index', c1); + + if (draggableObj.labelEl !== null) { + draggableObj.labelEl.css('z-index', c1); + } + } } // If a draggable was released in a wrong positione, we will @@ -630,11 +738,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { function moveBackToSlider() { draggableObj.containerEl.show(); + draggableObj.zIndex = draggableObj.oldZIndex; + draggableObj.iconEl.detach(); draggableObj.iconEl.css('border', 'none'); draggableObj.iconEl.css('background-color', 'transparent'); draggableObj.iconEl.css('padding-left', 0); draggableObj.iconEl.css('padding-right', 0); + draggableObj.iconEl.css('z-index', draggableObj.zIndex); draggableObj.iconEl.css( 'width', draggableObj.iconWidthSmall @@ -664,6 +775,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.labelEl.css('background-color', 'transparent'); draggableObj.labelEl.css('padding-left', 0); draggableObj.labelEl.css('padding-right', 0); + draggableObj.labelEl.css('z-index', draggableObj.zIndex); draggableObj.labelEl.css( 'left', 50 - draggableObj.labelWidth * 0.5 diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index fdd6678269..0bc246e49f 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -19,7 +19,7 @@ define(['logme'], function (logme) { return; function processTarget(obj) { - var targetEl, borderCss; + var targetEl, borderCss, numTextEl, targetObj; borderCss = ''; if (state.config.targetOutline === true) { @@ -40,14 +40,36 @@ define(['logme'], function (logme) { 'data-target-id="' + obj.id + '" ' + '>
' ); - targetEl.appendTo(state.baseImageEl.parent()); - targetEl.mousedown(function (event) { event.preventDefault(); }); - state.targets.push({ + if (state.config.one_per_target === false) { + numTextEl = $( + '
0
' + ); + } else { + numTextEl = null; + } + + targetObj = { 'id': obj.id, 'w': obj.w, @@ -56,9 +78,85 @@ define(['logme'], function (logme) { 'el': targetEl, 'offset': targetEl.position(), - 'draggable': [] - }); + 'draggable': [], + + 'targetEl': targetEl, + + 'numTextEl': numTextEl, + 'updateNumTextEl': updateNumTextEl + }; + + if (state.config.one_per_target === false) { + numTextEl.appendTo(state.baseImageEl.parent()); + numTextEl.mousedown(function (event) { + event.preventDefault(); + }); + numTextEl.mouseup(function () { + cycleDraggableOrder.call(targetObj) + }); + } + + state.targets.push(targetObj); } + + function cycleDraggableOrder() { + var draggablesInMe, c1, c2, lowestZIndex, highestZIndex; + + if (this.draggable.length === 0) { + return 0; + } + + draggablesInMe = []; + + for (c1 = 0; c1 < this.draggable.length; c1 += 1) { + for (c2 = 0; c2 < state.draggables.length; c2 += 1) { + if (this.draggable[c1] === state.draggables[c2].id) { + draggablesInMe.push(state.draggables[c2]); + } + } + } + + highestZIndex = -10000; + lowestZIndex = 10000; + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + logme( + 'draggablesInMe[' + c1 + '].id = ' + draggablesInMe[c1].id, + 'draggablesInMe[' + c1 + '].zIndex = ' + draggablesInMe[c1].zIndex, + 'draggablesInMe[' + c1 + '].oldZIndex = ' + draggablesInMe[c1].oldZIndex + ); + } + logme('------------------'); + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + if (draggablesInMe[c1].zIndex < lowestZIndex) { + lowestZIndex = draggablesInMe[c1].zIndex; + } + + if (draggablesInMe[c1].zIndex > highestZIndex) { + highestZIndex = draggablesInMe[c1].zIndex; + } + } + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + if (draggablesInMe[c1].zIndex === lowestZIndex) { + draggablesInMe[c1].zIndex = highestZIndex; + draggablesInMe[c1].oldZIndex = highestZIndex; + } else { + draggablesInMe[c1].zIndex -= 1; + draggablesInMe[c1].oldZIndex -= 1; + } + + draggablesInMe[c1].iconEl.css('z-index', draggablesInMe[c1].zIndex); + if (draggablesInMe[c1].labelEl !== null) { + draggablesInMe[c1].labelEl.css('z-index', draggablesInMe[c1].zIndex); + } + } + } + } // function Targets(state) { + + function updateNumTextEl() { + this.numTextEl.html(this.draggable.length); } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 486181c07c..3b5053d5d2 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -199,6 +199,10 @@ define(['logme'], function (logme) { draggable.setOnTarget(target); target.draggable.push(draggableId); + if (target.numTextEl !== null) { + target.updateNumTextEl(); + } + state.numDraggablesInSlider -= 1; state.updateArrowOpacity(); } From 63b79f76d7b55cb00c5b71f4c1053ef6108d1b54 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Tue, 15 Jan 2013 13:31:16 +0200 Subject: [PATCH 102/140] added can_reuse --- common/lib/capa/capa/inputtypes.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index cf5bee3b0b..919c4a3eec 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -810,9 +810,8 @@ class DragAndDropInput(InputTypeBase): tag_type: 'draggable' or 'target'. - If tag_type is 'draggable' : all attributes (name or label or - icon) are optional, but at least one attribute must be - presented. + If tag_type is 'draggable' : all attributes except id + (name or label or icon or can_reuse) are optional If tag_type is 'target' all attributes (name, x, y, w, h) are required. (x, y) - coordinates of center of target, @@ -820,11 +819,13 @@ class DragAndDropInput(InputTypeBase): Returns: Dictionary of vaues of attributes: - dict{'name': smth, 'label': smth, 'icon': smth}. + dict{'name': smth, 'label': smth, 'icon': smth, + 'can_reuse': smth}. """ tag_attrs = dict() tag_attrs['draggable'] = {'id': Attribute._sentinel, - 'label': "", 'icon': ""} + 'label': "", 'icon': "", + 'can_reuse': ""} tag_attrs['target'] = {'id': Attribute._sentinel, 'x': Attribute._sentinel, From 0349e157b5e552cd8b4babc17846daee68c2ff99 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Tue, 15 Jan 2013 15:52:14 +0200 Subject: [PATCH 103/140] renamed graders to verifiers --- common/lib/capa/capa/capa_problem.py | 6 +++--- common/lib/capa/capa/{graders => verifiers}/__init__.py | 0 common/lib/capa/capa/{graders => verifiers}/draganddrop.py | 0 common/lib/capa/capa/{graders => verifiers}/tests.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename common/lib/capa/capa/{graders => verifiers}/__init__.py (100%) rename common/lib/capa/capa/{graders => verifiers}/draganddrop.py (100%) rename common/lib/capa/capa/{graders => verifiers}/tests.py (99%) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 96a334ef97..4b0faa91a1 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -34,8 +34,8 @@ import chem import chem.chemcalc import chem.chemtools import chem.miller -import graders -import graders.draganddrop +import verifiers +import verifiers.draganddrop import calc from correctmap import CorrectMap @@ -72,7 +72,7 @@ global_context = {'random': random, 'chemcalc': chem.chemcalc, 'chemtools': chem.chemtools, 'miller': chem.miller, - 'draganddrop': graders.draganddrop} + 'draganddrop': verifiers.draganddrop} # These should be removed from HTML output, including all subelements html_problem_semantics = ["codeparam", "responseparam", "answer", "script", "hintgroup", "openendedparam","openendedrubric"] diff --git a/common/lib/capa/capa/graders/__init__.py b/common/lib/capa/capa/verifiers/__init__.py similarity index 100% rename from common/lib/capa/capa/graders/__init__.py rename to common/lib/capa/capa/verifiers/__init__.py diff --git a/common/lib/capa/capa/graders/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py similarity index 100% rename from common/lib/capa/capa/graders/draganddrop.py rename to common/lib/capa/capa/verifiers/draganddrop.py diff --git a/common/lib/capa/capa/graders/tests.py b/common/lib/capa/capa/verifiers/tests.py similarity index 99% rename from common/lib/capa/capa/graders/tests.py rename to common/lib/capa/capa/verifiers/tests.py index 90a8a9a98a..ddcaad511d 100644 --- a/common/lib/capa/capa/graders/tests.py +++ b/common/lib/capa/capa/verifiers/tests.py @@ -68,7 +68,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): user_input = '{"use_targets": true, "draggables": [{"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.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_position_and_targets(self): user_input = '{"use_targets": false, \ From e2bbaf08b9346abab205f0c0eb0b5106d48c1fc9 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Tue, 15 Jan 2013 16:31:45 +0200 Subject: [PATCH 104/140] removed use_targets flag --- common/lib/capa/capa/verifiers/draganddrop.py | 20 ++++------- common/lib/capa/capa/verifiers/tests.py | 35 +++++++++---------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 431b5d249c..44e50828f5 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -1,11 +1,10 @@ """ Grader of drag and drop input. Client side behavior: user can drag and drop images from list on base image. -Parameter 'use_targets' in xml can control two use cases. -if use_targets is true (defaut), then json returned from client is: + + Then json returned from client is: { - "use_targets": true, "draggable": [ { "image1": "t1" }, { "ant": "t2" }, @@ -14,9 +13,8 @@ if use_targets is true (defaut), then json returned from client is: } values are target names. -If use_targets is false: +or: { - "use_targets": false, "draggable": [ { "image1": "[10, 20]" }, { "ant": "[30, 40]" }, @@ -112,11 +110,8 @@ class DragAndDrop(object): def grade(self): ''' Grader user answer. - If use_targets is True - checks if every draggable isplaced on proper - target. - - If use_targets is False - checks if every draggable is placed on proper - coordinates within radius of forgiveness (default is 10). + Checks if every draggable isplaced on proper target or on proper + coordinates within radius of forgiveness (default is 10). Returns: bool. ''' @@ -260,7 +255,6 @@ class DragAndDrop(object): correct_answer = tmp user_answer = json.loads(user_answer) - self.use_targets = user_answer.get('use_targets') # check if we have draggables that are not in correct answer: self.excess_draggables = {} @@ -295,12 +289,12 @@ def grade(user_input, correct_answer): Args: user_input: json. Format:: - {"use_targets": false, "draggables": + { "draggables": [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' or - {"use_targets": true, "draggables": [{"1": "t1"}, \ + {"draggables": [{"1": "t1"}, \ {"name_with_icon": "t2"}]} correct_answer: dict or list. diff --git a/common/lib/capa/capa/verifiers/tests.py b/common/lib/capa/capa/verifiers/tests.py index ddcaad511d..c744734995 100644 --- a/common/lib/capa/capa/verifiers/tests.py +++ b/common/lib/capa/capa/verifiers/tests.py @@ -41,73 +41,72 @@ class Test_PositionsCompare(unittest.TestCase): class Test_DragAndDrop_Grade(unittest.TestCase): def test_targets_true(self): - user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + user_input = '{"draggables": [{"1": "t1"}, \ {"name_with_icon": "t2"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't2'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_targets_false(self): - user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + user_input = '{"draggables": [{"1": "t1"}, \ {"name_with_icon": "t2"}]}' correct_answer = {'1': 't3', 'name_with_icon': 't2'} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_multiple_images_per_target_true(self): - user_input = '{"use_targets": true, \ + user_input = '{\ "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't1'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_multiple_images_per_target_false(self): - user_input = '{"use_targets": true, \ + user_input = '{\ "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' correct_answer = {'1': 't2', 'name_with_icon': 't1'} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_targets_and_positions(self): - user_input = '{"use_targets": true, "draggables": [{"1": [10,10]}, \ + user_input = '{"draggables": [{"1": [10,10]}, \ {"name_with_icon": [[10,10],4]}]}' correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_position_and_targets(self): - user_input = '{"use_targets": false, \ - "draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' + user_input = '{"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' correct_answer = {'1': 't1', 'name_with_icon': 't2'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_positions_exact(self): - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_positions_false(self): - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_positions_true_in_radius(self): - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_positions_true_in_manual_radius(self): - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_positions_false_in_manual_radius(self): - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_correct_answer_not_has_key_from_user_answer(self): - user_input = '{"use_targets": true, "draggables": [{"1": "t1"}, \ + user_input = '{"draggables": [{"1": "t1"}, \ {"name_with_icon": "t2"}]}' correct_answer = {'3': 't3', 'name_with_icon': 't2'} self.assertFalse(draganddrop.grade(user_input, correct_answer)) @@ -116,14 +115,14 @@ class Test_DragAndDrop_Grade(unittest.TestCase): """Draggables can be places anywhere on base image. Place grass in the middle of the image and ant in the right upper corner.""" - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}' correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_lcao_correct(self): """Describe carbon molecule in LCAO-MO""" - user_input = '{"use_targets":true,"draggables":[{"1":"s_left"}, \ + user_input = '{"draggables":[{"1":"s_left"}, \ {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ {"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \ {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ @@ -162,7 +161,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): def test_lcao_extra_element_incorrect(self): """Describe carbon molecule in LCAO-MO""" - user_input = '{"use_targets":true,"draggables":[{"1":"s_left"}, \ + user_input = '{"draggables":[{"1":"s_left"}, \ {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ {"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \ {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ @@ -177,7 +176,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): 'rule': 'anyof' }, { 'draggables': ['7', '8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], + 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], 'rule': 'anyof' }, { 'draggables': ['11', '12'], @@ -205,7 +204,7 @@ class Test_DragAndDrop_Populate(unittest.TestCase): def test_1(self): dnd = draganddrop.DragAndDrop() correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} - user_input = '{"use_targets": false, "draggables": \ + user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' dnd.populate(correct_answer, user_input) From ead346ee5ff2fed0fdce639784ec77bc6bbacb78 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Tue, 15 Jan 2013 17:55:43 +0200 Subject: [PATCH 105/140] Refactoring. New feature: adding multiple draggables from one. --- .../js/capa/drag_and_drop/draggables.js | 1491 +++++++++-------- common/static/js/capa/drag_and_drop/main.js | 4 +- .../static/js/capa/drag_and_drop/targets.js | 9 - .../js/capa/drag_and_drop/update_input.js | 8 +- 4 files changed, 760 insertions(+), 752 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index d95b0e706f..5065e7193b 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -5,189 +5,170 @@ (function (requirejs, require, define) { define(['logme', 'update_input'], function (logme, updateInput) { - return Draggables; + return { + 'init': init + }; - function Draggables(state) { - var c1; + function init(state) { + logme('Draggables.init; state = ', state); state.draggables = []; state.numDraggablesInSlider = 0; - - for (c1 = 0; c1 < state.config.draggables.length; c1 += 1) { - processDraggable(state.config.draggables[c1], c1 + 1); - } - - state.updateArrowOpacity(); state.currentMovingDraggable = null; + (function (c1) { + while (c1 < state.config.draggables.length) { + processDraggable(state, state.config.draggables[c1], c1 + 1); + c1 += 1 + } + }(0)); + + state.updateArrowOpacity(); + $(document).mousemove(function (event) { - if (state.currentMovingDraggable !== null) { - state.currentMovingDraggable.iconEl.css( + documentMouseMove(state, event); + }); + } + + function documentMouseMove(state, event) { + if (state.currentMovingDraggable !== null) { + state.currentMovingDraggable.iconEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.iconWidth * 0.5 + - state.currentMovingDraggable.iconElLeftOffset + ); + state.currentMovingDraggable.iconEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top - + state.currentMovingDraggable.iconHeight * 0.5 + ); + + if (state.currentMovingDraggable.labelEl !== null) { + state.currentMovingDraggable.labelEl.css( 'left', event.pageX - state.baseImageEl.offset().left - - state.currentMovingDraggable.iconWidth * 0.5 - - state.currentMovingDraggable.iconElLeftOffset + state.currentMovingDraggable.labelWidth * 0.5 + - 9 // Account for padding, border. ); - state.currentMovingDraggable.iconEl.css( + state.currentMovingDraggable.labelEl.css( 'top', event.pageY - - state.baseImageEl.offset().top - - state.currentMovingDraggable.iconHeight * 0.5 + state.baseImageEl.offset().top + + state.currentMovingDraggable.iconHeight * 0.5 + + 5 + ); + } + } + } + + function processDraggable(state, obj, objIndex) { + var draggableObj; + + logme('processDraggable; state = ', state); + + logme('Processing draggable #' + objIndex); + + draggableObj = { + 'zIndex': objIndex, + 'oldZIndex': objIndex, + 'labelEl': null, + 'hasLoaded': false, + 'inContainer': true, + 'mousePressed': false, + 'onTarget': null, + + 'state': state, + + 'mouseDown': mouseDown, + 'mouseUp': mouseUp, + 'mouseMove': mouseMove, + 'checkLandingElement': checkLandingElement, + 'removeObjIdFromTarget': removeObjIdFromTarget, + 'checkIfOnTarget': checkIfOnTarget, + 'snapToTarget': snapToTarget, + 'correctZIndexes': correctZIndexes, + 'moveBackToSlider': moveBackToSlider + }; + + draggableObj.containerEl = $( + '
' + ); + + draggableObj.containerEl.appendTo(state.sliderEl); + + if (obj.icon.length > 0) { + draggableObj.iconElBGColor = 'transparent'; + draggableObj.iconElPadding = 0; + draggableObj.iconElBorder = 'none'; + draggableObj.iconElLeftOffset = 0; + + draggableObj.iconEl = $(''); + draggableObj.iconEl.attr( + 'src', + obj.icon + ); + draggableObj.iconEl.load(function () { + draggableObj.iconWidth = this.width; + draggableObj.iconHeight = this.height; + + if (draggableObj.iconWidth >= draggableObj.iconHeight) { + draggableObj.iconWidthSmall = 60; + draggableObj.iconHeightSmall = + draggableObj.iconWidthSmall * + (draggableObj.iconHeight / draggableObj.iconWidth); + } else { + draggableObj.iconHeightSmall = 60; + draggableObj.iconWidthSmall = + draggableObj.iconHeightSmall * + (draggableObj.iconWidth / draggableObj.iconHeight); + } + + draggableObj.iconEl.css('position', 'absolute'); + + draggableObj.iconEl.css( + 'width', + draggableObj.iconWidthSmall + ); + draggableObj.iconEl.css( + 'height', + draggableObj.iconHeightSmall ); - if (state.currentMovingDraggable.labelEl !== null) { - state.currentMovingDraggable.labelEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - state.currentMovingDraggable.labelWidth * 0.5 - - 9 // Account for padding, border. - ); - state.currentMovingDraggable.labelEl.css( + draggableObj.iconEl.css( + 'left', + 50 - draggableObj.iconWidthSmall * 0.5 + ); + + if (obj.label.length > 0) { + draggableObj.iconEl.css('top', 5); + } else { + draggableObj.iconEl.css( 'top', - event.pageY - - state.baseImageEl.offset().top + - state.currentMovingDraggable.iconHeight * 0.5 + - 5 + 50 - draggableObj.iconHeightSmall * 0.5 ); } - } - }); - return; + draggableObj.iconEl.appendTo(draggableObj.containerEl); - function processDraggable(obj, objIndex) { - var inContainer, mousePressed, onTarget, draggableObj; - - draggableObj = { - 'zIndex': objIndex, - 'oldZIndex': objIndex, - 'labelEl': null, - 'hasLoaded': false - }; - - draggableObj.containerEl = $( - '
' - ); - - draggableObj.containerEl.appendTo(state.sliderEl); - - if (obj.icon.length > 0) { - draggableObj.iconElBGColor = 'transparent'; - draggableObj.iconElPadding = 0; - draggableObj.iconElBorder = 'none'; - draggableObj.iconElLeftOffset = 0; - - draggableObj.iconEl = $(''); - draggableObj.iconEl.attr( - 'src', - obj.icon - ); - draggableObj.iconEl.load(function () { - draggableObj.iconWidth = this.width; - draggableObj.iconHeight = this.height; - - if (draggableObj.iconWidth >= draggableObj.iconHeight) { - draggableObj.iconWidthSmall = 60; - draggableObj.iconHeightSmall = - draggableObj.iconWidthSmall * - (draggableObj.iconHeight / draggableObj.iconWidth); - } else { - draggableObj.iconHeightSmall = 60; - draggableObj.iconWidthSmall = - draggableObj.iconHeightSmall * - (draggableObj.iconWidth / draggableObj.iconHeight); - } - - draggableObj.iconEl.css('position', 'absolute'); - - draggableObj.iconEl.css( - 'width', - draggableObj.iconWidthSmall - ); - draggableObj.iconEl.css( - 'height', - draggableObj.iconHeightSmall - ); - - draggableObj.iconEl.css( - 'left', - 50 - draggableObj.iconWidthSmall * 0.5 - ); - - if (obj.label.length > 0) { - draggableObj.iconEl.css('top', 5); - } else { - draggableObj.iconEl.css( - 'top', - 50 - draggableObj.iconHeightSmall * 0.5 - ); - } - - draggableObj.iconEl.appendTo(draggableObj.containerEl); - - if (obj.label.length > 0) { - draggableObj.labelEl = $( - '
' + - obj.label + - '
' - ); - - draggableObj.labelEl.appendTo( - draggableObj.containerEl - ); - - draggableObj.labelWidth = draggableObj.labelEl.width(); - - draggableObj.labelEl.css( - 'left', - 50 - draggableObj.labelWidth * 0.5 - ); - draggableObj.labelEl.css( - 'top', - 5 + draggableObj.iconHeightSmall + 5 - ); - - draggableObj.labelEl.mousedown(mouseDown); - draggableObj.labelEl.mouseup(mouseUp); - draggableObj.labelEl.mousemove(mouseMove); - } - - draggableObj.hasLoaded = true; - }); - } else { - // To make life easier, if there is no icon, but there is a - // label, we will create a label and store it as if it was an - // icon. All the existing code will work, and the user will - // see a label instead of an icon. if (obj.label.length > 0) { - draggableObj.iconElBGColor = state.config.labelBgColor; - draggableObj.iconElPadding = 8; - draggableObj.iconElBorder = '1px solid black'; - draggableObj.iconElLeftOffset = 9; - - draggableObj.iconEl = $( + draggableObj.labelEl = $( '
' + + obj.label + + '
' + ); - function mouseDown(event) { - if (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(); + draggableObj.iconEl.appendTo(draggableObj.containerEl); - // If this draggable is just being dragged out of the - // container, we must perform some additional tasks. - if (inContainer === true) { - draggableObj.containerEl.hide(); - - draggableObj.iconEl.detach(); - draggableObj.iconEl.css( - 'background-color', draggableObj.iconElBGColor - ); - draggableObj.iconEl.css( - 'padding-left', draggableObj.iconElPadding - ); - draggableObj.iconEl.css( - 'padding-right', draggableObj.iconElPadding - ); - draggableObj.iconEl.css( - 'border', draggableObj.iconElBorder - ); - draggableObj.iconEl.css( - 'width', - draggableObj.iconWidth - ); - draggableObj.iconEl.css( - 'height', - draggableObj.iconHeight - ); - draggableObj.iconEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - draggableObj.iconWidth * 0.5 - - draggableObj.iconElLeftOffset - ); - draggableObj.iconEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top - - draggableObj.iconHeight * 0.5 - ); - draggableObj.iconEl.appendTo( - state.baseImageEl.parent() - ); - - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.detach(); - draggableObj.labelEl.css( - 'background-color', state.config.labelBgColor - ); - draggableObj.labelEl.css( - 'padding-left', 8 - ); - draggableObj.labelEl.css( - 'padding-right', 8 - ); - draggableObj.labelEl.css( - 'border', '1px solid black' - ); - draggableObj.labelEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - draggableObj.labelWidth * 0.5 - - 9 // Account for padding, border. - ); - draggableObj.labelEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top + - draggableObj.iconHeight * 0.5 + 5 - ); - draggableObj.labelEl.appendTo( - state.baseImageEl.parent() - ); - } - - inContainer = false; - state.numDraggablesInSlider -= 1; - } - - draggableObj.oldZIndex = draggableObj.zIndex; - draggableObj.zIndex = 1000; - draggableObj.iconEl.css('z-index', '1000'); - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css('z-index', '1000'); - } - - mousePressed = true; - state.currentMovingDraggable = draggableObj; - } - } - - function mouseUp() { - if (mousePressed === true) { - state.currentMovingDraggable = null; - - checkLandingElement(); - } - } - - function mouseMove() { - if (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(); - - draggableObj.iconEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - draggableObj.iconWidth * 0.5 - - draggableObj.iconElLeftOffset - ); - draggableObj.iconEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top - - draggableObj.iconHeight * 0.5 - ); - - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - draggableObj.labelWidth * 0.5 - - 9 // Acoount for padding, border. - ); - draggableObj.labelEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top + - draggableObj.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, targetFound; - - mousePressed = false; - positionIE = draggableObj.iconEl.position(); - - if (state.individualTargets === true) { - targetFound = false; - - checkIfOnTarget(); - - if (targetFound === true) { - correctZIndexes(); - } else { - moveBackToSlider(); - removeObjIdFromTarget(); - - state.numDraggablesInSlider += 1; - } - } else { - if ( - (positionIE.left < 0) || - ( - positionIE.left + draggableObj.iconWidth > - state.baseImageEl.width() - ) || - (positionIE.top < 0) || - ( - positionIE.top + draggableObj.iconHeight > - state.baseImageEl.height() - ) - ) { - moveBackToSlider(); - - draggableObj.x = -1; - draggableObj.y = -1; - - state.numDraggablesInSlider += 1; - } else { - correctZIndexes(); - - draggableObj.x = - positionIE.left + draggableObj.iconWidth * 0.5; - draggableObj.y = - positionIE.top + draggableObj.iconHeight * 0.5; - } - } - - state.updateArrowOpacity(); - updateInput(state); + draggableObj.iconWidth = draggableObj.iconEl.width(); + draggableObj.iconHeight = draggableObj.iconEl.height(); + draggableObj.iconWidthSmall = draggableObj.iconWidth; + draggableObj.iconHeightSmall = draggableObj.iconHeight; + draggableObj.iconEl.css( + 'left', + 50 - draggableObj.iconWidthSmall * 0.5 + ); + draggableObj.iconEl.css( + 'top', + 50 - draggableObj.iconHeightSmall * 0.5 + ); + } else { + // If no icon and no label, don't create a draggable. return; - - function removeObjIdFromTarget() { - var c1; - - if (onTarget !== null) { - for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) { - if (onTarget.draggable[c1] === draggableObj.id) { - onTarget.draggable.splice(c1, 1); - - break; - } - } - - if (onTarget.numTextEl !== null) { - onTarget.updateNumTextEl(); - } - - onTarget = null; - } - } - - // - // 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 - // - // draggableObj.iconEl.position() - // - function checkIfOnTarget() { - var c1, target; - - for (c1 = 0; c1 < state.targets.length; c1 += 1) { - target = 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 ( - (state.config.one_per_target === true) && - (target.draggable.length === 1) && - (target.draggable[0] !== draggableObj.id) - ) { - continue; - } - - // Check if the draggable's center coordinate is within - // the target's dimensions. If not, go to next target. - if ( - positionIE.top + draggableObj.iconHeight * 0.5 < - target.offset.top - ) { - continue; - } - if ( - positionIE.top + draggableObj.iconHeight * 0.5 > - target.offset.top + target.h - ) { - continue; - } - if ( - positionIE.left + draggableObj.iconWidth * 0.5 < - target.offset.left - ) { - continue; - } - if ( - positionIE.left + draggableObj.iconWidth * 0.5 > - target.offset.left + target.w - ) { - continue; - } - - // If we got here, then our draggable is on top of a - // target. - targetFound = true; - - // If the draggable was moved from one target to - // another, then we need to remove it's ID from the - // previous target's draggables list, and add it to the - // new target's draggables list. - if ( - (onTarget !== null) && - (onTarget.id !== target.id) - ) { - removeObjIdFromTarget(); - onTarget = target; - target.draggable.push(draggableObj.id); - } - // 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 (onTarget === null) { - onTarget = target; - target.draggable.push(draggableObj.id); - } - - if (target.numTextEl !== null) { - target.updateNumTextEl(); - } - - // Reposition the draggable so that it's center - // coincides with the center of the target. - snapToTarget(target); - - break; - } - } - - function snapToTarget(target) { - var offset; - - offset = 0; - if (state.config.targetOutline === true) { - offset = 1; - } - - draggableObj.iconEl.css( - 'left', - target.offset.left + 0.5 * target.w - - draggableObj.iconWidth * 0.5 + offset - - draggableObj.iconElLeftOffset - ); - draggableObj.iconEl.css( - 'top', - target.offset.top + 0.5 * target.h - - draggableObj.iconHeight * 0.5 + offset - ); - - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css( - 'left', - target.offset.left + 0.5 * target.w - - draggableObj.labelWidth * 0.5 + offset - - 9 // Acoount for padding, border. - ); - draggableObj.labelEl.css( - 'top', - target.offset.top + 0.5 * target.h + - draggableObj.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 draggablesInMe, c1, c2, highestZIndex; - - if (state.individualTargets === true) { - - logme('In correctZIndexes.'); - - if (onTarget.draggable.length > 0) { - logme('onTarget.draggable.length > 0'); - - draggablesInMe = []; - - for (c1 = 0; c1 < onTarget.draggable.length; c1 += 1) { - for (c2 = 0; c2 < state.draggables.length; c2 += 1) { - if ( - onTarget.draggable[c1] === - state.draggables[c2].id - ) { - draggablesInMe.push(state.draggables[c2]); - } - } - } - - highestZIndex = -10000; - - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - if ( - (draggablesInMe[c1].zIndex > highestZIndex) && - (draggablesInMe[c1].zIndex !== 1000) - ) { - highestZIndex = draggablesInMe[c1].zIndex; - } - } - - if (highestZIndex === -10000) { - highestZIndex = onTarget.draggable.length; - } else if (highestZIndex < draggableObj.oldZIndex) { - highestZIndex = draggableObj.oldZIndex; - } else { - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - logme('draggablesInMe[' + c1 + '].id = ' + draggablesInMe[c1].id); - logme('draggablesInMe[' + c1 + '].zIndex = ' + draggablesInMe[c1].zIndex); - logme('draggablesInMe[' + c1 + '].oldZIndex = ' + draggablesInMe[c1].oldZIndex); - } - - logme('----------------'); - logme('highestZIndex = ' + highestZIndex); - - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - draggablesInMe[c1].zIndex -= 1; - draggablesInMe[c1].oldZIndex -= 1; - - draggablesInMe[c1].iconEl.css( - 'z-index', - draggablesInMe[c1].zIndex - ); - if (draggablesInMe[c1].labelEl !== null) { - draggablesInMe[c1].labelEl.css( - 'z-index', - draggablesInMe[c1].zIndex - ); - } - } - } - } else { - logme('highestZIndex = onTarget.draggable.length'); - highestZIndex = onTarget.draggable.length; - } - - draggableObj.zIndex = highestZIndex; - draggableObj.oldZIndex = highestZIndex; - - draggableObj.iconEl.css( - 'z-index', - draggableObj.zIndex - ); - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css( - 'z-index', - draggableObj.zIndex - ); - } - } else { - for (c1 = 0; c1 < state.draggables.length; c1++) { - if ( - draggableObj.oldZIndex < - state.draggables[c1].zIndex - ) { - state.draggables[c1].zIndex -= 1; - state.draggables[c1].oldZIndex = state.draggables[c1].zIndex; - state.draggables[c1].iconEl.css( - 'z-index', - state.draggables[c1].zIndex - ); - - if (state.draggables[c1].labelEl !== null) { - state.draggables[c1].labelEl.css( - 'z-index', - state.draggables[c1].zIndex - ); - } - } - } - - draggableObj.zIndex = c1; - draggableObj.oldZIndex = c1; - draggableObj.iconEl.css('z-index', c1); - - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.css('z-index', c1); - } - } - } - - // 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() { - draggableObj.containerEl.show(); - - draggableObj.zIndex = draggableObj.oldZIndex; - - draggableObj.iconEl.detach(); - draggableObj.iconEl.css('border', 'none'); - draggableObj.iconEl.css('background-color', 'transparent'); - draggableObj.iconEl.css('padding-left', 0); - draggableObj.iconEl.css('padding-right', 0); - draggableObj.iconEl.css('z-index', draggableObj.zIndex); - draggableObj.iconEl.css( - 'width', - draggableObj.iconWidthSmall - ); - draggableObj.iconEl.css( - 'height', - draggableObj.iconHeightSmall - ); - draggableObj.iconEl.css( - 'left', - 50 - draggableObj.iconWidthSmall * 0.5 - ); - if (draggableObj.labelEl !== null) { - draggableObj.iconEl.css('top', 5); - } else { - draggableObj.iconEl.css( - 'top', - 50 - draggableObj.iconHeightSmall * 0.5 - ); - } - - draggableObj.iconEl.appendTo(draggableObj.containerEl); - - if (draggableObj.labelEl !== null) { - draggableObj.labelEl.detach(); - draggableObj.labelEl.css('border', 'none'); - draggableObj.labelEl.css('background-color', 'transparent'); - draggableObj.labelEl.css('padding-left', 0); - draggableObj.labelEl.css('padding-right', 0); - draggableObj.labelEl.css('z-index', draggableObj.zIndex); - draggableObj.labelEl.css( - 'left', - 50 - draggableObj.labelWidth * 0.5 - ); - draggableObj.labelEl.css( - 'top', - 5 + draggableObj.iconHeightSmall + 5 - ); - draggableObj.labelEl.appendTo( - draggableObj.containerEl - ); - } - - inContainer = true; - } } } + + draggableObj.iconEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.iconEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.iconEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + + draggableObj.containerEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.containerEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.containerEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + + draggableObj.id = obj.id; + draggableObj.x = -1; + draggableObj.y = -1; + + state.numDraggablesInSlider += 1; + + if (obj.icon.length === 0) { + draggableObj.hasLoaded = true; + } + + state.draggables.push(draggableObj); + } + + function mouseDown(event) { + if (this.mousePressed === false) { + // So that the browser does not perform a default drag. + // If we don't do this, each drag operation will + // potentially cause the highlghting of the dragged element. + event.preventDefault(); + + // If this draggable is just being dragged out of the + // container, we must perform some additional tasks. + if (this.inContainer === true) { + this.containerEl.hide(); + + this.iconEl.detach(); + this.iconEl.css( + 'background-color', this.iconElBGColor + ); + this.iconEl.css( + 'padding-left', this.iconElPadding + ); + this.iconEl.css( + 'padding-right', this.iconElPadding + ); + this.iconEl.css( + 'border', this.iconElBorder + ); + this.iconEl.css( + 'width', + this.iconWidth + ); + this.iconEl.css( + 'height', + this.iconHeight + ); + this.iconEl.css( + 'left', + event.pageX - + this.state.baseImageEl.offset().left - + this.iconWidth * 0.5 + - this.iconElLeftOffset + ); + this.iconEl.css( + 'top', + event.pageY - + this.state.baseImageEl.offset().top - + this.iconHeight * 0.5 + ); + this.iconEl.appendTo( + state.baseImageEl.parent() + ); + + if (this.labelEl !== null) { + this.labelEl.detach(); + this.labelEl.css( + 'background-color', this.state.config.labelBgColor + ); + this.labelEl.css( + 'padding-left', 8 + ); + this.labelEl.css( + 'padding-right', 8 + ); + this.labelEl.css( + 'border', '1px solid black' + ); + this.labelEl.css( + 'left', + event.pageX - + this.state.baseImageEl.offset().left - + this.labelWidth * 0.5 + - 9 // Account for padding, border. + ); + this.labelEl.css( + 'top', + event.pageY - + this.state.baseImageEl.offset().top + + this.iconHeight * 0.5 + 5 + ); + this.labelEl.appendTo( + this.state.baseImageEl.parent() + ); + } + + this.inContainer = false; + this.state.numDraggablesInSlider -= 1; + } + + this.oldZIndex = this.zIndex; + 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() { + 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 + ); + this.iconEl.css( + '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. + ); + this.labelEl.css( + '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.individualTargets === true) { + if (this.checkIfOnTarget(positionIE) === true) { + this.correctZIndexes(); + } else { + this.moveBackToSlider(); + this.removeObjIdFromTarget(); + + 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; + + this.state.numDraggablesInSlider += 1; + } else { + this.correctZIndexes(); + + this.x = + positionIE.left + this.iconWidth * 0.5; + this.y = + positionIE.top + this.iconHeight * 0.5; + } + } + + this.state.updateArrowOpacity(); + updateInput(this.state); + } + + function removeObjIdFromTarget() { + var c1; + + if (this.onTarget !== null) { + for (c1 = 0; c1 < this.onTarget.draggable.length; c1 += 1) { + if (this.onTarget.draggable[c1] === this.id) { + this.onTarget.draggable.splice(c1, 1); + + break; + } + } + + if (this.onTarget.numTextEl !== null) { + this.onTarget.updateNumTextEl(); + } + + this.onTarget = null; + } + } + + // + // 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, targetFound; + + targetFound = false; + + 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.one_per_target === true) && + (target.draggable.length === 1) && + (target.draggable[0] !== this.id) + ) { + 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 + ) { + continue; + } + if ( + positionIE.top + this.iconHeight * 0.5 > + target.offset.top + target.h + ) { + continue; + } + if ( + positionIE.left + this.iconWidth * 0.5 < + target.offset.left + ) { + continue; + } + if ( + positionIE.left + this.iconWidth * 0.5 > + target.offset.left + target.w + ) { + continue; + } + + // If we got here, then our draggable is on top of a + // target. + targetFound = true; + + // If the draggable was moved from one target to + // another, then we need to remove it's ID 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.removeObjIdFromTarget(); + this.onTarget = target; + target.draggable.push(this.id); + } + // 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) { + this.onTarget = target; + target.draggable.push(this.id); + } + + if (target.numTextEl !== null) { + target.updateNumTextEl(); + } + + // Reposition the draggable so that it's center + // coincides with the center of the target. + this.snapToTarget(target); + + break; + } + + return targetFound; + } + + 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 + ); + this.iconEl.css( + '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. + ); + this.labelEl.css( + '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 draggablesInMe, c1, c2, highestZIndex; + + if (this.state.individualTargets === true) { + if (this.onTarget.draggable.length > 0) { + draggablesInMe = []; + + for (c1 = 0; c1 < this.onTarget.draggable.length; c1 += 1) { + for (c2 = 0; c2 < this.state.draggables.length; c2 += 1) { + if ( + this.onTarget.draggable[c1] === + this.state.draggables[c2].id + ) { + draggablesInMe.push(this.state.draggables[c2]); + } + } + } + + highestZIndex = -10000; + + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + if ( + (draggablesInMe[c1].zIndex > highestZIndex) && + (draggablesInMe[c1].zIndex !== 1000) + ) { + highestZIndex = draggablesInMe[c1].zIndex; + } + } + + if (highestZIndex === -10000) { + highestZIndex = this.onTarget.draggable.length; + } else if (highestZIndex < this.oldZIndex) { + highestZIndex = this.oldZIndex; + } else { + for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + draggablesInMe[c1].zIndex -= 1; + draggablesInMe[c1].oldZIndex -= 1; + + draggablesInMe[c1].iconEl.css( + 'z-index', + draggablesInMe[c1].zIndex + ); + if (draggablesInMe[c1].labelEl !== null) { + draggablesInMe[c1].labelEl.css( + 'z-index', + draggablesInMe[c1].zIndex + ); + } + } + } + } else { + highestZIndex = this.onTarget.draggable.length; + } + + this.zIndex = highestZIndex; + this.oldZIndex = highestZIndex; + + this.iconEl.css( + 'z-index', + this.zIndex + ); + if (this.labelEl !== null) { + this.labelEl.css( + 'z-index', + this.zIndex + ); + } + } else { + for (c1 = 0; c1 < this.state.draggables.length; c1++) { + if ( + this.oldZIndex < + this.state.draggables[c1].zIndex + ) { + this.state.draggables[c1].zIndex -= 1; + this.state.draggables[c1].oldZIndex = this.state.draggables[c1].zIndex; + this.state.draggables[c1].iconEl.css( + 'z-index', + this.state.draggables[c1].zIndex + ); + + if (this.state.draggables[c1].labelEl !== null) { + this.state.draggables[c1].labelEl.css( + 'z-index', + this.state.draggables[c1].zIndex + ); + } + } + } + + this.zIndex = c1; + this.oldZIndex = c1; + this.iconEl.css('z-index', c1); + + if (this.labelEl !== null) { + this.labelEl.css('z-index', c1); + } + } + } + + // 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() { + this.containerEl.show(); + + this.zIndex = this.oldZIndex; + + this.iconEl.detach(); + this.iconEl.css('border', 'none'); + this.iconEl.css('background-color', 'transparent'); + this.iconEl.css('padding-left', 0); + this.iconEl.css('padding-right', 0); + this.iconEl.css('z-index', this.zIndex); + this.iconEl.css( + 'width', + this.iconWidthSmall + ); + this.iconEl.css( + 'height', + this.iconHeightSmall + ); + this.iconEl.css( + 'left', + 50 - this.iconWidthSmall * 0.5 + ); + if (this.labelEl !== null) { + this.iconEl.css('top', 5); + } else { + this.iconEl.css( + 'top', + 50 - this.iconHeightSmall * 0.5 + ); + } + + this.iconEl.appendTo(this.containerEl); + + if (this.labelEl !== null) { + this.labelEl.detach(); + this.labelEl.css('border', 'none'); + this.labelEl.css('background-color', 'transparent'); + this.labelEl.css('padding-left', 0); + this.labelEl.css('padding-right', 0); + this.labelEl.css('z-index', this.zIndex); + this.labelEl.css( + 'left', + 50 - this.labelWidth * 0.5 + ); + this.labelEl.css( + 'top', + 5 + this.iconHeightSmall + 5 + ); + this.labelEl.appendTo( + this.containerEl + ); + } + + this.inContainer = true; } }); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 24cc8cbdcb..6b1164b6df 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -63,7 +63,9 @@ define( Targets(state); Scroller(state); - Draggables(state); + Draggables.init(state); + + logme('After Draggables.init(state); state = ', state); // Update the input element, checking first that it is not filled with // an answer from the server. diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index 0bc246e49f..af27c186f2 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -119,15 +119,6 @@ define(['logme'], function (logme) { highestZIndex = -10000; lowestZIndex = 10000; - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - logme( - 'draggablesInMe[' + c1 + '].id = ' + draggablesInMe[c1].id, - 'draggablesInMe[' + c1 + '].zIndex = ' + draggablesInMe[c1].zIndex, - 'draggablesInMe[' + c1 + '].oldZIndex = ' + draggablesInMe[c1].oldZIndex - ); - } - logme('------------------'); - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { if (draggablesInMe[c1].zIndex < lowestZIndex) { lowestZIndex = draggablesInMe[c1].zIndex; diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 3b5053d5d2..14534f1240 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -10,6 +10,8 @@ define(['logme'], function (logme) { function updateInput(state, checkFirst) { var inputEl, stateStr, targets, draggables, c1, c2, tempObj; + logme('updateInput; state = ', state); + if (checkFirst === true) { if (checkIfHasAnswer() === true) { return; @@ -126,7 +128,7 @@ define(['logme'], function (logme) { return; } - draggable.setInContainer(false); + draggable.inContainer = false; draggable.containerEl.hide(); draggable.iconEl.detach(); @@ -196,7 +198,7 @@ define(['logme'], function (logme) { ); } - draggable.setOnTarget(target); + draggable.onTarget = target; target.draggable.push(draggableId); if (target.numTextEl !== null) { @@ -244,7 +246,7 @@ define(['logme'], function (logme) { return; } - draggable.setInContainer(false); + draggable.inContainer = false; draggable.containerEl.hide(); draggable.iconEl.detach(); From c8bbcacfef5b477b170086f1ad0c80ce640d9b8f Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Tue, 15 Jan 2013 19:07:05 +0200 Subject: [PATCH 106/140] labels reuse logic and tests --- common/lib/capa/capa/verifiers/draganddrop.py | 42 ++- .../{tests.py => tests_draganddrop.py} | 271 +++++++++++++++++- 2 files changed, 298 insertions(+), 15 deletions(-) rename common/lib/capa/capa/verifiers/{tests.py => tests_draganddrop.py} (54%) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 44e50828f5..7ad1ce35eb 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -119,9 +119,24 @@ class DragAndDrop(object): if not self.excess_draggables[draggable]: return False # user answer has more draggables than correct answer - # Number of draggables in user_groups may be smaller that in - # correct_groups, that is incorrect. + # Number of draggables in user_groups may be differ that in + # correct_groups, that is incorrect, except special case with 'number' for groupname, draggable_ids in self.correct_groups.items(): + + # 'number' rule special case + # for reusable draggables we may get in self.user_groups + # {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']} + # if +number in rule - do not remove duplicates but clean rule + current_rule = self.correct_positions[groupname].keys()[0] + if 'number' in current_rule: + rule_values = self.correct_positions[groupname][current_rule] + #clean rule, do not do clean dublicate items + self.correct_positions[groupname].pop(current_rule, None) + parsed_rule = current_rule.replace('+', '').replace('number', '') + self.correct_positions[groupname][parsed_rule] = rule_values + else: # remove dublicates + self.user_groups[groupname] = list(set(self.user_groups[groupname])) + if sorted(draggable_ids) != sorted(self.user_groups[groupname]): return False @@ -129,8 +144,9 @@ class DragAndDrop(object): # every element are equal with correct positions for groupname in self.correct_groups: rules_executed = 0 - for rule in ('exact', 'anyof'): # every group has only one rule - if self.correct_positions[groupname].get(rule, []): + for rule in ('exact', 'anyof', 'unorderly_equal'): + # every group has only one rule + if self.correct_positions[groupname].get(rule, None): rules_executed += 1 if not self.compare_positions( self.correct_positions[groupname][rule], @@ -174,6 +190,8 @@ class DragAndDrop(object): - draggables can be placed in any order: user ['1','2','3','4'] is 'anyof' equal to ['4', '2', '1', 3'] + 'unorderly_equal' is same as 'exact' but disregards on order + Equality functions: Equality functon depends on type of element. They declared in @@ -193,14 +211,24 @@ class DragAndDrop(object): return False if flag == 'anyof': - count = 0 for u_el in user: for c_el in correct: if PositionsCompare(u_el) == PositionsCompare(c_el): - count += 1 break - if count != len(user): + else: + return False + + if flag == 'unorderly_equal': + if len(correct) != len(user): return False + temp = correct[:] + for u_el in user: + for c_el in temp: + if PositionsCompare(u_el) == PositionsCompare(c_el): + temp.remove(c_el) + break + else: + return False return True diff --git a/common/lib/capa/capa/verifiers/tests.py b/common/lib/capa/capa/verifiers/tests_draganddrop.py similarity index 54% rename from common/lib/capa/capa/verifiers/tests.py rename to common/lib/capa/capa/verifiers/tests_draganddrop.py index c744734995..257d404679 100644 --- a/common/lib/capa/capa/verifiers/tests.py +++ b/common/lib/capa/capa/verifiers/tests_draganddrop.py @@ -54,21 +54,25 @@ class Test_DragAndDrop_Grade(unittest.TestCase): def test_multiple_images_per_target_true(self): user_input = '{\ - "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' - correct_answer = {'1': 't1', 'name_with_icon': 't1'} + "draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \ + {"2": "t1"}]}' + correct_answer = {'1': 't1', 'name_with_icon': 't2', + '2': 't1'} self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_multiple_images_per_target_false(self): user_input = '{\ - "draggables": [{"1": "t1"}, {"name_with_icon": "t1"}]}' - correct_answer = {'1': 't2', 'name_with_icon': 't1'} + "draggables": [{"1": "t1"}, {"name_with_icon": "t2"}, \ + {"2": "t1"}]}' + correct_answer = {'1': 't2', 'name_with_icon': 't2', + '2': 't1'} self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_targets_and_positions(self): user_input = '{"draggables": [{"1": [10,10]}, \ {"name_with_icon": [[10,10],4]}]}' correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} - self.assertFalse(draganddrop.grade(user_input, correct_answer)) + self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_position_and_targets(self): user_input = '{"draggables": [{"1": "t1"}, {"name_with_icon": "t2"}]}' @@ -117,6 +121,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): right upper corner.""" user_input = '{"draggables": \ [{"ant":[610.5,57.449951171875]},{"grass":[322.5,199.449951171875]}]}' + correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} self.assertTrue(draganddrop.grade(user_input, correct_answer)) @@ -198,6 +203,255 @@ class Test_DragAndDrop_Grade(unittest.TestCase): self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def test_reuse_draggable_no_mupliples(self): + """Test reusable draggables (no mupltiple draggables per target)""" + user_input = '{"draggables":[{"1":"target1"}, \ + {"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"}, \ + {"3":"target6"}]}' + correct_answer = [ + { + 'draggables': ['1'], + 'targets': ['target1', 'target3'], + 'rule': 'anyof' + }, + { + 'draggables': ['2'], + 'targets': ['target2', 'target4', 'target5'], + 'rule': 'anyof' + }, + { + 'draggables': ['3'], + 'targets': ['target6'], + 'rule': 'anyof' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_reuse_draggable_with_mupliples(self): + """Test reusable draggables with mupltiple draggables per target""" + user_input = '{"draggables":[{"1":"target1"}, \ + {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ + {"3":"target6"}]}' + correct_answer = [ + { + 'draggables': ['1'], + 'targets': ['target1', 'target3'], + 'rule': 'anyof' + }, + { + 'draggables': ['2'], + 'targets': ['target2', 'target4'], + 'rule': 'anyof' + }, + { + 'draggables': ['3'], + 'targets': ['target6'], + 'rule': 'anyof' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_reuse_many_draggable_with_mupliples(self): + """Test reusable draggables with mupltiple draggables per target""" + user_input = '{"draggables":[{"1":"target1"}, \ + {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ + {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ + {"5": "target5"}, {"6": "target2"}]}' + correct_answer = [ + { + 'draggables': ['1', '4'], + 'targets': ['target1', 'target3'], + 'rule': 'anyof' + }, + { + 'draggables': ['2', '6'], + 'targets': ['target2', 'target4'], + 'rule': 'anyof' + }, + { + 'draggables': ['5'], + 'targets': ['target4', 'target5'], + 'rule': 'anyof' + }, + { + 'draggables': ['3'], + 'targets': ['target6'], + 'rule': 'anyof' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_reuse_many_draggable_with_mupliples_wrong(self): + """Test reusable draggables with mupltiple draggables per target""" + user_input = '{"draggables":[{"1":"target1"}, \ + {"2":"target2"},{"1":"target1"}, \ + {"2":"target3"}, \ + {"2":"target4"}, \ + {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ + {"5": "target5"}, {"6": "target2"}]}' + correct_answer = [ + { + 'draggables': ['1', '4'], + 'targets': ['target1', 'target3'], + 'rule': 'anyof' + }, + { + 'draggables': ['2', '6'], + 'targets': ['target2', 'target4'], + 'rule': 'anyof' + }, + { + 'draggables': ['5'], + 'targets': ['target4', 'target5'], + 'rule': 'anyof' + }, + { + 'draggables': ['3'], + 'targets': ['target6'], + 'rule': 'anyof' + }] + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_false(self): + """Test reusable draggables (no mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ + {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ + {"a":"target1"}]}' + correct_answer = [ + { + 'draggables': ['a'], + 'targets': ['target1', 'target4', 'target7', 'target10'], + 'rule': 'unorderly_equal' + }, + { + 'draggables': ['b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'unorderly_equal' + }, + { + 'draggables': ['c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal' + }] + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_(self): + """Test reusable draggables (no mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ + {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ + {"a":"target10"}]}' + correct_answer = [ + { + 'draggables': ['a'], + 'targets': ['target1', 'target4', 'target7', 'target10'], + 'rule': 'unorderly_equal' + }, + { + 'draggables': ['b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'unorderly_equal' + }, + { + 'draggables': ['c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_multiple(self): + """Test reusable draggables (mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"b":"target5"}, \ + {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ + {"a":"target1"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a', 'a'], + 'targets': ['target1', 'target4', 'target7', 'target10'], + 'rule': 'anyof+number' + }, + { + 'draggables': ['b', 'b', 'b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'anyof+number' + }, + { + 'draggables': ['c', 'c', 'c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'anyof+number' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_multiple_false(self): + """Test reusable draggables (mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ + {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ + {"a":"target1"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a', 'a'], + 'targets': ['target1', 'target4', 'target7', 'target10'], + 'rule': 'anyof+number' + }, + { + 'draggables': ['b', 'b', 'b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'anyof+number' + }, + { + 'draggables': ['c', 'c', 'c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'anyof+number' + }] + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_reused(self): + """Test reusable draggables (no mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"b":"target5"}, \ + {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ + {"a":"target10"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a'], + 'targets': ['target1', 'target10'], + 'rule': 'unorderly_equal+number' + }, + { + 'draggables': ['b', 'b', 'b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'unorderly_equal+number' + }, + { + 'draggables': ['c', 'c', 'c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal+number' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_label_10_targets_with_a_b_c_reused_false(self): + """Test reusable draggables (no mupltiple draggables per target)""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\ + {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ + {"a":"target10"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a'], + 'targets': ['target1', 'target10'], + 'rule': 'unorderly_equal+number' + }, + { + 'draggables': ['b', 'b', 'b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'unorderly_equal+number' + }, + { + 'draggables': ['c', 'c', 'c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal+number' + }] + self.assertFalse(draganddrop.grade(user_input, correct_answer)) class Test_DragAndDrop_Populate(unittest.TestCase): @@ -273,9 +527,10 @@ class Test_DraAndDrop_Compare_Positions(unittest.TestCase): def suite(): testcases = [Test_PositionsCompare, - Test_DragAndDrop_Populate, - Test_DragAndDrop_Grade, - Test_DraAndDrop_Compare_Positions] + Test_DragAndDrop_Populate, + Test_DragAndDrop_Grade, + Test_DraAndDrop_Compare_Positions + ] suites = [] for testcase in testcases: suites.append(unittest.TestLoader().loadTestsFromTestCase(testcase)) From e017297d3f4087faac1c0e2742c71dbd5c40ae02 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:18:06 +0200 Subject: [PATCH 107/140] fix --- common/lib/capa/capa/verifiers/draganddrop.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 7ad1ce35eb..91d5759390 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -206,6 +206,8 @@ class DragAndDrop(object): Returns: True if within rule lists are equal, otherwise False. """ if flag == 'exact': + if len(correct) != len(user): + return False for el1, el2 in zip(correct, user): if PositionsCompare(el1) != PositionsCompare(el2): return False From a6f87dcfdcd86fd552d80155fed592b7d6172482 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:18:21 +0200 Subject: [PATCH 108/140] updated docs --- docs/source/drag_and_drop_input.rst | 125 ++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index 3675de9c96..169d6883f5 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -42,6 +42,8 @@ attributes:: id - Unique identifier of the draggable object. label - Human readable label that will be shown to the user. icon - Relative path to an image that will be shown to the user. + can_reuse - true or false, default is false. If true, same draggable can be + used multiple times. A draggable is what the user must drag out of the slider and place onto the base image. After a drag operation, if the center of the draggable ends up @@ -81,6 +83,129 @@ the slider. If no targets are provided, then a draggable can be dragged and placed anywhere on the base image. +correct answer format +--------------------- + +There are two correct answer formats: short and long +If short from correct answer is mapping of 'draggable_id' to 'target_id':: + + correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} + correct_answer = {'name4': 't1', '7': 't2'} + +In long form correct answer is list of dicts. Every dict has 3 keys: +draggables, targets and rule. For example:: + + correct_answer = [ + { + 'draggables': ['7', '8'], + 'targets': ['t5_c', 't6_c'], + 'rule': 'anyof' + }, + { + 'draggables': ['1', '2'], + 'targets': ['t2_h', 't3_h', 't4_h', 't7_h', 't8_h', 't10_h'], + 'rule': 'anyof' + }] + +Draggables is list of draggables id. Target is list of targets id, draggables +must be dragged to with considering rule. Rule is string. + +Draggables in dicts inside correct_answer list must not intersect. +Wrong (for draggable id 7):: + + correct_answer = [ + { + 'draggables': ['7', '8'], + 'targets': ['t5_c', 't6_c'], + 'rule': 'anyof' + }, + { + 'draggables': ['7', '2'], + 'targets': ['t2_h', 't3_h', 't4_h', 't7_h', 't8_h', 't10_h'], + 'rule': 'anyof' + }] + +Rules are: exact, anyof, unorderly_equal, anyof+number, unorderly_equal+number + +- Exact rule means that targets for draggable id's in user_answer are the same +that targets from correct answer. For example, for draggables 7 and 8 user must +drag 7 to target1 and 8 to target2 if correct_answer is:: + + correct_answer = [ + { + 'draggables': ['7', '8'], + 'targets': ['tartget1', 'target2'], + 'rule': 'exact' + }] + + +- Unorderly_equal rule allows draggables be dragged to targets unorderly. If one +want to allow for student to drag 7 to target1 or target2 and 8 to target2 or +target 1 and 7 and 8 must be in different targets, then correct answer must be:: + + correct_answer = [ + { + 'draggables': ['7', '8'], + 'targets': ['tartget1', 'target2'], + 'rule': 'unorderly_equal' + }] + +- Anyof rule allows draggables to be dragged to any of targets. If one want to +allow for student to drag 7 and 8 to target1 or target2, which means that if 7 +is on target1 and 8 is on target1 or 7 on target2 and 8 on target2 or 7 on +target1 and 8 on target2. Any of theese are correct which anyof rule:: + + correct_answer = [ + { + 'draggables': ['7', '8'], + 'targets': ['tartget1', 'target2'], + 'rule': 'anyof' + }] + + +- If you have can_reuse true, then you, for example, have draggables a,b,c and 10 +targets. These will allow you to drag 4 'a' draggables to +['target1', 'target4', 'target7', 'target10'] , you do not need to write 'a' +four times. Also this will allow you to drag 'b' draggable to target2 or target5 +or target5 and target2 etc..:: + + correct_answer = [ + { + 'draggables': ['a'], + 'targets': ['target1', 'target4', 'target7', 'target10'], + 'rule': 'unorderly_equal' + }, + { + 'draggables': ['b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'anyof' + }, + { + 'draggables': ['c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal' + }] + +And sometimes you want to allow drag only two 'b' draggables, in these case +you sould use 'anyof+number' of 'unordered_equal+number' rule:: + + correct_answer = [ + { + 'draggables': ['a', 'a', 'a'], + 'targets': ['target1', 'target4', 'target7'], + 'rule': 'unorderly_equal+numbers' + }, + { + 'draggables': ['b', 'b'], + 'targets': ['target2', 'target5', 'target8'], + 'rule': 'anyof+numbers' + }, + { + 'draggables': ['c'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unorderly_equal' + }] + Example ======= From 099c79c8813d730ff9bca58bb36a6110d72ea080 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:21:40 +0200 Subject: [PATCH 109/140] update docs --- docs/source/drag_and_drop_input.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index 169d6883f5..1e5e6bf468 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -125,7 +125,7 @@ Wrong (for draggable id 7):: 'rule': 'anyof' }] -Rules are: exact, anyof, unorderly_equal, anyof+number, unorderly_equal+number +Rules are: exact, anyof, unordered_equal, anyof+number, unordered_equal+number - Exact rule means that targets for draggable id's in user_answer are the same that targets from correct answer. For example, for draggables 7 and 8 user must @@ -139,7 +139,7 @@ drag 7 to target1 and 8 to target2 if correct_answer is:: }] -- Unorderly_equal rule allows draggables be dragged to targets unorderly. If one +- unordered_equal rule allows draggables be dragged to targets unordered. If one want to allow for student to drag 7 to target1 or target2 and 8 to target2 or target 1 and 7 and 8 must be in different targets, then correct answer must be:: @@ -147,7 +147,7 @@ target 1 and 7 and 8 must be in different targets, then correct answer must be:: { 'draggables': ['7', '8'], 'targets': ['tartget1', 'target2'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }] - Anyof rule allows draggables to be dragged to any of targets. If one want to @@ -173,7 +173,7 @@ or target5 and target2 etc..:: { 'draggables': ['a'], 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }, { 'draggables': ['b'], @@ -183,7 +183,7 @@ or target5 and target2 etc..:: { 'draggables': ['c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }] And sometimes you want to allow drag only two 'b' draggables, in these case @@ -193,7 +193,7 @@ you sould use 'anyof+number' of 'unordered_equal+number' rule:: { 'draggables': ['a', 'a', 'a'], 'targets': ['target1', 'target4', 'target7'], - 'rule': 'unorderly_equal+numbers' + 'rule': 'unordered_equal+numbers' }, { 'draggables': ['b', 'b'], @@ -203,9 +203,11 @@ you sould use 'anyof+number' of 'unordered_equal+number' rule:: { 'draggables': ['c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }] +In case if we have no multiple draggables per targets (one_per_target="true"), +for same number of draggables, anyof is equal to unordered_equal Example ======= From e47f0e45f2aced4e22cc7d78b96d19df27782a8d Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:22:46 +0200 Subject: [PATCH 110/140] unorderly->unordered --- common/lib/capa/capa/verifiers/draganddrop.py | 6 +++--- common/lib/capa/capa/verifiers/tests_draganddrop.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 91d5759390..9bfbf982ff 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -144,7 +144,7 @@ class DragAndDrop(object): # every element are equal with correct positions for groupname in self.correct_groups: rules_executed = 0 - for rule in ('exact', 'anyof', 'unorderly_equal'): + for rule in ('exact', 'anyof', 'unordered_equal'): # every group has only one rule if self.correct_positions[groupname].get(rule, None): rules_executed += 1 @@ -190,7 +190,7 @@ class DragAndDrop(object): - draggables can be placed in any order: user ['1','2','3','4'] is 'anyof' equal to ['4', '2', '1', 3'] - 'unorderly_equal' is same as 'exact' but disregards on order + 'unordered_equal' is same as 'exact' but disregards on order Equality functions: @@ -220,7 +220,7 @@ class DragAndDrop(object): else: return False - if flag == 'unorderly_equal': + if flag == 'unordered_equal': if len(correct) != len(user): return False temp = correct[:] diff --git a/common/lib/capa/capa/verifiers/tests_draganddrop.py b/common/lib/capa/capa/verifiers/tests_draganddrop.py index 257d404679..cb9fef1865 100644 --- a/common/lib/capa/capa/verifiers/tests_draganddrop.py +++ b/common/lib/capa/capa/verifiers/tests_draganddrop.py @@ -343,17 +343,17 @@ class Test_DragAndDrop_Grade(unittest.TestCase): { 'draggables': ['a'], 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }, { 'draggables': ['b'], 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }, { 'draggables': ['c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }] self.assertTrue(draganddrop.grade(user_input, correct_answer)) From 79f5e98dfca3fc3a9a06e782a5cac3e6b413215a Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 16 Jan 2013 16:39:00 +0200 Subject: [PATCH 111/140] Drag and Drop: refactoring + preparing for reuse of draggables. --- .../js/capa/drag_and_drop/base_image.js | 6 +- .../js/capa/drag_and_drop/config_parser.js | 226 +++++--- .../js/capa/drag_and_drop/draggables.js | 149 +++++- common/static/js/capa/drag_and_drop/main.js | 6 +- .../static/js/capa/drag_and_drop/targets.js | 4 +- .../js/capa/drag_and_drop/update_input.js | 488 ++++++------------ 6 files changed, 457 insertions(+), 422 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index 0b64a7fd65..cd74267690 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -26,7 +26,7 @@ define(['logme'], function (logme) { state.baseImageEl.attr( 'src', - state.config.base_image + state.config.baseImage ); state.baseImageEl.load(function () { baseImageElContainer.css('width', this.width); @@ -43,11 +43,11 @@ define(['logme'], function (logme) { }); state.baseImageEl.error(function () { logme( - 'ERROR: Image "' + state.config.base_image + '" was not found!' + 'ERROR: Image "' + state.config.baseImage + '" was not found!' ); baseImageElContainer.html( '' + - 'ERROR: Image "' + state.config.base_image + '" was not found!' + + 'ERROR: Image "' + state.config.baseImage + '" was not found!' + '' ); baseImageElContainer.appendTo(state.containerEl); diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index b87f618bf4..b154a81826 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -8,154 +8,218 @@ define(['logme'], function (logme) { return configParser; function configParser(config, state) { - var returnStatus; - - returnStatus = true; - state.config = { 'draggables': [], + 'baseImage': '', 'targets': [], - 'base_image': '' + 'onePerTarget': null, + 'targetOutline': true, + 'labelBgColor': '#d6d6d6', + + 'individualTargets': null, + + 'errors': 0 // Number of errors found while parsing config. }; - if ($.isArray(config.draggables) === true) { + getDraggables(state, config); + getBaseImage(state, config); + getTargets(state, config); + getOnePerTarget(state, config); + getTargetOutline(state, config); + getLabelBgColor(state, config); + + setIndividualTargets(state); + + if (state.config.errors !== 0) { + return false; + } + + return true; + } + + function getDraggables(state, config) { + if (config.hasOwnProperty('draggables') === false) { + logme('ERROR: "config" does not have a property "draggables".'); + state.config.errors += 1; + } else if ($.isArray(config.draggables) === true) { (function (i) { while (i < config.draggables.length) { - if (processDraggable(config.draggables[i]) !== true) { - returnStatus = false; + if (processDraggable(state, config.draggables[i]) !== true) { + state.config.errors += 1; } i += 1; } }(0)); } else if ($.isPlainObject(config.draggables) === true) { - if (processDraggable(config.draggables) !== true) { - returnStatus = false; + if (processDraggable(state, config.draggables) !== true) { + state.config.errors += 1; } } else { logme('ERROR: The type of config.draggables is no supported.'); - returnStatus = false; + state.config.errors += 1; } + } - if (typeof config.base_image === 'string') { - state.config.base_image = config.base_image; + function getBaseImage(state, config) { + if (config.hasOwnProperty('base_image') === false) { + logme('ERROR: "config" does not have a property "base_image".'); + state.config.errors += 1; + } else if (typeof config.base_image === 'string') { + state.config.baseImage = config.base_image; } else { logme('ERROR: Property config.base_image is not of type "string".'); - returnStatus = false; + state.config.errors += 1; } + } - if ($.isArray(config.targets) === true) { + function getTargets(state, config) { + if (config.hasOwnProperty('targets') === false) { + // It is possible that no "targets" were specified. This is not an error. + // In this case the default value of "[]" (empty array) will be used. + // Draggables can be positioned anywhere on the image, and the server will + // get an answer in the form of (x, y) coordinates for each draggable. + } else if ($.isArray(config.targets) === true) { (function (i) { while (i < config.targets.length) { - if (processTarget(config.targets[i]) !== true) { - returnStatus = false; + if (processTarget(state, config.targets[i]) !== true) { + state.config.errors += 1; } i += 1; } }(0)); } else if ($.isPlainObject(config.targets) === true) { - if (processTarget(config.targets) !== true) { - returnStatus = false; + if (processTarget(state, config.targets) !== true) { + state.config.errors += 1; } - } else if (typeof config.targets !== 'undefined') { + } else { logme('ERROR: Property config.targets is not of a supported type.'); - returnStatus = false; + state.config.errors += 1; } + } - if (typeof config.one_per_target === 'string') { + function getOnePerTarget(state, config) { + if (config.hasOwnProperty('one_per_target') === false) { + logme('ERROR: "config" does not have a property "one_per_target".'); + state.config.errors += 1; + } else if (typeof config.one_per_target === 'string') { if (config.one_per_target.toLowerCase() === 'true') { - state.config.one_per_target = true; + state.config.onePerTarget = true; } else if (config.one_per_target.toLowerCase() === 'false') { - state.config.one_per_target = false; + state.config.onePerTarget = false; } else { logme('ERROR: Property config.one_per_target can either be "true", or "false".'); - returnStatus = false; + state.config.errors += 1; } - } else if (typeof config.one_per_target !== 'undefined') { + } else { logme('ERROR: Property config.one_per_target is not of a supported type.'); - returnStatus = false; + state.config.errors += 1; } + } - if (typeof config.target_outline === 'string') { + function getTargetOutline(state, config) { + if (config.hasOwnProperty('target_outline') === false) { + // It is possible that no "target_outline" was specified. This is not an error. + // In this case the default value of 'true' (boolean) will be used. + } else if (typeof config.target_outline === 'string') { if (config.target_outline.toLowerCase() === 'true') { state.config.targetOutline = true; } else if (config.target_outline.toLowerCase() === 'false') { state.config.targetOutline = false; } else { logme('ERROR: Property config.target_outline can either be "true", or "false".'); - returnStatus = false; + state.config.errors += 1; } - } else if (typeof config.target_outline !== 'undefined') { + } else { logme('ERROR: Property config.target_outline is not of a supported type.'); - returnStatus = false; + state.config.errors += 1; } + } - state.config.labelBgColor = '#d6d6d6'; - if (typeof config.label_bg_color === 'string') { + function getLabelBgColor(state, config) { + if (config.hasOwnProperty('label_bg_color') === false) { + // It is possible that no "label_bg_color" was specified. This is not an error. + // In this case the default value of '#d6d6d6' (string) will be used. + } else if (typeof config.label_bg_color === 'string') { state.config.labelBgColor = config.label_bg_color; - } else if (typeof config.label_bg_color !== 'undefined') { + } else { logme('ERROR: Property config.label_bg_color is not of a supported type.'); returnStatus = false; } + } + function setIndividualTargets(state) { if (state.config.targets.length === 0) { - state.individualTargets = false; + state.config.individualTargets = false; } else { - state.individualTargets = true; + state.config.individualTargets = true; + } + } + + function processDraggable(state, obj) { + if (!attrIsString(obj, 'id')) { + return false; + } + + if (!attrIsString(obj, 'icon')) { + return false; + } + if (!attrIsString(obj, 'label')) { + return false; + } + + state.config.draggables.push(obj); + + return true; + } + + function processTarget(state, obj) { + if (!attrIsString(obj, 'id')) { + return false; + } + + if (!attrIsInteger(obj, 'w')) { + return false; + } + if (!attrIsInteger(obj, 'h')) { + return false; + } + + if (!attrIsInteger(obj, 'x')) { + return false; + } + if (!attrIsInteger(obj, 'y')) { + return false; + } + + state.config.targets.push(obj); + + return true; + } + + function attrIsString(obj, attr) { + if (typeof obj[attr] !== 'string') { + logme('ERROR: Attribute "obj.' + attr + '" is not a string.'); + + return false; } return true; + } - function processDraggable(obj) { - if (!attrIsString(obj, 'id')) { return false; } + function attrIsInteger(obj, attr) { + var tempInt; - if (!attrIsString(obj, 'icon')) { return false; } - if (!attrIsString(obj, 'label')) { return false; } + tempInt = parseInt(obj[attr], 10); - state.config.draggables.push(obj); + if (isFinite(tempInt) === false) { + logme('ERROR: Attribute "obj.' + attr + '" is not an integer.'); - true; + return false; } - function processTarget(obj) { - if (!attrIsString(obj, 'id')) { return false; } + obj[attr] = tempInt; - if (!attrIsInteger(obj, 'w')) { return false; } - if (!attrIsInteger(obj, 'h')) { return false; } - - if (!attrIsInteger(obj, 'x')) { return false; } - if (!attrIsInteger(obj, 'y')) { return false; } - - state.config.targets.push(obj); - - true; - - } - - function attrIsString(obj, attr) { - if (typeof obj[attr] !== 'string') { - logme('ERROR: Attribute "obj.' + attr + '" is not a string.'); - - return false; - } - - return true; - } - - function attrIsInteger(obj, attr) { - var tempInt; - - tempInt = parseInt(obj[attr], 10); - - if (isFinite(tempInt) === false) { - logme('ERROR: Attribute "obj.' + attr + '" is not an integer.'); - - return false; - } - - obj[attr] = tempInt; - - return true; - } + return true; } }); diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 5065e7193b..4c4585d121 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -10,8 +10,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { }; function init(state) { - logme('Draggables.init; state = ', state); - state.draggables = []; state.numDraggablesInSlider = 0; state.currentMovingDraggable = null; @@ -65,13 +63,137 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } + function moveDraggableToXY(newPosition) { + var self, offset; + + if (this.hasLoaded === false) { + self = this; + + setTimeout(function () { + self.moveDraggableToXY(newPosition); + }, 50); + + return; + } + + offset = 0; + if (this.state.config.targetOutline === true) { + offset = 1; + } + + this.inContainer = false; + this.containerEl.hide(); + + this.iconEl.detach(); + this.iconEl.css('background-color', this.iconElBGColor); + this.iconEl.css('padding-left', this.iconElPadding); + this.iconEl.css('padding-right', this.iconElPadding); + this.iconEl.css('border', this.iconElBorder); + this.iconEl.css('width', this.iconWidth); + this.iconEl.css('height', this.iconHeight); + this.iconEl.css( + 'left', + newPosition.x - this.iconWidth * 0.5 + offset - this.iconElLeftOffset + ); + this.iconEl.css( + 'top', + newPosition.y - this.iconHeight * 0.5 + offset + ); + this.iconEl.appendTo(this.state.baseImageEl.parent()); + + if (this.labelEl !== null) { + this.labelEl.detach(); + this.labelEl.css('background-color', this.state.config.labelBgColor); + this.labelEl.css('padding-left', 8); + this.labelEl.css('padding-right', 8); + this.labelEl.css('border', '1px solid black'); + this.labelEl.css( + 'left', + newPosition.x - this.labelWidth * 0.5 + offset - 9 // Account for padding, border. + ); + this.labelEl.css( + 'top', + newPosition.y - this.iconHeight * 0.5 + this.iconHeight + 5 + offset + ); + this.labelEl.appendTo(this.state.baseImageEl.parent()); + } + + this.x = newPosition.x; + this.y = newPosition.y; + + this.state.numDraggablesInSlider -= 1; + this.state.updateArrowOpacity(); + } + + function moveDraggableToTarget(target) { + var self, offset; + + if (this.hasLoaded === false) { + self = this; + + setTimeout(function () { + self.moveDraggableToTarget(target); + }, 50); + + return; + } + + offset = 0; + if (this.state.config.targetOutline === true) { + offset = 1; + } + + this.inContainer = false; + this.containerEl.hide(); + + this.iconEl.detach(); + this.iconEl.css('background-color', this.iconElBGColor); + this.iconEl.css('padding-left', this.iconElPadding); + this.iconEl.css('padding-right', this.iconElPadding); + this.iconEl.css('border', this.iconElBorder); + this.iconEl.css('width', this.iconWidth); + this.iconEl.css('height', this.iconHeight); + this.iconEl.css( + 'left', + target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset + ); + this.iconEl.css( + 'top', + target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset + ); + this.iconEl.appendTo(this.state.baseImageEl.parent()); + + if (this.labelEl !== null) { + this.labelEl.detach(); + this.labelEl.css('background-color', this.state.config.labelBgColor); + this.labelEl.css('padding-left', 8); + this.labelEl.css('padding-right', 8); + this.labelEl.css('border', '1px solid black'); + this.labelEl.css( + 'left', + target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9 // Account for padding, border. + ); + this.labelEl.css( + 'top', + target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset + ); + this.labelEl.appendTo(this.state.baseImageEl.parent()); + } + + this.onTarget = target; + target.draggable.push(this.id); + + if (target.numTextEl !== null) { + target.updateNumTextEl(); + } + + this.state.numDraggablesInSlider -= 1; + this.state.updateArrowOpacity(); + } + function processDraggable(state, obj, objIndex) { var draggableObj; - logme('processDraggable; state = ', state); - - logme('Processing draggable #' + objIndex); - draggableObj = { 'zIndex': objIndex, 'oldZIndex': objIndex, @@ -91,7 +213,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'checkIfOnTarget': checkIfOnTarget, 'snapToTarget': snapToTarget, 'correctZIndexes': correctZIndexes, - 'moveBackToSlider': moveBackToSlider + 'moveBackToSlider': moveBackToSlider, + + 'moveDraggableToTarget': moveDraggableToTarget, + 'moveDraggableToXY': moveDraggableToXY }; draggableObj.containerEl = $( @@ -334,7 +459,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.iconHeight * 0.5 ); this.iconEl.appendTo( - state.baseImageEl.parent() + this.state.baseImageEl.parent() ); if (this.labelEl !== null) { @@ -447,7 +572,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.mousePressed = false; positionIE = this.iconEl.position(); - if (this.state.individualTargets === true) { + if (this.state.config.individualTargets === true) { if (this.checkIfOnTarget(positionIE) === true) { this.correctZIndexes(); } else { @@ -486,7 +611,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { } this.state.updateArrowOpacity(); - updateInput(this.state); + updateInput.update(this.state); } function removeObjIdFromTarget() { @@ -532,7 +657,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { // (with an ID different from the one we are checking // against), then go to next target. if ( - (this.state.config.one_per_target === true) && + (this.state.config.onePerTarget === true) && (target.draggable.length === 1) && (target.draggable[0] !== this.id) ) { @@ -652,7 +777,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { function correctZIndexes() { var draggablesInMe, c1, c2, highestZIndex; - if (this.state.individualTargets === true) { + if (this.state.config.individualTargets === true) { if (this.onTarget.draggable.length > 0) { draggablesInMe = []; diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 6b1164b6df..900ea34e15 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -65,11 +65,11 @@ define( Scroller(state); Draggables.init(state); - logme('After Draggables.init(state); state = ', state); - // Update the input element, checking first that it is not filled with // an answer from the server. - updateInput(state, true); + if (updateInput.check(state) === false) { + updateInput.update(state); + } }()); } }); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index af27c186f2..dbec5e199d 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -45,7 +45,7 @@ define(['logme'], function (logme) { event.preventDefault(); }); - if (state.config.one_per_target === false) { + if (state.config.onePerTarget === false) { numTextEl = $( '
Date: Wed, 16 Jan 2013 16:46:15 +0200 Subject: [PATCH 112/140] update docs --- docs/source/drag_and_drop_input.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index 1e5e6bf468..f98f4c99d6 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -110,7 +110,8 @@ draggables, targets and rule. For example:: Draggables is list of draggables id. Target is list of targets id, draggables must be dragged to with considering rule. Rule is string. -Draggables in dicts inside correct_answer list must not intersect. +Draggables in dicts inside correct_answer list must not intersect!!! + Wrong (for draggable id 7):: correct_answer = [ @@ -209,6 +210,8 @@ you sould use 'anyof+number' of 'unordered_equal+number' rule:: In case if we have no multiple draggables per targets (one_per_target="true"), for same number of draggables, anyof is equal to unordered_equal +If we have can_reuse=true, than one must use only long form of correct answer. + Example ======= From 4215f4f92307dd8a47c2be36437d12a30aafd6c1 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:46:28 +0200 Subject: [PATCH 113/140] add and update tests --- .../capa/capa/verifiers/tests_draganddrop.py | 75 ++++++++++++++++--- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/common/lib/capa/capa/verifiers/tests_draganddrop.py b/common/lib/capa/capa/verifiers/tests_draganddrop.py index cb9fef1865..f708413c46 100644 --- a/common/lib/capa/capa/verifiers/tests_draganddrop.py +++ b/common/lib/capa/capa/verifiers/tests_draganddrop.py @@ -319,17 +319,17 @@ class Test_DragAndDrop_Grade(unittest.TestCase): { 'draggables': ['a'], 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }, { 'draggables': ['b'], 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }, { 'draggables': ['c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal' + 'rule': 'unordered_equal' }] self.assertFalse(draganddrop.grade(user_input, correct_answer)) @@ -406,7 +406,7 @@ class Test_DragAndDrop_Grade(unittest.TestCase): self.assertFalse(draganddrop.grade(user_input, correct_answer)) def test_label_10_targets_with_a_b_c_reused(self): - """Test reusable draggables (no mupltiple draggables per target)""" + """Test a b c in 10 labels reused""" user_input = '{"draggables":[{"a":"target1"}, \ {"b":"target2"},{"c":"target3"},{"b":"target5"}, \ {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ @@ -415,22 +415,22 @@ class Test_DragAndDrop_Grade(unittest.TestCase): { 'draggables': ['a', 'a'], 'targets': ['target1', 'target10'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }, { 'draggables': ['b', 'b', 'b'], 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }, { 'draggables': ['c', 'c', 'c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }] self.assertTrue(draganddrop.grade(user_input, correct_answer)) def test_label_10_targets_with_a_b_c_reused_false(self): - """Test reusable draggables (no mupltiple draggables per target)""" + """Test a b c in 10 labels reused false""" user_input = '{"draggables":[{"a":"target1"}, \ {"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\ {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ @@ -439,20 +439,73 @@ class Test_DragAndDrop_Grade(unittest.TestCase): { 'draggables': ['a', 'a'], 'targets': ['target1', 'target10'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }, { 'draggables': ['b', 'b', 'b'], 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }, { 'draggables': ['c', 'c', 'c'], 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unorderly_equal+number' + 'rule': 'unordered_equal+number' }] self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def test_mixed_reuse_and_not_reuse(self): + """Test reusable draggables """ + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"}, {"a":"target4"},\ + {"a":"target5"}]}' + correct_answer = [ + { + 'draggables': ['a', 'b'], + 'targets': ['target1', 'target2', 'target4', 'target5'], + 'rule': 'anyof' + }, + { + 'draggables': ['c'], + 'targets': ['target3'], + 'rule': 'exact' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_mixed_reuse_and_not_reuse_number(self): + """Test reusable draggables with number """ + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"}, {"a":"target4"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a', 'b'], + 'targets': ['target1', 'target2', 'target4'], + 'rule': 'anyof+number' + }, + { + 'draggables': ['c'], + 'targets': ['target3'], + 'rule': 'exact' + }] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + + def test_mixed_reuse_and_not_reuse_number_false(self): + """Test reusable draggables with numbers, but wrong""" + user_input = '{"draggables":[{"a":"target1"}, \ + {"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]}' + correct_answer = [ + { + 'draggables': ['a', 'a', 'b'], + 'targets': ['target1', 'target2', 'target4', 'target10'], + 'rule': 'anyof_number' + }, + { + 'draggables': ['c'], + 'targets': ['target3'], + 'rule': 'exact' + }] + self.assertFalse(draganddrop.grade(user_input, correct_answer)) + + class Test_DragAndDrop_Populate(unittest.TestCase): def test_1(self): From d231b9ed048c07169e69fc0467baea192440b4ab Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 16:54:52 +0200 Subject: [PATCH 114/140] update docs --- docs/source/drag_and_drop_input.rst | 6 ++++++ docs/source/draganddrop_logic_flow.png | Bin 0 -> 305578 bytes 2 files changed, 6 insertions(+) create mode 100644 docs/source/draganddrop_logic_flow.png diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index f98f4c99d6..5f42063740 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -212,6 +212,12 @@ for same number of draggables, anyof is equal to unordered_equal If we have can_reuse=true, than one must use only long form of correct answer. +Logic flow +---------- + +.. image:: draganddrop_logic_flow.png + + Example ======= diff --git a/docs/source/draganddrop_logic_flow.png b/docs/source/draganddrop_logic_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..5708d3628477e989d11c228ad04b80e987a1591a GIT binary patch literal 305578 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 z z40_!E_<^cZ>`X$gkv4(rJh~2mAn@Poosod^bsgPX8BhAE9hosz{VUMdW*p3HebA8~ z+&%2E+~>CQY>}yTH5jB;))i`C;4z#Kaq$8KoN^i>-)Jw$_FBZ^Foka=1egNI zmjCmyK|Ifr@IqId09V>}es?87D1b8<=`Jcmf^Gg}eF!_(_s%<6e|yI3&4m8y44pXq z58+@BK`2+@siv}m4b;5chcyjO?7IR14+6V38Z(&aJAJ;VMd|2G^gz}AcHRDr?Yd-6^_3-W%`0bK2h3>{6-YhyWAMn|L6iVU_=xo9pSCLc21-iR+@1jg}{o*wRvOb(bziWwed6mGZ^Ayuw!(4O6{X0 zIS>E1z_!<;oS2Hqoe=M$N&bFZtVQ)p%j#O`xZ>Y~RI~nCrmMadwE|GG$*Bj739LD@E{O1?+JJ^+s`r+(1S6s0I170=BsieyO#TaMqfEpHjQf z&t2+DP>fsg(Kj9dcB-Eb$U|UkV*AFl4dVJIR?xDfV!^SrI{}F6oNI6Y3QlyFpCUFqyih3r*(82U|-5D?zH0#d)m#ia1%)8YC)O17HJ%vi>>mr zJFC-}absSUZQxLxf2nBGm}`#p$rn$92AFfr8Y(6I=9svnXQ`m9-?VgA<^4@lU!!zA zO=xPZuABe3YsJ&VXb9o|3DO9x@$`&zHTKQ-Gvu>#P3+qD3Sd(+m&5An>Uo{It2bDX zEDWaH*Da_M7wnC{;~LhPQ>l?c*-UqvaPt|V8nq#GapUSWiz~Ymme$DJg+#Q*Tf{v@ z5S*sM+V#eseFY??eMzb_KcBRBmIhZ&UtQoO%=Sd%q+C_Xd=+^yqUcxSx3Mr`lhQ@? zszXy`q@VP&tImSa$pI-rz9RWcaI zg%G^2&CxkJ-gfAMF=l%e2-z1Dj+zm8hq--_;mx*UJV`PR)NGhLp);lzd^LTTwO`q^ zV`S((X{hTKZbAu+^A^0|^>bZ6-!s6u90+&8=07ArV|F+|@J-U~sF;rN+lOO#0P4k* zW23n#Y1uwT%f`liL;W|TLYoKt z`v-kwNna~{qQ7;S-htxU^+&4YkMtLB-oH<{-%*5$M2nU6s^(4VhlsV1Sw%eQl)F}7 z7yDJQ-`?fkf;IaUz&2pYlRY5=aouCvL!-qZrG{e>EX;se61>5)WSNjwsbM9&Hk)iwXXh3j?{X4PN`dPxgT?FAuDYEdYx}m_=tPs!z20aPt1cM*VelV zyX$_=H%cXxo~a-8&1+kwt=k0yT1K6#b_R`o0p78hh6{rw9doO$XjvbG6RS$#!6^#e zLX;Enk~wk?P6kTB_arxnGCtEycspPfp!aA;!*=)dX%>mGYHgiGKG?H%xb%?9$6Ooh zj%a;blI^z$pI8?LTaemG2uGJDg#=cdf}z0tHU$Ml%xuv{rwe~~0dHl-?w{P`5VQMq zmDm85lRdM`rDg}>q=Ss@u6av1=KV*$siuXA!HULo3TMv*kzM=W@Shw)ZdDB)V^m1( zqyY%#P43o|rUr~3H z{sR9&EhjH_C^ArMsX9k6%0?DV(S+oW;v(F@^+8)m{VJSz<#z1*;%k(^(gY8<#H#={ z>*!z>x2lnhP+nmzpTHGVoj^(B($%VTeWeQ9UIsB*_u_>|8^K@|Mn;JM=>=4J0Oekx zP!jpzN$Z*Kl}Lh>_A}{Na{?=gV(LHs?o^I4tsZVB%fCDbrGD~xdQ$2hb~;1oy5iCN zDh}TH(8U=P-Ml#4L?2VWQ^2n>EM21WULHa3`dC2e#ze=X&8K7{TPG_&erL3+VU6mC zC~s9=;xB!43O~zG8FF1N8cX3oqV=rnj?<9_iCX&<8opu*4>7*70!;5awJAaMLDGc^ zJ;B>QtcjHZ4qqbY4ja`{rM-zefrWHhQE|S7b*`G4{g{~7v2g6fY@_~!6)UFy76jHYD=7ru&idss-QD=Xok)h6;9@fx zN=8+c#|jRSA0G>9q&^+s4K3`8*WPLqlg4O~g4heY0(sB)vE?Dg0&#W0ZHv@0(&_!W zb5}K_Pxoe??sGB?c2z1yDdkg{9vVNqQ%SYTh@V`Y+hE68H&U~jU#`S1*-3jH_?OE; zdPD=*7@Bxtq2sqk0uAD?rL?x&BcyNbgmybU&HUaO15Sf zx7)qHq%H`VmSyI$-7R^0a$@$QrRaS#eUclu5?M%cKdht)dP7BVtQf<$eKSJo8-ElpVJ@Khn1)?T}|nl<8tQg@wwCr4GrI9vxa z-|lDp1ilh0e)kvVnn~*#!02I^_D`eUu9z9{H{%Lnu)2tMg6Z&P{s7zC=bb5s3Px|o z$k;}TKi^iF<<5eX3l2DX+3LR4=*Z0Wk{xpz*(D7hzvm1(Yd|cu;X{aqnRcJN68Z! z+tbtE62mpE?WUYrpVjJq2H}#e7Ff@o8(9;}Kbz$-kELa0kuDG_kgldTH!7E-98k>X zpm6c(bX!OsxjfyLJLnqq=Anl2vIg4!H1GSGT!13Wv9%0D<d6CRbS+ugg(qpRHDdc`1P7>y4P22F$ZhZ7r5> z*EQ$Aop>C*dz6Z(#CUuF0mnS+4fboJ-B9Qo&^3(z=w!OiG@QKln9s*0$V)pd%j
u$iKtrCg!%A524a5^j0^35jZ8z!T&*Gkt%#HI>< zKYz+u-qGb{Qitkj(F~mzW|ru@dDJ+(S!3z>E-13n`DC4GzZ)%%I{3p_B5Yf5N^ZCm zt`f%)xW3nq>!Q4YG(k;|QAlm31D@fxx9}18poeC|NwLaVQ4+Lur^vM~3>SZYVn=0A zCF6*`FYOqxC->#}{=~sXnv7tDmIK0|`d51#1`x-eOuX{q9HPRsMQ0(J_$_)$ODq

fR{-p~ZKz@@2`DK>-H{H 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 Date: Wed, 16 Jan 2013 16:59:00 +0200 Subject: [PATCH 115/140] Refactoring. --- common/static/js/capa/drag_and_drop/draggables.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 4c4585d121..a78d336879 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -739,27 +739,21 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.iconEl.css( 'left', - target.offset.left + 0.5 * target.w - - this.iconWidth * 0.5 + offset - - this.iconElLeftOffset + target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset ); this.iconEl.css( 'top', - target.offset.top + 0.5 * target.h - - this.iconHeight * 0.5 + offset + 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. + target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9 // Acoount for padding, border. ); this.labelEl.css( 'top', - target.offset.top + 0.5 * target.h + - this.iconHeight * 0.5 + 5 + offset + target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset ); } } From 35174dd36c73e3645b2d0ba2818410f86d25626f Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Wed, 16 Jan 2013 17:29:17 +0200 Subject: [PATCH 116/140] resised picture --- docs/source/drag_and_drop_input.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index 5f42063740..fac75368b2 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -216,6 +216,7 @@ Logic flow ---------- .. image:: draganddrop_logic_flow.png + :scale: 36 % Example From f15887383e7ef0aba612f889c10815f9e05b5a1c Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 16 Jan 2013 18:08:40 +0200 Subject: [PATCH 117/140] Refactoring. --- .../js/capa/drag_and_drop/config_parser.js | 45 ++++++++++++++++++- .../js/capa/drag_and_drop/draggables.js | 20 +++++---- common/static/js/capa/drag_and_drop/main.js | 4 ++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index b154a81826..536430101d 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -163,10 +163,15 @@ define(['logme'], function (logme) { if (!attrIsString(obj, 'icon')) { return false; } + if (!attrIsString(obj, 'label')) { return false; } + if (!attrIsBoolean(obj, 'can_reuse', false)) { + return false; + } + state.config.draggables.push(obj); return true; @@ -197,7 +202,11 @@ define(['logme'], function (logme) { } function attrIsString(obj, attr) { - if (typeof obj[attr] !== 'string') { + if (obj.hasOwnProperty(attr) === false) { + logme('ERROR: Attribute "obj.' + attr + '" is not present.'); + + return false; + } else if (typeof obj[attr] !== 'string') { logme('ERROR: Attribute "obj.' + attr + '" is not a string.'); return false; @@ -209,6 +218,12 @@ define(['logme'], function (logme) { function attrIsInteger(obj, attr) { var tempInt; + if (obj.hasOwnProperty(attr) === false) { + logme('ERROR: Attribute "obj.' + attr + '" is not present.'); + + return false; + } + tempInt = parseInt(obj[attr], 10); if (isFinite(tempInt) === false) { @@ -221,6 +236,34 @@ define(['logme'], function (logme) { return true; } + + function attrIsBoolean(obj, attr, defaultVal) { + if (obj.hasOwnProperty(attr) === false) { + if (defaultVal === undefined) { + logme('ERROR: Attribute "obj.' + attr + '" is not present.'); + + return false; + } else { + obj[attr] = defaultVal; + + return true; + } + } + + if (obj[attr] === '') { + obj[attr] = defaultVal; + } else if ((obj[attr] === 'false') || (obj[attr] === false)) { + obj[attr] = false; + } else if ((obj[attr] === 'true') || (obj[attr] === true)) { + obj[attr] = true; + } else { + logme('ERROR: Attribute "obj.' + attr + '" is not a boolean.'); + + return false; + } + + return true; + } }); // End of wrapper for RequireJS. As you can see, we are passing diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index a78d336879..add445ebb0 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -195,6 +195,13 @@ define(['logme', 'update_input'], function (logme, updateInput) { var draggableObj; draggableObj = { + 'id': obj.id, + + 'isReusable': obj.can_reuse, + + 'x': -1, + 'y': -1, + 'zIndex': objIndex, 'oldZIndex': objIndex, 'labelEl': null, @@ -373,12 +380,15 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'top', 50 - draggableObj.iconHeightSmall * 0.5 ); + + draggableObj.hasLoaded = true; } else { // If no icon and no label, don't create a draggable. return; } } + // Attach events to "iconEl". draggableObj.iconEl.mousedown(function (event) { draggableObj.mouseDown.call(draggableObj, event); }); @@ -389,6 +399,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.mouseMove.call(draggableObj, event); }); + // Attach events to "containerEl". draggableObj.containerEl.mousedown(function (event) { draggableObj.mouseDown.call(draggableObj, event); }); @@ -399,16 +410,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.mouseMove.call(draggableObj, event); }); - draggableObj.id = obj.id; - draggableObj.x = -1; - draggableObj.y = -1; - state.numDraggablesInSlider += 1; - - if (obj.icon.length === 0) { - draggableObj.hasLoaded = true; - } - state.draggables.push(draggableObj); } diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 900ea34e15..7eb4f5101e 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -70,6 +70,10 @@ define( if (updateInput.check(state) === false) { updateInput.update(state); } + + setTimeout(function () { + logme('state.draggables', state.draggables); + }, 500); }()); } }); From 98977cabdb93dacdd34fd3262495af495044f5c1 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 17 Jan 2013 16:36:15 +0200 Subject: [PATCH 118/140] Refactoring of Drag and Drop complete. Draggable properties isUsable, isOriginal included, but functionality connected with them is not complete. Found and fixed bug with z-index ordering. --- common/static/js/capa/drag_and_drop.js | 2 +- .../js/capa/drag_and_drop/base_image.js | 9 +- .../js/capa/drag_and_drop/config_parser.js | 46 +-- .../js/capa/drag_and_drop/draggables.js | 331 +++++------------- common/static/js/capa/drag_and_drop/main.js | 16 +- common/static/js/capa/drag_and_drop/state.js | 17 +- .../static/js/capa/drag_and_drop/targets.js | 256 +++++++------- .../js/capa/drag_and_drop/update_input.js | 6 +- 8 files changed, 276 insertions(+), 407 deletions(-) diff --git a/common/static/js/capa/drag_and_drop.js b/common/static/js/capa/drag_and_drop.js index ce6f4c4559..2a9c2e4011 100644 --- a/common/static/js/capa/drag_and_drop.js +++ b/common/static/js/capa/drag_and_drop.js @@ -5,7 +5,7 @@ (function (requirejs, require, define) { requirejs.config({ - 'baseUrl': '/static/js/capa/drag_and_drop/', + 'baseUrl': '/static/js/capa/drag_and_drop/' }); // The current JS file will be loaded and run each time. It will require a diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index cd74267690..d818804be2 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -24,10 +24,7 @@ define(['logme'], function (logme) { state.baseImageEl = $(''); - state.baseImageEl.attr( - 'src', - state.config.baseImage - ); + state.baseImageEl.attr('src', state.config.baseImage); state.baseImageEl.load(function () { baseImageElContainer.css('width', this.width); baseImageElContainer.css('height', this.height); @@ -42,9 +39,7 @@ define(['logme'], function (logme) { state.baseImageLoaded = true; }); state.baseImageEl.error(function () { - logme( - 'ERROR: Image "' + state.config.baseImage + '" was not found!' - ); + logme('ERROR: Image "' + state.config.baseImage + '" was not found!'); baseImageElContainer.html( '' + 'ERROR: Image "' + state.config.baseImage + '" was not found!' + diff --git a/common/static/js/capa/drag_and_drop/config_parser.js b/common/static/js/capa/drag_and_drop/config_parser.js index 536430101d..e6c1e4d3c1 100644 --- a/common/static/js/capa/drag_and_drop/config_parser.js +++ b/common/static/js/capa/drag_and_drop/config_parser.js @@ -7,17 +7,15 @@ define(['logme'], function (logme) { return configParser; - function configParser(config, state) { + function configParser(state, config) { state.config = { 'draggables': [], 'baseImage': '', 'targets': [], - 'onePerTarget': null, + 'onePerTarget': null, // Specified by user. No default. 'targetOutline': true, 'labelBgColor': '#d6d6d6', - - 'individualTargets': null, - + 'individualTargets': null, // Depends on 'targets'. 'errors': 0 // Number of errors found while parsing config. }; @@ -156,19 +154,13 @@ define(['logme'], function (logme) { } function processDraggable(state, obj) { - if (!attrIsString(obj, 'id')) { - return false; - } + if ( + (attrIsString(obj, 'id') === false) || + (attrIsString(obj, 'icon') === false) || + (attrIsString(obj, 'label') === false) || - if (!attrIsString(obj, 'icon')) { - return false; - } - - if (!attrIsString(obj, 'label')) { - return false; - } - - if (!attrIsBoolean(obj, 'can_reuse', false)) { + (attrIsBoolean(obj, 'can_reuse', false) === false) + ) { return false; } @@ -178,21 +170,15 @@ define(['logme'], function (logme) { } function processTarget(state, obj) { - if (!attrIsString(obj, 'id')) { - return false; - } + if ( + (attrIsString(obj, 'id') === false) || - if (!attrIsInteger(obj, 'w')) { - return false; - } - if (!attrIsInteger(obj, 'h')) { - return false; - } + (attrIsInteger(obj, 'w') === false) || + (attrIsInteger(obj, 'h') === false) || - if (!attrIsInteger(obj, 'x')) { - return false; - } - if (!attrIsInteger(obj, 'y')) { + (attrIsInteger(obj, 'x') === false) || + (attrIsInteger(obj, 'y') === false) + ) { return false; } diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index add445ebb0..1946ed8033 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -10,13 +10,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { }; function init(state) { - state.draggables = []; - state.numDraggablesInSlider = 0; - state.currentMovingDraggable = null; - (function (c1) { while (c1 < state.config.draggables.length) { - processDraggable(state, state.config.draggables[c1], c1 + 1); + processDraggable(state, state.config.draggables[c1]); c1 += 1 } }(0)); @@ -121,6 +117,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.x = newPosition.x; this.y = newPosition.y; + this.zIndex = 1000; + this.correctZIndexes(); + this.state.numDraggablesInSlider -= 1; this.state.updateArrowOpacity(); } @@ -180,35 +179,49 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.labelEl.appendTo(this.state.baseImageEl.parent()); } - this.onTarget = target; - target.draggable.push(this.id); + target.addDraggable(this); - if (target.numTextEl !== null) { - target.updateNumTextEl(); - } + this.zIndex = 1000; + this.correctZIndexes(); this.state.numDraggablesInSlider -= 1; this.state.updateArrowOpacity(); } - function processDraggable(state, obj, objIndex) { + function processDraggable(state, obj) { var draggableObj; draggableObj = { 'id': obj.id, 'isReusable': obj.can_reuse, + 'isOriginal': true, 'x': -1, 'y': -1, - 'zIndex': objIndex, - 'oldZIndex': objIndex, + 'zIndex': 1, + + 'containerEl': null, + + 'iconEl': null, + 'iconElBGColor': null, + 'iconElPadding': null, + 'iconElBorder': null, + 'iconElLeftOffset': null, + 'iconWidth': null, + 'iconHeight': null, + 'iconWidthSmall': null, + 'iconHeightSmall': null, + 'labelEl': null, + 'labelWidth': null, + 'hasLoaded': false, 'inContainer': true, 'mousePressed': false, 'onTarget': null, + 'onTargetIndex': null, 'state': state, @@ -216,7 +229,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'mouseUp': mouseUp, 'mouseMove': mouseMove, 'checkLandingElement': checkLandingElement, - 'removeObjIdFromTarget': removeObjIdFromTarget, 'checkIfOnTarget': checkIfOnTarget, 'snapToTarget': snapToTarget, 'correctZIndexes': correctZIndexes, @@ -234,7 +246,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'display: inline; ' + 'float: left; ' + 'overflow: hidden; ' + - 'z-index: ' + objIndex + '; ' + 'border-left: 1px solid #CCC; ' + 'border-right: 1px solid #CCC; ' + 'text-align: center; ' + @@ -252,49 +263,28 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconElLeftOffset = 0; draggableObj.iconEl = $(''); - draggableObj.iconEl.attr( - 'src', - obj.icon - ); + draggableObj.iconEl.attr('src', obj.icon); draggableObj.iconEl.load(function () { draggableObj.iconWidth = this.width; draggableObj.iconHeight = this.height; if (draggableObj.iconWidth >= draggableObj.iconHeight) { draggableObj.iconWidthSmall = 60; - draggableObj.iconHeightSmall = - draggableObj.iconWidthSmall * - (draggableObj.iconHeight / draggableObj.iconWidth); + draggableObj.iconHeightSmall = draggableObj.iconWidthSmall * (draggableObj.iconHeight / draggableObj.iconWidth); } else { draggableObj.iconHeightSmall = 60; - draggableObj.iconWidthSmall = - draggableObj.iconHeightSmall * - (draggableObj.iconWidth / draggableObj.iconHeight); + draggableObj.iconWidthSmall = draggableObj.iconHeightSmall * (draggableObj.iconWidth / draggableObj.iconHeight); } draggableObj.iconEl.css('position', 'absolute'); - - draggableObj.iconEl.css( - 'width', - draggableObj.iconWidthSmall - ); - draggableObj.iconEl.css( - 'height', - draggableObj.iconHeightSmall - ); - - draggableObj.iconEl.css( - 'left', - 50 - draggableObj.iconWidthSmall * 0.5 - ); + draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); + draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); if (obj.label.length > 0) { draggableObj.iconEl.css('top', 5); } else { - draggableObj.iconEl.css( - 'top', - 50 - draggableObj.iconHeightSmall * 0.5 - ); + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); } draggableObj.iconEl.appendTo(draggableObj.containerEl); @@ -306,27 +296,16 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'position: absolute; ' + 'color: black; ' + 'font-size: 0.95em; ' + - 'z-index: ' + objIndex + '; ' + '" ' + '>' + obj.label + '

' ); - draggableObj.labelEl.appendTo( - draggableObj.containerEl - ); - + draggableObj.labelEl.appendTo(draggableObj.containerEl); draggableObj.labelWidth = draggableObj.labelEl.width(); - - draggableObj.labelEl.css( - 'left', - 50 - draggableObj.labelWidth * 0.5 - ); - draggableObj.labelEl.css( - 'top', - 5 + draggableObj.iconHeightSmall + 5 - ); + draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); + draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); draggableObj.labelEl.mousedown(function (event) { draggableObj.mouseDown.call(draggableObj, event); @@ -358,7 +337,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'position: absolute; ' + 'color: black; ' + 'font-size: 0.95em; ' + - 'z-index: ' + objIndex + '; ' + '" ' + '>' + obj.label + @@ -372,14 +350,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconWidthSmall = draggableObj.iconWidth; draggableObj.iconHeightSmall = draggableObj.iconHeight; - draggableObj.iconEl.css( - 'left', - 50 - draggableObj.iconWidthSmall * 0.5 - ); - draggableObj.iconEl.css( - 'top', - 50 - draggableObj.iconHeightSmall * 0.5 - ); + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); draggableObj.hasLoaded = true; } else { @@ -421,6 +393,11 @@ define(['logme', 'update_input'], function (logme, updateInput) { // potentially cause the highlghting of the dragged element. event.preventDefault(); + if ((this.isReusable === true) && (this.isOriginal === true)) { + + return; + } + // If this draggable is just being dragged out of the // container, we must perform some additional tasks. if (this.inContainer === true) { @@ -500,7 +477,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.state.numDraggablesInSlider -= 1; } - this.oldZIndex = this.zIndex; this.zIndex = 1000; this.iconEl.css('z-index', '1000'); if (this.labelEl !== null) { @@ -532,32 +508,21 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.iconEl.css( 'left', - event.pageX - - this.state.baseImageEl.offset().left - - this.iconWidth * 0.5 - - this.iconElLeftOffset + event.pageX - this.state.baseImageEl.offset().left - this.iconWidth * 0.5 - this.iconElLeftOffset ); this.iconEl.css( 'top', - event.pageY - - this.state.baseImageEl.offset().top - - this.iconHeight * 0.5 + 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. + event.pageX - this.state.baseImageEl.offset().left - this.labelWidth * 0.5 - 9 // Acoount for padding, border. ); this.labelEl.css( 'top', - event.pageY - - this.state.baseImageEl.offset().top + - this.iconHeight * 0.5 + - 5 + event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5 ); } } @@ -578,23 +543,20 @@ define(['logme', 'update_input'], function (logme, updateInput) { if (this.checkIfOnTarget(positionIE) === true) { this.correctZIndexes(); } else { + if (this.onTarget !== null) { + this.onTarget.removeDraggable(this); + } + this.moveBackToSlider(); - this.removeObjIdFromTarget(); this.state.numDraggablesInSlider += 1; } } else { if ( (positionIE.left < 0) || - ( - positionIE.left + this.iconWidth > - this.state.baseImageEl.width() - ) || + (positionIE.left + this.iconWidth > this.state.baseImageEl.width()) || (positionIE.top < 0) || - ( - positionIE.top + this.iconHeight > - this.state.baseImageEl.height() - ) + (positionIE.top + this.iconHeight > this.state.baseImageEl.height()) ) { this.moveBackToSlider(); @@ -605,10 +567,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } else { this.correctZIndexes(); - this.x = - positionIE.left + this.iconWidth * 0.5; - this.y = - positionIE.top + this.iconHeight * 0.5; + this.x = positionIE.left + this.iconWidth * 0.5; + this.y = positionIE.top + this.iconHeight * 0.5; } } @@ -616,26 +576,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { updateInput.update(this.state); } - function removeObjIdFromTarget() { - var c1; - - if (this.onTarget !== null) { - for (c1 = 0; c1 < this.onTarget.draggable.length; c1 += 1) { - if (this.onTarget.draggable[c1] === this.id) { - this.onTarget.draggable.splice(c1, 1); - - break; - } - } - - if (this.onTarget.numTextEl !== null) { - this.onTarget.updateNumTextEl(); - } - - this.onTarget = null; - } - } - // // Determine if a draggable, after it was relased, ends up on a // target. We do this by iterating over all of the targets, and @@ -647,9 +587,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { // this.iconEl.position() // function checkIfOnTarget(positionIE) { - var c1, target, targetFound; - - targetFound = false; + var c1, target; for (c1 = 0; c1 < this.state.targets.length; c1 += 1) { target = this.state.targets[c1]; @@ -660,75 +598,52 @@ define(['logme', 'update_input'], function (logme, updateInput) { // against), then go to next target. if ( (this.state.config.onePerTarget === true) && - (target.draggable.length === 1) && - (target.draggable[0] !== this.id) + (target.draggableList.length === 1) && + (target.draggableList[0].id !== this.id) ) { 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 - ) { + if (positionIE.top + this.iconHeight * 0.5 < target.offset.top) { continue; } - if ( - positionIE.top + this.iconHeight * 0.5 > - target.offset.top + target.h - ) { + if (positionIE.top + this.iconHeight * 0.5 > target.offset.top + target.h) { continue; } - if ( - positionIE.left + this.iconWidth * 0.5 < - target.offset.left - ) { + if (positionIE.left + this.iconWidth * 0.5 < target.offset.left) { continue; } - if ( - positionIE.left + this.iconWidth * 0.5 > - target.offset.left + target.w - ) { + if (positionIE.left + this.iconWidth * 0.5 > target.offset.left + target.w) { continue; } - // If we got here, then our draggable is on top of a - // target. - targetFound = true; - // If the draggable was moved from one target to - // another, then we need to remove it's ID from the + // 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.removeObjIdFromTarget(); - this.onTarget = target; - target.draggable.push(this.id); + 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) { - this.onTarget = target; - target.draggable.push(this.id); - } - - if (target.numTextEl !== null) { - target.updateNumTextEl(); + target.addDraggable(this); } // Reposition the draggable so that it's center // coincides with the center of the target. this.snapToTarget(target); - break; + // Target was found. + return true; } - return targetFound; + // Target was not found. + return false; } function snapToTarget(target) { @@ -771,101 +686,45 @@ define(['logme', 'update_input'], function (logme, updateInput) { // ordering of the visibility (z-index) of the other draggables // will not change. function correctZIndexes() { - var draggablesInMe, c1, c2, highestZIndex; + var c1, highestZIndex; + + highestZIndex = -10000; if (this.state.config.individualTargets === true) { - if (this.onTarget.draggable.length > 0) { - draggablesInMe = []; - - for (c1 = 0; c1 < this.onTarget.draggable.length; c1 += 1) { - for (c2 = 0; c2 < this.state.draggables.length; c2 += 1) { - if ( - this.onTarget.draggable[c1] === - this.state.draggables[c2].id - ) { - draggablesInMe.push(this.state.draggables[c2]); - } - } - } - - highestZIndex = -10000; - - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { + if (this.onTarget.draggableList.length > 0) { + for (c1 = 0; c1 < this.onTarget.draggableList.length; c1 += 1) { if ( - (draggablesInMe[c1].zIndex > highestZIndex) && - (draggablesInMe[c1].zIndex !== 1000) + (this.onTarget.draggableList[c1].zIndex > highestZIndex) && + (this.onTarget.draggableList[c1].zIndex !== 1000) ) { - highestZIndex = draggablesInMe[c1].zIndex; - } - } - - if (highestZIndex === -10000) { - highestZIndex = this.onTarget.draggable.length; - } else if (highestZIndex < this.oldZIndex) { - highestZIndex = this.oldZIndex; - } else { - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - draggablesInMe[c1].zIndex -= 1; - draggablesInMe[c1].oldZIndex -= 1; - - draggablesInMe[c1].iconEl.css( - 'z-index', - draggablesInMe[c1].zIndex - ); - if (draggablesInMe[c1].labelEl !== null) { - draggablesInMe[c1].labelEl.css( - 'z-index', - draggablesInMe[c1].zIndex - ); - } + highestZIndex = this.onTarget.draggableList[c1].zIndex; } } } else { - highestZIndex = this.onTarget.draggable.length; - } - - this.zIndex = highestZIndex; - this.oldZIndex = highestZIndex; - - this.iconEl.css( - 'z-index', - this.zIndex - ); - if (this.labelEl !== null) { - this.labelEl.css( - 'z-index', - this.zIndex - ); + highestZIndex = 0; } } else { for (c1 = 0; c1 < this.state.draggables.length; c1++) { - if ( - this.oldZIndex < - this.state.draggables[c1].zIndex - ) { - this.state.draggables[c1].zIndex -= 1; - this.state.draggables[c1].oldZIndex = this.state.draggables[c1].zIndex; - this.state.draggables[c1].iconEl.css( - 'z-index', - this.state.draggables[c1].zIndex - ); - - if (this.state.draggables[c1].labelEl !== null) { - this.state.draggables[c1].labelEl.css( - 'z-index', - this.state.draggables[c1].zIndex - ); + if (this.inContainer === false) { + if ( + (this.state.draggables[c1].zIndex > highestZIndex) && + (this.state.draggables[c1].zIndex !== 1000) + ) { + highestZIndex = this.state.draggables[c1].zIndex; } } } + } - this.zIndex = c1; - this.oldZIndex = c1; - this.iconEl.css('z-index', c1); + if (highestZIndex === -10000) { + highestZIndex = 0; + } - if (this.labelEl !== null) { - this.labelEl.css('z-index', c1); - } + this.zIndex = highestZIndex + 1; + + this.iconEl.css('z-index', highestZIndex); + if (this.labelEl !== null) { + this.labelEl.css('z-index', highestZIndex); } } diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 7eb4f5101e..e7b02dec47 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -5,10 +5,8 @@ (function (requirejs, require, define) { define( - ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', - 'draggables', 'targets', 'update_input'], - function (logme, State, configParser, Container, BaseImage, Scroller, - Draggables, Targets, updateInput) { + ['logme', 'state', 'config_parser', 'container', 'base_image', 'scroller', 'draggables', 'targets', 'update_input'], + function (logme, State, configParser, Container, BaseImage, Scroller, Draggables, Targets, updateInput) { return Main; function Main() { @@ -45,7 +43,7 @@ define( state = State(problemId); - if (configParser(config, state) !== true) { + if (configParser(state, config) !== true) { logme('ERROR: Could not make sense of the JSON configuration options.'); return; @@ -70,11 +68,11 @@ define( if (updateInput.check(state) === false) { updateInput.update(state); } - - setTimeout(function () { - logme('state.draggables', state.draggables); - }, 500); }()); + + setTimeout(function () { + logme('After 1000 ms:', state); + }, 1000); } }); diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index 99dd7dc8e4..d99b90872f 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -9,11 +9,24 @@ define([], function () { function State(problemId) { return { - 'problemId': problemId, + 'config': null, + 'baseImageEl': null, 'baseImageLoaded': false, - 'numDraggablesInSlider': 0 + 'containerEl': null, + + 'sliderEl': null, + + 'problemId': problemId, + + 'draggables': [], + 'numDraggablesInSlider': 0, + 'currentMovingDraggable': null, + + 'targets': [], + + 'updateArrowOpacity': null }; } }); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index dbec5e199d..40828ffba3 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -8,146 +8,166 @@ define(['logme'], function (logme) { return Targets; function Targets(state) { - var c1; + (function (c1) { + while (c1 < state.config.targets.length) { + processTarget(state, state.config.targets[c1]); - state.targets = []; + c1 += 1; + } + }(0)); + } // function Targets(state) { - for (c1 = 0; c1 < state.config.targets.length; c1++) { - processTarget(state.config.targets[c1]); + function processTarget(state, obj) { + var targetEl, borderCss, numTextEl, targetObj; + + borderCss = ''; + if (state.config.targetOutline === true) { + borderCss = 'border: 1px dashed gray; '; } - return; + targetEl = $( + '
' + ); + targetEl.appendTo(state.baseImageEl.parent()); + targetEl.mousedown(function (event) { + event.preventDefault(); + }); - function processTarget(obj) { - var targetEl, borderCss, numTextEl, targetObj; - - borderCss = ''; - if (state.config.targetOutline === true) { - borderCss = 'border: 1px dashed gray; '; - } - - targetEl = $( + if (state.config.onePerTarget === false) { + numTextEl = $( '
' + '>0
' ); - targetEl.appendTo(state.baseImageEl.parent()); - targetEl.mousedown(function (event) { + } else { + numTextEl = null; + } + + targetObj = { + 'id': obj.id, + + 'w': obj.w, + 'h': obj.h, + + 'el': targetEl, + 'offset': targetEl.position(), + + 'draggableList': [], + + 'targetEl': targetEl, + + 'numTextEl': numTextEl, + 'updateNumTextEl': updateNumTextEl, + + 'removeDraggable': removeDraggable, + 'addDraggable': addDraggable + }; + + if (state.config.onePerTarget === false) { + numTextEl.appendTo(state.baseImageEl.parent()); + numTextEl.mousedown(function (event) { event.preventDefault(); }); + numTextEl.mouseup(function () { + cycleDraggableOrder.call(targetObj) + }); + } - if (state.config.onePerTarget === false) { - numTextEl = $( - '
0
' - ); + state.targets.push(targetObj); + } + + function removeDraggable(draggable) { + this.draggableList.splice(draggable.onTargetIndex, 1); + + draggable.onTarget = null; + draggable.onTargetIndex = null; + + this.updateNumTextEl(); + } + + function addDraggable(draggable) { + draggable.onTarget = this; + draggable.onTargetIndex = this.draggableList.push(draggable) - 1; + + this.updateNumTextEl(); + } + + /* + * function cycleDraggableOrder + * + * Parameters: + * none - This function does not expect any parameters. + * + * Returns: + * undefined - The return value of this function is not used. + * + * Description: + * Go through all draggables that are on the current target, and decrease their + * z-index by 1, making sure that the bottom-most draggable ends up on the top. + */ + function cycleDraggableOrder() { + var c1, lowestZIndex, highestZIndex; + + if (this.draggableList.length === 0) { + return; + } + + highestZIndex = -10000; + lowestZIndex = 10000; + + for (c1 = 0; c1 < this.draggableList.length; c1 += 1) { + if (this.draggableList[c1].zIndex < lowestZIndex) { + lowestZIndex = this.draggableList[c1].zIndex; + } + + if (this.draggableList[c1].zIndex > highestZIndex) { + highestZIndex = this.draggableList[c1].zIndex; + } + } + + for (c1 = 0; c1 < this.draggableList.length; c1 += 1) { + if (this.draggableList[c1].zIndex === lowestZIndex) { + this.draggableList[c1].zIndex = highestZIndex; } else { - numTextEl = null; + this.draggableList[c1].zIndex -= 1; } - targetObj = { - 'id': obj.id, - - 'w': obj.w, - 'h': obj.h, - - 'el': targetEl, - 'offset': targetEl.position(), - - 'draggable': [], - - 'targetEl': targetEl, - - 'numTextEl': numTextEl, - 'updateNumTextEl': updateNumTextEl - }; - - if (state.config.onePerTarget === false) { - numTextEl.appendTo(state.baseImageEl.parent()); - numTextEl.mousedown(function (event) { - event.preventDefault(); - }); - numTextEl.mouseup(function () { - cycleDraggableOrder.call(targetObj) - }); - } - - state.targets.push(targetObj); - } - - function cycleDraggableOrder() { - var draggablesInMe, c1, c2, lowestZIndex, highestZIndex; - - if (this.draggable.length === 0) { - return 0; - } - - draggablesInMe = []; - - for (c1 = 0; c1 < this.draggable.length; c1 += 1) { - for (c2 = 0; c2 < state.draggables.length; c2 += 1) { - if (this.draggable[c1] === state.draggables[c2].id) { - draggablesInMe.push(state.draggables[c2]); - } - } - } - - highestZIndex = -10000; - lowestZIndex = 10000; - - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - if (draggablesInMe[c1].zIndex < lowestZIndex) { - lowestZIndex = draggablesInMe[c1].zIndex; - } - - if (draggablesInMe[c1].zIndex > highestZIndex) { - highestZIndex = draggablesInMe[c1].zIndex; - } - } - - for (c1 = 0; c1 < draggablesInMe.length; c1 += 1) { - if (draggablesInMe[c1].zIndex === lowestZIndex) { - draggablesInMe[c1].zIndex = highestZIndex; - draggablesInMe[c1].oldZIndex = highestZIndex; - } else { - draggablesInMe[c1].zIndex -= 1; - draggablesInMe[c1].oldZIndex -= 1; - } - - draggablesInMe[c1].iconEl.css('z-index', draggablesInMe[c1].zIndex); - if (draggablesInMe[c1].labelEl !== null) { - draggablesInMe[c1].labelEl.css('z-index', draggablesInMe[c1].zIndex); - } + this.draggableList[c1].iconEl.css('z-index', this.draggableList[c1].zIndex); + if (this.draggableList[c1].labelEl !== null) { + this.draggableList[c1].labelEl.css('z-index', this.draggableList[c1].zIndex); } } - } // function Targets(state) { + } function updateNumTextEl() { - this.numTextEl.html(this.draggable.length); + if (this.numTextEl !== null) { + this.numTextEl.html(this.draggableList.length); + } } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 6c2b72f6c2..e9c6222058 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -13,8 +13,6 @@ define(['logme'], function (logme) { function update(state) { var draggables, tempObj; - logme('state.problemId = ' + state.problemId); - draggables = []; if (state.config.individualTargets === false) { @@ -37,9 +35,9 @@ define(['logme'], function (logme) { (function (c1) { while (c1 < state.targets.length) { (function (c2) { - while (c2 < state.targets[c1].draggable.length) { + while (c2 < state.targets[c1].draggableList.length) { tempObj = {}; - tempObj[state.targets[c1].draggable[c2]] = state.targets[c1].id; + tempObj[state.targets[c1].draggableList[c2].id] = state.targets[c1].id; draggables.push(tempObj); tempObj = null; From 5001680356b38e1caec516321ec446d9d2e183ea Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Thu, 17 Jan 2013 18:26:12 +0200 Subject: [PATCH 119/140] Implemented isReusable feature. Not fully tested. --- .../js/capa/drag_and_drop/draggables.js | 329 +++++++++++++----- common/static/js/capa/drag_and_drop/main.js | 4 - .../js/capa/drag_and_drop/update_input.js | 11 +- 3 files changed, 257 insertions(+), 87 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 1946ed8033..aaa3001010 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -59,8 +59,156 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } + function makeDraggableCopy() { + var draggableObj, obj; + + draggableObj = { + 'originalConfigObj': this.originalConfigObj, + 'stateDraggablesIndex': /* this.stateDraggablesIndex */ null, // Will be set. + + 'id': this.id, + + 'isReusable': this.isReusable, + 'isOriginal': false, + + 'x': this.x, + 'y': this.y, + + 'zIndex': this.zIndex, + + // Not needed, since a copy will never return to a container element. + 'containerEl': null, + + 'iconEl': /* this.iconEl */ null, // Will be created. + 'iconElBGColor': this.iconElBGColor, + 'iconElPadding': this.iconElPadding, + 'iconElBorder': this.iconElBorder, + 'iconElLeftOffset': this.iconElLeftOffset, + 'iconWidth': this.iconWidth, + 'iconHeight': this.iconHeight, + 'iconWidthSmall': this.iconWidthSmall, + 'iconHeightSmall': this.iconHeightSmall, + + 'labelEl': /* this.labelEl */ null, // Will be created. + 'labelWidth': this.labelWidth, + + 'hasLoaded': this.hasLoaded, + 'inContainer': this.inContainer, + 'mousePressed': this.mousePressed, + 'onTarget': this.onTarget, + 'onTargetIndex': this.onTargetIndex, + + 'state': this.state, + + 'mouseDown': this.mouseDown, + 'mouseUp': this.mouseUp, + 'mouseMove': this.mouseMove, + 'checkLandingElement': this.checkLandingElement, + 'checkIfOnTarget': this.checkIfOnTarget, + 'snapToTarget': this.snapToTarget, + 'correctZIndexes': this.correctZIndexes, + 'moveBackToSlider': this.moveBackToSlider, + + 'moveDraggableToTarget': this.moveDraggableToTarget, + 'moveDraggableToXY': this.moveDraggableToXY, + + 'makeDraggableCopy': this.makeDraggableCopy + }; + + obj = draggableObj.originalConfigObj; + + if (obj.icon.length > 0) { + draggableObj.iconEl = $(''); + draggableObj.iconEl.attr('src', obj.icon); + draggableObj.iconEl.load(function () { + draggableObj.iconEl.css('position', 'absolute'); + draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); + draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + + if (obj.label.length > 0) { + draggableObj.iconEl.css('top', 5); + } else { + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + } + + if (obj.label.length > 0) { + draggableObj.labelEl = $( + '
' + + obj.label + + '
' + ); + + draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); + draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); + + draggableObj.labelEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.labelEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.labelEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + } + }); + } else { + if (obj.label.length > 0) { + draggableObj.iconEl = $( + '
' + + obj.label + + '
' + ); + + draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + } + } + + // Attach events to "iconEl". + draggableObj.iconEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.iconEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.iconEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + + // Attach events to "containerEl". + draggableObj.containerEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.containerEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.containerEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + + draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); + + logme('Returning from draggableCopy.'); + + return draggableObj; + } + function moveDraggableToXY(newPosition) { - var self, offset; + var self, offset, draggableCopy; if (this.hasLoaded === false) { self = this; @@ -72,15 +220,25 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; } + logme('moveDraggableToXY: isReusable = ' + this.isReusable); + if ((this.isReusable === true) && (this.isOriginal === true)) { + draggableCopy = this.makeDraggableCopy(); + draggableCopy.moveDraggableToXY(newPosition); + + return; + } + offset = 0; if (this.state.config.targetOutline === true) { offset = 1; } this.inContainer = false; - this.containerEl.hide(); - this.iconEl.detach(); + if (this.isOriginal === true) { + this.containerEl.hide(); + this.iconEl.detach(); + } this.iconEl.css('background-color', this.iconElBGColor); this.iconEl.css('padding-left', this.iconElPadding); this.iconEl.css('padding-right', this.iconElPadding); @@ -98,7 +256,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.iconEl.appendTo(this.state.baseImageEl.parent()); if (this.labelEl !== null) { - this.labelEl.detach(); + if (this.isOriginal === true) { + this.labelEl.detach(); + } this.labelEl.css('background-color', this.state.config.labelBgColor); this.labelEl.css('padding-left', 8); this.labelEl.css('padding-right', 8); @@ -120,12 +280,14 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.zIndex = 1000; this.correctZIndexes(); - this.state.numDraggablesInSlider -= 1; - this.state.updateArrowOpacity(); + if (this.isOriginal === true) { + this.state.numDraggablesInSlider -= 1; + this.state.updateArrowOpacity(); + } } function moveDraggableToTarget(target) { - var self, offset; + var self, offset, draggableCopy; if (this.hasLoaded === false) { self = this; @@ -137,15 +299,25 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; } + logme('moveDraggableToTarget: isReusable = ' + this.isReusable); + if ((this.isReusable === true) && (this.isOriginal === true)) { + draggableCopy = this.makeDraggableCopy(); + draggableCopy.moveDraggableToTarget(target); + + return; + } + offset = 0; if (this.state.config.targetOutline === true) { offset = 1; } this.inContainer = false; - this.containerEl.hide(); - this.iconEl.detach(); + if (this.isOriginal === true) { + this.containerEl.hide(); + this.iconEl.detach(); + } this.iconEl.css('background-color', this.iconElBGColor); this.iconEl.css('padding-left', this.iconElPadding); this.iconEl.css('padding-right', this.iconElPadding); @@ -163,7 +335,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.iconEl.appendTo(this.state.baseImageEl.parent()); if (this.labelEl !== null) { - this.labelEl.detach(); + if (this.isOriginal === true) { + this.labelEl.detach(); + } this.labelEl.css('background-color', this.state.config.labelBgColor); this.labelEl.css('padding-left', 8); this.labelEl.css('padding-right', 8); @@ -184,14 +358,20 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.zIndex = 1000; this.correctZIndexes(); - this.state.numDraggablesInSlider -= 1; - this.state.updateArrowOpacity(); + if (this.isOriginal === true) { + this.state.numDraggablesInSlider -= 1; + this.state.updateArrowOpacity(); + } + } function processDraggable(state, obj) { var draggableObj; draggableObj = { + 'originalConfigObj': obj, + 'stateDraggablesIndex': null, + 'id': obj.id, 'isReusable': obj.can_reuse, @@ -235,7 +415,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'moveBackToSlider': moveBackToSlider, 'moveDraggableToTarget': moveDraggableToTarget, - 'moveDraggableToXY': moveDraggableToXY + 'moveDraggableToXY': moveDraggableToXY, + + 'makeDraggableCopy': makeDraggableCopy }; draggableObj.containerEl = $( @@ -383,98 +565,71 @@ define(['logme', 'update_input'], function (logme, updateInput) { }); state.numDraggablesInSlider += 1; - state.draggables.push(draggableObj); + draggableObj.stateDraggablesIndex = state.draggables.push(draggableObj) - 1; } function mouseDown(event) { + var draggableCopy; + 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(); - if ((this.isReusable === true) && (this.isOriginal === true)) { - - return; - } - // If this draggable is just being dragged out of the // container, we must perform some additional tasks. if (this.inContainer === true) { - this.containerEl.hide(); + if ((this.isReusable === true) && (this.isOriginal === true)) { + draggableCopy = this.makeDraggableCopy(); + draggableCopy.mouseDown(event); - this.iconEl.detach(); - this.iconEl.css( - 'background-color', this.iconElBGColor - ); - this.iconEl.css( - 'padding-left', this.iconElPadding - ); - this.iconEl.css( - 'padding-right', this.iconElPadding - ); - this.iconEl.css( - 'border', this.iconElBorder - ); - this.iconEl.css( - 'width', - this.iconWidth - ); - this.iconEl.css( - 'height', - this.iconHeight - ); + return; + } + + if (this.isOriginal === true) { + this.containerEl.hide(); + this.iconEl.detach(); + } + this.iconEl.css('background-color', this.iconElBGColor); + this.iconEl.css('padding-left', this.iconElPadding); + this.iconEl.css('padding-right', this.iconElPadding); + this.iconEl.css('border', this.iconElBorder); + this.iconEl.css('width', this.iconWidth); + this.iconEl.css('height', this.iconHeight); this.iconEl.css( 'left', - event.pageX - - this.state.baseImageEl.offset().left - - this.iconWidth * 0.5 - - this.iconElLeftOffset + event.pageX - this.state.baseImageEl.offset().left - this.iconWidth * 0.5 - this.iconElLeftOffset ); this.iconEl.css( 'top', - event.pageY - - this.state.baseImageEl.offset().top - - this.iconHeight * 0.5 - ); - this.iconEl.appendTo( - this.state.baseImageEl.parent() + event.pageY - this.state.baseImageEl.offset().top - this.iconHeight * 0.5 ); + this.iconEl.appendTo(this.state.baseImageEl.parent()); if (this.labelEl !== null) { - this.labelEl.detach(); - this.labelEl.css( - 'background-color', this.state.config.labelBgColor - ); - this.labelEl.css( - 'padding-left', 8 - ); - this.labelEl.css( - 'padding-right', 8 - ); - this.labelEl.css( - 'border', '1px solid black' - ); + if (this.isOriginal === true) { + this.labelEl.detach(); + } + this.labelEl.css('background-color', this.state.config.labelBgColor); + this.labelEl.css('padding-left', 8); + this.labelEl.css('padding-right', 8); + this.labelEl.css('border', '1px solid black'); this.labelEl.css( 'left', - event.pageX - - this.state.baseImageEl.offset().left - - this.labelWidth * 0.5 - - 9 // Account for padding, border. + event.pageX - this.state.baseImageEl.offset().left - this.labelWidth * 0.5 - 9 // Account for padding, border. ); this.labelEl.css( 'top', - event.pageY - - this.state.baseImageEl.offset().top + - this.iconHeight * 0.5 + 5 - ); - this.labelEl.appendTo( - this.state.baseImageEl.parent() + event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5 ); + this.labelEl.appendTo(this.state.baseImageEl.parent()); } this.inContainer = false; - this.state.numDraggablesInSlider -= 1; + if (this.isOriginal === true) { + this.state.numDraggablesInSlider -= 1; + } } this.zIndex = 1000; @@ -549,7 +704,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.moveBackToSlider(); - this.state.numDraggablesInSlider += 1; + if (this.isOriginal === true) { + this.state.numDraggablesInSlider += 1; + } } } else { if ( @@ -563,7 +720,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.x = -1; this.y = -1; - this.state.numDraggablesInSlider += 1; + if (this.isOriginal === true) { + this.state.numDraggablesInSlider += 1; + } } else { this.correctZIndexes(); @@ -572,11 +731,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - this.state.updateArrowOpacity(); + 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 @@ -585,7 +745,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { // positionIE is the object as returned by // // this.iconEl.position() - // function checkIfOnTarget(positionIE) { var c1, target; @@ -732,9 +891,17 @@ define(['logme', 'update_input'], function (logme, updateInput) { // move it back to the slider, placing it in the same position // that it was dragged out of. function moveBackToSlider() { + if (this.isOriginal === false) { + this.iconEl.empty(); + if (this.labelEl !== null) { + this.labelEl.empty(); + } + this.state.draggables.splice(this.stateDraggablesIndex, 1); + } + this.containerEl.show(); - this.zIndex = this.oldZIndex; + this.zIndex = 1; this.iconEl.detach(); this.iconEl.css('border', 'none'); diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index e7b02dec47..114aa22616 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -69,10 +69,6 @@ define( updateInput.update(state); } }()); - - setTimeout(function () { - logme('After 1000 ms:', state); - }, 1000); } }); diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index e9c6222058..6685672420 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -198,9 +198,16 @@ define(['logme'], function (logme) { function getById(state, type, id) { return (function (c1) { while (c1 < state[type].length) { - if (state[type][c1].id === id) { - return state[type][c1]; + if (type === 'draggables') { + if ((state[type][c1].id === id) && (state[type][c1].isOriginal === true)) { + return state[type][c1]; + } + } else { // 'targets' + if (state[type][c1].id === id) { + return state[type][c1]; + } } + c1 += 1; } From 0f7b89faccb0f2d4495b8cf23e20d06474e1644d Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 12:07:19 +0200 Subject: [PATCH 120/140] Bug fixes for new functionality: Draggable "is reusable". --- .../js/capa/drag_and_drop/draggables.js | 83 ++++++++++++------- .../static/js/capa/drag_and_drop/targets.js | 17 +++- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index aaa3001010..72d424c7af 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -59,7 +59,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - function makeDraggableCopy() { + function makeDraggableCopy(callbackFunc) { var draggableObj, obj; draggableObj = { @@ -158,7 +158,26 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.mouseMove.call(draggableObj, event); }); } + + // Attach events to "iconEl". + draggableObj.iconEl.mousedown(function (event) { + draggableObj.mouseDown.call(draggableObj, event); + }); + draggableObj.iconEl.mouseup(function (event) { + draggableObj.mouseUp.call(draggableObj, event); + }); + draggableObj.iconEl.mousemove(function (event) { + draggableObj.mouseMove.call(draggableObj, event); + }); + + draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); + + setTimeout(function () { + callbackFunc(draggableObj); + }, 0); }); + + return; } else { if (obj.label.length > 0) { draggableObj.iconEl = $( @@ -189,26 +208,17 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.mouseMove.call(draggableObj, event); }); - // Attach events to "containerEl". - draggableObj.containerEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.containerEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.containerEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); - draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); - logme('Returning from draggableCopy.'); + setTimeout(function () { + callbackFunc(draggableObj); + }, 0); - return draggableObj; + return; } function moveDraggableToXY(newPosition) { - var self, offset, draggableCopy; + var self, offset; if (this.hasLoaded === false) { self = this; @@ -220,10 +230,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; } - logme('moveDraggableToXY: isReusable = ' + this.isReusable); if ((this.isReusable === true) && (this.isOriginal === true)) { - draggableCopy = this.makeDraggableCopy(); - draggableCopy.moveDraggableToXY(newPosition); + this.makeDraggableCopy(function (draggableCopy) { + draggableCopy.moveDraggableToXY(newPosition); + }); return; } @@ -287,7 +297,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function moveDraggableToTarget(target) { - var self, offset, draggableCopy; + var self, offset; if (this.hasLoaded === false) { self = this; @@ -299,10 +309,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; } - logme('moveDraggableToTarget: isReusable = ' + this.isReusable); if ((this.isReusable === true) && (this.isOriginal === true)) { - draggableCopy = this.makeDraggableCopy(); - draggableCopy.moveDraggableToTarget(target); + this.makeDraggableCopy(function (draggableCopy) { + draggableCopy.moveDraggableToTarget(target); + }); return; } @@ -569,20 +579,20 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function mouseDown(event) { - var draggableCopy; - 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)) { - draggableCopy = this.makeDraggableCopy(); - draggableCopy.mouseDown(event); + this.makeDraggableCopy(function (draggableCopy) { + draggableCopy.mouseDown(event); + }); return; } @@ -651,7 +661,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - function mouseMove() { + 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 @@ -881,9 +891,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.zIndex = highestZIndex + 1; - this.iconEl.css('z-index', highestZIndex); + this.iconEl.css('z-index', this.zIndex); if (this.labelEl !== null) { - this.labelEl.css('z-index', highestZIndex); + this.labelEl.css('z-index', this.zIndex); } } @@ -891,12 +901,27 @@ define(['logme', 'update_input'], function (logme, updateInput) { // 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.empty(); + this.iconEl.remove(); if (this.labelEl !== null) { this.labelEl.empty(); + this.labelEl.remove(); } this.state.draggables.splice(this.stateDraggablesIndex, 1); + + c1 = 0; + while (c1 < this.state.draggables) { + if (this.state.draggables[c1].stateDraggablesIndex > this.stateDraggablesIndex) { + this.state.draggables[c1].stateDraggablesIndex -= 1; + } + + c1 += 1; + } + + return; } this.containerEl.show(); diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index 40828ffba3..2d1c8b8326 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -79,6 +79,8 @@ define(['logme'], function (logme) { 'draggableList': [], + 'state': state, + 'targetEl': targetEl, 'numTextEl': numTextEl, @@ -102,8 +104,21 @@ define(['logme'], function (logme) { } function removeDraggable(draggable) { + var c1; + this.draggableList.splice(draggable.onTargetIndex, 1); + // An item from the array was removed. We need to updated all indexes accordingly. + // Shift all indexes down by one if they are higher than the index of the removed item. + c1 = 0; + while (c1 < this.draggableList.length) { + if (this.draggableList[c1].onTargetIndex > draggable.onTargetIndex) { + this.draggableList[c1].onTargetIndex -= 1; + } + + c1 += 1; + } + draggable.onTarget = null; draggable.onTargetIndex = null; @@ -133,7 +148,7 @@ define(['logme'], function (logme) { function cycleDraggableOrder() { var c1, lowestZIndex, highestZIndex; - if (this.draggableList.length === 0) { + if (this.draggableList.length < 2) { return; } From 1eec7bada710b90f10a05a39ee36a89246a35965 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 12:51:48 +0200 Subject: [PATCH 121/140] Updated docs. Added debug output info for tracing problem related to server communications. --- common/static/js/capa/drag_and_drop/update_input.js | 1 + docs/source/drag_and_drop_input.rst | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 6685672420..91881b21ec 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -50,6 +50,7 @@ define(['logme'], function (logme) { }(0)); } + logme('input = "' + JSON.stringify({'draggables': draggables}) + '".'); $('#input_' + state.problemId).val(JSON.stringify({'draggables': draggables})); } diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index fac75368b2..dc13dcfb11 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -215,8 +215,11 @@ If we have can_reuse=true, than one must use only long form of correct answer. Logic flow ---------- +(Click on image to see full size version.) + .. image:: draganddrop_logic_flow.png - :scale: 36 % + :width: 100% + :target: _images/draganddrop_logic_flow.png Example From 7ad6ef21f47e469cdb7d71bd303b9f12f82f55f4 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 13:30:41 +0200 Subject: [PATCH 122/140] Removed unnecessary debug information. --- common/static/js/capa/drag_and_drop/update_input.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 91881b21ec..20a75ecff3 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -50,7 +50,6 @@ define(['logme'], function (logme) { }(0)); } - logme('input = "' + JSON.stringify({'draggables': draggables}) + '".'); $('#input_' + state.problemId).val(JSON.stringify({'draggables': draggables})); } @@ -183,6 +182,10 @@ define(['logme'], function (logme) { } function repositionDraggables(state, answer) { + if (answer.draggables.length === 0) { + return; + } + if (state.config.individualTargets !== getUseTargets(answer)) { logme('ERROR: JSON config is not consistent with server response.'); From 5457f35f81f26c9bdf50022af549b4de697a4159 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 16:05:33 +0200 Subject: [PATCH 123/140] Drag and Drop: Further testing of new feature "can reuse". --- .../js/capa/drag_and_drop/draggables.js | 6 ++++- common/static/js/capa/drag_and_drop/state.js | 26 ++++++++++++++++++- .../static/js/capa/drag_and_drop/targets.js | 3 +-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 72d424c7af..87081a78f8 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -63,6 +63,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { var draggableObj, obj; draggableObj = { + 'uniqueId': /* this.uniqueId */ this.state.getUniqueId(), // Is newly set. + 'originalConfigObj': this.originalConfigObj, 'stateDraggablesIndex': /* this.stateDraggablesIndex */ null, // Will be set. @@ -379,6 +381,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { var draggableObj; draggableObj = { + 'uniqueId': state.getUniqueId(), + 'originalConfigObj': obj, 'stateDraggablesIndex': null, @@ -768,7 +772,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { if ( (this.state.config.onePerTarget === true) && (target.draggableList.length === 1) && - (target.draggableList[0].id !== this.id) + (target.draggableList[0].uniqueId !== this.uniqueId) ) { continue; } diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index d99b90872f..d0ea6b65ef 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -26,9 +26,33 @@ define([], function () { 'targets': [], - 'updateArrowOpacity': null + 'updateArrowOpacity': null, + + 'uniqueId': 0, + 'salt': makeSalt(), + + 'getUniqueId': getUniqueId }; } + + function getUniqueId() { + this.uniqueId += 1; + + return this.salt + '_' + this.uniqueId.toFixed(0); + } + + function makeSalt() { + var text, possible, i; + + text = ''; + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + for(i = 0; i < 5; i += 1) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return text; + } }); // End of wrapper for RequireJS. As you can see, we are passing diff --git a/common/static/js/capa/drag_and_drop/targets.js b/common/static/js/capa/drag_and_drop/targets.js index 2d1c8b8326..e56020aac6 100644 --- a/common/static/js/capa/drag_and_drop/targets.js +++ b/common/static/js/capa/drag_and_drop/targets.js @@ -15,7 +15,7 @@ define(['logme'], function (logme) { c1 += 1; } }(0)); - } // function Targets(state) { + } function processTarget(state, obj) { var targetEl, borderCss, numTextEl, targetObj; @@ -36,7 +36,6 @@ define(['logme'], function (logme) { 'left: ' + obj.x + 'px; ' + borderCss + '" ' + - 'data-target-id="' + obj.id + '" ' + '>
' ); targetEl.appendTo(state.baseImageEl.parent()); From a228c163c7746dfc5691a959cc659ea49cea1c3b Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Fri, 18 Jan 2013 17:02:02 +0200 Subject: [PATCH 124/140] added test for alternative answer notation --- common/lib/capa/capa/verifiers/tests_draganddrop.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/lib/capa/capa/verifiers/tests_draganddrop.py b/common/lib/capa/capa/verifiers/tests_draganddrop.py index f708413c46..55425ae910 100644 --- a/common/lib/capa/capa/verifiers/tests_draganddrop.py +++ b/common/lib/capa/capa/verifiers/tests_draganddrop.py @@ -505,6 +505,17 @@ class Test_DragAndDrop_Grade(unittest.TestCase): }] self.assertFalse(draganddrop.grade(user_input, correct_answer)) + def test_alternative_correct_answer(self): + user_input = '{"draggables":[{"name_with_icon":"t1"},\ + {"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"}, \ + {"name4":"t1"}]}' + correct_answer = [ + {'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'}, + {'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'], + 'rule': 'exact'} + ] + self.assertTrue(draganddrop.grade(user_input, correct_answer)) + class Test_DragAndDrop_Populate(unittest.TestCase): From 3cc4a663ff047239150c4d9869fc8e97f0da6868 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 18:32:27 +0200 Subject: [PATCH 125/140] Refactoring and cleaning of code. --- .../js/capa/drag_and_drop/base_image.js | 6 +- .../js/capa/drag_and_drop/draggables.js | 591 ++++++------------ .../js/capa/drag_and_drop/update_input.js | 4 +- 3 files changed, 196 insertions(+), 405 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/base_image.js b/common/static/js/capa/drag_and_drop/base_image.js index d818804be2..da875c4329 100644 --- a/common/static/js/capa/drag_and_drop/base_image.js +++ b/common/static/js/capa/drag_and_drop/base_image.js @@ -26,8 +26,10 @@ define(['logme'], function (logme) { state.baseImageEl.attr('src', state.config.baseImage); state.baseImageEl.load(function () { - baseImageElContainer.css('width', this.width); - baseImageElContainer.css('height', this.height); + baseImageElContainer.css({ + 'width': this.width, + 'height': this.height + }); state.baseImageEl.appendTo(baseImageElContainer); baseImageElContainer.appendTo(state.containerEl); diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index 87081a78f8..a7c36e40fc 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -60,81 +60,37 @@ define(['logme', 'update_input'], function (logme, updateInput) { } function makeDraggableCopy(callbackFunc) { - var draggableObj, obj; + var draggableObj, property; - draggableObj = { - 'uniqueId': /* this.uniqueId */ this.state.getUniqueId(), // Is newly set. + // Make a full proper copy of the draggable object, with some modifications. + draggableObj = {}; + for (property in this) { + if (this.hasOwnProperty(property) === true) { + draggableObj[property] = this[property]; + } + } + // The modifications to the draggable copy. + draggableObj.isOriginal = false; // This new draggable is a copy. + draggableObj.uniqueId = draggableObj.state.getUniqueId(); // Is newly set. + draggableObj.stateDraggablesIndex = null; // Will be set. + draggableObj.containerEl = null; // Not needed, since a copy will never return to a container element. + draggableObj.iconEl = null; // Will be created. + draggableObj.labelEl = null; // Will be created. - 'originalConfigObj': this.originalConfigObj, - 'stateDraggablesIndex': /* this.stateDraggablesIndex */ null, // Will be set. - - 'id': this.id, - - 'isReusable': this.isReusable, - 'isOriginal': false, - - 'x': this.x, - 'y': this.y, - - 'zIndex': this.zIndex, - - // Not needed, since a copy will never return to a container element. - 'containerEl': null, - - 'iconEl': /* this.iconEl */ null, // Will be created. - 'iconElBGColor': this.iconElBGColor, - 'iconElPadding': this.iconElPadding, - 'iconElBorder': this.iconElBorder, - 'iconElLeftOffset': this.iconElLeftOffset, - 'iconWidth': this.iconWidth, - 'iconHeight': this.iconHeight, - 'iconWidthSmall': this.iconWidthSmall, - 'iconHeightSmall': this.iconHeightSmall, - - 'labelEl': /* this.labelEl */ null, // Will be created. - 'labelWidth': this.labelWidth, - - 'hasLoaded': this.hasLoaded, - 'inContainer': this.inContainer, - 'mousePressed': this.mousePressed, - 'onTarget': this.onTarget, - 'onTargetIndex': this.onTargetIndex, - - 'state': this.state, - - 'mouseDown': this.mouseDown, - 'mouseUp': this.mouseUp, - 'mouseMove': this.mouseMove, - 'checkLandingElement': this.checkLandingElement, - 'checkIfOnTarget': this.checkIfOnTarget, - 'snapToTarget': this.snapToTarget, - 'correctZIndexes': this.correctZIndexes, - 'moveBackToSlider': this.moveBackToSlider, - - 'moveDraggableToTarget': this.moveDraggableToTarget, - 'moveDraggableToXY': this.moveDraggableToXY, - - 'makeDraggableCopy': this.makeDraggableCopy - }; - - obj = draggableObj.originalConfigObj; - - if (obj.icon.length > 0) { + // Create DOM elements and attach events. + if (draggableObj.originalConfigObj.icon.length > 0) { draggableObj.iconEl = $(''); - draggableObj.iconEl.attr('src', obj.icon); + draggableObj.iconEl.attr('src', draggableObj.originalConfigObj.icon); draggableObj.iconEl.load(function () { - draggableObj.iconEl.css('position', 'absolute'); - draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); - draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); + draggableObj.iconEl.css({ + 'position': 'absolute', + 'width': draggableObj.iconWidthSmall, + 'height': draggableObj.iconHeightSmall, + 'left': 50 - draggableObj.iconWidthSmall * 0.5, + 'top': ((draggableObj.originalConfigObj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5) + }); - if (obj.label.length > 0) { - draggableObj.iconEl.css('top', 5); - } else { - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); - } - - if (obj.label.length > 0) { + if (draggableObj.originalConfigObj.label.length > 0) { draggableObj.labelEl = $( '
' + - obj.label + + draggableObj.originalConfigObj.label + '
' ); + draggableObj.labelEl.css({ + 'left': 50 - draggableObj.labelWidth * 0.5, + 'top': 5 + draggableObj.iconHeightSmall + 5 + }); - draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); - draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); - - draggableObj.labelEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.labelEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.labelEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); + draggableObj.attachMouseEventsTo('labelEl'); } - // Attach events to "iconEl". - draggableObj.iconEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.iconEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.iconEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); + draggableObj.attachMouseEventsTo('iconEl'); draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); @@ -181,7 +121,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { return; } else { - if (obj.label.length > 0) { + if (draggableObj.originalConfigObj.label.length > 0) { draggableObj.iconEl = $( '
' + - obj.label + + draggableObj.originalConfigObj.label + '
' ); + draggableObj.iconEl.css({ + 'left': 50 - draggableObj.iconWidthSmall * 0.5, + 'top': 50 - draggableObj.iconHeightSmall * 0.5 + }); - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + draggableObj.attachMouseEventsTo('iconEl'); + + draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); + + setTimeout(function () { + callbackFunc(draggableObj); + }, 0); + + return; } } - - // Attach events to "iconEl". - draggableObj.iconEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.iconEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.iconEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); - - draggableObj.stateDraggablesIndex = draggableObj.state.draggables.push(draggableObj); - - setTimeout(function () { - callbackFunc(draggableObj); - }, 0); - - return; } - function moveDraggableToXY(newPosition) { + 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.moveDraggableToXY(newPosition); + self.moveDraggableTo(moveType, target); }, 50); return; @@ -234,7 +182,7 @@ define(['logme', 'update_input'], function (logme, updateInput) { if ((this.isReusable === true) && (this.isOriginal === true)) { this.makeDraggableCopy(function (draggableCopy) { - draggableCopy.moveDraggableToXY(newPosition); + draggableCopy.moveDraggableTo(moveType, target); }); return; @@ -251,121 +199,57 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.containerEl.hide(); this.iconEl.detach(); } - this.iconEl.css('background-color', this.iconElBGColor); - this.iconEl.css('padding-left', this.iconElPadding); - this.iconEl.css('padding-right', this.iconElPadding); - this.iconEl.css('border', this.iconElBorder); - this.iconEl.css('width', this.iconWidth); - this.iconEl.css('height', this.iconHeight); - this.iconEl.css( - 'left', - newPosition.x - this.iconWidth * 0.5 + offset - this.iconElLeftOffset - ); - this.iconEl.css( - 'top', - newPosition.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); - this.labelEl.css('padding-left', 8); - this.labelEl.css('padding-right', 8); - this.labelEl.css('border', '1px solid black'); - this.labelEl.css( - 'left', - newPosition.x - this.labelWidth * 0.5 + offset - 9 // Account for padding, border. - ); - this.labelEl.css( - 'top', - newPosition.y - this.iconHeight * 0.5 + this.iconHeight + 5 + offset - ); - this.labelEl.appendTo(this.state.baseImageEl.parent()); - } - - this.x = newPosition.x; - this.y = newPosition.y; - - this.zIndex = 1000; - this.correctZIndexes(); - - if (this.isOriginal === true) { - this.state.numDraggablesInSlider -= 1; - this.state.updateArrowOpacity(); - } - } - - function moveDraggableToTarget(target) { - var self, offset; - - if (this.hasLoaded === false) { - self = this; - - setTimeout(function () { - self.moveDraggableToTarget(target); - }, 50); - - return; - } - - if ((this.isReusable === true) && (this.isOriginal === true)) { - this.makeDraggableCopy(function (draggableCopy) { - draggableCopy.moveDraggableToTarget(target); + 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 }); - - 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); - this.iconEl.css('padding-left', this.iconElPadding); - this.iconEl.css('padding-right', this.iconElPadding); - this.iconEl.css('border', this.iconElBorder); - this.iconEl.css('width', this.iconWidth); - this.iconEl.css('height', this.iconHeight); - this.iconEl.css( - 'left', - target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset - ); - this.iconEl.css( - 'top', - target.offset.top + 0.5 * target.h - 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); - this.labelEl.css('padding-left', 8); - this.labelEl.css('padding-right', 8); - this.labelEl.css('border', '1px solid black'); - this.labelEl.css( - 'left', - target.offset.left + 0.5 * target.w - this.labelWidth * 0.5 + offset - 9 // Account for padding, border. - ); - this.labelEl.css( - 'top', - target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset - ); + 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()); } - target.addDraggable(this); + if (moveType === 'target') { + target.addDraggable(this); + } else { + this.x = target.x; + this.y = target.y; + } this.zIndex = 1000; this.correctZIndexes(); @@ -374,7 +258,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.state.numDraggablesInSlider -= 1; this.state.updateArrowOpacity(); } - } function processDraggable(state, obj) { @@ -382,22 +265,15 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj = { 'uniqueId': state.getUniqueId(), - 'originalConfigObj': obj, 'stateDraggablesIndex': null, - 'id': obj.id, - 'isReusable': obj.can_reuse, 'isOriginal': true, - 'x': -1, 'y': -1, - 'zIndex': 1, - 'containerEl': null, - 'iconEl': null, 'iconElBGColor': null, 'iconElPadding': null, @@ -407,16 +283,13 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'iconHeight': null, 'iconWidthSmall': null, 'iconHeightSmall': null, - 'labelEl': null, 'labelWidth': null, - 'hasLoaded': false, 'inContainer': true, 'mousePressed': false, 'onTarget': null, 'onTargetIndex': null, - 'state': state, 'mouseDown': mouseDown, @@ -427,11 +300,9 @@ define(['logme', 'update_input'], function (logme, updateInput) { 'snapToTarget': snapToTarget, 'correctZIndexes': correctZIndexes, 'moveBackToSlider': moveBackToSlider, - - 'moveDraggableToTarget': moveDraggableToTarget, - 'moveDraggableToXY': moveDraggableToXY, - - 'makeDraggableCopy': makeDraggableCopy + 'moveDraggableTo': moveDraggableTo, + 'makeDraggableCopy': makeDraggableCopy, + 'attachMouseEventsTo': attachMouseEventsTo }; draggableObj.containerEl = $( @@ -472,17 +343,13 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconWidthSmall = draggableObj.iconHeightSmall * (draggableObj.iconWidth / draggableObj.iconHeight); } - draggableObj.iconEl.css('position', 'absolute'); - draggableObj.iconEl.css('width', draggableObj.iconWidthSmall); - draggableObj.iconEl.css('height', draggableObj.iconHeightSmall); - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); - - if (obj.label.length > 0) { - draggableObj.iconEl.css('top', 5); - } else { - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); - } - + draggableObj.iconEl.css({ + 'position': 'absolute', + 'width': draggableObj.iconWidthSmall, + 'height': draggableObj.iconHeightSmall, + 'left': 50 - draggableObj.iconWidthSmall * 0.5, + 'top': ((obj.label.length > 0) ? 5 : 50 - draggableObj.iconHeightSmall * 0.5) + }); draggableObj.iconEl.appendTo(draggableObj.containerEl); if (obj.label.length > 0) { @@ -500,18 +367,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.labelEl.appendTo(draggableObj.containerEl); draggableObj.labelWidth = draggableObj.labelEl.width(); - draggableObj.labelEl.css('left', 50 - draggableObj.labelWidth * 0.5); - draggableObj.labelEl.css('top', 5 + draggableObj.iconHeightSmall + 5); + draggableObj.labelEl.css({ + 'left': 50 - draggableObj.labelWidth * 0.5, + 'top': 5 + draggableObj.iconHeightSmall + 5 + }); - draggableObj.labelEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.labelEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.labelEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); + draggableObj.attachMouseEventsTo('labelEl'); } draggableObj.hasLoaded = true; @@ -546,8 +407,10 @@ define(['logme', 'update_input'], function (logme, updateInput) { draggableObj.iconWidthSmall = draggableObj.iconWidth; draggableObj.iconHeightSmall = draggableObj.iconHeight; - draggableObj.iconEl.css('left', 50 - draggableObj.iconWidthSmall * 0.5); - draggableObj.iconEl.css('top', 50 - draggableObj.iconHeightSmall * 0.5); + draggableObj.iconEl.css({ + 'left': 50 - draggableObj.iconWidthSmall * 0.5, + 'top': 50 - draggableObj.iconHeightSmall * 0.5 + }); draggableObj.hasLoaded = true; } else { @@ -556,27 +419,8 @@ define(['logme', 'update_input'], function (logme, updateInput) { } } - // Attach events to "iconEl". - draggableObj.iconEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.iconEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.iconEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); - - // Attach events to "containerEl". - draggableObj.containerEl.mousedown(function (event) { - draggableObj.mouseDown.call(draggableObj, event); - }); - draggableObj.containerEl.mouseup(function (event) { - draggableObj.mouseUp.call(draggableObj, event); - }); - draggableObj.containerEl.mousemove(function (event) { - draggableObj.mouseMove.call(draggableObj, event); - }); + draggableObj.attachMouseEventsTo('iconEl'); + draggableObj.attachMouseEventsTo('containerEl'); state.numDraggablesInSlider += 1; draggableObj.stateDraggablesIndex = state.draggables.push(draggableObj) - 1; @@ -605,38 +449,30 @@ define(['logme', 'update_input'], function (logme, updateInput) { this.containerEl.hide(); this.iconEl.detach(); } - this.iconEl.css('background-color', this.iconElBGColor); - this.iconEl.css('padding-left', this.iconElPadding); - this.iconEl.css('padding-right', this.iconElPadding); - this.iconEl.css('border', this.iconElBorder); - this.iconEl.css('width', this.iconWidth); - this.iconEl.css('height', this.iconHeight); - this.iconEl.css( - 'left', - event.pageX - this.state.baseImageEl.offset().left - this.iconWidth * 0.5 - this.iconElLeftOffset - ); - this.iconEl.css( - 'top', - event.pageY - this.state.baseImageEl.offset().top - this.iconHeight * 0.5 - ); + 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); - this.labelEl.css('padding-left', 8); - this.labelEl.css('padding-right', 8); - this.labelEl.css('border', '1px solid black'); - this.labelEl.css( - 'left', - event.pageX - this.state.baseImageEl.offset().left - this.labelWidth * 0.5 - 9 // Account for padding, border. - ); - this.labelEl.css( - 'top', - event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5 - ); + 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()); } @@ -675,24 +511,16 @@ define(['logme', 'update_input'], function (logme, updateInput) { // 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 - ); - this.iconEl.css( - 'top', - event.pageY - this.state.baseImageEl.offset().top - this.iconHeight * 0.5 - ); + 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. - ); - this.labelEl.css( - 'top', - event.pageY - this.state.baseImageEl.offset().top + this.iconHeight * 0.5 + 5 - ); + 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 + }); } } } @@ -779,16 +607,12 @@ define(['logme', 'update_input'], function (logme, updateInput) { // 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) { - continue; - } - if (positionIE.top + this.iconHeight * 0.5 > target.offset.top + target.h) { - continue; - } - if (positionIE.left + this.iconWidth * 0.5 < target.offset.left) { - continue; - } - if (positionIE.left + this.iconWidth * 0.5 > target.offset.left + target.w) { + 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; } @@ -827,24 +651,16 @@ define(['logme', 'update_input'], function (logme, updateInput) { offset = 1; } - this.iconEl.css( - 'left', - target.offset.left + 0.5 * target.w - this.iconWidth * 0.5 + offset - this.iconElLeftOffset - ); - this.iconEl.css( - 'top', - target.offset.top + 0.5 * target.h - this.iconHeight * 0.5 + offset - ); + 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. - ); - this.labelEl.css( - 'top', - target.offset.top + 0.5 * target.h + this.iconHeight * 0.5 + 5 + offset - ); + 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 + }); } } @@ -908,77 +724,50 @@ define(['logme', 'update_input'], function (logme, updateInput) { var c1; if (this.isOriginal === false) { - this.iconEl.empty(); this.iconEl.remove(); if (this.labelEl !== null) { - this.labelEl.empty(); this.labelEl.remove(); } this.state.draggables.splice(this.stateDraggablesIndex, 1); - c1 = 0; - while (c1 < this.state.draggables) { + for (c1 = 0; c1 < this.state.draggables; c1 += 1) { if (this.state.draggables[c1].stateDraggablesIndex > this.stateDraggablesIndex) { this.state.draggables[c1].stateDraggablesIndex -= 1; } - - c1 += 1; } return; } this.containerEl.show(); - this.zIndex = 1; this.iconEl.detach(); - this.iconEl.css('border', 'none'); - this.iconEl.css('background-color', 'transparent'); - this.iconEl.css('padding-left', 0); - this.iconEl.css('padding-right', 0); - this.iconEl.css('z-index', this.zIndex); - this.iconEl.css( - 'width', - this.iconWidthSmall - ); - this.iconEl.css( - 'height', - this.iconHeightSmall - ); - this.iconEl.css( - 'left', - 50 - this.iconWidthSmall * 0.5 - ); - if (this.labelEl !== null) { - this.iconEl.css('top', 5); - } else { - this.iconEl.css( - 'top', - 50 - this.iconHeightSmall * 0.5 - ); - } - + 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'); - this.labelEl.css('background-color', 'transparent'); - this.labelEl.css('padding-left', 0); - this.labelEl.css('padding-right', 0); - this.labelEl.css('z-index', this.zIndex); - this.labelEl.css( - 'left', - 50 - this.labelWidth * 0.5 - ); - this.labelEl.css( - 'top', - 5 + this.iconHeightSmall + 5 - ); - this.labelEl.appendTo( - this.containerEl - ); + 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; diff --git a/common/static/js/capa/drag_and_drop/update_input.js b/common/static/js/capa/drag_and_drop/update_input.js index 20a75ecff3..04715a3ecf 100644 --- a/common/static/js/capa/drag_and_drop/update_input.js +++ b/common/static/js/capa/drag_and_drop/update_input.js @@ -142,7 +142,7 @@ define(['logme'], function (logme) { continue; } - draggable.moveDraggableToTarget(target); + draggable.moveDraggableTo('target', target); } c1 += 1; @@ -170,7 +170,7 @@ define(['logme'], function (logme) { continue; } - draggable.moveDraggableToXY({ + draggable.moveDraggableTo('XY', { 'x': answer.draggables[c1][draggableId][0], 'y': answer.draggables[c1][draggableId][1] }); From 47d1173394fa5b8302cd1bcc98285c1b4ec3a1ce Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Fri, 18 Jan 2013 18:44:50 +0200 Subject: [PATCH 126/140] Refactoring and cleaning of code. --- .../js/capa/drag_and_drop/draggables.js | 41 ----------------- common/static/js/capa/drag_and_drop/main.js | 2 + common/static/js/capa/drag_and_drop/state.js | 45 ++++++++++++++++++- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/common/static/js/capa/drag_and_drop/draggables.js b/common/static/js/capa/drag_and_drop/draggables.js index a7c36e40fc..a867cf73fe 100644 --- a/common/static/js/capa/drag_and_drop/draggables.js +++ b/common/static/js/capa/drag_and_drop/draggables.js @@ -16,47 +16,6 @@ define(['logme', 'update_input'], function (logme, updateInput) { c1 += 1 } }(0)); - - state.updateArrowOpacity(); - - $(document).mousemove(function (event) { - documentMouseMove(state, event); - }); - } - - function documentMouseMove(state, event) { - if (state.currentMovingDraggable !== null) { - state.currentMovingDraggable.iconEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - state.currentMovingDraggable.iconWidth * 0.5 - - state.currentMovingDraggable.iconElLeftOffset - ); - state.currentMovingDraggable.iconEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top - - state.currentMovingDraggable.iconHeight * 0.5 - ); - - if (state.currentMovingDraggable.labelEl !== null) { - state.currentMovingDraggable.labelEl.css( - 'left', - event.pageX - - state.baseImageEl.offset().left - - state.currentMovingDraggable.labelWidth * 0.5 - - 9 // Account for padding, border. - ); - state.currentMovingDraggable.labelEl.css( - 'top', - event.pageY - - state.baseImageEl.offset().top + - state.currentMovingDraggable.iconHeight * 0.5 + - 5 - ); - } - } } function makeDraggableCopy(callbackFunc) { diff --git a/common/static/js/capa/drag_and_drop/main.js b/common/static/js/capa/drag_and_drop/main.js index 114aa22616..89cf08001d 100644 --- a/common/static/js/capa/drag_and_drop/main.js +++ b/common/static/js/capa/drag_and_drop/main.js @@ -63,6 +63,8 @@ define( Scroller(state); Draggables.init(state); + state.updateArrowOpacity(); + // Update the input element, checking first that it is not filled with // an answer from the server. if (updateInput.check(state) === false) { diff --git a/common/static/js/capa/drag_and_drop/state.js b/common/static/js/capa/drag_and_drop/state.js index d0ea6b65ef..4565acd842 100644 --- a/common/static/js/capa/drag_and_drop/state.js +++ b/common/static/js/capa/drag_and_drop/state.js @@ -8,7 +8,9 @@ define([], function () { return State; function State(problemId) { - return { + var state; + + state = { 'config': null, 'baseImageEl': null, @@ -33,6 +35,12 @@ define([], function () { 'getUniqueId': getUniqueId }; + + $(document).mousemove(function (event) { + documentMouseMove(state, event); + }); + + return state; } function getUniqueId() { @@ -53,6 +61,41 @@ define([], function () { return text; } + + function documentMouseMove(state, event) { + if (state.currentMovingDraggable !== null) { + state.currentMovingDraggable.iconEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.iconWidth * 0.5 + - state.currentMovingDraggable.iconElLeftOffset + ); + state.currentMovingDraggable.iconEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top - + state.currentMovingDraggable.iconHeight * 0.5 + ); + + if (state.currentMovingDraggable.labelEl !== null) { + state.currentMovingDraggable.labelEl.css( + 'left', + event.pageX - + state.baseImageEl.offset().left - + state.currentMovingDraggable.labelWidth * 0.5 + - 9 // Account for padding, border. + ); + state.currentMovingDraggable.labelEl.css( + 'top', + event.pageY - + state.baseImageEl.offset().top + + state.currentMovingDraggable.iconHeight * 0.5 + + 5 + ); + } + } + } }); // End of wrapper for RequireJS. As you can see, we are passing From a4e8cb362d7e85e6e0ac391ac2fc68c42025ffd9 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 14:10:17 +0200 Subject: [PATCH 127/140] updated docs --- docs/source/drag_and_drop_input.rst | 87 +++++++++++++++++++++++++ docs/source/draganddrop_logic_flow.png | Bin 305578 -> 305398 bytes 2 files changed, 87 insertions(+) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index dc13dcfb11..82cb99436a 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -212,6 +212,93 @@ for same number of draggables, anyof is equal to unordered_equal If we have can_reuse=true, than one must use only long form of correct answer. + +Grading logic +------------- + +1. User answer and correct answer populated to the same form:: + + group_id: group_draggables, group_targets, group_rule + +Group_id is ordinal number, for every dict in correct answer incremental +group_id is assigned: 0, 1, 2, ... + +Draggables from user answer are added to same group_id where identical draggables +from correct answer are:: + + If correct_draggables[group_0] = [t1, t2] then + user_draggables[group_0] are all draggables t1 and t2 from user answer: + [t1] or [t1, t2] or [t1, t2, t2] etc.. + +2. For every group user_draggables, if 'number' not in rule, set() is applicated, +if 'number' not in rule, set is not applicated:: + + from [t1, t2, t3, t3] -> [t1, t2, ,t3] + +Set() and 'number' are needed only for case of reusable draggables, +for other cases there are no equal draggables in list, so set() does nothing. + +Usege of set() operation allows easily create rule for case of +``any number of same draggable can be dragged to some targets``:: + + { + 'draggables': ['draggable_1'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'anyof' + } + +'number' rule is used for the case of reusable draggables, when one want to +fix number of draggable to drag. In this example only two instances of +draggables_1 are allowed to be dragged:: + + { + 'draggables': ['draggable_1', 'draggable_1'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'anyof+number' + } + +Note, that in using rule 'exact', one does not need 'number', because you can't +recognize from user interface which reusable draggable on which target. +Absurd example:: + + { + 'draggables': ['draggable_1', 'draggable_1', 'draggable_2'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'exact' + } + + +Correct handling of upper example is create different rule for draggable_1 and +draggable_2 + +For 'unordered_equal' (or 'exact') we not need 'number' if you have only same +draggable in group, as targets length will provide contraing for the number of +draggables:: + + { + 'draggables': ['draggable_1'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unordered_equal' + } + +This means that only three draggaggables 'draggable_1' can be dragged. + +But if you have more that one different reusable draggable in lis, you may use +'number' rule:: + + { + 'draggables': ['draggable_1', 'draggable_1', 'draggable_2'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'unordered_equal+number' + } + +If not use number, draggables list will be setted to ['draggable_1', 'draggable_2'] + +For every group, at this step, draggables lists are equal + +3. For every group, lists of targets are compared using rule for that group. + + Logic flow ---------- diff --git a/docs/source/draganddrop_logic_flow.png b/docs/source/draganddrop_logic_flow.png index 5708d3628477e989d11c228ad04b80e987a1591a..2bb1c11a41eebe8ca593671f01c6ea4a5a6d6c85 100644 GIT binary patch delta 193942 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 D1f0qo5#`YW=hq|x^QG_e8KdG_||M- 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 delta 194078 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 z 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 F{{xl0t>*v$ From b25d4224b5c827c94c5d418d4399ae153790e2db Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 15:42:41 +0200 Subject: [PATCH 128/140] updated docs --- docs/source/drag_and_drop_input.rst | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index 82cb99436a..d03d9755bd 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -128,9 +128,10 @@ Wrong (for draggable id 7):: Rules are: exact, anyof, unordered_equal, anyof+number, unordered_equal+number -- Exact rule means that targets for draggable id's in user_answer are the same -that targets from correct answer. For example, for draggables 7 and 8 user must -drag 7 to target1 and 8 to target2 if correct_answer is:: + +.. such long lines are needed for sphinx to display lists correctly + +- Exact rule means that targets for draggable id's in user_answer are the same that targets from correct answer. For example, for draggables 7 and 8 user must drag 7 to target1 and 8 to target2 if correct_answer is:: correct_answer = [ { @@ -140,9 +141,7 @@ drag 7 to target1 and 8 to target2 if correct_answer is:: }] -- unordered_equal rule allows draggables be dragged to targets unordered. If one -want to allow for student to drag 7 to target1 or target2 and 8 to target2 or -target 1 and 7 and 8 must be in different targets, then correct answer must be:: +- unordered_equal rule allows draggables be dragged to targets unordered. If one want to allow for student to drag 7 to target1 or target2 and 8 to target2 or target 1 and 7 and 8 must be in different targets, then correct answer must be:: correct_answer = [ { @@ -151,10 +150,8 @@ target 1 and 7 and 8 must be in different targets, then correct answer must be:: 'rule': 'unordered_equal' }] -- Anyof rule allows draggables to be dragged to any of targets. If one want to -allow for student to drag 7 and 8 to target1 or target2, which means that if 7 -is on target1 and 8 is on target1 or 7 on target2 and 8 on target2 or 7 on -target1 and 8 on target2. Any of theese are correct which anyof rule:: + +- Anyof rule allows draggables to be dragged to any of targets. If one want to allow for student to drag 7 and 8 to target1 or target2, which means that if 7 is on target1 and 8 is on target1 or 7 on target2 and 8 on target2 or 7 on target1 and 8 on target2. Any of theese are correct which anyof rule:: correct_answer = [ { @@ -164,11 +161,7 @@ target1 and 8 on target2. Any of theese are correct which anyof rule:: }] -- If you have can_reuse true, then you, for example, have draggables a,b,c and 10 -targets. These will allow you to drag 4 'a' draggables to -['target1', 'target4', 'target7', 'target10'] , you do not need to write 'a' -four times. Also this will allow you to drag 'b' draggable to target2 or target5 -or target5 and target2 etc..:: +- If you have can_reuse true, then you, for example, have draggables a,b,c and 10 targets. These will allow you to drag 4 'a' draggables to ['target1', 'target4', 'target7', 'target10'] , you do not need to write 'a' four times. Also this will allow you to drag 'b' draggable to target2 or target5 for target5 and target2 etc..:: correct_answer = [ { @@ -187,8 +180,7 @@ or target5 and target2 etc..:: 'rule': 'unordered_equal' }] -And sometimes you want to allow drag only two 'b' draggables, in these case -you sould use 'anyof+number' of 'unordered_equal+number' rule:: +- And sometimes you want to allow drag only two 'b' draggables, in these case you sould use 'anyof+number' of 'unordered_equal+number' rule:: correct_answer = [ { @@ -216,40 +208,52 @@ If we have can_reuse=true, than one must use only long form of correct answer. Grading logic ------------- -1. User answer and correct answer populated to the same form:: +1. User answer (that comes from browser) and correct answer (from xml) are parsed to the same format:: group_id: group_draggables, group_targets, group_rule + Group_id is ordinal number, for every dict in correct answer incremental group_id is assigned: 0, 1, 2, ... Draggables from user answer are added to same group_id where identical draggables -from correct answer are:: +from correct answer are, for example:: If correct_draggables[group_0] = [t1, t2] then - user_draggables[group_0] are all draggables t1 and t2 from user answer: - [t1] or [t1, t2] or [t1, t2, t2] etc.. + user_draggables[group_0] are all draggables t1 and t2 from user answer: + [t1] or [t1, t2] or [t1, t2, t2] etc.. -2. For every group user_draggables, if 'number' not in rule, set() is applicated, -if 'number' not in rule, set is not applicated:: +2. For every group from user answer, for that group draggables, if 'number' is in group rule, set() is applied, +if 'number' is not in rule, set is not applied:: - from [t1, t2, t3, t3] -> [t1, t2, ,t3] + set() : [t1, t2, t3, t3] -> [t1, t2, ,t3] -Set() and 'number' are needed only for case of reusable draggables, +For every group, at this step, draggables lists are equal. + + +3. For every group, lists of targets are compared using rule for that group. + + +Set and '+number' cases +....................... + +Set() and '+number' are needed only for case of reusable draggables, for other cases there are no equal draggables in list, so set() does nothing. -Usege of set() operation allows easily create rule for case of -``any number of same draggable can be dragged to some targets``:: +.. such long lines needed for sphinx to display nicely - { - 'draggables': ['draggable_1'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'anyof' - } +* Usage of set() operation allows easily create rule for case of "any number of same draggable can be dragged to some targets":: -'number' rule is used for the case of reusable draggables, when one want to -fix number of draggable to drag. In this example only two instances of -draggables_1 are allowed to be dragged:: + { + 'draggables': ['draggable_1'], + 'targets': ['target3', 'target6', 'target9'], + 'rule': 'anyof' + } + + + + +* 'number' rule is used for the case of reusable draggables, when one want to fix number of draggable to drag. In this example only two instances of draggables_1 are allowed to be dragged:: { 'draggables': ['draggable_1', 'draggable_1'], @@ -257,23 +261,20 @@ draggables_1 are allowed to be dragged:: 'rule': 'anyof+number' } -Note, that in using rule 'exact', one does not need 'number', because you can't -recognize from user interface which reusable draggable on which target. -Absurd example:: - { +* Note, that in using rule 'exact', one does not need 'number', because you can't recognize from user interface which reusable draggable is on which target. Absurd example:: + + { 'draggables': ['draggable_1', 'draggable_1', 'draggable_2'], 'targets': ['target3', 'target6', 'target9'], 'rule': 'exact' } -Correct handling of upper example is create different rule for draggable_1 and -draggable_2 + Correct handling of this example is to create different rules for draggable_1 and + draggable_2 -For 'unordered_equal' (or 'exact') we not need 'number' if you have only same -draggable in group, as targets length will provide contraing for the number of -draggables:: +* For 'unordered_equal' (or 'exact' too) we don't need 'number' if you have only same draggable in group, as targets length will provide constraint for the number of draggables:: { 'draggables': ['draggable_1'], @@ -281,10 +282,10 @@ draggables:: 'rule': 'unordered_equal' } -This means that only three draggaggables 'draggable_1' can be dragged. -But if you have more that one different reusable draggable in lis, you may use -'number' rule:: + This means that only three draggaggables 'draggable_1' can be dragged. + +* But if you have more that one different reusable draggable in list, you may use 'number' rule:: { 'draggables': ['draggable_1', 'draggable_1', 'draggable_2'], @@ -292,11 +293,10 @@ But if you have more that one different reusable draggable in lis, you may use 'rule': 'unordered_equal+number' } -If not use number, draggables list will be setted to ['draggable_1', 'draggable_2'] -For every group, at this step, draggables lists are equal + If not use number, draggables list will be setted to ['draggable_1', 'draggable_2'] + -3. For every group, lists of targets are compared using rule for that group. Logic flow From e273bb12591d399592c201fe15bf9de059fcadc3 Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Mon, 21 Jan 2013 15:47:59 +0200 Subject: [PATCH 129/140] Updated DOCS with sample XML for DnD. --- docs/source/drag-n-drop-demo.xml | 245 +++++++++--------- docs/source/drag-n-drop-demo2.xml | 373 ++++++++++++++++++++++++++++ docs/source/drag_and_drop_input.rst | 9 +- 3 files changed, 498 insertions(+), 129 deletions(-) create mode 100644 docs/source/drag-n-drop-demo2.xml diff --git a/docs/source/drag-n-drop-demo.xml b/docs/source/drag-n-drop-demo.xml index 6f82db24a2..67712407a1 100644 --- a/docs/source/drag-n-drop-demo.xml +++ b/docs/source/drag-n-drop-demo.xml @@ -1,13 +1,15 @@ - + -

    Please set hydrogen atoms to LEFT carbon atom

    -
    +

    [Anyof rule example]


    +

    Please label hydrogen atoms connected with left carbon atom.

    +
    - + @@ -39,43 +41,44 @@ else: -

    Describe carbon molecule in LCAO-MO

    -
    +

    [Complex grading example]


    +

    Describe carbon molecule in LCAO-MO.

    +
    - + - - - - - - + + + + + + - - - - + + + + - - + + - - + + - + - + - - + + @@ -112,27 +115,27 @@ correct_answer = [ 'targets': [ 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' ], - 'rule': 'anyof' + 'rule': 'unordered_equal' }, { 'draggables': ['7','8', '9', '10'], 'targets': ['p_left_1', 'p_left_2', 'p_right_1','p_right_2'], - 'rule': 'anyof' + 'rule': 'unordered_equal' }, { 'draggables': ['11', '12'], 'targets': ['s_sigma_name', 'p_sigma_name'], - 'rule': 'anyof' + 'rule': 'unordered_equal' }, { 'draggables': ['13', '14'], 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], - 'rule': 'anyof' + 'rule': 'unordered_equal' }, { 'draggables': ['15'], 'targets': ['p_pi_name'], - 'rule': 'anyof' + 'rule': 'unordered_equal' }, { 'draggables': ['16'], 'targets': ['p_pi_star_name'], - 'rule': 'anyof' + 'rule': 'unordered_equal' }] if draganddrop.grade(submission[0], correct_answer): @@ -144,47 +147,48 @@ else: -

    Describe oxygen molecule in LCAO-MO

    -
    +

    [Another complex grading example]


    +

    Describe oxygen molecule in LCAO-MO

    +
    - + - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - - + + - - + + - + - + - - + + @@ -255,30 +259,21 @@ else: ]]>
    - -

    - Drag and drop pictures to proper positions. -

    -

    - Drag images to the cow)). -

    -
    - -

    Individual targets with outlines. One draggable per target.

    -

    Drag ant to first and star to 3rd.

    -
    +

    [Individual targets with outlines, One draggable per target]


    +

    + Drag -Ant- to first position and -Star- to third position


    - + - - + + - - - + + + @@ -298,22 +293,22 @@ else: -

    - SMALL IMAGE. Individual targets WITHOUT outlines. Move Star to the - volcano opening, and Label3 on to the right ear of the cow. - One draggable per target. -

    -
    +

    [SMALL IMAGE, Individual targets WITHOUT outlines, One draggable + per target]


    +

    + Move -Star- to the volcano opening, and -Label3- on to + the right ear of the cow. +


    - + - - + + - - - + + + @@ -333,19 +328,19 @@ else: -

    Many draggable per target. Move star and ant to most left target - and lable 3 and label 2 to most right target.

    -
    +

    [Many draggables per target]


    +

    Move -Star- and -Ant- to most left target + and -Label3- and -Label2- to most right target.


    - + - - + + - - - + + + @@ -368,20 +363,20 @@ else: -

    Draggables can be places anywhere on base image. - Place grass in the middle of the image and ant in the - right upper corner.

    -
    +

    [Draggables can be placed anywhere on base image]


    +

    + Place -Grass- in the middle of the image and -Ant- in the + right upper corner.


    - + - - + + - - - + + + @@ -398,11 +393,11 @@ else: -

    Please identify the Carbon and Oxygen atoms in the molecule.

    -
    +

    [Another anyof example]


    +

    Please identify the Carbon and Oxygen atoms in the molecule.


    - + @@ -449,13 +444,13 @@ else: -

    If the element appears in this molecule, - drag the label onto it

    -
    +

    [Again another anyof example]


    +

    If the element appears in this molecule, drag the label onto it

    +
    - + @@ -499,22 +494,18 @@ else: -

    - SMALL IMAGE. Individual targets WITHOUT outlines. Move Star to the - volcano opening, and Label3 on to the right ear of the cow. - One draggable per target. -

    -
    +

    [Wrong base image url example] +


    - + - - + + - - - + + + diff --git a/docs/source/drag-n-drop-demo2.xml b/docs/source/drag-n-drop-demo2.xml new file mode 100644 index 0000000000..6ffac18e44 --- /dev/null +++ b/docs/source/drag-n-drop-demo2.xml @@ -0,0 +1,373 @@ + + + + +

    [Draggable is reusable example]

    +
    +

    Please label all hydrogen atoms.

    +
    +
    + + + + + + + + + + + + + + + + + + + +
    + + + +

    [Complex grading example]


    +

    Describe carbon molecule in LCAO-MO.

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +

    [Many draggables per target]


    +

    Move two Stars and three Ants to most left target + and one Label3 and four Label2 to most right target.


    +
    + + + + + + + + + + + + + + + + + + +
    + + + +

    [Draggables can be placed anywhere on base image]


    +

    + Place -Grass- in the middle of the image and -Ant- in the + right upper corner.


    +
    + + + + + + + + + + + + + + +
    + + + +

    [Another anyof example]


    +

    Please identify the Carbon and Oxygen atoms in the molecule.


    +
    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +

    [Exact number of draggables for a set of targets.]


    +

    Drag two Grass and one Star to first or second positions, and three Cloud to any of the three positions.

    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + +

    [As many as you like draggables for a set of targets.]


    +

    Drag some Grass to any of the targets, and some Stars to either first or last target.

    +
    +
    + + + + + + + + + + + + + + + + + + +
    + +
    diff --git a/docs/source/drag_and_drop_input.rst b/docs/source/drag_and_drop_input.rst index d03d9755bd..06a28a5926 100644 --- a/docs/source/drag_and_drop_input.rst +++ b/docs/source/drag_and_drop_input.rst @@ -312,7 +312,12 @@ Logic flow Example ======= -Various configuration of DnD on one page ----------------------------------------- +Examples of draggables that can't be reused +------------------------------------------- .. literalinclude:: drag-n-drop-demo.xml + +Draggables can be reused +------------------------ + +.. literalinclude:: drag-n-drop-demo2.xml From 3e0333309d5635d36549c1bd5da0b98783173670 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 18:03:04 +0200 Subject: [PATCH 130/140] removed openendedinput --- common/lib/capa/capa/inputtypes.py | 49 ------------------------------ 1 file changed, 49 deletions(-) diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 919c4a3eec..0b0e86ce66 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -740,55 +740,6 @@ registry.register(ChemicalEquationInput) #----------------------------------------------------------------------------- -class OpenEndedInput(InputTypeBase): - """ - A text area input for code--uses codemirror, does syntax highlighting, special tab handling, - etc. - """ - - template = "openendedinput.html" - tags = ['openendedinput'] - - # pulled out for testing - submitted_msg = ("Feedback not yet available. Reload to check again. " - "Once the problem is graded, this message will be " - "replaced with the grader's feedback") - - @classmethod - def get_attributes(cls): - """ - Convert options to a convenient format. - """ - return [Attribute('rows', '30'), - Attribute('cols', '80'), - Attribute('hidden', ''), - ] - - def setup(self): - """ - Implement special logic: handle queueing state, and default input. - """ - # if no student input yet, then use the default input given by the problem - if not self.value: - self.value = self.xml.text - - # Check if problem has been queued - self.queue_len = 0 - # Flag indicating that the problem has been queued, 'msg' is length of queue - if self.status == 'incomplete': - self.status = 'queued' - self.queue_len = self.msg - self.msg = self.submitted_msg - - def _extra_context(self): - """Defined queue_len, add it """ - return {'queue_len': self.queue_len, } - -registry.register(OpenEndedInput) - -# ------------------------------------------------------------------------- - - class DragAndDropInput(InputTypeBase): """ Input for drag and drop problems. Allows student to drag and drop images and From 07c10859669fbc5c9539980e833ce4b62a6f3034 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 18:33:17 +0200 Subject: [PATCH 131/140] improved readability of drag and drop render test --- common/lib/capa/capa/tests/test_inputtypes.py | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index a61d47a243..6c282baf95 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -16,6 +16,7 @@ TODO: """ +import json from lxml import etree import unittest import xml.sax.saxutils as saxutils @@ -533,6 +534,25 @@ class DragAndDropTest(unittest.TestCase): state = {'value': value, 'status': 'unsubmitted'} + user_input = { # order matters, for string comparison + "target_outline": "false", + "base_image": "/static/images/about_1.png", + "draggables": [ +{"can_reuse": "", "label": "Label 1", "id": "1", "icon": ""}, +{"can_reuse": "", "label": "cc", "id": "name_with_icon", "icon": "/static/images/cc.jpg", }, +{"can_reuse": "", "label": "arrow-left", "id": "with_icon", "icon": "/static/images/arrow-left.png", "can_reuse": ""}, +{"can_reuse": "", "label": "Label2", "id": "5", "icon": "", "can_reuse": ""}, +{"can_reuse": "", "label": "Mute", "id": "2", "icon": "/static/images/mute.png", "can_reuse": ""}, +{"can_reuse": "", "label": "spinner", "id": "name_label_icon3", "icon": "/static/images/spinner.gif", "can_reuse": ""}, +{"can_reuse": "", "label": "Star", "id": "name4", "icon": "/static/images/volume.png", "can_reuse": ""}, +{"can_reuse": "", "label": "Label3", "id": "7", "icon": "", "can_reuse": ""}], + "one_per_target": "True", + "targets": [ + {"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"}, + {"y": "160", "x": "370", "id": "t2", "w": "90", "h": "90"} + ] + } + the_input = lookup_tag('drag_and_drop_input')(test_system, element, state) context = the_input._get_render_context() @@ -540,20 +560,12 @@ class DragAndDropTest(unittest.TestCase): 'value': value, 'status': 'unsubmitted', 'msg': '', - 'drag_and_drop_json': '{"use_targets": "True", \ -"target_outline": "false", "one_per_target": "True", \ -"draggables": [{"label": "Label 1", "id": "1", "icon": ""}, \ -{"label": "cc", "id": "name_with_icon", "icon": \ -"/static/images/cc.jpg"}, {"label": "arrow-left", "id": \ -"with_icon", "icon": "/static/images/arrow-left.png"}, \ -{"label": "Label2", "id": "5", "icon": ""}, {"label": \ -"Mute", "id": "2", "icon": "/static/images/mute.png"}, \ -{"label": "spinner", "id": "name_label_icon3", "icon": \ -"/static/images/spinner.gif"}, {"label": "Star", "id": \ -"name4", "icon": "/static/images/volume.png"}, {"label": \ -"Label3", "id": "7", "icon": ""}], "base_image": \ -"/static/images/about_1.png", "targets": \ -[{"y": "90", "x": "210", "id": "t1", "w": "90", "h": "90"}, \ -{"y": "160", "x": "370", "id": "t2", "w": "90", "h": "90"}]}', + 'drag_and_drop_json': json.dumps(user_input) } + + # as we are dumping 'draggables' dicts while dumping user_input, string + # comparison will fail, as order of keys is random. + self.assertEqual(json.loads(context['drag_and_drop_json']), user_input) + context.pop('drag_and_drop_json') + expected.pop('drag_and_drop_json') self.assertEqual(context, expected) From e78c020a52dd0b8e979b2d746dfc20599be05e04 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 18:38:03 +0200 Subject: [PATCH 132/140] removed dublicate comment --- common/lib/capa/capa/verifiers/draganddrop.py | 1 - 1 file changed, 1 deletion(-) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 9bfbf982ff..906b9ffb7e 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -130,7 +130,6 @@ class DragAndDrop(object): current_rule = self.correct_positions[groupname].keys()[0] if 'number' in current_rule: rule_values = self.correct_positions[groupname][current_rule] - #clean rule, do not do clean dublicate items self.correct_positions[groupname].pop(current_rule, None) parsed_rule = current_rule.replace('+', '').replace('number', '') self.correct_positions[groupname][parsed_rule] = rule_values From 0f5e7cd9e44b04d47b298b2f28a1652c729a4784 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 18:55:29 +0200 Subject: [PATCH 133/140] explained for - else --- common/lib/capa/capa/verifiers/draganddrop.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 906b9ffb7e..b1072507d2 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -217,6 +217,13 @@ class DragAndDrop(object): if PositionsCompare(u_el) == PositionsCompare(c_el): break else: + # General: the else is executed after the for, + # only if the for terminates normally (not by a break) + + # In this case, 'for' is terminated normally if every element + # from 'correct' list isn't equal to concrete element from + # 'user' list. So as we found one element from 'user' list, + # that not in 'correct' list - we return False return False if flag == 'unordered_equal': @@ -229,6 +236,8 @@ class DragAndDrop(object): temp.remove(c_el) break else: + # same as upper - if we found element from 'user' list, + # that not in 'correct' list - we return False. return False return True From 2d67ee477eaaa5c984abbbddf1c56c2233f30978 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 18:58:30 +0200 Subject: [PATCH 134/140] commend about rule in simple form --- common/lib/capa/capa/verifiers/draganddrop.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index b1072507d2..6d4f0623ab 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -255,6 +255,9 @@ class DragAndDrop(object): It is draggable_name: dragable_position mapping. + Complex form converted from simple form uses 'exact' rule + for matching. + Correct answer in list form is designed for complex cases:: correct_answers = [ From a95e809824cfa03f54ded72354bb166250d4c756 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 19:05:30 +0200 Subject: [PATCH 135/140] documentation extended --- common/lib/capa/capa/verifiers/draganddrop.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 6d4f0623ab..1756ab355b 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -126,7 +126,8 @@ class DragAndDrop(object): # 'number' rule special case # for reusable draggables we may get in self.user_groups # {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']} - # if +number in rule - do not remove duplicates but clean rule + # if '+number' is in rule - do not remove duplicates and strip + # '+number' from rule current_rule = self.correct_positions[groupname].keys()[0] if 'number' in current_rule: rule_values = self.correct_positions[groupname][current_rule] @@ -246,7 +247,7 @@ class DragAndDrop(object): """ Populates DragAndDrop variables from user_answer and correct_answer. If correct_answer is dict, converts it to list. Correct answer in dict form is simpe structure for fast and simple - grading. Example of corrrect answer dict example:: + grading. Example of correct answer dict example:: correct_answer = {'name4': 't1', 'name_with_icon': 't1', @@ -255,10 +256,10 @@ class DragAndDrop(object): It is draggable_name: dragable_position mapping. - Complex form converted from simple form uses 'exact' rule + Advanced form converted from simple form uses 'exact' rule for matching. - Correct answer in list form is designed for complex cases:: + Correct answer in list form is designed for advanced cases:: correct_answers = [ { @@ -273,10 +274,17 @@ class DragAndDrop(object): } ] - Correct answer in list form is list of dicts, and every dict must have + Advanced answer in list form is list of dicts, and every dict must have 3 keys: 'draggables', 'targets' and 'rule'. 'Draggables' value is list of draggables ids, 'targes' values are list of targets ids, 'rule' - value is 'exact' or 'anyof'. + value one of 'exact', 'anyof', 'unordered_equal', 'anyof+number', + 'unordered_equal+number' + + Advanced form uses "all dicts must match with their rule" logic. + + Same draggable cannot appears more that in one dict. + + Behavior is more widely explained in sphinx documentation. Args: user_answer: json From 00f966a94a0d32691f990bcc41b3ab2fccf5c861 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 19:22:23 +0200 Subject: [PATCH 136/140] populate -> __init__ --- common/lib/capa/capa/verifiers/draganddrop.py | 23 ++++++++----------- .../capa/capa/verifiers/tests_draganddrop.py | 19 ++++++++------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 1756ab355b..87381fad76 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -101,11 +101,6 @@ class PositionsCompare(list): class DragAndDrop(object): """ Grader class for drag and drop inputtype. """ - def __init__(self): - self.correct_groups = dict() # correct groups from xml - self.correct_positions = dict() # correct positions for comparing - self.user_groups = dict() # will be populated from user answer - self.user_positions = dict() # will be populated from user answer def grade(self): ''' Grader user answer. @@ -243,7 +238,7 @@ class DragAndDrop(object): return True - def populate(self, correct_answer, user_answer): + def __init__(self, correct_answer, user_answer): """ Populates DragAndDrop variables from user_answer and correct_answer. If correct_answer is dict, converts it to list. Correct answer in dict form is simpe structure for fast and simple @@ -289,10 +284,13 @@ class DragAndDrop(object): Args: user_answer: json correct_answer: dict or list + """ - Returns: None + self.correct_groups = dict() # correct groups from xml + self.correct_positions = dict() # correct positions for comparing + self.user_groups = dict() # will be populated from user answer + self.user_positions = dict() # will be populated from user answer - """ # convert from dict answer format to list format if isinstance(correct_answer, dict): tmp = [] @@ -330,8 +328,8 @@ class DragAndDrop(object): def grade(user_input, correct_answer): - """ Populates DragAndDrop instance from user_input and correct_answer and - calls DragAndDrop.drade for grading. + """ Creates DragAndDrop instance from user_input and correct_answer and + calls DragAndDrop.grade for grading. Supports two interfaces for correct_answer: dict and list. @@ -373,6 +371,5 @@ def grade(user_input, correct_answer): Returns: bool """ - dnd = DragAndDrop() - dnd.populate(correct_answer=correct_answer, user_answer=user_input) - return dnd.grade() + return DragAndDrop(correct_answer=correct_answer, + user_answer=user_input).grade() diff --git a/common/lib/capa/capa/verifiers/tests_draganddrop.py b/common/lib/capa/capa/verifiers/tests_draganddrop.py index 55425ae910..9b1b15ce0c 100644 --- a/common/lib/capa/capa/verifiers/tests_draganddrop.py +++ b/common/lib/capa/capa/verifiers/tests_draganddrop.py @@ -520,11 +520,10 @@ class Test_DragAndDrop_Grade(unittest.TestCase): class Test_DragAndDrop_Populate(unittest.TestCase): def test_1(self): - dnd = draganddrop.DragAndDrop() correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} user_input = '{"draggables": \ [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' - dnd.populate(correct_answer, user_input) + dnd = draganddrop.DragAndDrop(correct_answer, user_input) correct_groups = {'1': ['name_with_icon'], '0': ['1']} correct_positions = {'1': {'exact': [[20, 20]]}, '0': {'exact': [[[40, 10], 29]]}} @@ -540,49 +539,49 @@ class Test_DragAndDrop_Populate(unittest.TestCase): class Test_DraAndDrop_Compare_Positions(unittest.TestCase): def test_1(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='anyof')) def test_2a(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertTrue(dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='exact')) def test_2b(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertFalse(dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 13], [1, 1]], flag='exact')) def test_3(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertFalse(dnd.compare_positions(correct=["a", "b"], user=["a", "b", "c"], flag='anyof')) def test_4(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], user=["a", "b"], flag='anyof')) def test_5(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertFalse(dnd.compare_positions(correct=["a", "b", "c"], user=["a", "c", "b"], flag='exact')) def test_6(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertTrue(dnd.compare_positions(correct=["a", "b", "c"], user=["a", "c", "b"], flag='anyof')) def test_7(self): - dnd = draganddrop.DragAndDrop() + dnd = draganddrop.DragAndDrop({'1': 't1'}, '{"draggables": [{"1": "t1"}]}') self.assertFalse(dnd.compare_positions(correct=["a", "b", "b"], user=["a", "c", "b"], flag='anyof')) From 91281915c4b7f8ed4a0c70488ffb3e7485f6ed79 Mon Sep 17 00:00:00 2001 From: Alexander Kryklia Date: Mon, 21 Jan 2013 19:28:49 +0200 Subject: [PATCH 137/140] update of deleted comment --- common/lib/capa/capa/verifiers/draganddrop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/common/lib/capa/capa/verifiers/draganddrop.py b/common/lib/capa/capa/verifiers/draganddrop.py index 87381fad76..eb91208923 100644 --- a/common/lib/capa/capa/verifiers/draganddrop.py +++ b/common/lib/capa/capa/verifiers/draganddrop.py @@ -126,6 +126,7 @@ class DragAndDrop(object): current_rule = self.correct_positions[groupname].keys()[0] if 'number' in current_rule: rule_values = self.correct_positions[groupname][current_rule] + # clean rule, do not do clean duplicate items self.correct_positions[groupname].pop(current_rule, None) parsed_rule = current_rule.replace('+', '').replace('number', '') self.correct_positions[groupname][parsed_rule] = rule_values From 899c501d3d29ea3b4fa83610e45e90ae075bdc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Mon, 21 Jan 2013 13:56:01 -0500 Subject: [PATCH 138/140] Add announcement of Walter Lewin's course [#157] --- .../dr-lewin-276_2400x1600.jpg.REMOVED.git-id | 1 + .../press/releases/dr-lewin-276_240x180.jpg | Bin 0 -> 18144 bytes .../press/releases/dr-lewin-316_240x180.jpg | Bin 0 -> 9681 bytes lms/templates/feed.rss | 23 ++++-- .../Lewin_course_announcement.html | 77 ++++++++++++++++++ lms/urls.py | 4 +- 6 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 lms/static/images/press/releases/dr-lewin-276_2400x1600.jpg.REMOVED.git-id create mode 100644 lms/static/images/press/releases/dr-lewin-276_240x180.jpg create mode 100644 lms/static/images/press/releases/dr-lewin-316_240x180.jpg create mode 100644 lms/templates/static_templates/press_releases/Lewin_course_announcement.html diff --git a/lms/static/images/press/releases/dr-lewin-276_2400x1600.jpg.REMOVED.git-id b/lms/static/images/press/releases/dr-lewin-276_2400x1600.jpg.REMOVED.git-id new file mode 100644 index 0000000000..7166a5027c --- /dev/null +++ b/lms/static/images/press/releases/dr-lewin-276_2400x1600.jpg.REMOVED.git-id @@ -0,0 +1 @@ +b4d043bb1ca4a8815d4a388a2c9d96038211417b \ No newline at end of file diff --git a/lms/static/images/press/releases/dr-lewin-276_240x180.jpg b/lms/static/images/press/releases/dr-lewin-276_240x180.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1a5300b1ec60f0a4b63ea92d2794d159ed279c1 GIT binary patch literal 18144 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 literal 0 HcmV?d00001 diff --git a/lms/static/images/press/releases/dr-lewin-316_240x180.jpg b/lms/static/images/press/releases/dr-lewin-316_240x180.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6a659a36124a62dcc1649700edbff710184f17bb GIT binary patch literal 9681 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 gHqmgL-?sZ?mQYi3 ## EdX Blog - 2012-12-19T14:00:12-07:00 + 2013-01-21T14:00:12-07:00 - tag:www.edx.org,2012:Post/10 - 2012-12-19T14:00:00-07:00 - 2012-12-19T14:00:00-07:00 - - edX announces first wave of new courses for Spring 2013 - <img src="${static.url('images/press/releases/edx-logo_240x180.png')}" /> + tag:www.edx.org,2012:Post/11 + 2013-01-22T10:00:00-07:00 + 2013-01-22T10:00:00-07:00 + + New course from legendary MIT physics professor Walter Lewin + <img src="${static.url('images/press/releases/dr-lewin-316_240x180.jpg')}" /> <p></p> + + + + + + + + + tag:www.edx.org,2012:Post/9 2012-12-10T14:00:00-07:00 diff --git a/lms/templates/static_templates/press_releases/Lewin_course_announcement.html b/lms/templates/static_templates/press_releases/Lewin_course_announcement.html new file mode 100644 index 0000000000..4fb2a2c83e --- /dev/null +++ b/lms/templates/static_templates/press_releases/Lewin_course_announcement.html @@ -0,0 +1,77 @@ +<%! from django.core.urlresolvers import reverse %> +<%inherit file="../../main.html" /> + +<%namespace name='static' file='../../static_content.html'/> + +<%block name="title">New Course from legendary MIT physics professor Walter Lewin +
    + + +
    diff --git a/lms/urls.py b/lms/urls.py index cab0533f89..ad3b324534 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -118,9 +118,11 @@ urlpatterns = ('', {'template': 'press_releases/Georgetown_joins_edX.html'}, name="press/georgetown-joins-edx"), url(r'^press/spring-courses$', 'static_template_view.views.render', {'template': 'press_releases/Spring_2013_course_announcements.html'}, name="press/spring-courses"), + url(r'^press/lewin-course-announcement$', 'static_template_view.views.render', + {'template': 'press_releases/Lewin_course_announcement.html'}, name="press/lewin-course-announcement"), # Should this always update to point to the latest press release? - (r'^pressrelease$', 'django.views.generic.simple.redirect_to', {'url': '/press/spring-courses'}), + (r'^pressrelease$', 'django.views.generic.simple.redirect_to', {'url': '/press/lewin-course-announcement'}), (r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/static/images/favicon.ico'}), From 183721ecf5247e00d0dc8e0cc903509a3b907760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Mon, 21 Jan 2013 13:59:05 -0500 Subject: [PATCH 139/140] Correct last screenshot caption of media-kit page. [#159] --- lms/templates/static_templates/media-kit.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/templates/static_templates/media-kit.html b/lms/templates/static_templates/media-kit.html index 458cfb8e15..73eea9c3b8 100644 --- a/lms/templates/static_templates/media-kit.html +++ b/lms/templates/static_templates/media-kit.html @@ -89,7 +89,7 @@
    -
    Screenshot of 6.00x: Introduction to Computer Science and Programming.
    +
    Screenshot of 3.091x: Introduction to Solid State Chemistry.
    Download (High Resolution Photo)
    @@ -108,4 +108,4 @@ return false; }); - \ No newline at end of file + From 45f2a690d039276b797d503f6f37ee3250c81a47 Mon Sep 17 00:00:00 2001 From: Peter Baratta Date: Thu, 17 Jan 2013 06:51:57 -0700 Subject: [PATCH 140/140] Commented on high level functions --- lms/lib/symmath/formula.py | 16 ++++++++++---- lms/lib/symmath/symmath_check.py | 37 +++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lms/lib/symmath/formula.py b/lms/lib/symmath/formula.py index bab0ab3691..c34156da52 100644 --- a/lms/lib/symmath/formula.py +++ b/lms/lib/symmath/formula.py @@ -154,8 +154,9 @@ def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False class formula(object): ''' - Representation of a mathematical formula object. Accepts mathml math expression for constructing, - and can produce sympy translation. The formula may or may not include an assignment (=). + Representation of a mathematical formula object. Accepts mathml math expression + for constructing, and can produce sympy translation. The formula may or may not + include an assignment (=). ''' def __init__(self, expr, asciimath='', options=None): self.expr = expr.strip() @@ -194,8 +195,12 @@ class formula(object): def preprocess_pmathml(self, xml): ''' - Pre-process presentation MathML from ASCIIMathML to make it more acceptable for SnuggleTeX, and also - to accomodate some sympy conventions (eg hat(i) for \hat{i}). + Pre-process presentation MathML from ASCIIMathML to make it more + acceptable for SnuggleTeX, and also to accomodate some sympy + conventions (eg hat(i) for \hat{i}). + + This method would be a good spot to look for an integral and convert + it, if possible... ''' if type(xml) == str or type(xml) == unicode: @@ -266,6 +271,9 @@ class formula(object): ''' Return sympy expression for the math formula. The math formula is converted to Content MathML then that is parsed. + + This is a recursive function, called on every CMML node. Support for + more functions can be added by modifying opdict, abould halfway down ''' if self.the_sympy: return self.the_sympy diff --git a/lms/lib/symmath/symmath_check.py b/lms/lib/symmath/symmath_check.py index bcb4a0d490..3cc4fd7d3c 100644 --- a/lms/lib/symmath/symmath_check.py +++ b/lms/lib/symmath/symmath_check.py @@ -157,13 +157,33 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ''' Check a symbolic mathematical expression using sympy. The input may be presentation MathML. Uses formula. + + This is the default Symbolic Response checking function + + Desc of args: + expect is a sympy string representing the correct answer. It is interpreted + using my_sympify (from formula.py), which reads strings as sympy input + (e.g. 'integrate(x^2, (x,1,2))' would be valid, and evaluate to give 1.5) + + ans is student-typed answer. It is expected to be ascii math, but the code + below would support a sympy string. + + dynamath is the PMathML string converted by MathJax. It is used if + evaluation with ans is not sufficient. + + options is a string with these possible substrings, set as an xml property + of the problem: + -matrix - make a sympy matrix, rather than a list of lists, if possible + -qubit - passed to my_sympify + -imaginary - used in formla, presumably to signal to use i as sqrt(-1)? + -numerical - force numerical comparison. ''' msg = '' # msg += '

    abname=%s' % abname # msg += '

    adict=%s' % (repr(adict).replace('<','<')) - threshold = 1.0e-3 + threshold = 1.0e-3 # for numerical comparison (also with matrices) DEBUG = debug if xml is not None: @@ -184,13 +204,17 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None msg += '

    Error %s in parsing OUR expected answer "%s"

    ' % (err, expect) return {'ok': False, 'msg': make_error_message(msg)} + + ###### Sympy input ####### # if expected answer is a number, try parsing provided answer as a number also try: fans = my_sympify(str(ans), matrix=do_matrix, do_qubit=do_qubit) except Exception, err: fans = None - if hasattr(fexpect, 'is_number') and fexpect.is_number and fans and hasattr(fans, 'is_number') and fans.is_number: + # do a numerical comparison if both expected and answer are numbers + if (hasattr(fexpect, 'is_number') and fexpect.is_number and fans + and hasattr(fans, 'is_number') and fans.is_number): if abs(abs(fans - fexpect) / fexpect) < threshold: return {'ok': True, 'msg': msg} else: @@ -208,6 +232,8 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None msg += '

    You entered: %s

    ' % to_latex(fans) return {'ok': True, 'msg': msg} + + ###### PMathML input ###### # convert mathml answer to formula try: if dynamath: @@ -216,6 +242,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None mmlans = None if not mmlans: return {'ok': False, 'msg': '[symmath_check] failed to get MathML for input; dynamath=%s' % dynamath} + f = formula(mmlans, options=options) # get sympy representation of the formula @@ -238,7 +265,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None msg += '
    ' return {'ok': False, 'msg': make_error_message(msg)} - # compare with expected + # do numerical comparison with expected if hasattr(fexpect, 'is_number') and fexpect.is_number: if hasattr(fsym, 'is_number') and fsym.is_number: if abs(abs(fsym - fexpect) / fexpect) < threshold: @@ -250,6 +277,10 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None # msg += "

    cmathml =

    %s

    " % str(f.cmathml).replace('<','<') return {'ok': False, 'msg': make_error_message(msg)} + # Here is a good spot for adding calls to X.simplify() or X.expand(), + # allowing equivalence over binomial expansion or trig identities + + # exactly the same? if fexpect == fsym: return {'ok': True, 'msg': msg}
    +
    +

    Afraid of physics? Do you hate it?
    Walter Lewin will make you love physics whether you like it or not

    +
    +
    +

    MIT physics professor and online web star brings his renowned Electricity and Magnetism course to edX

    + +
    + +
    +

    Walter Lewin, legendary MIT physics professor, demonstrates, in his inimitable fashion, one of the many laws of physics covered in his new course on edX.

    +

    Credit: Dominick Reuter

    + High Resolution Image

    +
    +
    + +

    CAMBRIDGE, MA – January 22, 2013 – EdX, the not-for-profit online learning initiative founded by Harvard University and the Massachusetts Institute of Technology (MIT), announced today a new course from the legendary Professor Walter Lewin who, for 47 years, has provided generations of MIT students – and millions watching online – with his inspiring and unconventional lectures. Now, with this edX version of Professor Lewin’s famous course Electricity and Magnetism (Physics), people around the world can experience it just like his students on the MIT campus. MITx 8.02x Electricity and Magnetism is now open for enrollment and classes will begin on February 18, 2013.

    + +

    “I have taught this course to tens of thousands and many tell me it changed their lives,” said Walter Lewin, Professor of Physics at MIT. “Teaching is my passion: I want to open peoples’ eyes and minds to the beauty of physics so they will begin to see the world in a new way.”

    + +

    In 8.02x Electricity and Magnetism, Professor Lewin will teach students to “see” the world instead of just “looking at” it. He will make them “see” natural phenomena such as rainbows in a way they never imagined before. Through his dynamic teaching, enthusiasm and great sense of humor, Professor Lewin has an innate ability to make difficult concepts easy. The New York Times has crowned him a “Web Star” and noted how his lectures, with their engaging physics demonstrations, have won him devotees around the world. While this course is MIT level, edX and Professor Lewin encourage even senior high school students from around the world to watch his lectures and take the course.

    + +

    “Walter Lewin is an international treasure,” said Anant Agarwal, President of edX. “His physics lectures on the MIT campus were already legendary before he put them online and they became an international sensation. We know edX learners will be awestruck by his provocative and enlightening course.”

    + +

    In addition to the basic concepts of Electromagnetism, a vast variety of interesting topics are covered, including Lightning, Pacemakers, Electric Shock Treatment, Electrocardiograms, Metal Detectors, Musical Instruments, Magnetic Levitation, Bullet Trains, Electric Motors, Radios, TV, Car Coils, Superconductivity, Aurora Borealis, Rainbows, Radio Telescopes, Interferometers, Particle Accelerators such as the Large Hadron Collider, Mass Spectrometers, Red Sunsets, Blue Skies, Haloes around Sun and Moon, Color Perception, Doppler Effect and Big-Bang Cosmology.

    + +

    Professor Lewin received his PhD in Nuclear Physics at the Technical University in Delft, the Netherlands in 1965. He joined the Physics faculty at MIT in 1966 and became a pioneer in the new field of X-ray Astronomy. His 105 online lectures are world-renowned and are viewed by nearly 2 million people annually. Professor Lewin has received five teaching awards and is the only MIT professor featured in "The Best 300 Professors" by The Princeton Review. He has co-authored with Warren Goldstein the book "For the Love of Physics" (Free Press, Simon & Schuster), which has been translated into 9 languages.

    + +

    Previously announced new 2013 courses include: Justice from Michael Sandel; Introduction to Statistics from Ani Adhikari; The Challenges of Global Poverty from Esther Duflo; The Ancient Greek Hero from Gregory Nagy; Quantum Mechanics and Quantum Computation from Umesh Vazirani; Human Health and Global Environmental Change, from Aaron Bernstein and Jack Spengler.

    + +

    In addition to these new courses, edX is bringing back several courses from the popular fall 2012 semester: Introduction to Computer Science and Programming; Introduction to Solid State Chemistry; Introduction to Artificial Intelligence; Software as a Service I; Software as a Service II; Foundations of Computer Graphics.

    + +

    About edX

    + +

    EdX is a not-for-profit enterprise of its founding partners Harvard University and the Massachusetts Institute of Technology focused on transforming online and on-campus learning through groundbreaking methodologies, game-like experiences and cutting-edge research. EdX provides inspirational and transformative knowledge to students of all ages, social status, and income who form worldwide communities of learners. EdX uses its open source technology to transcend physical and social borders. We’re focused on people, not profit. EdX is based in Cambridge, Massachusetts in the USA.

    + +
    +

    Contact:

    +

    Brad Baker, Weber Shandwick for edX

    +

    BBaker@webershandwick.com

    +

    (617) 520-7043

    +
    + + +
    +
    +

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