MDL-37664 course: Fix drag & drop RTL mode issue
[moodle.git] / lib / yui / blocks / blocks.js
CommitLineData
56838156
RK
1YUI.add('moodle-core-blocks', function(Y) {
2
3 var AJAXURL = '/lib/ajax/blocks.php',
4 CSS = {
5 BLOCK : 'block',
6 BLOCKREGION : 'block-region',
7 BLOCKADMINBLOCK : 'block_adminblock',
8 EDITINGMOVE : 'editing_move',
9 HEADER : 'header',
10 LIGHTBOX : 'lightbox',
56838156
RK
11 REGIONCONTENT : 'region-content',
12 SKIPBLOCK : 'skip-block',
dec70f25 13 SKIPBLOCKTO : 'skip-block-to'
56838156
RK
14 }
15
16 var DRAGBLOCK = function() {
17 DRAGBLOCK.superclass.constructor.apply(this, arguments);
18 };
19 Y.extend(DRAGBLOCK, M.core.dragdrop, {
20 skipnodetop : null,
21 skipnodebottom : null,
22 dragsourceregion : null,
23 initializer : function(params) {
24 // Set group for parent class
25 this.groups = ['block'];
26 this.samenodeclass = CSS.BLOCK;
27 this.parentnodeclass = CSS.REGIONCONTENT;
28
29 // Initialise blocks dragging
2d724607
RK
30 // Find all block regions on the page
31 var blockregionlist = Y.Node.all('div.'+CSS.BLOCKREGION);
56838156
RK
32
33 if (blockregionlist.size() === 0) {
34 return false;
35 }
36
37 // See if we are missing either of block regions,
38 // if yes we need to add an empty one to use as target
39 if (blockregionlist.size() != this.get('regions').length) {
40 var blockregion = Y.Node.create('<div></div>')
41 .addClass(CSS.BLOCKREGION);
42 var regioncontent = Y.Node.create('<div></div>')
43 .addClass(CSS.REGIONCONTENT);
44 blockregion.appendChild(regioncontent);
45
46 var regionid = this.get_region_id(blockregionlist.item(0));
47 if (regionid === 'post') {
48 // pre block is missing, instert it before post
49 blockregion.setAttrs({id : 'region-pre'});
50 blockregionlist.item(0).insert(blockregion, 'before');
51 blockregionlist.unshift(blockregion);
52 } else {
53 // post block is missing, instert it after pre
54 blockregion.setAttrs({id : 'region-post'});
55 blockregionlist.item(0).insert(blockregion, 'after');
56 blockregionlist.push(blockregion);
57 }
58 }
59
60 blockregionlist.each(function(blockregionnode) {
61
62 // Setting blockregion as droptarget (the case when it is empty)
63 // The region-post (the right one)
64 // is very narrow, so add extra padding on the left to drop block on it.
65 var tar = new Y.DD.Drop({
66 node: blockregionnode.one('div.'+CSS.REGIONCONTENT),
67 groups: this.groups,
68 padding: '40 240 40 240'
69 });
70
6cc43fed
PN
71 // Make each div element in the list of blocks draggable
72 var del = new Y.DD.Delegate({
73 container: blockregionnode,
74 nodes: '.'+CSS.BLOCK,
75 target: true,
76 handles: ['.'+CSS.HEADER],
1e78b3e6 77 invalid: '.block-hider-hide, .block-hider-show, .moveto',
6cc43fed
PN
78 dragConfig: {groups: this.groups}
79 });
80 del.dd.plug(Y.Plugin.DDProxy, {
81 // Don't move the node at the end of the drag
82 moveOnEnd: false
83 });
84 del.dd.plug(Y.Plugin.DDWinScroll);
85
56838156
RK
86 var blocklist = blockregionnode.all('.'+CSS.BLOCK);
87 blocklist.each(function(blocknode) {
88 var move = blocknode.one('a.'+CSS.EDITINGMOVE);
89 if (move) {
90 move.remove();
91 blocknode.one('.'+CSS.HEADER).setStyle('cursor', 'move');
56838156
RK
92 }
93 }, this);
94 }, this);
95 },
96
97 get_block_id : function(node) {
98 return Number(node.get('id').replace(/inst/i, ''));
99 },
100
101 get_block_region : function(node) {
e893c6d4
MN
102 var region = node.ancestor('div.'+CSS.BLOCKREGION).get('id').replace(/region-/i, '');
103 if (Y.Array.indexOf(this.get('regions'), region) === -1) {
104 // Must be standard side-X
ba23e9a5
SR
105 if (right_to_left()) {
106 if (region == 'post') {
107 region = 'pre';
108 } else if (region == 'pre') {
109 region = 'post';
110 }
111 }
e893c6d4
MN
112 return 'side-' + region;
113 }
114 // Perhaps custom region
115 return region;
56838156
RK
116 },
117
118 get_region_id : function(node) {
119 return node.get('id').replace(/region-/i, '');
120 },
121
122 drag_start : function(e) {
123 // Get our drag object
124 var drag = e.target;
125
126 // Store the parent node of original drag node (block)
127 // we will need it later for show/hide empty regions
128 this.dragsourceregion = drag.get('node').ancestor('div.'+CSS.BLOCKREGION);
129
130 // Determine skipnodes and store them
131 if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) {
132 this.skipnodetop = drag.get('node').previous();
133 }
134 if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) {
135 this.skipnodebottom = drag.get('node').next();
136 }
137 },
138
139 drop_over : function(e) {
140 // Get a reference to our drag and drop nodes
141 var drag = e.drag.get('node');
142 var drop = e.drop.get('node');
143
144 // We need to fix the case when parent drop over event has determined
145 // 'goingup' and appended the drag node after admin-block.
146 if (drop.hasClass(this.parentnodeclass) && drop.one('.'+CSS.BLOCKADMINBLOCK) && drop.one('.'+CSS.BLOCKADMINBLOCK).next('.'+CSS.BLOCK)) {
147 drop.prepend(drag);
148 }
149
150 // Block is moved within the same region
151 // stop here, no need to modify anything.
152 if (this.dragsourceregion.contains(drop)) {
153 return false;
154 }
155
2d724607
RK
156 // TODO: Hiding-displaying block region only works for base theme blocks
157 // (region-pre, region-post) at the moment. It should be improved
158 // to work with custom block regions as well.
159
56838156
RK
160 // TODO: Fix this for the case when user drag block towards empty section,
161 // then the section appears, then user chnages his mind and moving back to
162 // original section. The opposite section remains opened and empty.
163
164 var documentbody = Y.one('body');
165 // Moving block towards hidden region-content, display it
166 var regionname = this.get_region_id(this.dragsourceregion);
167 if (documentbody.hasClass('side-'+regionname+'-only')) {
168 documentbody.removeClass('side-'+regionname+'-only');
169 }
170
171 // Moving from empty region-content towards the opposite one,
2d724607 172 // hide empty one (only for region-pre, region-post areas at the moment).
56838156 173 regionname = this.get_region_id(drop.ancestor('div.'+CSS.BLOCKREGION));
2d724607 174 if (this.dragsourceregion.all('.'+CSS.BLOCK).size() == 0 && this.dragsourceregion.get('id').match(/(region-pre|region-post)/i)) {
56838156
RK
175 if (!documentbody.hasClass('side-'+regionname+'-only')) {
176 documentbody.addClass('side-'+regionname+'-only');
177 }
178 }
179 },
180
181 drop_end : function(e) {
182 // clear variables
183 this.skipnodetop = null;
184 this.skipnodebottom = null;
185 this.dragsourceregion = null;
186 },
187
243e9bf9
RK
188 drag_dropmiss : function(e) {
189 // Missed the target, but we assume the user intended to drop it
190 // on the last last ghost node location, e.drag and e.drop should be
191 // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
192 this.drop_hit(e);
193 },
194
56838156
RK
195 drop_hit : function(e) {
196 var drag = e.drag;
197 // Get a reference to our drag node
198 var dragnode = drag.get('node');
199 var dropnode = e.drop.get('node');
200
201 // Amend existing skipnodes
202 if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) {
203 // the one that belongs to block below move below
204 dragnode.insert(dragnode.previous(), 'after');
205 }
206 // Move original skipnodes
207 if (this.skipnodetop) {
208 dragnode.insert(this.skipnodetop, 'before');
209 }
210 if (this.skipnodebottom) {
211 dragnode.insert(this.skipnodebottom, 'after');
212 }
213
214 // Add lightbox if it not there
215 var lightbox = M.util.add_lightbox(Y, dragnode);
216
217 // Prepare request parameters
218 var params = {
219 sesskey : M.cfg.sesskey,
220 courseid : this.get('courseid'),
221 pagelayout : this.get('pagelayout'),
222 pagetype : this.get('pagetype'),
e893c6d4 223 subpage : this.get('subpage'),
56838156
RK
224 action : 'move',
225 bui_moveid : this.get_block_id(dragnode),
226 bui_newregion : this.get_block_region(dropnode)
227 };
228
229 if (this.get('cmid')) {
230 params.cmid = this.get('cmid');
231 }
232
233 if (dragnode.next('.'+this.samenodeclass) && !dragnode.next('.'+this.samenodeclass).hasClass(CSS.BLOCKADMINBLOCK)) {
234 params.bui_beforeid = this.get_block_id(dragnode.next('.'+this.samenodeclass));
235 }
236
237 // Do AJAX request
238 Y.io(M.cfg.wwwroot+AJAXURL, {
239 method: 'POST',
240 data: params,
241 on: {
242 start : function(tid) {
243 lightbox.show();
244 },
245 success: function(tid, response) {
246 window.setTimeout(function(e) {
247 lightbox.hide();
248 }, 250);
249 try {
250 var responsetext = Y.JSON.parse(response.responseText);
251 if (responsetext.error) {
252 new M.core.ajaxException(responsetext);
253 }
254 } catch (e) {}
255 },
256 failure: function(tid, response) {
257 this.ajax_failure(response);
258 lightbox.hide();
259 }
260 },
261 context:this
262 });
263 }
264 }, {
265 NAME : 'core-blocks-dragdrop',
266 ATTRS : {
267 courseid : {
268 value : null
269 },
270 cmid : {
271 value : null
272 },
273 pagelayout : {
274 value : null
275 },
276 pagetype : {
277 value : null
278 },
e893c6d4
MN
279 subpage : {
280 value : null
281 },
56838156
RK
282 regions : {
283 value : null
284 }
285 }
286 });
287
288 M.core_blocks = M.core_blocks || {};
289 M.core_blocks.init_dragdrop = function(params) {
290 new DRAGBLOCK(params);
291 }
d2a27ab0 292}, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification']});
56838156 293