8b02ae961d1a912d4ca45091d6df7d39b7a7f796
[redakcja.git] / redakcja / static / edumed / js / edumed.coffee
1
2 $ = jQuery
3
4 class Binding
5   constructor: (@handler, @element) ->
6     $(@element).data(@handler, this)
7
8
9 class EduModule extends Binding
10   constructor: (element) ->
11     super 'edumodule', element
12
13     $("[name=teacher-toggle]").change (ev) =>
14       if $(ev.target).is(":checked")
15         $(".teacher", @element).addClass "show"
16       else
17         $(".teacher", @element).removeClass "show"
18
19
20 class Excercise extends Binding
21   constructor: (element) ->
22     super 'excercise', element
23
24     $(".check", @element).click =>
25       @check()
26     $('.solutions', @element).click =>
27       @show_solutions()
28
29   piece_correct: (qpiece) ->
30     $(qpiece).removeClass('incorrect').addClass('correct')
31
32   piece_incorrect: (qpiece) ->
33     $(qpiece).removeClass('correct').addClass('incorrect')
34
35   check: ->
36     scores = []
37     $(".question", @element).each (i, question) =>
38       scores.push(@check_question question)
39
40     score = [0, 0]
41     $.each scores, (i, s) ->
42       score[0] += s[0]
43       score[1] += s[1]
44     @show_score(score)
45
46   get_value_list: (elem, data_key, numbers) ->
47     vl = $(elem).attr("data-" + data_key).split(/[ ,]+/).map($.trim) #.map((x) -> parseInt(x))
48     if numbers
49       vl = vl.map((x) -> parseInt(x))
50     return vl
51
52   get_value_optional_list: (elem, data_key) ->
53     vals = @get_value_list(elem, data_key)
54     mandat = []
55     opt = []
56     for v in vals
57       if v.slice(-1) == "?"
58         opt.push v.slice(0, -1)
59       else
60         mandat.push v
61     return [mandat, opt]
62
63   show_score: (score) ->
64     $(".message", @element).text("Wynik: #{score[0]} / #{score[1]}")
65
66
67 class Wybor extends Excercise
68   constructor: (element) ->
69     super element
70
71
72   check_question: (question) ->
73     all = 0
74     good = 0
75     solution = @get_value_list(question, 'solution')
76     $(".question-piece", question).each (i, qpiece) =>
77       piece_no = $(qpiece).attr 'data-no'
78       piece_name = $(qpiece).attr 'data-name'
79       if piece_name
80         should_be_checked = solution.indexOf(piece_name) >= 0
81       else
82         should_be_checked = solution.indexOf(piece_no) >= 0
83       is_checked = $("input", qpiece).is(":checked")
84
85       if should_be_checked
86         all += 1
87
88       if is_checked
89         if should_be_checked
90           good += 1
91           @piece_correct qpiece
92         else
93           @piece_incorrect qpiece
94       else
95         $(qpiece).removeClass("correct,incorrect")
96
97     return [good, all]
98
99   show_solutions: ->
100
101
102 class Uporzadkuj extends Excercise
103   constructor: (element) ->
104     super element
105     $('ol, ul', @element).sortable({ items: "> li" })
106
107   check_question: (question) ->
108     positions = @get_value_list(question, 'original', true)
109     sorted = positions.sort()
110     pkts = $('.question-piece', question)
111
112     correct = 0
113     all = 0
114
115     for pkt in [0...pkts.length]
116       all +=1
117       if pkts.eq(pkt).data('pos') == sorted[pkt]
118         correct += 1
119         @piece_correct pkts.eq(pkt)
120       else
121         @piece_incorrect pkts.eq(pkt)
122     return [correct, all]
123
124
125 # XXX propozycje="1/0"
126 class Luki extends Excercise
127   constructor: (element) ->
128     super element
129
130   check: ->
131     all = 0
132     correct = 0
133     $(".question-piece", @element).each (i, qpiece) =>
134       if $(qpiece).data('solution') == $(qpiece).val()
135         @piece_correct qpiece
136         correct += 1
137       else
138         @piece_incorrect qpiece
139       all += 1
140
141     @show_score [correct, all]
142
143
144 class Zastap extends Excercise
145   constructor: (element) ->
146     super element
147     $(".paragraph", @element).each (i, par) =>
148       @wrap_words $(par), $('<span class="zastap question-piece"/>')
149       spans = $("> span", par).attr("contenteditable", "true")
150       spans.click (ev) =>
151         spans.filter(':not(:empty)').removeClass('editing')
152         $(ev.target).addClass('editing')
153
154
155   check: ->
156     all = 0
157     correct = 0
158     $(".question-piece", @element).each (i, qpiece) =>
159       txt = $(qpiece).data('original')
160       should_be_changed = false
161       if not txt?
162         txt = $(qpiece).data('solution')
163         should_be_changed = true
164       if not txt?
165         return
166
167       if should_be_changed
168         all += 1
169
170       if txt != $(qpiece).text()
171         @piece_incorrect qpiece
172       else
173         if should_be_changed
174           @piece_correct qpiece
175           correct += 1
176
177     @show_score [correct, all]
178
179   wrap_words: (element, wrapper) ->
180     # This function wraps each word of element in wrapper, but does not descend into child-tags of element.
181     # It doesn't wrap things between words (defined by ignore RE below). Warning - ignore must begin with ^
182     ignore = /^[ \t.,:;()]+/
183
184     insertWrapped = (txt, elem) ->
185       nw = wrapper.clone()
186       $(document.createTextNode(txt))
187         .wrap(nw).parent().attr("data-original", txt).insertBefore(elem)
188
189     for j in [element.get(0).childNodes.length-1..0]
190       chld = element.get(0).childNodes[j]
191       if chld.nodeType == document.TEXT_NODE
192         len = chld.textContent.length
193         wordb = 0
194         i = 0
195         while i < len
196           space = ignore.exec(chld.textContent.substr(i))
197           if space?
198             if wordb < i
199               insertWrapped(chld.textContent.substr(wordb, i-wordb), chld)
200
201             $(document.createTextNode(space[0])).insertBefore(chld)
202             i += space[0].length
203             wordb = i
204           else
205             i = i + 1
206         if wordb < len - 1
207           insertWrapped(chld.textContent.substr(wordb, len - 1 - wordb), chld)
208         $(chld).remove()
209
210
211 class Przyporzadkuj extends Excercise
212   is_multiple: ->
213     for qp in $(".question-piece", @element)
214       if $(qp).data('solution').split(/[ ,]+/).length > 1
215         return true
216     return false
217
218   constructor: (element) ->
219     super element
220
221     @multiple = @is_multiple()
222
223     $(".question", @element).each (i, question) =>
224       draggable_opts =
225         revert: 'invalid'
226       if @multiple
227         helper_opts = { helper: "clone" }
228       else helper_opts = {}
229
230       $(".draggable", question).draggable($.extend({}, draggable_opts,
231         helper_opts))
232
233       $(".predicate .droppable", question).droppable
234         accept: (draggable) ->
235           $draggable = $(draggable)
236           if not $draggable.is(".draggable")
237             return false
238           $predicate = $(this)
239
240           for added in $predicate.find("li")
241             if $(added).text() == $draggable.text()
242               return false
243           return true
244
245         drop: (ev, ui) =>
246           added = ui.draggable.clone()
247
248           added.attr('style', '')
249           $(ev.target).append(added)
250           added.draggable(draggable_opts)
251
252           if not @multiple or ui.draggable.closest(".predicate").length > 0
253             ui.draggable.remove()
254
255
256       $(".subject", question).droppable
257         accept: ".draggable"
258         drop: (ev, ui) =>
259           # this is to prevent a situation of dragging out and
260           # dropping back to the same place
261           if $(ui.draggable).closest(".subject").length > 0
262             return
263
264
265           added = ui.draggable.clone()
266
267           added.attr('style', '')
268           if not @multiple
269             $(ev.target).append(added)
270             added.draggable($.extend({}, draggable_opts, helper_opts))
271
272           ui.draggable.remove()
273
274   check_question: (question) ->
275     # subjects placed in predicates
276     count = 0
277     all = 0
278     all_multiple = 0
279     for qp in $(".predicate .question-piece", question)
280       pred = $(qp).closest("[data-predicate]")
281       v = @get_value_optional_list qp, 'solution'
282       mandatory = v[0]
283       optional = v[1]
284       all_multiple += mandatory.length + optional.length
285       pn = pred.data('predicate')
286       if mandatory.indexOf(pn) >= 0 or optional.indexOf(pn) >= 0
287         count += 1
288         @piece_correct qp
289       else
290         @piece_incorrect qp
291       all += 1
292
293     if @multiple
294       for qp in $(".subject .question-piece", question)
295         v = @get_value_optional_list qp, 'solution'
296         mandatory = v[0]
297         optional = v[1]
298         all_multiple += mandatory.length + optional.length
299       return [count, all_multiple]
300     else
301       return [count, all]
302
303
304
305
306
307
308 ##########
309
310 excercise = (ele) ->
311   es =
312     wybor: Wybor
313     uporzadkuj: Uporzadkuj
314     luki: Luki
315     zastap: Zastap
316     przyporzadkuj: Przyporzadkuj
317
318
319   cls = es[$(ele).attr('data-type')]
320   new cls(ele)
321
322
323 window.edumed =
324   'EduModule': EduModule
325
326
327
328
329 $(document).ready () ->
330   new EduModule($("#book-text"))
331
332   $(".excercise").each (i, el) ->
333     excercise(this)