Skip to content
Browse files

MDL-39842 js: fixed block drag and drop

  • Loading branch information...
1 parent 6964e6a commit ee7a52ae91b71fcc05224ee5c5558e8ac7e5155e @samhemelryk samhemelryk committed
View
683 lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
@@ -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, {
}
});
+/**
+ * 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);
+};/**
+ * 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": [
View
3 lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js
@@ -1 +1,2 @@
-YUI.add("moodle-core-blocks",function(e,t){var n="/lib/ajax/blocks.php",r={BLOCK:"block",BLOCKREGION:"block-region",BLOCKADMINBLOCK:"block_adminblock",EDITINGMOVE:"editing_move",HEADER:"header",LIGHTBOX:"lightbox",REGIONCONTENT:"region-content",SKIPBLOCK:"skip-block",SKIPBLOCKTO:"skip-block-to",MYINDEX:"page-my-index",REGIONMAIN:"region-main"},i=function(){i.superclass.constructor.apply(this,arguments)};e.extend(i,M.core.dragdrop,{skipnodetop:null,skipnodebottom:null,dragsourceregion:null,initializer:function(){this.groups=["block"],this.samenodeclass=r.BLOCK,this.parentnodeclass=r.REGIONCONTENT;var t=e.Node.all("body#"+r.MYINDEX+" #"+r.REGIONMAIN+" > ."+r.REGIONCONTENT);if(t.size()>0){var n=t.item(0);n.addClass(r.BLOCKREGION),n.set("id",r.REGIONCONTENT),n.one("div").addClass(r.REGIONCONTENT)}var i=e.Node.all("div."+r.BLOCKREGION);if(i.size()===0)return!1;if(i.size()!=this.get("regions").length){var s=e.Node.create("<div></div>").addClass(r.BLOCKREGION),o=e.Node.create("<div></div>").addClass(r.REGIONCONTENT);s.appendChild(o);var u=i.filter("#region-pre"),a=i.filter("#region-post");u.size()===0&&a.size()===1?(s.setAttrs({id:"region-pre"}),a.item(0).insert(s,"before"),i.unshift(s)):a.size()===0&&u.size()===1&&(s.setAttrs({id:"region-post"}),u.item(0).insert(s,"after"),i.push(s))}i.each(function(t){var n=new e.DD.Drop({node:t.one("div."+r.REGIONCONTENT),groups:this.groups,padding:"40 240 40 240"}),i=new e.DD.Delegate({container:t,nodes:"."+r.BLOCK,target:!0,handles:["."+r.HEADER],invalid:".block-hider-hide, .block-hider-show, .moveto",dragConfig:{groups:this.groups}});i.dd.plug(e.Plugin.DDProxy,{moveOnEnd:!1}),i.dd.plug(e.Plugin.DDWinScroll);var s=t.all("."+r.BLOCK);s.each(function(e){var t=e.one("a."+r.EDITINGMOVE);t&&(t.remove(),e.one("."+r.HEADER).setStyle("cursor","move"))},this)},this)},get_block_id:function(e){return Number(e.get("id").replace(/inst/i,""))},get_block_region:function(t){var n=t.ancestor("div."+r.BLOCKREGION).get("id").replace(/region-/i,"");return e.Array.indexOf(this.get("regions"),n)===-1?(right_to_left()&&(n==="post"?n="pre":n==="pre"&&(n="post")),"side-"+n):n},get_region_id:function(e){return e.get("id").replace(/region-/i,"")},drag_start:function(e){var t=e.target;this.dragsourceregion=t.get("node").ancestor("div."+r.BLOCKREGION),t.get("node").previous()&&t.get("node").previous().hasClass(r.SKIPBLOCK)&&(this.skipnodetop=t.get("node").previous()),t.get("node").next()&&t.get("node").next().hasClass(r.SKIPBLOCKTO)&&(this.skipnodebottom=t.get("node").next())},drop_over:function(t){var n=t.drag.get("node"),i=t.drop.get("node");i.hasClass(this.parentnodeclass)&&i.one("."+r.BLOCKADMINBLOCK)&&i.one("."+r.BLOCKADMINBLOCK).next("."+r.BLOCK)&&i.prepend(n);if(this.dragsourceregion.contains(i))return!1;var s=e.one("body"),o=this.get_region_id(this.dragsourceregion);s.hasClass("side-"+o+"-only")&&s.removeClass("side-"+o+"-only"),o=this.get_region_id(i.ancestor("div."+r.BLOCKREGION)),this.dragsourceregion.all("."+r.BLOCK).size()==0&&this.dragsourceregion.get("id").match(/(region-pre|region-post)/i)&&(s.hasClass("side-"+o+"-only")||s.addClass("side-"+o+"-only"))},drop_end:function(){this.skipnodetop=null,this.skipnodebottom=null,this.dragsourceregion=null},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(t){var i=t.drag,s=i.get("node"),o=t.drop.get("node");s.previous()&&s.previous().hasClass(r.SKIPBLOCK)&&s.insert(s.previous(),"after"),this.skipnodetop&&s.insert(this.skipnodetop,"before"),this.skipnodebottom&&s.insert(this.skipnodebottom,"after");var u=M.util.add_lightbox(e,s),a={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(s),bui_newregion:this.get_block_region(o)};this.get("cmid")&&(a.cmid=this.get("cmid")),s.next("."+this.samenodeclass)&&!s.next("."+this.samenodeclass).hasClass(r.BLOCKADMINBLOCK)&&(a.bui_beforeid=this.get_block_id(s.next("."+this.samenodeclass))),e.io(M.cfg.wwwroot+n,{method:"POST",data:a,on:{start:function(){u.show()},success:function(t,n){window.setTimeout(function(){u.hide()},250);try{var r=e.JSON.parse(n.responseText);r.error&&new M.core.ajaxException(r)}catch(i){}},failure:function(e,t){this.ajax_failure(t),u.hide()}},context:this})}},{NAME:"core-blocks-dragdrop",ATTRS:{courseid:{value:null},cmid:{value:null},contextid:{value:null},pagelayout:{value:null},pagetype:{value:null},subpage:{value:null},regions:{value:null}}}),M.core_blocks=M.core_blocks||{},M.core_blocks.init_dragdrop=function(e){new i(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification"]});
+YUI.add("moodle-core-blocks",function(e,t){var n="/lib/ajax/blocks.php",r={BLOCK:"block",BLOCKREGION:"block-region",BLOCKADMINBLOCK:"block_adminblock",EDITINGMOVE:"editing_move",HEADER:"header",LIGHTBOX:"lightbox",REGIONCONTENT:"region-content",SKIPBLOCK:"skip-block",SKIPBLOCKTO:"skip-block-to",MYINDEX:"page-my-index",REGIONMAIN:"region-main"},i=function(){i.superclass.constructor.apply(this,arguments)};e.extend(i,M.core.dragdrop,{skipnodetop:null,skipnodebottom:null,dragsourceregion:null,initializer:function(){this.groups=["block"],this.samenodeclass=r.BLOCK,this.parentnodeclass=r.REGIONCONTENT;var t=e.Node.all("body#"+r.MYINDEX+" #"+r.REGIONMAIN+" > ."+r.REGIONCONTENT);if(t.size()>0){var n=t.item(0);n.addClass(r.BLOCKREGION),n.set("id",r.REGIONCONTENT),n.one("div").addClass(r.REGIONCONTENT)}var i=e.Node.all("div."+r.BLOCKREGION);if(i.size()===0)return!1;if(i.size()!=this.get("regions").length){var s=e.Node.create("<div></div>").addClass(r.BLOCKREGION),o=e.Node.create("<div></div>").addClass(r.REGIONCONTENT);s.appendChild(o);var u=i.filter("#region-pre"),a=i.filter("#region-post");u.size()===0&&a.size()===1?(s.setAttrs({id:"region-pre"}),a.item(0).insert(s,"before"),i.unshift(s)):a.size()===0&&u.size()===1&&(s.setAttrs({id:"region-post"}),u.item(0).insert(s,"after"),i.push(s))}i.each(function(t){var n=new e.DD.Drop({node:t.one("div."+r.REGIONCONTENT),groups:this.groups,padding:"40 240 40 240"}),i=new e.DD.Delegate({container:t,nodes:"."+r.BLOCK,target:!0,handles:["."+r.HEADER],invalid:".block-hider-hide, .block-hider-show, .moveto",dragConfig:{groups:this.groups}});i.dd.plug(e.Plugin.DDProxy,{moveOnEnd:!1}),i.dd.plug(e.Plugin.DDWinScroll);var s=t.all("."+r.BLOCK);s.each(function(e){var t=e.one("a."+r.EDITINGMOVE);t&&(t.remove(),e.one("."+r.HEADER).setStyle("cursor","move"))},this)},this)},get_block_id:function(e){return Number(e.get("id").replace(/inst/i,""))},get_block_region:function(t){var n=t.ancestor("div."+r.BLOCKREGION).get("id").replace(/region-/i,"");return e.Array.indexOf(this.get("regions"),n)===-1?(right_to_left()&&(n==="post"?n="pre":n==="pre"&&(n="post")),"side-"+n):n},get_region_id:function(e){return e.get("id").replace(/region-/i,"")},drag_start:function(e){var t=e.target;this.dragsourceregion=t.get("node").ancestor("div."+r.BLOCKREGION),t.get("node").previous()&&t.get("node").previous().hasClass(r.SKIPBLOCK)&&(this.skipnodetop=t.get("node").previous()),t.get("node").next()&&t.get("node").next().hasClass(r.SKIPBLOCKTO)&&(this.skipnodebottom=t.get("node").next())},drop_over:function(t){var n=t.drag.get("node"),i=t.drop.get("node");i.hasClass(this.parentnodeclass)&&i.one("."+r.BLOCKADMINBLOCK)&&i.one("."+r.BLOCKADMINBLOCK).next("."+r.BLOCK)&&i.prepend(n);if(this.dragsourceregion.contains(i))return!1;var s=e.one("body"),o=this.get_region_id(this.dragsourceregion);s.hasClass("side-"+o+"-only")&&s.removeClass("side-"+o+"-only"),o=this.get_region_id(i.ancestor("div."+r.BLOCKREGION)),this.dragsourceregion.all("."+r.BLOCK).size()==0&&this.dragsourceregion.get("id").match(/(region-pre|region-post)/i)&&(s.hasClass("side-"+o+"-only")||s.addClass("side-"+o+"-only"))},drop_end:function(){this.skipnodetop=null,this.skipnodebottom=null,this.dragsourceregion=null},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(t){var i=t.drag,s=i.get("node"),o=t.drop.get("node");s.previous()&&s.previous().hasClass(r.SKIPBLOCK)&&s.insert(s.previous(),"after"),this.skipnodetop&&s.insert(this.skipnodetop,"before"),this.skipnodebottom&&s.insert(this.skipnodebottom,"after");var u=M.util.add_lightbox(e,s),a={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(s),bui_newregion:this.get_block_region(o)};this.get("cmid")&&(a.cmid=this.get("cmid")),s.next("."+this.samenodeclass)&&!s.next("."+this.samenodeclass).hasClass(r.BLOCKADMINBLOCK)&&(a.bui_beforeid=this.get_block_id(s.next("."+this.samenodeclass))),e.io(M.cfg.wwwroot+n,{method:"POST",data:a,on:{start:function(){u.show()},success:function(t,n){window.setTimeout(function(){u.hide()},250);try{var r=e.JSON.parse(n.responseText);r.error&&new M.core.ajaxException(r)}catch(i){}},failure:function(e,t){this.ajax_failure(t),u.hide()}},context:this})}},{NAME:"core-blocks-dragdrop",ATTRS:{courseid:{value:null},cmid:{value:null},contextid:{value:null},pagelayout:{value:null},pagetype:{value:null},subpage:{value:null},regions:{value:null}}}),M.core=M.core||{},M.core.blockdraganddrop=M.core.blockdraganddrop||{},M.core.blockdraganddrop._isusingnewblocksmethod=null,M.core.blockdraganddrop.is_using_blocks_render_method=function(){if(this._isusingnewblocksmethod===null){var t=e.all(".block-region[data-blockregion]").size(),n=e.all(".block-region").size();this._isusingnewblocksmethod=n===t}return this._isusingnewblocksmethod},M.core.blockdraganddrop.init=function(e){this.is_using_blocks_render_method()?new s(e):new i(e)},M.core_blocks=M.core_blocks||{},M.core_blocks.init_dragdrop=function(e){M.core.blockdraganddrop.init(e)};var s=function(){s.superclass.constructor.apply(this,arguments)};s.prototype={skipnodetop:null,skipnodebottom:null,regionobjects:{},initializer:function(){var t=this.get("regions"),n=0,i,s,u,a;this.groups=["block"],this.samenodeclass=r.BLOCK,this.parentnodeclass=r.BLOCKREGION;var f=e.Node.all("body#"+r.MYINDEX+" #"+r.REGIONMAIN+" > ."+r.REGIONCONTENT);if(f.size()>0){var l=f.item(0);l.addClass(r.BLOCKREGION),l.set("id",r.REGIONCONTENT),l.one("div").addClass(r.REGIONCONTENT)}for(n in t)s=t[n],i=new o({manager:this,region:s,node:e.one("#block-region-"+s)}),this.regionobjects[s]=i,u=new e.DD.Drop({node:i.get_droptarget(),groups:this.groups,padding:"40 240 40 240"}),a=new e.DD.Delegate({container:i.get_droptarget(),nodes:"."+r.BLOCK,target:!0,handles:["."+r.HEADER],invalid:".block-hider-hide, .block-hider-show, .moveto",dragConfig:{groups:this.groups}}),a.dd.plug(e.Plugin.DDProxy,{moveOnEnd
+:!1}),a.dd.plug(e.Plugin.DDWinScroll),a.on("drag:mouseDown",this.enable_all_regions,this),i.remove_block_move_icons()},get_block_id:function(e){return Number(e.get("id").replace(/inst/i,""))},get_block_region:function(e){return e.test("[data-blockregion]")||(e=e.ancestor("[data-blockregion]")),e.getData("blockregion")},get_region_object:function(e){return this.regionobjects[this.get_block_region(e)]},enable_all_regions:function(){var e=0;for(e in this.regionobjects)this.regionobjects[e].enable()},disable_regions_if_required:function(){var e=0;for(e in this.regionobjects)this.regionobjects[e].disable_if_required()},drag_start:function(e){var t=e.target;t.get("node").previous()&&t.get("node").previous().hasClass(r.SKIPBLOCK)&&(this.skipnodetop=t.get("node").previous()),t.get("node").next()&&t.get("node").next().hasClass(r.SKIPBLOCKTO)&&(this.skipnodebottom=t.get("node").next())},drop_over:function(e){var t=e.drag.get("node"),n=e.drop.get("node");n.hasClass(r.REGIONCONTENT)&&n.one("."+r.BLOCKADMINBLOCK)&&n.one("."+r.BLOCKADMINBLOCK).next("."+r.BLOCK)&&n.prepend(t)},drop_end:function(){this.skipnodetop=null,this.skipnodebottom=null,this.disable_regions_if_required()},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(t){var i=t.drag.get("node"),s=t.drop.get("node");i.previous()&&i.previous().hasClass(r.SKIPBLOCK)&&i.insert(i.previous(),"after"),this.skipnodetop&&i.insert(this.skipnodetop,"before"),this.skipnodebottom&&i.insert(this.skipnodebottom,"after");var o=M.util.add_lightbox(e,i),u={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(i),bui_newregion:this.get_block_region(s)};this.get("cmid")&&(u.cmid=this.get("cmid")),i.next("."+r.BLOCK)&&!i.next("."+r.BLOCK).hasClass(r.BLOCKADMINBLOCK)&&(u.bui_beforeid=this.get_block_id(i.next("."+r.BLOCK))),e.io(M.cfg.wwwroot+n,{method:"POST",data:u,on:{start:function(){o.show()},success:function(t,n){window.setTimeout(function(){o.hide()},250);try{var r=e.JSON.parse(n.responseText);r.error&&new M.core.ajaxException(r)}catch(i){}},failure:function(e,t){this.ajax_failure(t),o.hide()},complete:function(){this.disable_regions_if_required()}},context:this})}},e.extend(s,M.core.dragdrop,s.prototype,{NAME:"core-blocks-dragdrop-manager",ATTRS:{courseid:{value:null},cmid:{value:null},contextid:{value:null},pagelayout:{value:null},pagetype:{value:null},subpage:{value:null},regions:{value:[]}}});var o=function(){o.superclass.constructor.apply(this,arguments)};o.prototype={initializer:function(){var t=this.get("node");t||this.create_and_add_node();var n=e.one("body"),i=t.all("."+r.BLOCK).size()>0,s=this.get_has_region_class();this.set("hasblocks",i),n.hasClass(s)||n.addClass(s),n.addClass(i?this.get_used_region_class():this.get_empty_region_class()),n.removeClass(i?this.get_empty_region_class():this.get_used_region_class())},create_and_add_node:function(){var t=e.Node.create,n=this.get("region"),i=t('<div id="block-region-'+n+'" data-droptarget="1"></div>').addClass(r.BLOCKREGION).setData("blockregion",n),s=this.get("manager").get("regions"),o,u=!1,a=!1,f=!1,l,c;for(o in s)s[o].match(/(pre|left)/)?u=s[o]:s[o].match(/(post|right)/)&&(a=s[o]);u!==!1&&a!==!1&&(n===u?(c=e.one("#block-region-"+a),c&&(c.insert(i,"before"),f=!0)):(l=e.one("#block-region-"+u),l&&(l.insert(i,"after"),f=!0))),f===!1&&e.one("body").append(i),this.set("node",i)},remove_block_move_icons:function(){this.get("node").all("."+r.BLOCK+" a."+r.EDITINGMOVE).each(function(e){e.ancestor("."+r.BLOCK).one("."+r.HEADER).setStyle("cursor","move"),e.remove()})},get_has_region_class:function(){return"has-region-"+this.get("region")},get_empty_region_class:function(){return"empty-region-"+this.get("region")},get_used_region_class:function(){return"used-region-"+this.get("region")},get_droptarget:function(){var e=this.get("node");return e.test('[data-droptarget="1"]')?e:e.one('[data-droptarget="1"]')},enable:function(){e.one("body").addClass(this.get_used_region_class()).removeClass(this.get_empty_region_class())},disable_if_required:function(){this.get("node").all("."+r.BLOCK).size()===0&&e.one("body").addClass(this.get_empty_region_class()).removeClass(this.get_used_region_class())}},e.extend(o,e.Base,o.prototype,{NAME:"core-blocks-dragdrop-blockregion",ATTRS:{manager:{writeOnce:"initOnly",validator:function(t){return e.Lang.isObject(t)&&t instanceof s}},region:{writeOnce:"initOnly",validator:function(t){return e.Lang.isString(t)}},node:{validator:function(t){return e.Lang.isObject(t)||e.Lang.isNull(t)}},hasblocks:{value:!1,validator:function(t){return e.Lang.isBoolean(t)}}}})},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification"]});
View
679 lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
@@ -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, {
}
});
+/**
+ * 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);
+};/**
+ * 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": [
View
2 lib/yui/dragdrop/dragdrop.js
@@ -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);
View
4 lib/yui/src/blocks/build.json
@@ -3,7 +3,9 @@
"builds": {
"moodle-core-blocks": {
"jsfiles": [
- "blocks.js"
+ "blocks.js",
+ "manager.js",
+ "blockregion.js"
]
}
}
View
216 lib/yui/src/blocks/js/blockregion.js
@@ -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);
+ }
+ }
+ }
+});
View
79 lib/yui/src/blocks/js/blocks.js
@@ -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);
+};
View
393 lib/yui/src/blocks/js/manager.js
@@ -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 : []
+ }
+ }
+});

0 comments on commit ee7a52a

Please sign in to comment.
Something went wrong with that request. Please try again.