771e7c72e9e7b4e552dbed29b2afacdd404f039f
[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).parent().droppable
234         accept: (draggable) ->
235           $draggable = $(draggable)
236           if not $draggable.is(".draggable")
237             console.log('not draggable?')
238             return false
239           $predicate = $(this)
240
241           for added in $predicate.find("li")
242             if $(added).text() == $draggable.text()
243               console.log('already here:' + $draggable.text())
244               return false
245           return true
246
247         drop: (ev, ui) =>
248           added = ui.draggable.clone()
249
250           added.attr('style', '')
251           $(ev.target).find(".droppable").append(added)
252           added.draggable(draggable_opts)
253
254           if not @multiple or ui.draggable.closest(".predicate").length > 0
255             ui.draggable.remove()
256
257       $(".predicate .droppable", question).sortable
258         items: "> li"
259
260       $(".subject", question).droppable
261         accept: ".draggable"
262         drop: (ev, ui) =>
263           # this is to prevent a situation of dragging out and
264           # dropping back to the same place
265           if $(ui.draggable).closest(".subject").length > 0
266             return
267
268
269           added = ui.draggable.clone()
270
271           added.attr('style', '')
272           if not @multiple
273             $(ev.target).append(added)
274             added.draggable($.extend({}, draggable_opts, helper_opts))
275
276           ui.draggable.remove()
277
278   check_question: (question) ->
279     # subjects placed in predicates
280     count = 0
281     all = 0
282     all_multiple = 0
283     for qp in $(".predicate .question-piece", question)
284       pred = $(qp).closest("[data-predicate]")
285       v = @get_value_optional_list qp, 'solution'
286       mandatory = v[0]
287       optional = v[1]
288       all_multiple += mandatory.length + optional.length
289       pn = pred.data('predicate')
290       if mandatory.indexOf(pn) >= 0 or optional.indexOf(pn) >= 0
291         count += 1
292         @piece_correct qp
293       else
294         @piece_incorrect qp
295       all += 1
296
297     if @multiple
298       for qp in $(".subject .question-piece", question)
299         v = @get_value_optional_list qp, 'solution'
300         mandatory = v[0]
301         optional = v[1]
302         all_multiple += mandatory.length + optional.length
303       return [count, all_multiple]
304     else
305       return [count, all]
306
307
308 class PrawdaFalsz extends Excercise
309   constructor: (element) ->
310     super element
311
312     for qp in $(".question-piece", @element)
313       $(".true", qp).click (ev) ->
314         ev.preventDefault()
315         $(this).closest(".question-piece").data("value", "true")
316         $(this).addClass('chosen').siblings('a').removeClass('chosen')
317       $(".false", qp).click (ev) ->
318         ev.preventDefault()
319         $(this).closest(".question-piece").data("value", "false")
320         $(this).addClass('chosen').siblings('a').removeClass('chosen')
321
322
323   check_question: ->
324     all = 0
325     good = 0
326     for qp in $(".question-piece", @element)
327       if $(qp).data("solution").toString() == $(qp).data("value")
328         good += 1
329         @piece_correct qp
330       else
331         @piece_incorrect qp
332
333       all += 1
334
335     return [good, all]
336
337 ##########
338
339 excercise = (ele) ->
340   es =
341     wybor: Wybor
342     uporzadkuj: Uporzadkuj
343     luki: Luki
344     zastap: Zastap
345     przyporzadkuj: Przyporzadkuj
346     prawdafalsz: PrawdaFalsz
347
348
349   cls = es[$(ele).attr('data-type')]
350   new cls(ele)
351
352
353 window.edumed =
354   'EduModule': EduModule
355
356
357
358
359 $(document).ready () ->
360   new EduModule($("#book-text"))
361
362   $(".excercise").each (i, el) ->
363     excercise(this)