shuffle answers in exercise order + fix answer numbering
[fnpeditor.git] / src / editor / plugins / core / edumed / order / view.js
1 define(function(require) {
2     
3 'use strict';
4 var $ = require('libs/jquery');
5
6 var _ = require('libs/underscore'),
7     Backbone = require('libs/backbone'),
8     viewTemplate = require('libs/text!./view.html'),
9     viewItemTemplate = require('libs/text!./viewItem.html');
10
11
12 var OrderExerciseView = function(element) {
13     this.element = element;
14     this.dom = $(_.template(viewTemplate)());
15     this.modePills = this.dom.find('.modePills');
16     this.list = this.dom.find('ol');
17     this.description = this.dom.find('.description');
18     this.shuffleButton = this.dom.find('.shuffle');
19     this.itemViews = [];
20
21     this.modePills.find('a').on('click', function(e) {
22         e.stopPropagation();
23         e.preventDefault();
24         this.setMode($(e.target).parent().attr('mode'));
25     }.bind(this));
26
27     this.shuffleButton.on('click', function(e) {
28         e.stopPropagation();
29         e.preventDefault();
30         this.trigger('shuffleItems');
31     }.bind(this));
32
33     this.mode = 'initial';
34
35     var dropTargets = this.dom.find('.placeholder-top');
36
37     dropTargets.on('dragend', function() {
38         dropTargets.removeClass('dragged');
39     });
40
41     dropTargets.on('dragenter', function() {
42         var first = this.itemViews[0];
43         if(this.mode === 'correct') {
44             first = this.sortedItemViews()[0];
45         }
46         if(!this.allowDropAt(first, true)) {
47             return;
48         }
49         dropTargets.addClass('active');
50     }.bind(this));
51
52     dropTargets.on('dragleave', function() {
53         dropTargets.removeClass('active');
54     }.bind(this));
55
56     dropTargets.on('dragover', function(e) {
57         e.preventDefault();
58         e.originalEvent.dataTransfer.dropEffect = 'move';
59     });
60
61     dropTargets.on('drop', function(e) {
62         var vid = e.originalEvent.dataTransfer.getData('text');
63         var droppedItem = $('[vid='+vid+']').data('viewInstance');
64
65         var first = this.itemViews[0];
66         if(this.mode === 'correct') {
67             first = this.sortedItemViews()[0];
68         }
69
70         this.trigger(this.mode === 'initial' ? 'moveItem' : 'moveAnswer', droppedItem.item, first.item, 'before');
71         dropTargets.removeClass('active');
72         e.preventDefault();
73     }.bind(this));
74 };
75 _.extend(OrderExerciseView.prototype, Backbone.Events, {
76     addItem: function(item) {
77         var view = new ItemView(item, this);
78         view.on('receivedDrop', function(droppedItem) {
79             this.trigger(this.mode === 'initial' ? 'moveItem' : 'moveAnswer', droppedItem.item, item, 'after');
80         }.bind(this));
81         view.on('dragStarted', function(view) {
82             this.draggedView = view;
83         }.bind(this));
84         this.list.append(view.dom);
85         this.itemViews.push(view);
86
87         if(this.mode === 'correct') {
88             this.setMode(this.mode);
89         }
90     },
91     clearItems: function() {
92         this.list.empty();
93         this.itemViews.forEach(function(view) {
94             view.remove();
95         });
96         this.itemViews = [];
97     },
98     setMode: function(mode) {
99         this.modePills.find('li').removeClass('active');
100         this.modePills.find('[mode='+mode+']').addClass('active');
101         this.mode = mode;
102         this.list.children().detach();
103
104         if(this.mode === 'initial') {
105             this.itemViews.forEach(function(itemView) {
106                 this.list.append(itemView.dom);
107             }.bind(this));
108             this.shuffleButton.show();
109         } else {
110             this.sortedItemViews()
111                 .forEach(function(itemView) {
112                     this.list.append(itemView.dom);
113                 }.bind(this));
114             this.shuffleButton.hide();
115         }
116     },
117     sortedItemViews: function () {
118         return this.itemViews.slice(0)
119             .sort(function(view1, view2) {
120                 if(view1.item.getAnswer() > view2.item.getAnswer()) {
121                     return 1;
122                 }
123                 return -1;
124             });
125     },
126     allowDropAt: function(view, up) {
127         var arr = [this.draggedView.dom[0]];
128         if(!up) {
129             arr.push(this.draggedView.dom.prev()[0]);
130         }
131         return !_.contains(arr, view.dom[0]);
132     }
133 });
134
135 var ItemView = function(item, exerciseView) {
136     this.item = item;
137     this.exerciseView = exerciseView;
138     this.dom = $(_.template(viewItemTemplate)());
139     this.content = this.dom.find('.content');
140
141
142     var dropTargets = this.dom.find('.placeholder'),
143         dragSources = this.dom.find('.handle');
144
145     dragSources.on('dragstart', function(e) {
146         this.dom.addClass('dragged');
147         e.originalEvent.dataTransfer.setData('text', this.dom.attr('vid'));
148         e.originalEvent.effectAllowed = 'move';
149         this.trigger('dragStarted', this);
150
151     }.bind(this));
152
153     dropTargets.on('dragend', function() {
154         this.dom.removeClass('dragged');
155     });
156
157     dropTargets.on('dragenter', function() {
158         if(!this.exerciseView.allowDropAt(this)) {
159             return;
160         }
161         dropTargets.addClass('active');
162     }.bind(this));
163
164     dropTargets.on('dragleave', function() {
165         dropTargets.removeClass('active');
166     }.bind(this));
167
168     dropTargets.on('dragover', function(e) {
169         e.preventDefault();
170         e.originalEvent.dataTransfer.dropEffect = 'move';
171     });
172
173     dropTargets.on('drop', function(e) {
174         var vid = e.originalEvent.dataTransfer.getData('text');
175         var droppedItem = $('[vid='+vid+']').data('viewInstance');
176         e.preventDefault();
177         this.trigger('receivedDrop', droppedItem);
178     }.bind(this));
179
180     var content = this.content;
181     this.container = exerciseView.element.createContainer(item.node.contents(), {
182         resetBackground: true,
183         manages: function(node, originaParent) {
184             return item.node.sameNode(node.parent() || originaParent);
185         },
186         dom: content
187     });
188
189     this.dom.data('viewInstance', this);
190     this.dom.attr('vid', _.uniqueId());
191 };
192
193 _.extend(ItemView.prototype, Backbone.Events, {
194     remove: function() {
195         this.container.remove();
196     }
197 });
198
199 return OrderExerciseView;
200
201 });
202
203