MDL-39842 js: fixed block drag and drop
This commit is contained in:
+684
-3
@@ -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
@@ -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": [
|
||||
|
||||
Vendored
+1
-1
@@ -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,7 +3,9 @@
|
||||
"builds": {
|
||||
"moodle-core-blocks": {
|
||||
"jsfiles": [
|
||||
"blocks.js"
|
||||
"blocks.js",
|
||||
"manager.js",
|
||||
"blockregion.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+216
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Vendored
+77
-2
@@ -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);
|
||||
};
|
||||
Vendored
+393
@@ -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 : []
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user