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