dragging overhaul
[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   draggable_equal: ($draggable1, $draggable2) ->
68     return false
69
70   draggable_accept: ($draggable, $droppable) ->
71     dropped = $droppable.closest("ul, ol").find(".draggable")
72     for d in dropped
73       if @draggable_equal $draggable, $(d)
74         return false
75     return true
76
77   draggable_dropped: ($draggable) ->
78     $draggable.append('<span class="close">x</span>')
79
80   dragging: (ismultiple, issortable) ->
81     $(".question", @element).each (i, question) =>
82       draggable_opts =
83         revert: 'invalid'
84         helper: 'clone'
85
86       $(".draggable", question).draggable(draggable_opts)
87       self = this
88       $(".placeholder", question).droppable
89         accept: (draggable) ->
90           $draggable = $(draggable)
91           if not $draggable.is(".draggable")
92             return false
93           return self.draggable_accept $draggable, $(this)
94
95         drop: (ev, ui) ->
96           added = $(ui.draggable).clone()
97           $added = added
98           $added.data("original", ui.draggable)
99           if not ismultiple
100             $(ui.draggable).addClass('disabled').draggable('disable')
101
102           $(ev.target).after(added)
103           if not $(ev.target).hasClass('multiple')
104             $(ev.target).hide()
105           $added.append('<span class="remove">x</span>')
106           $('.remove', added).click (ev) =>
107             $added.prev(".placeholder:not(.multiple)").show()
108             if not ismultiple
109               $added.data('original').removeClass('disabled').draggable('enable')
110             $(added).remove()
111
112
113
114 #       $(".droppable", question).each (i, droppable) =>
115 #         $droppable = $(droppable)
116 #         $droppable.droppable
117 #           accept: (draggable) =>
118 #             return false
119 #             $draggable = $(draggable)
120 #             if not $draggable.is(".draggable")
121 #               console.log('not draggable?')
122 #               return false
123
124 #             for added in $droppable.children(".draggable")
125 #               if @draggable_equal($(added), $draggable)
126 #                 console.log('already here:' + $draggable.text())
127 #                 return false
128
129 #             return @draggable_accept $draggable
130
131 #           drop: (ev, ui) =>
132 #             added = ui.draggable.clone()
133 # #          added.attr('style', '')
134 #             $(ev.target).append(added)
135 #             added.draggable(draggable_opts)
136 #             @draggable_dropped added
137
138 #             if not @multiple
139 #               $(ui.draggable).draggable("disable")
140
141
142       # if issortable
143       #   $(".droppable", question).sortable
144       #     items: "> li"
145
146       #     receive: (event, ui) =>
147       #       console.log("receive " + $(ui.item).text())
148       #       if not accept(ui.item, event.target)
149       #         console.log("REVERT")
150       #         $(event.target).sortable('cancel')
151
152           # over: (event, ui) =>
153           #   if not accept(ui.item, event.target)
154           #     $(event.target).sortable('disable')
155           # out: (event, ui) =>
156           #   $(event.target).sortable('enable')
157
158
159 class Wybor extends Excercise
160   constructor: (element) ->
161     super element
162
163
164   check_question: (question) ->
165     all = 0
166     good = 0
167     solution = @get_value_list(question, 'solution')
168     $(".question-piece", question).each (i, qpiece) =>
169       piece_no = $(qpiece).attr 'data-no'
170       piece_name = $(qpiece).attr 'data-name'
171       if piece_name
172         should_be_checked = solution.indexOf(piece_name) >= 0
173       else
174         should_be_checked = solution.indexOf(piece_no) >= 0
175       is_checked = $("input", qpiece).is(":checked")
176
177       if should_be_checked
178         all += 1
179
180       if is_checked
181         if should_be_checked
182           good += 1
183           @piece_correct qpiece
184         else
185           @piece_incorrect qpiece
186       else
187         $(qpiece).removeClass("correct,incorrect")
188
189     return [good, all]
190
191   show_solutions: ->
192
193
194 class Uporzadkuj extends Excercise
195   constructor: (element) ->
196     super element
197     $('ol, ul', @element).sortable({ items: "> li" })
198
199   check_question: (question) ->
200     positions = @get_value_list(question, 'original', true)
201     sorted = positions.sort()
202     pkts = $('.question-piece', question)
203
204     correct = 0
205     all = 0
206
207     for pkt in [0...pkts.length]
208       all +=1
209       if pkts.eq(pkt).data('pos') == sorted[pkt]
210         correct += 1
211         @piece_correct pkts.eq(pkt)
212       else
213         @piece_incorrect pkts.eq(pkt)
214     return [correct, all]
215
216
217 # XXX propozycje="1/0"
218 class Luki extends Excercise
219   constructor: (element) ->
220     super element
221     @dragging false, false
222
223   check: ->
224     all = 0
225     correct = 0
226     $(".placeholder + .question-piece", @element).each (i, qpiece) =>
227       $placeholder = $(qpiece).prev(".placeholder")
228       if $placeholder.data('solution') == $(qpiece).data('no')
229         @piece_correct qpiece
230         correct += 1
231       else
232         @piece_incorrect qpiece
233       all += 1
234
235     @show_score [correct, all]
236
237
238 class Zastap extends Excercise
239   constructor: (element) ->
240     super element
241     $(".paragraph", @element).each (i, par) =>
242       @wrap_words $(par), $('<span class="zastap question-piece"/>')
243       spans = $("> span", par).attr("contenteditable", "true")
244       spans.click (ev) =>
245         spans.filter(':not(:empty)').removeClass('editing')
246         $(ev.target).addClass('editing')
247
248
249   check: ->
250     all = 0
251     correct = 0
252     $(".question-piece", @element).each (i, qpiece) =>
253       txt = $(qpiece).data('original')
254       should_be_changed = false
255       if not txt?
256         txt = $(qpiece).data('solution')
257         should_be_changed = true
258       if not txt?
259         return
260
261       if should_be_changed
262         all += 1
263
264       if txt != $(qpiece).text()
265         @piece_incorrect qpiece
266       else
267         if should_be_changed
268           @piece_correct qpiece
269           correct += 1
270
271     @show_score [correct, all]
272
273   wrap_words: (element, wrapper) ->
274     # This function wraps each word of element in wrapper, but does not descend into child-tags of element.
275     # It doesn't wrap things between words (defined by ignore RE below). Warning - ignore must begin with ^
276     ignore = /^[ \t.,:;()]+/
277
278     insertWrapped = (txt, elem) ->
279       nw = wrapper.clone()
280       $(document.createTextNode(txt))
281         .wrap(nw).parent().attr("data-original", txt).insertBefore(elem)
282
283     for j in [element.get(0).childNodes.length-1..0]
284       chld = element.get(0).childNodes[j]
285       if chld.nodeType == document.TEXT_NODE
286         len = chld.textContent.length
287         wordb = 0
288         i = 0
289         while i < len
290           space = ignore.exec(chld.textContent.substr(i))
291           if space?
292             if wordb < i
293               insertWrapped(chld.textContent.substr(wordb, i-wordb), chld)
294
295             $(document.createTextNode(space[0])).insertBefore(chld)
296             i += space[0].length
297             wordb = i
298           else
299             i = i + 1
300         if wordb < len - 1
301           insertWrapped(chld.textContent.substr(wordb, len - 1 - wordb), chld)
302         $(chld).remove()
303
304
305 class Przyporzadkuj extends Excercise
306   is_multiple: ->
307     for qp in $(".question-piece", @element)
308       if $(qp).data('solution').split(/[ ,]+/).length > 1
309         return true
310     return false
311
312   constructor: (element) ->
313     super element
314
315     @multiple = @is_multiple()
316
317     @dragging @multiple, true
318
319   draggable_equal: (d1, d2) ->
320     return d1.data("no") == d2.data("no")
321
322
323   check_question: (question) ->
324     # subjects placed in predicates
325     count = 0
326     all = 0
327     all_multiple = 0
328     for qp in $(".predicate .question-piece", question)
329       pred = $(qp).closest("[data-predicate]")
330       v = @get_value_optional_list qp, 'solution'
331       mandatory = v[0]
332       optional = v[1]
333       all_multiple += mandatory.length + optional.length
334       pn = pred.data('predicate')
335       if mandatory.indexOf(pn) >= 0 or optional.indexOf(pn) >= 0
336         count += 1
337         @piece_correct qp
338       else
339         @piece_incorrect qp
340       all += 1
341
342     if @multiple
343       for qp in $(".subject .question-piece", question)
344         v = @get_value_optional_list qp, 'solution'
345         mandatory = v[0]
346         optional = v[1]
347         all_multiple += mandatory.length + optional.length
348       return [count, all_multiple]
349     else
350       return [count, all]
351
352
353 class PrawdaFalsz extends Excercise
354   constructor: (element) ->
355     super element
356
357     for qp in $(".question-piece", @element)
358       $(".true", qp).click (ev) ->
359         ev.preventDefault()
360         $(this).closest(".question-piece").data("value", "true")
361         $(this).addClass('chosen').siblings('a').removeClass('chosen')
362       $(".false", qp).click (ev) ->
363         ev.preventDefault()
364         $(this).closest(".question-piece").data("value", "false")
365         $(this).addClass('chosen').siblings('a').removeClass('chosen')
366
367
368   check_question: ->
369     all = 0
370     good = 0
371     for qp in $(".question-piece", @element)
372       if $(qp).data("solution").toString() == $(qp).data("value")
373         good += 1
374         @piece_correct qp
375       else
376         @piece_incorrect qp
377
378       all += 1
379
380     return [good, all]
381
382 ##########
383
384 excercise = (ele) ->
385   es =
386     wybor: Wybor
387     uporzadkuj: Uporzadkuj
388     luki: Luki
389     zastap: Zastap
390     przyporzadkuj: Przyporzadkuj
391     prawdafalsz: PrawdaFalsz
392
393
394   cls = es[$(ele).attr('data-type')]
395   new cls(ele)
396
397
398 window.edumed =
399   'EduModule': EduModule
400
401
402
403
404 $(document).ready () ->
405   new EduModule($("#book-text"))
406
407   $(".excercise").each (i, el) ->
408     excercise(this)