MDL-39842 js: fixed block drag and drop

This commit is contained in:
Sam Hemelryk
2013-06-10 09:29:58 +12:00
parent 6964e6a580
commit ee7a52ae91
8 changed files with 2056 additions and 11 deletions
+684 -3
View File
@@ -1,5 +1,11 @@
YUI.add('moodle-core-blocks', function (Y, NAME) {
/**
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
var AJAXURL = '/lib/ajax/blocks.php',
CSS = {
BLOCK : 'block',
@@ -15,6 +21,16 @@ CSS = {
REGIONMAIN : 'region-main'
};
/**
* Legacy drag and drop manager.
* This drag and drop manager is specifically designed for themes using side-pre and side-post
* that do not make use of the block output methods introduced by MDL-39824.
*
* @namespace M.core.blockdraganddrop
* @class LegacyManager
* @constructor
* @extends M.core.dragdrop
*/
var DRAGBLOCK = function() {
DRAGBLOCK.superclass.constructor.apply(this, arguments);
};
@@ -301,11 +317,676 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
}
});
M.core_blocks = M.core_blocks || {};
M.core_blocks.init_dragdrop = function(params) {
new DRAGBLOCK(params);
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Block drag and drop static class.
* @namespace M.core
* @class blockdraganddrop
* @static
*/
M.core.blockdraganddrop = M.core.blockdraganddrop || {};
/**
* True if the page is using the new blocks methods.
* @private
* @static
* @property _isusingnewblocksmethod
* @type Boolean
* @default null
*/
M.core.blockdraganddrop._isusingnewblocksmethod = null;
/**
* Returns true if the page is using the new blocks methods.
* @static
* @method is_using_blocks_render_method
* @return Boolean
*/
M.core.blockdraganddrop.is_using_blocks_render_method = function() {
if (this._isusingnewblocksmethod === null) {
var goodregions = Y.all('.block-region[data-blockregion]').size();
var allregions = Y.all('.block-region').size();
this._isusingnewblocksmethod = (allregions === goodregions);
}
return this._isusingnewblocksmethod;
};
/**
* Initialises a drag and drop manager.
* This should only ever be called once for a page.
* @static
* @method init
* @param {Object} params
* @return Manager
*/
M.core.blockdraganddrop.init = function(params) {
if (this.is_using_blocks_render_method()) {
new MANAGER(params);
} else {
new DRAGBLOCK(params);
}
};
/**
* Legacy code to keep things working.
*/
M.core_blocks = M.core_blocks || {};
M.core_blocks.init_dragdrop = function(params) {
M.core.blockdraganddrop.init(params);
};/**
* This file contains the drag and drop manager class.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new Block drag and drop manager.
*
* @namespace M.core.blockdraganddrop
* @class Manager
* @constructor
* @extends M.core.dragdrop
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
MANAGER.prototype = {
/**
* The skip block link from above the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodetop
* @type Node
* @default null
*/
skipnodetop : null,
/**
* The skip block link from below the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodebottom
* @type Node
* @default null
*/
skipnodebottom : null,
/**
* An associative object of regions and the
* @property regionobjects
* @type {Object} Primitive object mocking an associative array.
* @type {BLOCKREGION} [regionname]* Each item uses the region name as the key with the value being
* an instance of the BLOCKREGION class.
*/
regionobjects : {},
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
Y.log('Initialising drag and drop for blocks.', 'info');
var regionnames = this.get('regions'),
i = 0,
region,
regionname,
droptarget,
dragdelegation;
// Evil required by M.core.dragdrop.
this.groups = ['block'];
this.samenodeclass = CSS.BLOCK;
this.parentnodeclass = CSS.BLOCKREGION;
// Add relevant classes and ID to 'content' block region on My Home page.
var myhomecontent = Y.Node.all('body#'+CSS.MYINDEX+' #'+CSS.REGIONMAIN+' > .'+CSS.REGIONCONTENT);
if (myhomecontent.size() > 0) {
var contentregion = myhomecontent.item(0);
contentregion.addClass(CSS.BLOCKREGION);
contentregion.set('id', CSS.REGIONCONTENT);
contentregion.one('div').addClass(CSS.REGIONCONTENT);
}
for (i in regionnames) {
regionname = regionnames[i];
region = new BLOCKREGION({
manager : this,
region : regionname,
node : Y.one('#block-region-'+regionname)
});
this.regionobjects[regionname] = region;
// Setting blockregion as droptarget (the case when it is empty)
// The region-post (the right one)
// is very narrow, so add extra padding on the left to drop block on it.
droptarget = new Y.DD.Drop({
node: region.get_droptarget(),
groups: this.groups,
padding: '40 240 40 240'
});
// Make each div element in the list of blocks draggable
dragdelegation = new Y.DD.Delegate({
container: region.get_droptarget(),
nodes: '.'+CSS.BLOCK,
target: true,
handles: ['.'+CSS.HEADER],
invalid: '.block-hider-hide, .block-hider-show, .moveto',
dragConfig: {groups: this.groups}
});
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
// Don't move the node at the end of the drag
moveOnEnd: false
});
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
// On the mouse down event we will enable all block regions so that they can be dragged to.
// This is VERY important as without it dnd won't work for empty block regions.
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
region.remove_block_move_icons();
}
Y.log('Initialisation of drag and drop for blocks complete.', 'info');
},
/**
* Returns the ID of the block the given node represents.
* @method get_block_id
* @param {Node} node
* @returns {int} The blocks ID in the database.
*/
get_block_id : function(node) {
return Number(node.get('id').replace(/inst/i, ''));
},
/**
* Returns the block region that the node is part of or belonging to.
* @method get_block_region
* @param {Y.Node} node
* @returns {string} The region name.
*/
get_block_region : function(node) {
if (!node.test('[data-blockregion]')) {
node = node.ancestor('[data-blockregion]');
}
return node.getData('blockregion');
},
/**
* Returns the BLOCKREGION instance that represents the block region the given node is part of.
* @method get_region_object
* @param {Y.Node} node
* @returns {BLOCKREGION}
*/
get_region_object : function(node) {
return this.regionobjects[this.get_block_region(node)];
},
/**
* Enables all fo the regions so that they are all visible while dragging is occuring.
* @method enable_all_regions
* @returns {undefined}
*/
enable_all_regions : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].enable();
}
},
/**
* Disables enabled regions if they contain no blocks.
* @method disable_regions_if_required
* @returns {undefined}
*/
disable_regions_if_required : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].disable_if_required();
}
},
/**
* Called by M.core.dragdrop.global_drag_start when dragging starts.
* @method drag_start
* @param {Event} e
* @returns {undefined}
*/
drag_start : function(e) {
// Get our drag object
var drag = e.target;
// Store the parent node of original drag node (block)
// we will need it later for show/hide empty regions
// Determine skipnodes and store them
if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) {
this.skipnodetop = drag.get('node').previous();
}
if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) {
this.skipnodebottom = drag.get('node').next();
}
},
/**
* Called by M.core.dragdrop.global_drop_over when something is dragged over a drop target.
* @method drop_over
* @param {Event} e
* @returns {undefined}
*/
drop_over : function(e) {
// Get a reference to our drag and drop nodes
var drag = e.drag.get('node');
var drop = e.drop.get('node');
// We need to fix the case when parent drop over event has determined
// 'goingup' and appended the drag node after admin-block.
if (drop.hasClass(CSS.REGIONCONTENT) && drop.one('.'+CSS.BLOCKADMINBLOCK) && drop.one('.'+CSS.BLOCKADMINBLOCK).next('.'+CSS.BLOCK)) {
drop.prepend(drag);
}
},
/**
* Called by M.core.dragdrop.global_drop_end when a drop has been completed.
* @method drop_end
* @returns {undefined}
*/
drop_end : function() {
// Clear variables.
this.skipnodetop = null;
this.skipnodebottom = null;
this.disable_regions_if_required();
},
/**
* Called by M.core.dragdrop.global_drag_dropmiss when something has been dropped on a node that isn't contained by a drop target.
* @method drag_dropmiss
* @param {Event} e
* @returns {undefined}
*/
drag_dropmiss : function(e) {
// Missed the target, but we assume the user intended to drop it
// on the last ghost node location, e.drag and e.drop should be
// prepared by global_drag_dropmiss parent so simulate drop_hit(e).
this.drop_hit(e);
},
/**
* Called by M.core.dragdrop.global_drag_hit when something has been dropped on a drop target.
* @method drop_hit
* @param {Event} e
* @returns {undefined}
*/
drop_hit : function(e) {
// Get a reference to our drag node
var dragnode = e.drag.get('node');
var dropnode = e.drop.get('node');
// Amend existing skipnodes
if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) {
// the one that belongs to block below move below
dragnode.insert(dragnode.previous(), 'after');
}
// Move original skipnodes
if (this.skipnodetop) {
dragnode.insert(this.skipnodetop, 'before');
}
if (this.skipnodebottom) {
dragnode.insert(this.skipnodebottom, 'after');
}
// Add lightbox if it not there
var lightbox = M.util.add_lightbox(Y, dragnode);
// Prepare request parameters
var params = {
sesskey : M.cfg.sesskey,
courseid : this.get('courseid'),
pagelayout : this.get('pagelayout'),
pagetype : this.get('pagetype'),
subpage : this.get('subpage'),
contextid : this.get('contextid'),
action : 'move',
bui_moveid : this.get_block_id(dragnode),
bui_newregion : this.get_block_region(dropnode)
};
if (this.get('cmid')) {
params.cmid = this.get('cmid');
}
if (dragnode.next('.'+CSS.BLOCK) && !dragnode.next('.'+CSS.BLOCK).hasClass(CSS.BLOCKADMINBLOCK)) {
params.bui_beforeid = this.get_block_id(dragnode.next('.'+CSS.BLOCK));
}
// Do AJAX request
Y.io(M.cfg.wwwroot+AJAXURL, {
method: 'POST',
data: params,
on: {
start : function() {
lightbox.show();
},
success: function(tid, response) {
window.setTimeout(function() {
lightbox.hide();
}, 250);
try {
var responsetext = Y.JSON.parse(response.responseText);
if (responsetext.error) {
new M.core.ajaxException(responsetext);
}
} catch (e) {}
},
failure: function(tid, response) {
this.ajax_failure(response);
lightbox.hide();
},
complete : function() {
this.disable_regions_if_required();
}
},
context:this
});
}
};
Y.extend(MANAGER, M.core.dragdrop, MANAGER.prototype, {
NAME : 'core-blocks-dragdrop-manager',
ATTRS : {
/**
* The Course ID if there is one.
* @attribute courseid
* @type int|null
* @default null
*/
courseid : {
value : null
},
/**
* The Course Module ID if there is one.
* @attribute cmid
* @type int|null
* @default null
*/
cmid : {
value : null
},
/**
* The Context ID.
* @attribute contextid
* @type int|null
* @default null
*/
contextid : {
value : null
},
/**
* The current page layout.
* @attribute pagelayout
* @type string|null
* @default null
*/
pagelayout : {
value : null
},
/**
* The page type string, should be used as the id for the body tag in the theme.
* @attribute pagetype
* @type string|null
* @default null
*/
pagetype : {
value : null
},
/**
* The subpage identifier, if any.
* @attribute subpage
* @type string|null
* @default null
*/
subpage : {
value : null
},
/**
* An array of block regions that are present on the page.
* @attribute regions
* @type array|null
* @default Array[]
*/
regions : {
value : []
}
}
});/**
* This file contains the Block Region class used by the drag and drop manager.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new block region object.
*
* @namespace M.core.blockdraganddrop
* @class BlockRegion
* @constructor
* @extends Y.Base
*/
var BLOCKREGION = function() {
BLOCKREGION.superclass.constructor.apply(this, arguments);
};
BLOCKREGION.prototype = {
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var node = this.get('node');
Y.log('Block region `'+this.get('region')+'` initialising', 'info');
if (!node) {
Y.log('block region known about but no HTML structure found for it. Guessing structure.', 'warn');
this.create_and_add_node();
}
var body = Y.one('body'),
hasblocks = node.all('.'+CSS.BLOCK).size() > 0,
hasregionclass = this.get_has_region_class();
this.set('hasblocks', hasblocks);
if (!body.hasClass(hasregionclass)) {
body.addClass(hasregionclass);
}
body.addClass((hasblocks) ? this.get_used_region_class() : this.get_empty_region_class());
body.removeClass((hasblocks) ? this.get_empty_region_class() : this.get_used_region_class());
},
/**
* Creates a generic block region node and adds it to the DOM at the best guess location.
* Any calling of this method is an unfortunate circumstance.
* @method create_and_add_node
*/
create_and_add_node : function() {
var c = Y.Node.create,
region = this.get('region'),
node = c('<div id="block-region-'+region+'" data-droptarget="1"></div>')
.addClass(CSS.BLOCKREGION)
.setData('blockregion', region),
regions = this.get('manager').get('regions'),
i,
haspre = false,
haspost = false,
added = false,
pre,
post;
for (i in regions) {
if (regions[i].match(/(pre|left)/)) {
haspre = regions[i];
} else if (regions[i].match(/(post|right)/)) {
haspost = regions[i];
}
}
if (haspre !== false && haspost !== false) {
if (region === haspre) {
post = Y.one('#block-region-'+haspost);
if (post) {
post.insert(node, 'before');
added = true;
}
} else {
pre = Y.one('#block-region-'+haspre);
if (pre) {
pre.insert(node, 'after');
added = true;
}
}
}
if (added === false) {
Y.one('body').append(node);
}
this.set('node', node);
},
/**
* Removes the move icons and changes the cursor to a move icon when over the header.
* @method remove_block_move_icons
*/
remove_block_move_icons : function() {
this.get('node').all('.'+CSS.BLOCK+' a.'+CSS.EDITINGMOVE).each(function(moveicon){
moveicon.ancestor('.'+CSS.BLOCK).one('.'+CSS.HEADER).setStyle('cursor', 'move');
moveicon.remove();
});
},
/**
* Returns the class name on the body that signifies the document knows about this region.
* @method get_has_region_class
* @return String
*/
get_has_region_class : function() {
return 'has-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains no blocks.
* @method get_empty_region_class
* @return String
*/
get_empty_region_class : function() {
return 'empty-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains blocks.
* @method get_used_region_class
* @return String
*/
get_used_region_class : function() {
return 'used-region-'+this.get('region');
},
/**
* Returns the node to use as the drop target for this region.
* @method get_droptarget
* @return Node
*/
get_droptarget : function() {
var node = this.get('node');
if (node.test('[data-droptarget="1"]')) {
return node;
}
return node.one('[data-droptarget="1"]');
},
/**
* Enables the block region so that we can be sure the user can see it.
* This is done even if it is empty.
* @method enable
*/
enable : function() {
Y.one('body').addClass(this.get_used_region_class()).removeClass(this.get_empty_region_class());
},
/**
* Disables the region if it contains no blocks, essentially hiding it from the user.
* @method disable_if_required
*/
disable_if_required : function() {
if (this.get('node').all('.'+CSS.BLOCK).size() === 0) {
Y.one('body').addClass(this.get_empty_region_class()).removeClass(this.get_used_region_class());
}
}
};
Y.extend(BLOCKREGION, Y.Base, BLOCKREGION.prototype, {
NAME : 'core-blocks-dragdrop-blockregion',
ATTRS : {
/**
* The drag and drop manager that created this block region instance.
* @attribute manager
* @type M.core.blockdraganddrop.Manager
* @writeOnce
*/
manager : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isObject(value) && value instanceof MANAGER;
}
},
/**
* The name of the block region this object represents.
* @attribute region
* @type String
* @writeOnce
*/
region : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isString(value);
}
},
/**
* The node the block region HTML starts at.s
* @attribute region
* @type Y.Node
*/
node : {
validator : function (value) {
return Y.Lang.isObject(value) || Y.Lang.isNull(value);
}
},
/**
* True if the block region currently contains blocks.
* @attribute hasblocks
* @type Boolean
* @default false
*/
hasblocks : {
value : false,
validator : function (value) {
return Y.Lang.isBoolean(value);
}
}
}
});
}, '@VERSION@', {
"requires": [
File diff suppressed because one or more lines are too long
+680 -3
View File
@@ -1,5 +1,11 @@
YUI.add('moodle-core-blocks', function (Y, NAME) {
/**
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
var AJAXURL = '/lib/ajax/blocks.php',
CSS = {
BLOCK : 'block',
@@ -15,6 +21,16 @@ CSS = {
REGIONMAIN : 'region-main'
};
/**
* Legacy drag and drop manager.
* This drag and drop manager is specifically designed for themes using side-pre and side-post
* that do not make use of the block output methods introduced by MDL-39824.
*
* @namespace M.core.blockdraganddrop
* @class LegacyManager
* @constructor
* @extends M.core.dragdrop
*/
var DRAGBLOCK = function() {
DRAGBLOCK.superclass.constructor.apply(this, arguments);
};
@@ -301,11 +317,672 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
}
});
M.core_blocks = M.core_blocks || {};
M.core_blocks.init_dragdrop = function(params) {
new DRAGBLOCK(params);
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Block drag and drop static class.
* @namespace M.core
* @class blockdraganddrop
* @static
*/
M.core.blockdraganddrop = M.core.blockdraganddrop || {};
/**
* True if the page is using the new blocks methods.
* @private
* @static
* @property _isusingnewblocksmethod
* @type Boolean
* @default null
*/
M.core.blockdraganddrop._isusingnewblocksmethod = null;
/**
* Returns true if the page is using the new blocks methods.
* @static
* @method is_using_blocks_render_method
* @return Boolean
*/
M.core.blockdraganddrop.is_using_blocks_render_method = function() {
if (this._isusingnewblocksmethod === null) {
var goodregions = Y.all('.block-region[data-blockregion]').size();
var allregions = Y.all('.block-region').size();
this._isusingnewblocksmethod = (allregions === goodregions);
}
return this._isusingnewblocksmethod;
};
/**
* Initialises a drag and drop manager.
* This should only ever be called once for a page.
* @static
* @method init
* @param {Object} params
* @return Manager
*/
M.core.blockdraganddrop.init = function(params) {
if (this.is_using_blocks_render_method()) {
new MANAGER(params);
} else {
new DRAGBLOCK(params);
}
};
/**
* Legacy code to keep things working.
*/
M.core_blocks = M.core_blocks || {};
M.core_blocks.init_dragdrop = function(params) {
M.core.blockdraganddrop.init(params);
};/**
* This file contains the drag and drop manager class.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new Block drag and drop manager.
*
* @namespace M.core.blockdraganddrop
* @class Manager
* @constructor
* @extends M.core.dragdrop
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
MANAGER.prototype = {
/**
* The skip block link from above the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodetop
* @type Node
* @default null
*/
skipnodetop : null,
/**
* The skip block link from below the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodebottom
* @type Node
* @default null
*/
skipnodebottom : null,
/**
* An associative object of regions and the
* @property regionobjects
* @type {Object} Primitive object mocking an associative array.
* @type {BLOCKREGION} [regionname]* Each item uses the region name as the key with the value being
* an instance of the BLOCKREGION class.
*/
regionobjects : {},
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var regionnames = this.get('regions'),
i = 0,
region,
regionname,
droptarget,
dragdelegation;
// Evil required by M.core.dragdrop.
this.groups = ['block'];
this.samenodeclass = CSS.BLOCK;
this.parentnodeclass = CSS.BLOCKREGION;
// Add relevant classes and ID to 'content' block region on My Home page.
var myhomecontent = Y.Node.all('body#'+CSS.MYINDEX+' #'+CSS.REGIONMAIN+' > .'+CSS.REGIONCONTENT);
if (myhomecontent.size() > 0) {
var contentregion = myhomecontent.item(0);
contentregion.addClass(CSS.BLOCKREGION);
contentregion.set('id', CSS.REGIONCONTENT);
contentregion.one('div').addClass(CSS.REGIONCONTENT);
}
for (i in regionnames) {
regionname = regionnames[i];
region = new BLOCKREGION({
manager : this,
region : regionname,
node : Y.one('#block-region-'+regionname)
});
this.regionobjects[regionname] = region;
// Setting blockregion as droptarget (the case when it is empty)
// The region-post (the right one)
// is very narrow, so add extra padding on the left to drop block on it.
droptarget = new Y.DD.Drop({
node: region.get_droptarget(),
groups: this.groups,
padding: '40 240 40 240'
});
// Make each div element in the list of blocks draggable
dragdelegation = new Y.DD.Delegate({
container: region.get_droptarget(),
nodes: '.'+CSS.BLOCK,
target: true,
handles: ['.'+CSS.HEADER],
invalid: '.block-hider-hide, .block-hider-show, .moveto',
dragConfig: {groups: this.groups}
});
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
// Don't move the node at the end of the drag
moveOnEnd: false
});
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
// On the mouse down event we will enable all block regions so that they can be dragged to.
// This is VERY important as without it dnd won't work for empty block regions.
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
region.remove_block_move_icons();
}
},
/**
* Returns the ID of the block the given node represents.
* @method get_block_id
* @param {Node} node
* @returns {int} The blocks ID in the database.
*/
get_block_id : function(node) {
return Number(node.get('id').replace(/inst/i, ''));
},
/**
* Returns the block region that the node is part of or belonging to.
* @method get_block_region
* @param {Y.Node} node
* @returns {string} The region name.
*/
get_block_region : function(node) {
if (!node.test('[data-blockregion]')) {
node = node.ancestor('[data-blockregion]');
}
return node.getData('blockregion');
},
/**
* Returns the BLOCKREGION instance that represents the block region the given node is part of.
* @method get_region_object
* @param {Y.Node} node
* @returns {BLOCKREGION}
*/
get_region_object : function(node) {
return this.regionobjects[this.get_block_region(node)];
},
/**
* Enables all fo the regions so that they are all visible while dragging is occuring.
* @method enable_all_regions
* @returns {undefined}
*/
enable_all_regions : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].enable();
}
},
/**
* Disables enabled regions if they contain no blocks.
* @method disable_regions_if_required
* @returns {undefined}
*/
disable_regions_if_required : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].disable_if_required();
}
},
/**
* Called by M.core.dragdrop.global_drag_start when dragging starts.
* @method drag_start
* @param {Event} e
* @returns {undefined}
*/
drag_start : function(e) {
// Get our drag object
var drag = e.target;
// Store the parent node of original drag node (block)
// we will need it later for show/hide empty regions
// Determine skipnodes and store them
if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) {
this.skipnodetop = drag.get('node').previous();
}
if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) {
this.skipnodebottom = drag.get('node').next();
}
},
/**
* Called by M.core.dragdrop.global_drop_over when something is dragged over a drop target.
* @method drop_over
* @param {Event} e
* @returns {undefined}
*/
drop_over : function(e) {
// Get a reference to our drag and drop nodes
var drag = e.drag.get('node');
var drop = e.drop.get('node');
// We need to fix the case when parent drop over event has determined
// 'goingup' and appended the drag node after admin-block.
if (drop.hasClass(CSS.REGIONCONTENT) && drop.one('.'+CSS.BLOCKADMINBLOCK) && drop.one('.'+CSS.BLOCKADMINBLOCK).next('.'+CSS.BLOCK)) {
drop.prepend(drag);
}
},
/**
* Called by M.core.dragdrop.global_drop_end when a drop has been completed.
* @method drop_end
* @returns {undefined}
*/
drop_end : function() {
// Clear variables.
this.skipnodetop = null;
this.skipnodebottom = null;
this.disable_regions_if_required();
},
/**
* Called by M.core.dragdrop.global_drag_dropmiss when something has been dropped on a node that isn't contained by a drop target.
* @method drag_dropmiss
* @param {Event} e
* @returns {undefined}
*/
drag_dropmiss : function(e) {
// Missed the target, but we assume the user intended to drop it
// on the last ghost node location, e.drag and e.drop should be
// prepared by global_drag_dropmiss parent so simulate drop_hit(e).
this.drop_hit(e);
},
/**
* Called by M.core.dragdrop.global_drag_hit when something has been dropped on a drop target.
* @method drop_hit
* @param {Event} e
* @returns {undefined}
*/
drop_hit : function(e) {
// Get a reference to our drag node
var dragnode = e.drag.get('node');
var dropnode = e.drop.get('node');
// Amend existing skipnodes
if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) {
// the one that belongs to block below move below
dragnode.insert(dragnode.previous(), 'after');
}
// Move original skipnodes
if (this.skipnodetop) {
dragnode.insert(this.skipnodetop, 'before');
}
if (this.skipnodebottom) {
dragnode.insert(this.skipnodebottom, 'after');
}
// Add lightbox if it not there
var lightbox = M.util.add_lightbox(Y, dragnode);
// Prepare request parameters
var params = {
sesskey : M.cfg.sesskey,
courseid : this.get('courseid'),
pagelayout : this.get('pagelayout'),
pagetype : this.get('pagetype'),
subpage : this.get('subpage'),
contextid : this.get('contextid'),
action : 'move',
bui_moveid : this.get_block_id(dragnode),
bui_newregion : this.get_block_region(dropnode)
};
if (this.get('cmid')) {
params.cmid = this.get('cmid');
}
if (dragnode.next('.'+CSS.BLOCK) && !dragnode.next('.'+CSS.BLOCK).hasClass(CSS.BLOCKADMINBLOCK)) {
params.bui_beforeid = this.get_block_id(dragnode.next('.'+CSS.BLOCK));
}
// Do AJAX request
Y.io(M.cfg.wwwroot+AJAXURL, {
method: 'POST',
data: params,
on: {
start : function() {
lightbox.show();
},
success: function(tid, response) {
window.setTimeout(function() {
lightbox.hide();
}, 250);
try {
var responsetext = Y.JSON.parse(response.responseText);
if (responsetext.error) {
new M.core.ajaxException(responsetext);
}
} catch (e) {}
},
failure: function(tid, response) {
this.ajax_failure(response);
lightbox.hide();
},
complete : function() {
this.disable_regions_if_required();
}
},
context:this
});
}
};
Y.extend(MANAGER, M.core.dragdrop, MANAGER.prototype, {
NAME : 'core-blocks-dragdrop-manager',
ATTRS : {
/**
* The Course ID if there is one.
* @attribute courseid
* @type int|null
* @default null
*/
courseid : {
value : null
},
/**
* The Course Module ID if there is one.
* @attribute cmid
* @type int|null
* @default null
*/
cmid : {
value : null
},
/**
* The Context ID.
* @attribute contextid
* @type int|null
* @default null
*/
contextid : {
value : null
},
/**
* The current page layout.
* @attribute pagelayout
* @type string|null
* @default null
*/
pagelayout : {
value : null
},
/**
* The page type string, should be used as the id for the body tag in the theme.
* @attribute pagetype
* @type string|null
* @default null
*/
pagetype : {
value : null
},
/**
* The subpage identifier, if any.
* @attribute subpage
* @type string|null
* @default null
*/
subpage : {
value : null
},
/**
* An array of block regions that are present on the page.
* @attribute regions
* @type array|null
* @default Array[]
*/
regions : {
value : []
}
}
});/**
* This file contains the Block Region class used by the drag and drop manager.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new block region object.
*
* @namespace M.core.blockdraganddrop
* @class BlockRegion
* @constructor
* @extends Y.Base
*/
var BLOCKREGION = function() {
BLOCKREGION.superclass.constructor.apply(this, arguments);
};
BLOCKREGION.prototype = {
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var node = this.get('node');
if (!node) {
this.create_and_add_node();
}
var body = Y.one('body'),
hasblocks = node.all('.'+CSS.BLOCK).size() > 0,
hasregionclass = this.get_has_region_class();
this.set('hasblocks', hasblocks);
if (!body.hasClass(hasregionclass)) {
body.addClass(hasregionclass);
}
body.addClass((hasblocks) ? this.get_used_region_class() : this.get_empty_region_class());
body.removeClass((hasblocks) ? this.get_empty_region_class() : this.get_used_region_class());
},
/**
* Creates a generic block region node and adds it to the DOM at the best guess location.
* Any calling of this method is an unfortunate circumstance.
* @method create_and_add_node
*/
create_and_add_node : function() {
var c = Y.Node.create,
region = this.get('region'),
node = c('<div id="block-region-'+region+'" data-droptarget="1"></div>')
.addClass(CSS.BLOCKREGION)
.setData('blockregion', region),
regions = this.get('manager').get('regions'),
i,
haspre = false,
haspost = false,
added = false,
pre,
post;
for (i in regions) {
if (regions[i].match(/(pre|left)/)) {
haspre = regions[i];
} else if (regions[i].match(/(post|right)/)) {
haspost = regions[i];
}
}
if (haspre !== false && haspost !== false) {
if (region === haspre) {
post = Y.one('#block-region-'+haspost);
if (post) {
post.insert(node, 'before');
added = true;
}
} else {
pre = Y.one('#block-region-'+haspre);
if (pre) {
pre.insert(node, 'after');
added = true;
}
}
}
if (added === false) {
Y.one('body').append(node);
}
this.set('node', node);
},
/**
* Removes the move icons and changes the cursor to a move icon when over the header.
* @method remove_block_move_icons
*/
remove_block_move_icons : function() {
this.get('node').all('.'+CSS.BLOCK+' a.'+CSS.EDITINGMOVE).each(function(moveicon){
moveicon.ancestor('.'+CSS.BLOCK).one('.'+CSS.HEADER).setStyle('cursor', 'move');
moveicon.remove();
});
},
/**
* Returns the class name on the body that signifies the document knows about this region.
* @method get_has_region_class
* @return String
*/
get_has_region_class : function() {
return 'has-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains no blocks.
* @method get_empty_region_class
* @return String
*/
get_empty_region_class : function() {
return 'empty-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains blocks.
* @method get_used_region_class
* @return String
*/
get_used_region_class : function() {
return 'used-region-'+this.get('region');
},
/**
* Returns the node to use as the drop target for this region.
* @method get_droptarget
* @return Node
*/
get_droptarget : function() {
var node = this.get('node');
if (node.test('[data-droptarget="1"]')) {
return node;
}
return node.one('[data-droptarget="1"]');
},
/**
* Enables the block region so that we can be sure the user can see it.
* This is done even if it is empty.
* @method enable
*/
enable : function() {
Y.one('body').addClass(this.get_used_region_class()).removeClass(this.get_empty_region_class());
},
/**
* Disables the region if it contains no blocks, essentially hiding it from the user.
* @method disable_if_required
*/
disable_if_required : function() {
if (this.get('node').all('.'+CSS.BLOCK).size() === 0) {
Y.one('body').addClass(this.get_empty_region_class()).removeClass(this.get_used_region_class());
}
}
};
Y.extend(BLOCKREGION, Y.Base, BLOCKREGION.prototype, {
NAME : 'core-blocks-dragdrop-blockregion',
ATTRS : {
/**
* The drag and drop manager that created this block region instance.
* @attribute manager
* @type M.core.blockdraganddrop.Manager
* @writeOnce
*/
manager : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isObject(value) && value instanceof MANAGER;
}
},
/**
* The name of the block region this object represents.
* @attribute region
* @type String
* @writeOnce
*/
region : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isString(value);
}
},
/**
* The node the block region HTML starts at.s
* @attribute region
* @type Y.Node
*/
node : {
validator : function (value) {
return Y.Lang.isObject(value) || Y.Lang.isNull(value);
}
},
/**
* True if the block region currently contains blocks.
* @attribute hasblocks
* @type Boolean
* @default false
*/
hasblocks : {
value : false,
validator : function (value) {
return Y.Lang.isBoolean(value);
}
}
}
});
}, '@VERSION@', {
"requires": [
+1 -1
View File
@@ -158,7 +158,7 @@ YUI.add('moodle-core-dragdrop', function(Y) {
}
//Add the node
e.drop.get('node').ancestor().insertBefore(drag, drop);
} else if (drop.hasClass(this.parentnodeclass) && !drop.contains(drag)) {
} else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) {
// We are dropping on parent node and it is empty
if (this.goingup) {
drop.append(drag);
+3 -1
View File
@@ -3,7 +3,9 @@
"builds": {
"moodle-core-blocks": {
"jsfiles": [
"blocks.js"
"blocks.js",
"manager.js",
"blockregion.js"
]
}
}
+216
View File
@@ -0,0 +1,216 @@
/**
* This file contains the Block Region class used by the drag and drop manager.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new block region object.
*
* @namespace M.core.blockdraganddrop
* @class BlockRegion
* @constructor
* @extends Y.Base
*/
var BLOCKREGION = function() {
BLOCKREGION.superclass.constructor.apply(this, arguments);
};
BLOCKREGION.prototype = {
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var node = this.get('node');
Y.log('Block region `'+this.get('region')+'` initialising', 'info');
if (!node) {
Y.log('block region known about but no HTML structure found for it. Guessing structure.', 'warn');
this.create_and_add_node();
}
var body = Y.one('body'),
hasblocks = node.all('.'+CSS.BLOCK).size() > 0,
hasregionclass = this.get_has_region_class();
this.set('hasblocks', hasblocks);
if (!body.hasClass(hasregionclass)) {
body.addClass(hasregionclass);
}
body.addClass((hasblocks) ? this.get_used_region_class() : this.get_empty_region_class());
body.removeClass((hasblocks) ? this.get_empty_region_class() : this.get_used_region_class());
},
/**
* Creates a generic block region node and adds it to the DOM at the best guess location.
* Any calling of this method is an unfortunate circumstance.
* @method create_and_add_node
*/
create_and_add_node : function() {
var c = Y.Node.create,
region = this.get('region'),
node = c('<div id="block-region-'+region+'" data-droptarget="1"></div>')
.addClass(CSS.BLOCKREGION)
.setData('blockregion', region),
regions = this.get('manager').get('regions'),
i,
haspre = false,
haspost = false,
added = false,
pre,
post;
for (i in regions) {
if (regions[i].match(/(pre|left)/)) {
haspre = regions[i];
} else if (regions[i].match(/(post|right)/)) {
haspost = regions[i];
}
}
if (haspre !== false && haspost !== false) {
if (region === haspre) {
post = Y.one('#block-region-'+haspost);
if (post) {
post.insert(node, 'before');
added = true;
}
} else {
pre = Y.one('#block-region-'+haspre);
if (pre) {
pre.insert(node, 'after');
added = true;
}
}
}
if (added === false) {
Y.one('body').append(node);
}
this.set('node', node);
},
/**
* Removes the move icons and changes the cursor to a move icon when over the header.
* @method remove_block_move_icons
*/
remove_block_move_icons : function() {
this.get('node').all('.'+CSS.BLOCK+' a.'+CSS.EDITINGMOVE).each(function(moveicon){
moveicon.ancestor('.'+CSS.BLOCK).one('.'+CSS.HEADER).setStyle('cursor', 'move');
moveicon.remove();
});
},
/**
* Returns the class name on the body that signifies the document knows about this region.
* @method get_has_region_class
* @return String
*/
get_has_region_class : function() {
return 'has-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains no blocks.
* @method get_empty_region_class
* @return String
*/
get_empty_region_class : function() {
return 'empty-region-'+this.get('region');
},
/**
* Returns the class name to use on the body if the region contains blocks.
* @method get_used_region_class
* @return String
*/
get_used_region_class : function() {
return 'used-region-'+this.get('region');
},
/**
* Returns the node to use as the drop target for this region.
* @method get_droptarget
* @return Node
*/
get_droptarget : function() {
var node = this.get('node');
if (node.test('[data-droptarget="1"]')) {
return node;
}
return node.one('[data-droptarget="1"]');
},
/**
* Enables the block region so that we can be sure the user can see it.
* This is done even if it is empty.
* @method enable
*/
enable : function() {
Y.one('body').addClass(this.get_used_region_class()).removeClass(this.get_empty_region_class());
},
/**
* Disables the region if it contains no blocks, essentially hiding it from the user.
* @method disable_if_required
*/
disable_if_required : function() {
if (this.get('node').all('.'+CSS.BLOCK).size() === 0) {
Y.one('body').addClass(this.get_empty_region_class()).removeClass(this.get_used_region_class());
}
}
};
Y.extend(BLOCKREGION, Y.Base, BLOCKREGION.prototype, {
NAME : 'core-blocks-dragdrop-blockregion',
ATTRS : {
/**
* The drag and drop manager that created this block region instance.
* @attribute manager
* @type M.core.blockdraganddrop.Manager
* @writeOnce
*/
manager : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isObject(value) && value instanceof MANAGER;
}
},
/**
* The name of the block region this object represents.
* @attribute region
* @type String
* @writeOnce
*/
region : {
// Can only be set during initialisation and must be set then.
writeOnce : 'initOnly',
validator : function (value) {
return Y.Lang.isString(value);
}
},
/**
* The node the block region HTML starts at.s
* @attribute region
* @type Y.Node
*/
node : {
validator : function (value) {
return Y.Lang.isObject(value) || Y.Lang.isNull(value);
}
},
/**
* True if the block region currently contains blocks.
* @attribute hasblocks
* @type Boolean
* @default false
*/
hasblocks : {
value : false,
validator : function (value) {
return Y.Lang.isBoolean(value);
}
}
}
});
+77 -2
View File
@@ -1,3 +1,9 @@
/**
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
var AJAXURL = '/lib/ajax/blocks.php',
CSS = {
BLOCK : 'block',
@@ -13,6 +19,16 @@ CSS = {
REGIONMAIN : 'region-main'
};
/**
* Legacy drag and drop manager.
* This drag and drop manager is specifically designed for themes using side-pre and side-post
* that do not make use of the block output methods introduced by MDL-39824.
*
* @namespace M.core.blockdraganddrop
* @class LegacyManager
* @constructor
* @extends M.core.dragdrop
*/
var DRAGBLOCK = function() {
DRAGBLOCK.superclass.constructor.apply(this, arguments);
};
@@ -299,7 +315,66 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
}
});
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Block drag and drop static class.
* @namespace M.core
* @class blockdraganddrop
* @static
*/
M.core.blockdraganddrop = M.core.blockdraganddrop || {};
/**
* True if the page is using the new blocks methods.
* @private
* @static
* @property _isusingnewblocksmethod
* @type Boolean
* @default null
*/
M.core.blockdraganddrop._isusingnewblocksmethod = null;
/**
* Returns true if the page is using the new blocks methods.
* @static
* @method is_using_blocks_render_method
* @return Boolean
*/
M.core.blockdraganddrop.is_using_blocks_render_method = function() {
if (this._isusingnewblocksmethod === null) {
var goodregions = Y.all('.block-region[data-blockregion]').size();
var allregions = Y.all('.block-region').size();
this._isusingnewblocksmethod = (allregions === goodregions);
}
return this._isusingnewblocksmethod;
};
/**
* Initialises a drag and drop manager.
* This should only ever be called once for a page.
* @static
* @method init
* @param {Object} params
* @return Manager
*/
M.core.blockdraganddrop.init = function(params) {
if (this.is_using_blocks_render_method()) {
new MANAGER(params);
} else {
new DRAGBLOCK(params);
}
};
/**
* Legacy code to keep things working.
*/
M.core_blocks = M.core_blocks || {};
M.core_blocks.init_dragdrop = function(params) {
new DRAGBLOCK(params);
};
M.core.blockdraganddrop.init(params);
};
+393
View File
@@ -0,0 +1,393 @@
/**
* This file contains the drag and drop manager class.
*
* Provides drag and drop functionality for blocks.
*
* @module moodle-core-blockdraganddrop
*/
/**
* Constructs a new Block drag and drop manager.
*
* @namespace M.core.blockdraganddrop
* @class Manager
* @constructor
* @extends M.core.dragdrop
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
MANAGER.prototype = {
/**
* The skip block link from above the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodetop
* @type Node
* @default null
*/
skipnodetop : null,
/**
* The skip block link from below the block being dragged while a drag is in progress.
* Required by the M.core.dragdrop from whom this class extends.
* @private
* @property skipnodebottom
* @type Node
* @default null
*/
skipnodebottom : null,
/**
* An associative object of regions and the
* @property regionobjects
* @type {Object} Primitive object mocking an associative array.
* @type {BLOCKREGION} [regionname]* Each item uses the region name as the key with the value being
* an instance of the BLOCKREGION class.
*/
regionobjects : {},
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
Y.log('Initialising drag and drop for blocks.', 'info');
var regionnames = this.get('regions'),
i = 0,
region,
regionname,
droptarget,
dragdelegation;
// Evil required by M.core.dragdrop.
this.groups = ['block'];
this.samenodeclass = CSS.BLOCK;
this.parentnodeclass = CSS.BLOCKREGION;
// Add relevant classes and ID to 'content' block region on My Home page.
var myhomecontent = Y.Node.all('body#'+CSS.MYINDEX+' #'+CSS.REGIONMAIN+' > .'+CSS.REGIONCONTENT);
if (myhomecontent.size() > 0) {
var contentregion = myhomecontent.item(0);
contentregion.addClass(CSS.BLOCKREGION);
contentregion.set('id', CSS.REGIONCONTENT);
contentregion.one('div').addClass(CSS.REGIONCONTENT);
}
for (i in regionnames) {
regionname = regionnames[i];
region = new BLOCKREGION({
manager : this,
region : regionname,
node : Y.one('#block-region-'+regionname)
});
this.regionobjects[regionname] = region;
// Setting blockregion as droptarget (the case when it is empty)
// The region-post (the right one)
// is very narrow, so add extra padding on the left to drop block on it.
droptarget = new Y.DD.Drop({
node: region.get_droptarget(),
groups: this.groups,
padding: '40 240 40 240'
});
// Make each div element in the list of blocks draggable
dragdelegation = new Y.DD.Delegate({
container: region.get_droptarget(),
nodes: '.'+CSS.BLOCK,
target: true,
handles: ['.'+CSS.HEADER],
invalid: '.block-hider-hide, .block-hider-show, .moveto',
dragConfig: {groups: this.groups}
});
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
// Don't move the node at the end of the drag
moveOnEnd: false
});
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
// On the mouse down event we will enable all block regions so that they can be dragged to.
// This is VERY important as without it dnd won't work for empty block regions.
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
region.remove_block_move_icons();
}
Y.log('Initialisation of drag and drop for blocks complete.', 'info');
},
/**
* Returns the ID of the block the given node represents.
* @method get_block_id
* @param {Node} node
* @returns {int} The blocks ID in the database.
*/
get_block_id : function(node) {
return Number(node.get('id').replace(/inst/i, ''));
},
/**
* Returns the block region that the node is part of or belonging to.
* @method get_block_region
* @param {Y.Node} node
* @returns {string} The region name.
*/
get_block_region : function(node) {
if (!node.test('[data-blockregion]')) {
node = node.ancestor('[data-blockregion]');
}
return node.getData('blockregion');
},
/**
* Returns the BLOCKREGION instance that represents the block region the given node is part of.
* @method get_region_object
* @param {Y.Node} node
* @returns {BLOCKREGION}
*/
get_region_object : function(node) {
return this.regionobjects[this.get_block_region(node)];
},
/**
* Enables all fo the regions so that they are all visible while dragging is occuring.
* @method enable_all_regions
* @returns {undefined}
*/
enable_all_regions : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].enable();
}
},
/**
* Disables enabled regions if they contain no blocks.
* @method disable_regions_if_required
* @returns {undefined}
*/
disable_regions_if_required : function() {
var i = 0;
for (i in this.regionobjects) {
this.regionobjects[i].disable_if_required();
}
},
/**
* Called by M.core.dragdrop.global_drag_start when dragging starts.
* @method drag_start
* @param {Event} e
* @returns {undefined}
*/
drag_start : function(e) {
// Get our drag object
var drag = e.target;
// Store the parent node of original drag node (block)
// we will need it later for show/hide empty regions
// Determine skipnodes and store them
if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) {
this.skipnodetop = drag.get('node').previous();
}
if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) {
this.skipnodebottom = drag.get('node').next();
}
},
/**
* Called by M.core.dragdrop.global_drop_over when something is dragged over a drop target.
* @method drop_over
* @param {Event} e
* @returns {undefined}
*/
drop_over : function(e) {
// Get a reference to our drag and drop nodes
var drag = e.drag.get('node');
var drop = e.drop.get('node');
// We need to fix the case when parent drop over event has determined
// 'goingup' and appended the drag node after admin-block.
if (drop.hasClass(CSS.REGIONCONTENT) && drop.one('.'+CSS.BLOCKADMINBLOCK) && drop.one('.'+CSS.BLOCKADMINBLOCK).next('.'+CSS.BLOCK)) {
drop.prepend(drag);
}
},
/**
* Called by M.core.dragdrop.global_drop_end when a drop has been completed.
* @method drop_end
* @returns {undefined}
*/
drop_end : function() {
// Clear variables.
this.skipnodetop = null;
this.skipnodebottom = null;
this.disable_regions_if_required();
},
/**
* Called by M.core.dragdrop.global_drag_dropmiss when something has been dropped on a node that isn't contained by a drop target.
* @method drag_dropmiss
* @param {Event} e
* @returns {undefined}
*/
drag_dropmiss : function(e) {
// Missed the target, but we assume the user intended to drop it
// on the last ghost node location, e.drag and e.drop should be
// prepared by global_drag_dropmiss parent so simulate drop_hit(e).
this.drop_hit(e);
},
/**
* Called by M.core.dragdrop.global_drag_hit when something has been dropped on a drop target.
* @method drop_hit
* @param {Event} e
* @returns {undefined}
*/
drop_hit : function(e) {
// Get a reference to our drag node
var dragnode = e.drag.get('node');
var dropnode = e.drop.get('node');
// Amend existing skipnodes
if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) {
// the one that belongs to block below move below
dragnode.insert(dragnode.previous(), 'after');
}
// Move original skipnodes
if (this.skipnodetop) {
dragnode.insert(this.skipnodetop, 'before');
}
if (this.skipnodebottom) {
dragnode.insert(this.skipnodebottom, 'after');
}
// Add lightbox if it not there
var lightbox = M.util.add_lightbox(Y, dragnode);
// Prepare request parameters
var params = {
sesskey : M.cfg.sesskey,
courseid : this.get('courseid'),
pagelayout : this.get('pagelayout'),
pagetype : this.get('pagetype'),
subpage : this.get('subpage'),
contextid : this.get('contextid'),
action : 'move',
bui_moveid : this.get_block_id(dragnode),
bui_newregion : this.get_block_region(dropnode)
};
if (this.get('cmid')) {
params.cmid = this.get('cmid');
}
if (dragnode.next('.'+CSS.BLOCK) && !dragnode.next('.'+CSS.BLOCK).hasClass(CSS.BLOCKADMINBLOCK)) {
params.bui_beforeid = this.get_block_id(dragnode.next('.'+CSS.BLOCK));
}
// Do AJAX request
Y.io(M.cfg.wwwroot+AJAXURL, {
method: 'POST',
data: params,
on: {
start : function() {
lightbox.show();
},
success: function(tid, response) {
window.setTimeout(function() {
lightbox.hide();
}, 250);
try {
var responsetext = Y.JSON.parse(response.responseText);
if (responsetext.error) {
new M.core.ajaxException(responsetext);
}
} catch (e) {}
},
failure: function(tid, response) {
this.ajax_failure(response);
lightbox.hide();
},
complete : function() {
this.disable_regions_if_required();
}
},
context:this
});
}
};
Y.extend(MANAGER, M.core.dragdrop, MANAGER.prototype, {
NAME : 'core-blocks-dragdrop-manager',
ATTRS : {
/**
* The Course ID if there is one.
* @attribute courseid
* @type int|null
* @default null
*/
courseid : {
value : null
},
/**
* The Course Module ID if there is one.
* @attribute cmid
* @type int|null
* @default null
*/
cmid : {
value : null
},
/**
* The Context ID.
* @attribute contextid
* @type int|null
* @default null
*/
contextid : {
value : null
},
/**
* The current page layout.
* @attribute pagelayout
* @type string|null
* @default null
*/
pagelayout : {
value : null
},
/**
* The page type string, should be used as the id for the body tag in the theme.
* @attribute pagetype
* @type string|null
* @default null
*/
pagetype : {
value : null
},
/**
* The subpage identifier, if any.
* @attribute subpage
* @type string|null
* @default null
*/
subpage : {
value : null
},
/**
* An array of block regions that are present on the page.
* @attribute regions
* @type array|null
* @default Array[]
*/
regions : {
value : []
}
}
});