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