mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
MDL-40167 dock: converted the dock JS to a YUI shifted module.
The patch converts the dock into a YUI shifter module and at the same time improves several aspects of its operation. The features of this patch include: * Dock Module conversion. * A loader that ensures we don't include the dock JS or its requirements unless actually required. * We no longer include the dock JS for themes that don't enable it. * Blocks no longer add registration events to the page instead a dockable attribute is added to the html and the loader looks for that. * The dock module is properly documented and running YUIDoc gives good quality documentation. * We no longer need the dock module registration or subcomponent. * All events that can be delegated are now delegated. * Removed unused variables and code left over after fixes. * Support for docking blocks renderered using the new blocks render method. Better support for custom block regions.
This commit is contained in:
parent
07bbbcf174
commit
84192d7836
1141
blocks/dock.js
1141
blocks/dock.js
File diff suppressed because it is too large
Load Diff
@ -271,6 +271,10 @@ class block_base {
|
||||
$bc->collapsible = block_contents::VISIBLE;
|
||||
}
|
||||
|
||||
if ($this->instance_can_be_docked() && !$this->hide_header()) {
|
||||
$bc->dockable = true;
|
||||
}
|
||||
|
||||
$bc->annotation = ''; // TODO MDL-19398 need to work out what to say here.
|
||||
|
||||
return $bc;
|
||||
@ -435,9 +439,13 @@ class block_base {
|
||||
$this->specialization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the block to load any JS it requires into the page.
|
||||
*
|
||||
* By default this function simply permits the user to dock the block if it is dockable.
|
||||
*/
|
||||
function get_required_javascript() {
|
||||
if ($this->instance_can_be_docked() && !$this->hide_header()) {
|
||||
$this->page->requires->js_init_call('M.core_dock.init_genericblock', array($this->instance->id));
|
||||
user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ class block_navigation extends block_base {
|
||||
* Set the initial properties for the block
|
||||
*/
|
||||
function init() {
|
||||
global $CFG;
|
||||
$this->blockname = get_class($this);
|
||||
$this->title = get_string('pluginname', $this->blockname);
|
||||
}
|
||||
@ -109,8 +108,7 @@ class block_navigation extends block_base {
|
||||
*/
|
||||
function get_required_javascript() {
|
||||
global $CFG;
|
||||
user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
|
||||
$this->page->requires->js_module('core_dock');
|
||||
parent::get_required_javascript();
|
||||
$limit = 20;
|
||||
if (!empty($CFG->navcourselimit)) {
|
||||
$limit = $CFG->navcourselimit;
|
||||
@ -127,7 +125,7 @@ class block_navigation extends block_base {
|
||||
'expansionlimit' => $expansionlimit
|
||||
);
|
||||
$this->page->requires->string_for_js('viewallcourses', 'moodle');
|
||||
$this->page->requires->yui_module(array('core_dock', 'moodle-block_navigation-navigation'), 'M.block_navigation.init_add_tree', array($arguments));
|
||||
$this->page->requires->yui_module('moodle-block_navigation-navigation', 'M.block_navigation.init_add_tree', array($arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,7 +134,6 @@ class block_navigation extends block_base {
|
||||
* @return object $this->content
|
||||
*/
|
||||
function get_content() {
|
||||
global $CFG, $OUTPUT;
|
||||
// First check if we have already generated, don't waste cycles
|
||||
if ($this->contentgenerated === true) {
|
||||
return $this->content;
|
||||
|
@ -1,5 +1,51 @@
|
||||
YUI.add('moodle-block_navigation-navigation', function (Y, NAME) {
|
||||
|
||||
/**
|
||||
* Navigation block JS.
|
||||
*
|
||||
* This file contains the Navigation block JS..
|
||||
*
|
||||
* @module moodle-block_navigation-navigation
|
||||
*/
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace M
|
||||
* @class block_navigation
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {};
|
||||
/**
|
||||
* The number of expandable branches in existence.
|
||||
*
|
||||
* @property expandablebranchcount
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.expandablebranchcount = 1;
|
||||
/**
|
||||
* The maximum number of courses to show as part of a branch.
|
||||
*
|
||||
* @property courselimit
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.courselimit = 20;
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*
|
||||
* @method init_add_tree
|
||||
* @static
|
||||
* @param {Object} properties
|
||||
*/
|
||||
M.block_navigation.init_add_tree = function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
new TREE(properties);
|
||||
};
|
||||
|
||||
/**
|
||||
* A 'actionkey' Event to help with Y.delegate().
|
||||
* The event consists of the left arrow, right arrow, enter and space keys.
|
||||
@ -9,24 +55,35 @@ YUI.add('moodle-block_navigation-navigation', function (Y, NAME) {
|
||||
* This event is delegated to branches in the navigation tree.
|
||||
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
|
||||
*
|
||||
* Todo: This could be centralised, a similar Event is defined in blocks/dock.js
|
||||
* @namespace M.block_navigation
|
||||
* @class ActionKey
|
||||
*/
|
||||
Y.Event.define("actionkey", {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
/**
|
||||
* The keys to trigger on.
|
||||
* @method _keys
|
||||
*/
|
||||
_keys: {
|
||||
//arrows
|
||||
'37': 'collapse',
|
||||
'39': 'expand',
|
||||
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
|
||||
'32': 'toggle',
|
||||
'13': 'enter'
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles key events
|
||||
* @method _keyHandler
|
||||
* @param {EventFacade} e
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {Object} args
|
||||
*/
|
||||
_keyHandler: function (e, notifier, args) {
|
||||
var actObj;
|
||||
if (!args.actions) {
|
||||
@ -40,6 +97,13 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes to events.
|
||||
* @method on
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
*/
|
||||
on: function (node, sub, notifier) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -50,11 +114,23 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches an event listener
|
||||
* @method detach
|
||||
*/
|
||||
detach: function (node, sub) {
|
||||
//detach our _detacher handle of the subscription made in on()
|
||||
sub._detacher.detach();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a delegated event listener.
|
||||
* @method delegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
delegate: function (node, sub, notifier, filter) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -65,45 +141,50 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches a delegated event listener.
|
||||
* @method detachDelegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
detachDelegate: function (node, sub) {
|
||||
sub._delegateDetacher.detach();
|
||||
}
|
||||
});
|
||||
|
||||
var EXPANSIONLIMIT_EVERYTHING = 0,
|
||||
//EXPANSIONLIMIT_COURSE = 20,
|
||||
//EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_COURSE = 20,
|
||||
EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_ACTIVITY = 40;
|
||||
|
||||
/**
|
||||
* Mappings for the different types of nodes coming from the navigation.
|
||||
* Copied from lib/navigationlib.php navigation_node constants.
|
||||
* @type object
|
||||
*/
|
||||
// Mappings for the different types of nodes coming from the navigation.
|
||||
// Copied from lib/navigationlib.php navigation_node constants.
|
||||
var NODETYPE = {
|
||||
/** @type int Root node = 0 */
|
||||
// @type int Root node = 0
|
||||
ROOTNODE : 0,
|
||||
/** @type int System context = 1 */
|
||||
// @type int System context = 1
|
||||
SYSTEM : 1,
|
||||
/** @type int Course category = 10 */
|
||||
// @type int Course category = 10
|
||||
CATEGORY : 10,
|
||||
/** @type int MYCATEGORY = 11 */
|
||||
// @type int MYCATEGORY = 11
|
||||
MYCATEGORY : 11,
|
||||
/** @type int Course = 20 */
|
||||
// @type int Course = 20
|
||||
COURSE : 20,
|
||||
/** @type int Course section = 30 */
|
||||
// @type int Course section = 30
|
||||
SECTION : 30,
|
||||
/** @type int Activity (course module) = 40 */
|
||||
// @type int Activity (course module) = 40
|
||||
ACTIVITY : 40,
|
||||
/** @type int Resource (course module = 50 */
|
||||
// @type int Resource (course module = 50
|
||||
RESOURCE : 50,
|
||||
/** @type int Custom node (could be anything) = 60 */
|
||||
// @type int Custom node (could be anything) = 60
|
||||
CUSTOM : 60,
|
||||
/** @type int Setting = 70 */
|
||||
// @type int Setting = 70
|
||||
SETTING : 70,
|
||||
/** @type int User context = 80 */
|
||||
// @type int User context = 80
|
||||
USER : 80,
|
||||
/** @type int Container = 90 */
|
||||
// @type int Container = 90
|
||||
CONTAINER : 90
|
||||
};
|
||||
|
||||
@ -112,6 +193,11 @@ var NODETYPE = {
|
||||
*
|
||||
* This class establishes the tree initially, creating expandable branches as
|
||||
* required, and delegating the expand/collapse event.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Tree
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
var TREE = function() {
|
||||
TREE.superclass.constructor.apply(this, arguments);
|
||||
@ -119,17 +205,27 @@ var TREE = function() {
|
||||
TREE.prototype = {
|
||||
/**
|
||||
* The tree's ID, normally its block instance id.
|
||||
* @property id
|
||||
* @type Int
|
||||
* @protected
|
||||
*/
|
||||
id : null,
|
||||
/**
|
||||
* An array of initialised branches.
|
||||
* @property branches
|
||||
* @type Array
|
||||
* @protected
|
||||
*/
|
||||
branches : [],
|
||||
/**
|
||||
* Initialise the tree object when its first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
this.id = config.id;
|
||||
Y.log('Initialising navigation block tree', 'note', 'moodle-block_navigation');
|
||||
|
||||
this.id = parseInt(config.id, 10);
|
||||
|
||||
var node = Y.one('#inst'+config.id);
|
||||
|
||||
@ -168,14 +264,11 @@ TREE.prototype = {
|
||||
Y.delegate('click', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
Y.delegate('actionkey', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
}
|
||||
|
||||
// Call the generic blocks init method to add all the generic stuff
|
||||
if (this.get('candock')) {
|
||||
this.initialise_block(Y, node);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fire actions for a branch when an event occurs.
|
||||
* @method fire_branch_action
|
||||
* @param {EventFacade} event
|
||||
*/
|
||||
fire_branch_action : function(event) {
|
||||
var id = event.currentTarget.getAttribute('id');
|
||||
@ -185,6 +278,9 @@ TREE.prototype = {
|
||||
/**
|
||||
* This is a callback function responsible for expanding and collapsing the
|
||||
* branches of the tree. It is delegated to rather than multiple event handles.
|
||||
* @method toggleExpansion
|
||||
* @param {EventFacade} e
|
||||
* @return Boolean
|
||||
*/
|
||||
toggleExpansion : function(e) {
|
||||
// First check if they managed to click on the li iteslf, then find the closest
|
||||
@ -240,46 +336,66 @@ TREE.prototype = {
|
||||
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('candock')) {
|
||||
M.core_dock.resize();
|
||||
var panel = M.core_dock.getPanel();
|
||||
if (panel.visible) {
|
||||
panel.correctWidth();
|
||||
}
|
||||
if (this.get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.id);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
};
|
||||
// The tree extends the YUI base foundation.
|
||||
Y.extend(TREE, Y.Base, TREE.prototype, {
|
||||
NAME : 'navigation-tree',
|
||||
ATTRS : {
|
||||
instance : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* True if the block can dock.
|
||||
* @attribute candock
|
||||
* @type Boolean
|
||||
*/
|
||||
candock : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* If set to true nodes will be opened/closed in an accordian fashion.
|
||||
* @attribute accordian
|
||||
* @type Boolean
|
||||
*/
|
||||
accordian : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The nodes that get shown.
|
||||
* @attribute expansionlimit
|
||||
* @type Integer
|
||||
*/
|
||||
expansionlimit : {
|
||||
value : 0,
|
||||
setter : function(val) {
|
||||
return parseInt(val, 10);
|
||||
val = parseInt(val, 10);
|
||||
if (val !== EXPANSIONLIMIT_EVERYTHING &&
|
||||
val !== EXPANSIONLIMIT_COURSE &&
|
||||
val !== EXPANSIONLIMIT_SECTION &&
|
||||
val !== EXPANSIONLIMIT_ACTIVITY) {
|
||||
val = EXPANSIONLIMIT_EVERYTHING;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (M.core_dock && M.core_dock.genericblock) {
|
||||
Y.augment(TREE, M.core_dock.genericblock);
|
||||
}
|
||||
|
||||
/**
|
||||
* The tree branch class.
|
||||
* The Branch class.
|
||||
*
|
||||
* This class is used to manage a tree branch, in particular its ability to load
|
||||
* its contents by AJAX.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Branch
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
BRANCH = function() {
|
||||
BRANCH.superclass.constructor.apply(this, arguments);
|
||||
@ -287,10 +403,15 @@ BRANCH = function() {
|
||||
BRANCH.prototype = {
|
||||
/**
|
||||
* The node for this branch (p)
|
||||
* @property node
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
node : null,
|
||||
/**
|
||||
* Initialises the branch when it is first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
var i,
|
||||
@ -310,8 +431,7 @@ BRANCH.prototype = {
|
||||
}
|
||||
}
|
||||
// Get the node for this branch
|
||||
this.node = Y.one('#', this.get('id'));
|
||||
// Now check whether the branch is not expandable because of the expansionlimit
|
||||
this.node = Y.one('#'+this.get('id'));
|
||||
var expansionlimit = this.get('tree').get('expansionlimit');
|
||||
var type = this.get('type');
|
||||
if (expansionlimit !== EXPANSIONLIMIT_EVERYTHING && type >= expansionlimit && type <= EXPANSIONLIMIT_ACTIVITY) {
|
||||
@ -324,6 +444,11 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function creates a DOM structure for the branch and then injects
|
||||
* it into the navigation tree at the correct point.
|
||||
*
|
||||
* @method draw
|
||||
* @chainable
|
||||
* @param {Node} element
|
||||
* @return Branch
|
||||
*/
|
||||
draw : function(element) {
|
||||
|
||||
@ -345,7 +470,7 @@ BRANCH.prototype = {
|
||||
// Prepare the icon, should be an object representing a pix_icon
|
||||
var branchicon = false;
|
||||
var icon = this.get('icon');
|
||||
if (icon && (!isbranch || this.get('type') == NODETYPE.ACTIVITY)) {
|
||||
if (icon && (!isbranch || this.get('type') === NODETYPE.ACTIVITY)) {
|
||||
branchicon = Y.Node.create('<img alt="" />');
|
||||
branchicon.setAttribute('src', M.util.image_url(icon.pix, icon.component));
|
||||
branchli.addClass('item_with_icon');
|
||||
@ -409,6 +534,8 @@ BRANCH.prototype = {
|
||||
},
|
||||
/**
|
||||
* Gets the UL element that children for this branch should be inserted into.
|
||||
* @method getChildrenUL
|
||||
* @return Node
|
||||
*/
|
||||
getChildrenUL : function() {
|
||||
var ul = this.node.next('ul');
|
||||
@ -423,6 +550,10 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function calls ajaxProcessResponse with the result of the AJAX
|
||||
* request made here.
|
||||
*
|
||||
* @method ajaxLoad
|
||||
* @param {EventFacade} e
|
||||
* @return Bool
|
||||
*/
|
||||
ajaxLoad : function(e) {
|
||||
if (e.type === 'actionkey' && e.action !== 'enter') {
|
||||
@ -446,6 +577,7 @@ BRANCH.prototype = {
|
||||
// We've already loaded this stuff.
|
||||
return true;
|
||||
}
|
||||
Y.log('Loading navigation branch via AJAX: '+this.get('key'), 'note', 'moodle-block_navigation');
|
||||
this.node.addClass('loadingbranch');
|
||||
|
||||
var params = {
|
||||
@ -469,6 +601,11 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Processes an AJAX request to load the content of this branch through
|
||||
* AJAX.
|
||||
*
|
||||
* @method ajaxProcessResponse
|
||||
* @param {Int} tid The transaction id.
|
||||
* @param {Object} outcome
|
||||
* @return Boolean
|
||||
*/
|
||||
ajaxProcessResponse : function(tid, outcome) {
|
||||
this.node.removeClass('loadingbranch');
|
||||
@ -479,20 +616,28 @@ BRANCH.prototype = {
|
||||
var coursecount = 0;
|
||||
for (var i in object.children) {
|
||||
if (typeof(object.children[i])==='object') {
|
||||
if (object.children[i].type == NODETYPE.COURSE) {
|
||||
if (object.children[i].type === NODETYPE.COURSE) {
|
||||
coursecount++;
|
||||
}
|
||||
this.addChild(object.children[i]);
|
||||
}
|
||||
}
|
||||
if ((this.get('type') == NODETYPE.CATEGORY || this.get('type') == NODETYPE.ROOTNODE || this.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((this.get('type') === NODETYPE.CATEGORY || this.get('type') === NODETYPE.ROOTNODE || this.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& coursecount >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(this);
|
||||
}
|
||||
Y.log('AJAX loading complete.', 'note', 'moodle-block_navigation');
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('tree').get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.get('tree').id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
|
||||
} catch (ex) {
|
||||
// If we got here then there was an error parsing the result
|
||||
// If we got here then there was an error parsing the result.
|
||||
Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
|
||||
}
|
||||
// The branch is empty so class it accordingly
|
||||
this.node.replaceClass('branch', 'emptybranch');
|
||||
@ -501,6 +646,10 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Turns the branch object passed to the method into a proper branch object
|
||||
* and then adds it as a child of this branch.
|
||||
*
|
||||
* @method addChild
|
||||
* @param {Object} branchobj
|
||||
* @return Boolean
|
||||
*/
|
||||
addChild : function(branchobj) {
|
||||
// Make the new branch into an object
|
||||
@ -511,14 +660,14 @@ BRANCH.prototype = {
|
||||
var count = 0, i, children = branch.get('children');
|
||||
for (i in children) {
|
||||
// Add each branch to the tree
|
||||
if (children[i].type == NODETYPE.COURSE) {
|
||||
if (children[i].type === NODETYPE.COURSE) {
|
||||
count++;
|
||||
}
|
||||
if (typeof(children[i]) === 'object') {
|
||||
branch.addChild(children[i]);
|
||||
}
|
||||
}
|
||||
if ((branch.get('type') == NODETYPE.CATEGORY || branch.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((branch.get('type') === NODETYPE.CATEGORY || branch.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& count >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(branch);
|
||||
}
|
||||
@ -528,10 +677,13 @@ BRANCH.prototype = {
|
||||
|
||||
/**
|
||||
* Add a link to view all courses in a category
|
||||
*
|
||||
* @method addViewAllCoursesChild
|
||||
* @param {BRANCH} branch
|
||||
*/
|
||||
addViewAllCoursesChild: function(branch) {
|
||||
var url = null;
|
||||
if (branch.get('type') == NODETYPE.ROOTNODE) {
|
||||
if (branch.get('type') === NODETYPE.ROOTNODE) {
|
||||
if (branch.get('key') === 'mycourses') {
|
||||
url = M.cfg.wwwroot + '/my';
|
||||
} else {
|
||||
@ -552,9 +704,22 @@ BRANCH.prototype = {
|
||||
Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
NAME : 'navigation-branch',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The Tree this branch belongs to.
|
||||
* @attribute tree
|
||||
* @type TREE
|
||||
* @required
|
||||
* @writeOnce
|
||||
*/
|
||||
tree : {
|
||||
writeOnce : 'initOnly',
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* The name of this branch.
|
||||
* @attribute name
|
||||
* @type String
|
||||
*/
|
||||
name : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -562,10 +727,21 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val.replace(/\n/g, '<br />');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The title to use for this branch.
|
||||
* @attribute title
|
||||
* @type String
|
||||
*/
|
||||
title : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString
|
||||
},
|
||||
/**
|
||||
* The ID of this branch.
|
||||
* The ID and Type should always form a unique pair.
|
||||
* @attribute id
|
||||
* @type String
|
||||
*/
|
||||
id : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -577,31 +753,74 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The key used to identify this branch easily if there is one.
|
||||
* @attribute key
|
||||
* @type String
|
||||
*/
|
||||
key : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The type of this branch.
|
||||
* @attribute type
|
||||
* @type Int
|
||||
*/
|
||||
type : {
|
||||
value : null
|
||||
value : null,
|
||||
setter : function(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The link to use for this branch.
|
||||
* @attribute link
|
||||
* @type String
|
||||
*/
|
||||
link : {
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The Icon to add when displaying this branch.
|
||||
* @attribute icon
|
||||
* @type Object
|
||||
*/
|
||||
icon : {
|
||||
value : false,
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* True if this branch is expandable.
|
||||
* @attribute expandable
|
||||
* @type Boolean
|
||||
*/
|
||||
expandable : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch is hidden and should be displayed greyed out.
|
||||
* @attribute hidden
|
||||
* @type Boolean
|
||||
*/
|
||||
hidden : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch has any children.
|
||||
* @attribute haschildren
|
||||
* @type Boolean
|
||||
*/
|
||||
haschildren : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* An array of other branches that appear as children of this branch.
|
||||
* @attribute children
|
||||
* @type Array
|
||||
*/
|
||||
children : {
|
||||
value : [],
|
||||
validator : Y.Lang.isArray
|
||||
@ -609,40 +828,5 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {
|
||||
/** The number of expandable branches in existence */
|
||||
expandablebranchcount:1,
|
||||
courselimit : 20,
|
||||
instance : null,
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*/
|
||||
init_add_tree:function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
if (M.core_dock) {
|
||||
M.core_dock.init(Y);
|
||||
}
|
||||
new TREE(properties);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {
|
||||
"requires": [
|
||||
"base",
|
||||
"core_dock",
|
||||
"io-base",
|
||||
"node",
|
||||
"dom",
|
||||
"event-custom",
|
||||
"event-delegate",
|
||||
"json-parse"
|
||||
]
|
||||
});
|
||||
}, '@VERSION@', {"requires": ["base", "io-base", "node", "event-synthetic", "event-delegate", "json-parse"]});
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,51 @@
|
||||
YUI.add('moodle-block_navigation-navigation', function (Y, NAME) {
|
||||
|
||||
/**
|
||||
* Navigation block JS.
|
||||
*
|
||||
* This file contains the Navigation block JS..
|
||||
*
|
||||
* @module moodle-block_navigation-navigation
|
||||
*/
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace M
|
||||
* @class block_navigation
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {};
|
||||
/**
|
||||
* The number of expandable branches in existence.
|
||||
*
|
||||
* @property expandablebranchcount
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.expandablebranchcount = 1;
|
||||
/**
|
||||
* The maximum number of courses to show as part of a branch.
|
||||
*
|
||||
* @property courselimit
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.courselimit = 20;
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*
|
||||
* @method init_add_tree
|
||||
* @static
|
||||
* @param {Object} properties
|
||||
*/
|
||||
M.block_navigation.init_add_tree = function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
new TREE(properties);
|
||||
};
|
||||
|
||||
/**
|
||||
* A 'actionkey' Event to help with Y.delegate().
|
||||
* The event consists of the left arrow, right arrow, enter and space keys.
|
||||
@ -9,24 +55,35 @@ YUI.add('moodle-block_navigation-navigation', function (Y, NAME) {
|
||||
* This event is delegated to branches in the navigation tree.
|
||||
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
|
||||
*
|
||||
* Todo: This could be centralised, a similar Event is defined in blocks/dock.js
|
||||
* @namespace M.block_navigation
|
||||
* @class ActionKey
|
||||
*/
|
||||
Y.Event.define("actionkey", {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
/**
|
||||
* The keys to trigger on.
|
||||
* @method _keys
|
||||
*/
|
||||
_keys: {
|
||||
//arrows
|
||||
'37': 'collapse',
|
||||
'39': 'expand',
|
||||
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
|
||||
'32': 'toggle',
|
||||
'13': 'enter'
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles key events
|
||||
* @method _keyHandler
|
||||
* @param {EventFacade} e
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {Object} args
|
||||
*/
|
||||
_keyHandler: function (e, notifier, args) {
|
||||
var actObj;
|
||||
if (!args.actions) {
|
||||
@ -40,6 +97,13 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes to events.
|
||||
* @method on
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
*/
|
||||
on: function (node, sub, notifier) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -50,11 +114,23 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches an event listener
|
||||
* @method detach
|
||||
*/
|
||||
detach: function (node, sub) {
|
||||
//detach our _detacher handle of the subscription made in on()
|
||||
sub._detacher.detach();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a delegated event listener.
|
||||
* @method delegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
delegate: function (node, sub, notifier, filter) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -65,45 +141,50 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches a delegated event listener.
|
||||
* @method detachDelegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
detachDelegate: function (node, sub) {
|
||||
sub._delegateDetacher.detach();
|
||||
}
|
||||
});
|
||||
|
||||
var EXPANSIONLIMIT_EVERYTHING = 0,
|
||||
//EXPANSIONLIMIT_COURSE = 20,
|
||||
//EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_COURSE = 20,
|
||||
EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_ACTIVITY = 40;
|
||||
|
||||
/**
|
||||
* Mappings for the different types of nodes coming from the navigation.
|
||||
* Copied from lib/navigationlib.php navigation_node constants.
|
||||
* @type object
|
||||
*/
|
||||
// Mappings for the different types of nodes coming from the navigation.
|
||||
// Copied from lib/navigationlib.php navigation_node constants.
|
||||
var NODETYPE = {
|
||||
/** @type int Root node = 0 */
|
||||
// @type int Root node = 0
|
||||
ROOTNODE : 0,
|
||||
/** @type int System context = 1 */
|
||||
// @type int System context = 1
|
||||
SYSTEM : 1,
|
||||
/** @type int Course category = 10 */
|
||||
// @type int Course category = 10
|
||||
CATEGORY : 10,
|
||||
/** @type int MYCATEGORY = 11 */
|
||||
// @type int MYCATEGORY = 11
|
||||
MYCATEGORY : 11,
|
||||
/** @type int Course = 20 */
|
||||
// @type int Course = 20
|
||||
COURSE : 20,
|
||||
/** @type int Course section = 30 */
|
||||
// @type int Course section = 30
|
||||
SECTION : 30,
|
||||
/** @type int Activity (course module) = 40 */
|
||||
// @type int Activity (course module) = 40
|
||||
ACTIVITY : 40,
|
||||
/** @type int Resource (course module = 50 */
|
||||
// @type int Resource (course module = 50
|
||||
RESOURCE : 50,
|
||||
/** @type int Custom node (could be anything) = 60 */
|
||||
// @type int Custom node (could be anything) = 60
|
||||
CUSTOM : 60,
|
||||
/** @type int Setting = 70 */
|
||||
// @type int Setting = 70
|
||||
SETTING : 70,
|
||||
/** @type int User context = 80 */
|
||||
// @type int User context = 80
|
||||
USER : 80,
|
||||
/** @type int Container = 90 */
|
||||
// @type int Container = 90
|
||||
CONTAINER : 90
|
||||
};
|
||||
|
||||
@ -112,6 +193,11 @@ var NODETYPE = {
|
||||
*
|
||||
* This class establishes the tree initially, creating expandable branches as
|
||||
* required, and delegating the expand/collapse event.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Tree
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
var TREE = function() {
|
||||
TREE.superclass.constructor.apply(this, arguments);
|
||||
@ -119,17 +205,26 @@ var TREE = function() {
|
||||
TREE.prototype = {
|
||||
/**
|
||||
* The tree's ID, normally its block instance id.
|
||||
* @property id
|
||||
* @type Int
|
||||
* @protected
|
||||
*/
|
||||
id : null,
|
||||
/**
|
||||
* An array of initialised branches.
|
||||
* @property branches
|
||||
* @type Array
|
||||
* @protected
|
||||
*/
|
||||
branches : [],
|
||||
/**
|
||||
* Initialise the tree object when its first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
this.id = config.id;
|
||||
|
||||
this.id = parseInt(config.id, 10);
|
||||
|
||||
var node = Y.one('#inst'+config.id);
|
||||
|
||||
@ -168,14 +263,11 @@ TREE.prototype = {
|
||||
Y.delegate('click', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
Y.delegate('actionkey', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
}
|
||||
|
||||
// Call the generic blocks init method to add all the generic stuff
|
||||
if (this.get('candock')) {
|
||||
this.initialise_block(Y, node);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fire actions for a branch when an event occurs.
|
||||
* @method fire_branch_action
|
||||
* @param {EventFacade} event
|
||||
*/
|
||||
fire_branch_action : function(event) {
|
||||
var id = event.currentTarget.getAttribute('id');
|
||||
@ -185,6 +277,9 @@ TREE.prototype = {
|
||||
/**
|
||||
* This is a callback function responsible for expanding and collapsing the
|
||||
* branches of the tree. It is delegated to rather than multiple event handles.
|
||||
* @method toggleExpansion
|
||||
* @param {EventFacade} e
|
||||
* @return Boolean
|
||||
*/
|
||||
toggleExpansion : function(e) {
|
||||
// First check if they managed to click on the li iteslf, then find the closest
|
||||
@ -240,46 +335,66 @@ TREE.prototype = {
|
||||
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('candock')) {
|
||||
M.core_dock.resize();
|
||||
var panel = M.core_dock.getPanel();
|
||||
if (panel.visible) {
|
||||
panel.correctWidth();
|
||||
}
|
||||
if (this.get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.id);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
};
|
||||
// The tree extends the YUI base foundation.
|
||||
Y.extend(TREE, Y.Base, TREE.prototype, {
|
||||
NAME : 'navigation-tree',
|
||||
ATTRS : {
|
||||
instance : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* True if the block can dock.
|
||||
* @attribute candock
|
||||
* @type Boolean
|
||||
*/
|
||||
candock : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* If set to true nodes will be opened/closed in an accordian fashion.
|
||||
* @attribute accordian
|
||||
* @type Boolean
|
||||
*/
|
||||
accordian : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The nodes that get shown.
|
||||
* @attribute expansionlimit
|
||||
* @type Integer
|
||||
*/
|
||||
expansionlimit : {
|
||||
value : 0,
|
||||
setter : function(val) {
|
||||
return parseInt(val, 10);
|
||||
val = parseInt(val, 10);
|
||||
if (val !== EXPANSIONLIMIT_EVERYTHING &&
|
||||
val !== EXPANSIONLIMIT_COURSE &&
|
||||
val !== EXPANSIONLIMIT_SECTION &&
|
||||
val !== EXPANSIONLIMIT_ACTIVITY) {
|
||||
val = EXPANSIONLIMIT_EVERYTHING;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (M.core_dock && M.core_dock.genericblock) {
|
||||
Y.augment(TREE, M.core_dock.genericblock);
|
||||
}
|
||||
|
||||
/**
|
||||
* The tree branch class.
|
||||
* The Branch class.
|
||||
*
|
||||
* This class is used to manage a tree branch, in particular its ability to load
|
||||
* its contents by AJAX.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Branch
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
BRANCH = function() {
|
||||
BRANCH.superclass.constructor.apply(this, arguments);
|
||||
@ -287,10 +402,15 @@ BRANCH = function() {
|
||||
BRANCH.prototype = {
|
||||
/**
|
||||
* The node for this branch (p)
|
||||
* @property node
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
node : null,
|
||||
/**
|
||||
* Initialises the branch when it is first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
var i,
|
||||
@ -310,8 +430,7 @@ BRANCH.prototype = {
|
||||
}
|
||||
}
|
||||
// Get the node for this branch
|
||||
this.node = Y.one('#', this.get('id'));
|
||||
// Now check whether the branch is not expandable because of the expansionlimit
|
||||
this.node = Y.one('#'+this.get('id'));
|
||||
var expansionlimit = this.get('tree').get('expansionlimit');
|
||||
var type = this.get('type');
|
||||
if (expansionlimit !== EXPANSIONLIMIT_EVERYTHING && type >= expansionlimit && type <= EXPANSIONLIMIT_ACTIVITY) {
|
||||
@ -324,6 +443,11 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function creates a DOM structure for the branch and then injects
|
||||
* it into the navigation tree at the correct point.
|
||||
*
|
||||
* @method draw
|
||||
* @chainable
|
||||
* @param {Node} element
|
||||
* @return Branch
|
||||
*/
|
||||
draw : function(element) {
|
||||
|
||||
@ -345,7 +469,7 @@ BRANCH.prototype = {
|
||||
// Prepare the icon, should be an object representing a pix_icon
|
||||
var branchicon = false;
|
||||
var icon = this.get('icon');
|
||||
if (icon && (!isbranch || this.get('type') == NODETYPE.ACTIVITY)) {
|
||||
if (icon && (!isbranch || this.get('type') === NODETYPE.ACTIVITY)) {
|
||||
branchicon = Y.Node.create('<img alt="" />');
|
||||
branchicon.setAttribute('src', M.util.image_url(icon.pix, icon.component));
|
||||
branchli.addClass('item_with_icon');
|
||||
@ -409,6 +533,8 @@ BRANCH.prototype = {
|
||||
},
|
||||
/**
|
||||
* Gets the UL element that children for this branch should be inserted into.
|
||||
* @method getChildrenUL
|
||||
* @return Node
|
||||
*/
|
||||
getChildrenUL : function() {
|
||||
var ul = this.node.next('ul');
|
||||
@ -423,6 +549,10 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function calls ajaxProcessResponse with the result of the AJAX
|
||||
* request made here.
|
||||
*
|
||||
* @method ajaxLoad
|
||||
* @param {EventFacade} e
|
||||
* @return Bool
|
||||
*/
|
||||
ajaxLoad : function(e) {
|
||||
if (e.type === 'actionkey' && e.action !== 'enter') {
|
||||
@ -469,6 +599,11 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Processes an AJAX request to load the content of this branch through
|
||||
* AJAX.
|
||||
*
|
||||
* @method ajaxProcessResponse
|
||||
* @param {Int} tid The transaction id.
|
||||
* @param {Object} outcome
|
||||
* @return Boolean
|
||||
*/
|
||||
ajaxProcessResponse : function(tid, outcome) {
|
||||
this.node.removeClass('loadingbranch');
|
||||
@ -479,20 +614,25 @@ BRANCH.prototype = {
|
||||
var coursecount = 0;
|
||||
for (var i in object.children) {
|
||||
if (typeof(object.children[i])==='object') {
|
||||
if (object.children[i].type == NODETYPE.COURSE) {
|
||||
if (object.children[i].type === NODETYPE.COURSE) {
|
||||
coursecount++;
|
||||
}
|
||||
this.addChild(object.children[i]);
|
||||
}
|
||||
}
|
||||
if ((this.get('type') == NODETYPE.CATEGORY || this.get('type') == NODETYPE.ROOTNODE || this.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((this.get('type') === NODETYPE.CATEGORY || this.get('type') === NODETYPE.ROOTNODE || this.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& coursecount >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(this);
|
||||
}
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('tree').get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.get('tree').id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (ex) {
|
||||
// If we got here then there was an error parsing the result
|
||||
// If we got here then there was an error parsing the result.
|
||||
}
|
||||
// The branch is empty so class it accordingly
|
||||
this.node.replaceClass('branch', 'emptybranch');
|
||||
@ -501,6 +641,10 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Turns the branch object passed to the method into a proper branch object
|
||||
* and then adds it as a child of this branch.
|
||||
*
|
||||
* @method addChild
|
||||
* @param {Object} branchobj
|
||||
* @return Boolean
|
||||
*/
|
||||
addChild : function(branchobj) {
|
||||
// Make the new branch into an object
|
||||
@ -511,14 +655,14 @@ BRANCH.prototype = {
|
||||
var count = 0, i, children = branch.get('children');
|
||||
for (i in children) {
|
||||
// Add each branch to the tree
|
||||
if (children[i].type == NODETYPE.COURSE) {
|
||||
if (children[i].type === NODETYPE.COURSE) {
|
||||
count++;
|
||||
}
|
||||
if (typeof(children[i]) === 'object') {
|
||||
branch.addChild(children[i]);
|
||||
}
|
||||
}
|
||||
if ((branch.get('type') == NODETYPE.CATEGORY || branch.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((branch.get('type') === NODETYPE.CATEGORY || branch.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& count >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(branch);
|
||||
}
|
||||
@ -528,10 +672,13 @@ BRANCH.prototype = {
|
||||
|
||||
/**
|
||||
* Add a link to view all courses in a category
|
||||
*
|
||||
* @method addViewAllCoursesChild
|
||||
* @param {BRANCH} branch
|
||||
*/
|
||||
addViewAllCoursesChild: function(branch) {
|
||||
var url = null;
|
||||
if (branch.get('type') == NODETYPE.ROOTNODE) {
|
||||
if (branch.get('type') === NODETYPE.ROOTNODE) {
|
||||
if (branch.get('key') === 'mycourses') {
|
||||
url = M.cfg.wwwroot + '/my';
|
||||
} else {
|
||||
@ -552,9 +699,22 @@ BRANCH.prototype = {
|
||||
Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
NAME : 'navigation-branch',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The Tree this branch belongs to.
|
||||
* @attribute tree
|
||||
* @type TREE
|
||||
* @required
|
||||
* @writeOnce
|
||||
*/
|
||||
tree : {
|
||||
writeOnce : 'initOnly',
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* The name of this branch.
|
||||
* @attribute name
|
||||
* @type String
|
||||
*/
|
||||
name : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -562,10 +722,21 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val.replace(/\n/g, '<br />');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The title to use for this branch.
|
||||
* @attribute title
|
||||
* @type String
|
||||
*/
|
||||
title : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString
|
||||
},
|
||||
/**
|
||||
* The ID of this branch.
|
||||
* The ID and Type should always form a unique pair.
|
||||
* @attribute id
|
||||
* @type String
|
||||
*/
|
||||
id : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -577,31 +748,74 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The key used to identify this branch easily if there is one.
|
||||
* @attribute key
|
||||
* @type String
|
||||
*/
|
||||
key : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The type of this branch.
|
||||
* @attribute type
|
||||
* @type Int
|
||||
*/
|
||||
type : {
|
||||
value : null
|
||||
value : null,
|
||||
setter : function(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The link to use for this branch.
|
||||
* @attribute link
|
||||
* @type String
|
||||
*/
|
||||
link : {
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The Icon to add when displaying this branch.
|
||||
* @attribute icon
|
||||
* @type Object
|
||||
*/
|
||||
icon : {
|
||||
value : false,
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* True if this branch is expandable.
|
||||
* @attribute expandable
|
||||
* @type Boolean
|
||||
*/
|
||||
expandable : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch is hidden and should be displayed greyed out.
|
||||
* @attribute hidden
|
||||
* @type Boolean
|
||||
*/
|
||||
hidden : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch has any children.
|
||||
* @attribute haschildren
|
||||
* @type Boolean
|
||||
*/
|
||||
haschildren : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* An array of other branches that appear as children of this branch.
|
||||
* @attribute children
|
||||
* @type Array
|
||||
*/
|
||||
children : {
|
||||
value : [],
|
||||
validator : Y.Lang.isArray
|
||||
@ -609,40 +823,5 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {
|
||||
/** The number of expandable branches in existence */
|
||||
expandablebranchcount:1,
|
||||
courselimit : 20,
|
||||
instance : null,
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*/
|
||||
init_add_tree:function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
if (M.core_dock) {
|
||||
M.core_dock.init(Y);
|
||||
}
|
||||
new TREE(properties);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {
|
||||
"requires": [
|
||||
"base",
|
||||
"core_dock",
|
||||
"io-base",
|
||||
"node",
|
||||
"dom",
|
||||
"event-custom",
|
||||
"event-delegate",
|
||||
"json-parse"
|
||||
]
|
||||
});
|
||||
}, '@VERSION@', {"requires": ["base", "io-base", "node", "event-synthetic", "event-delegate", "json-parse"]});
|
||||
|
@ -1,3 +1,49 @@
|
||||
/**
|
||||
* Navigation block JS.
|
||||
*
|
||||
* This file contains the Navigation block JS..
|
||||
*
|
||||
* @module moodle-block_navigation-navigation
|
||||
*/
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace M
|
||||
* @class block_navigation
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {};
|
||||
/**
|
||||
* The number of expandable branches in existence.
|
||||
*
|
||||
* @property expandablebranchcount
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.expandablebranchcount = 1;
|
||||
/**
|
||||
* The maximum number of courses to show as part of a branch.
|
||||
*
|
||||
* @property courselimit
|
||||
* @protected
|
||||
* @static
|
||||
*/
|
||||
M.block_navigation.courselimit = 20;
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*
|
||||
* @method init_add_tree
|
||||
* @static
|
||||
* @param {Object} properties
|
||||
*/
|
||||
M.block_navigation.init_add_tree = function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
new TREE(properties);
|
||||
};
|
||||
|
||||
/**
|
||||
* A 'actionkey' Event to help with Y.delegate().
|
||||
* The event consists of the left arrow, right arrow, enter and space keys.
|
||||
@ -7,24 +53,35 @@
|
||||
* This event is delegated to branches in the navigation tree.
|
||||
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
|
||||
*
|
||||
* Todo: This could be centralised, a similar Event is defined in blocks/dock.js
|
||||
* @namespace M.block_navigation
|
||||
* @class ActionKey
|
||||
*/
|
||||
Y.Event.define("actionkey", {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
/**
|
||||
* The keys to trigger on.
|
||||
* @method _keys
|
||||
*/
|
||||
_keys: {
|
||||
//arrows
|
||||
'37': 'collapse',
|
||||
'39': 'expand',
|
||||
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
|
||||
'32': 'toggle',
|
||||
'13': 'enter'
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles key events
|
||||
* @method _keyHandler
|
||||
* @param {EventFacade} e
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {Object} args
|
||||
*/
|
||||
_keyHandler: function (e, notifier, args) {
|
||||
var actObj;
|
||||
if (!args.actions) {
|
||||
@ -38,6 +95,13 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes to events.
|
||||
* @method on
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
*/
|
||||
on: function (node, sub, notifier) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -48,11 +112,23 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches an event listener
|
||||
* @method detach
|
||||
*/
|
||||
detach: function (node, sub) {
|
||||
//detach our _detacher handle of the subscription made in on()
|
||||
sub._detacher.detach();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a delegated event listener.
|
||||
* @method delegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
delegate: function (node, sub, notifier, filter) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
@ -63,45 +139,50 @@ Y.Event.define("actionkey", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches a delegated event listener.
|
||||
* @method detachDelegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
detachDelegate: function (node, sub) {
|
||||
sub._delegateDetacher.detach();
|
||||
}
|
||||
});
|
||||
|
||||
var EXPANSIONLIMIT_EVERYTHING = 0,
|
||||
//EXPANSIONLIMIT_COURSE = 20,
|
||||
//EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_COURSE = 20,
|
||||
EXPANSIONLIMIT_SECTION = 30,
|
||||
EXPANSIONLIMIT_ACTIVITY = 40;
|
||||
|
||||
/**
|
||||
* Mappings for the different types of nodes coming from the navigation.
|
||||
* Copied from lib/navigationlib.php navigation_node constants.
|
||||
* @type object
|
||||
*/
|
||||
// Mappings for the different types of nodes coming from the navigation.
|
||||
// Copied from lib/navigationlib.php navigation_node constants.
|
||||
var NODETYPE = {
|
||||
/** @type int Root node = 0 */
|
||||
// @type int Root node = 0
|
||||
ROOTNODE : 0,
|
||||
/** @type int System context = 1 */
|
||||
// @type int System context = 1
|
||||
SYSTEM : 1,
|
||||
/** @type int Course category = 10 */
|
||||
// @type int Course category = 10
|
||||
CATEGORY : 10,
|
||||
/** @type int MYCATEGORY = 11 */
|
||||
// @type int MYCATEGORY = 11
|
||||
MYCATEGORY : 11,
|
||||
/** @type int Course = 20 */
|
||||
// @type int Course = 20
|
||||
COURSE : 20,
|
||||
/** @type int Course section = 30 */
|
||||
// @type int Course section = 30
|
||||
SECTION : 30,
|
||||
/** @type int Activity (course module) = 40 */
|
||||
// @type int Activity (course module) = 40
|
||||
ACTIVITY : 40,
|
||||
/** @type int Resource (course module = 50 */
|
||||
// @type int Resource (course module = 50
|
||||
RESOURCE : 50,
|
||||
/** @type int Custom node (could be anything) = 60 */
|
||||
// @type int Custom node (could be anything) = 60
|
||||
CUSTOM : 60,
|
||||
/** @type int Setting = 70 */
|
||||
// @type int Setting = 70
|
||||
SETTING : 70,
|
||||
/** @type int User context = 80 */
|
||||
// @type int User context = 80
|
||||
USER : 80,
|
||||
/** @type int Container = 90 */
|
||||
// @type int Container = 90
|
||||
CONTAINER : 90
|
||||
};
|
||||
|
||||
@ -110,6 +191,11 @@ var NODETYPE = {
|
||||
*
|
||||
* This class establishes the tree initially, creating expandable branches as
|
||||
* required, and delegating the expand/collapse event.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Tree
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
var TREE = function() {
|
||||
TREE.superclass.constructor.apply(this, arguments);
|
||||
@ -117,17 +203,27 @@ var TREE = function() {
|
||||
TREE.prototype = {
|
||||
/**
|
||||
* The tree's ID, normally its block instance id.
|
||||
* @property id
|
||||
* @type Int
|
||||
* @protected
|
||||
*/
|
||||
id : null,
|
||||
/**
|
||||
* An array of initialised branches.
|
||||
* @property branches
|
||||
* @type Array
|
||||
* @protected
|
||||
*/
|
||||
branches : [],
|
||||
/**
|
||||
* Initialise the tree object when its first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
this.id = config.id;
|
||||
Y.log('Initialising navigation block tree', 'note', 'moodle-block_navigation');
|
||||
|
||||
this.id = parseInt(config.id, 10);
|
||||
|
||||
var node = Y.one('#inst'+config.id);
|
||||
|
||||
@ -166,14 +262,11 @@ TREE.prototype = {
|
||||
Y.delegate('click', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
Y.delegate('actionkey', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
|
||||
}
|
||||
|
||||
// Call the generic blocks init method to add all the generic stuff
|
||||
if (this.get('candock')) {
|
||||
this.initialise_block(Y, node);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fire actions for a branch when an event occurs.
|
||||
* @method fire_branch_action
|
||||
* @param {EventFacade} event
|
||||
*/
|
||||
fire_branch_action : function(event) {
|
||||
var id = event.currentTarget.getAttribute('id');
|
||||
@ -183,6 +276,9 @@ TREE.prototype = {
|
||||
/**
|
||||
* This is a callback function responsible for expanding and collapsing the
|
||||
* branches of the tree. It is delegated to rather than multiple event handles.
|
||||
* @method toggleExpansion
|
||||
* @param {EventFacade} e
|
||||
* @return Boolean
|
||||
*/
|
||||
toggleExpansion : function(e) {
|
||||
// First check if they managed to click on the li iteslf, then find the closest
|
||||
@ -238,46 +334,66 @@ TREE.prototype = {
|
||||
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('candock')) {
|
||||
M.core_dock.resize();
|
||||
var panel = M.core_dock.getPanel();
|
||||
if (panel.visible) {
|
||||
panel.correctWidth();
|
||||
}
|
||||
if (this.get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.id);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
};
|
||||
// The tree extends the YUI base foundation.
|
||||
Y.extend(TREE, Y.Base, TREE.prototype, {
|
||||
NAME : 'navigation-tree',
|
||||
ATTRS : {
|
||||
instance : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* True if the block can dock.
|
||||
* @attribute candock
|
||||
* @type Boolean
|
||||
*/
|
||||
candock : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* If set to true nodes will be opened/closed in an accordian fashion.
|
||||
* @attribute accordian
|
||||
* @type Boolean
|
||||
*/
|
||||
accordian : {
|
||||
validator : Y.Lang.isBool,
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The nodes that get shown.
|
||||
* @attribute expansionlimit
|
||||
* @type Integer
|
||||
*/
|
||||
expansionlimit : {
|
||||
value : 0,
|
||||
setter : function(val) {
|
||||
return parseInt(val, 10);
|
||||
val = parseInt(val, 10);
|
||||
if (val !== EXPANSIONLIMIT_EVERYTHING &&
|
||||
val !== EXPANSIONLIMIT_COURSE &&
|
||||
val !== EXPANSIONLIMIT_SECTION &&
|
||||
val !== EXPANSIONLIMIT_ACTIVITY) {
|
||||
val = EXPANSIONLIMIT_EVERYTHING;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (M.core_dock && M.core_dock.genericblock) {
|
||||
Y.augment(TREE, M.core_dock.genericblock);
|
||||
}
|
||||
|
||||
/**
|
||||
* The tree branch class.
|
||||
* The Branch class.
|
||||
*
|
||||
* This class is used to manage a tree branch, in particular its ability to load
|
||||
* its contents by AJAX.
|
||||
*
|
||||
* @namespace M.block_navigation
|
||||
* @class Branch
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
BRANCH = function() {
|
||||
BRANCH.superclass.constructor.apply(this, arguments);
|
||||
@ -285,10 +401,15 @@ BRANCH = function() {
|
||||
BRANCH.prototype = {
|
||||
/**
|
||||
* The node for this branch (p)
|
||||
* @property node
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
node : null,
|
||||
/**
|
||||
* Initialises the branch when it is first created.
|
||||
* @method initializer
|
||||
* @param {Object} config
|
||||
*/
|
||||
initializer : function(config) {
|
||||
var i,
|
||||
@ -308,8 +429,7 @@ BRANCH.prototype = {
|
||||
}
|
||||
}
|
||||
// Get the node for this branch
|
||||
this.node = Y.one('#', this.get('id'));
|
||||
// Now check whether the branch is not expandable because of the expansionlimit
|
||||
this.node = Y.one('#'+this.get('id'));
|
||||
var expansionlimit = this.get('tree').get('expansionlimit');
|
||||
var type = this.get('type');
|
||||
if (expansionlimit !== EXPANSIONLIMIT_EVERYTHING && type >= expansionlimit && type <= EXPANSIONLIMIT_ACTIVITY) {
|
||||
@ -322,6 +442,11 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function creates a DOM structure for the branch and then injects
|
||||
* it into the navigation tree at the correct point.
|
||||
*
|
||||
* @method draw
|
||||
* @chainable
|
||||
* @param {Node} element
|
||||
* @return Branch
|
||||
*/
|
||||
draw : function(element) {
|
||||
|
||||
@ -343,7 +468,7 @@ BRANCH.prototype = {
|
||||
// Prepare the icon, should be an object representing a pix_icon
|
||||
var branchicon = false;
|
||||
var icon = this.get('icon');
|
||||
if (icon && (!isbranch || this.get('type') == NODETYPE.ACTIVITY)) {
|
||||
if (icon && (!isbranch || this.get('type') === NODETYPE.ACTIVITY)) {
|
||||
branchicon = Y.Node.create('<img alt="" />');
|
||||
branchicon.setAttribute('src', M.util.image_url(icon.pix, icon.component));
|
||||
branchli.addClass('item_with_icon');
|
||||
@ -407,6 +532,8 @@ BRANCH.prototype = {
|
||||
},
|
||||
/**
|
||||
* Gets the UL element that children for this branch should be inserted into.
|
||||
* @method getChildrenUL
|
||||
* @return Node
|
||||
*/
|
||||
getChildrenUL : function() {
|
||||
var ul = this.node.next('ul');
|
||||
@ -421,6 +548,10 @@ BRANCH.prototype = {
|
||||
*
|
||||
* This function calls ajaxProcessResponse with the result of the AJAX
|
||||
* request made here.
|
||||
*
|
||||
* @method ajaxLoad
|
||||
* @param {EventFacade} e
|
||||
* @return Bool
|
||||
*/
|
||||
ajaxLoad : function(e) {
|
||||
if (e.type === 'actionkey' && e.action !== 'enter') {
|
||||
@ -444,6 +575,7 @@ BRANCH.prototype = {
|
||||
// We've already loaded this stuff.
|
||||
return true;
|
||||
}
|
||||
Y.log('Loading navigation branch via AJAX: '+this.get('key'), 'note', 'moodle-block_navigation');
|
||||
this.node.addClass('loadingbranch');
|
||||
|
||||
var params = {
|
||||
@ -467,6 +599,11 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Processes an AJAX request to load the content of this branch through
|
||||
* AJAX.
|
||||
*
|
||||
* @method ajaxProcessResponse
|
||||
* @param {Int} tid The transaction id.
|
||||
* @param {Object} outcome
|
||||
* @return Boolean
|
||||
*/
|
||||
ajaxProcessResponse : function(tid, outcome) {
|
||||
this.node.removeClass('loadingbranch');
|
||||
@ -477,20 +614,28 @@ BRANCH.prototype = {
|
||||
var coursecount = 0;
|
||||
for (var i in object.children) {
|
||||
if (typeof(object.children[i])==='object') {
|
||||
if (object.children[i].type == NODETYPE.COURSE) {
|
||||
if (object.children[i].type === NODETYPE.COURSE) {
|
||||
coursecount++;
|
||||
}
|
||||
this.addChild(object.children[i]);
|
||||
}
|
||||
}
|
||||
if ((this.get('type') == NODETYPE.CATEGORY || this.get('type') == NODETYPE.ROOTNODE || this.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((this.get('type') === NODETYPE.CATEGORY || this.get('type') === NODETYPE.ROOTNODE || this.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& coursecount >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(this);
|
||||
}
|
||||
Y.log('AJAX loading complete.', 'note', 'moodle-block_navigation');
|
||||
// If this block can dock tell the dock to resize if required and check
|
||||
// the width on the dock panel in case it is presently in use.
|
||||
if (this.get('tree').get('candock') && M.core.dock.notifyBlockChange) {
|
||||
M.core.dock.notifyBlockChange(this.get('tree').id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
|
||||
} catch (ex) {
|
||||
// If we got here then there was an error parsing the result
|
||||
// If we got here then there was an error parsing the result.
|
||||
Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
|
||||
}
|
||||
// The branch is empty so class it accordingly
|
||||
this.node.replaceClass('branch', 'emptybranch');
|
||||
@ -499,6 +644,10 @@ BRANCH.prototype = {
|
||||
/**
|
||||
* Turns the branch object passed to the method into a proper branch object
|
||||
* and then adds it as a child of this branch.
|
||||
*
|
||||
* @method addChild
|
||||
* @param {Object} branchobj
|
||||
* @return Boolean
|
||||
*/
|
||||
addChild : function(branchobj) {
|
||||
// Make the new branch into an object
|
||||
@ -509,14 +658,14 @@ BRANCH.prototype = {
|
||||
var count = 0, i, children = branch.get('children');
|
||||
for (i in children) {
|
||||
// Add each branch to the tree
|
||||
if (children[i].type == NODETYPE.COURSE) {
|
||||
if (children[i].type === NODETYPE.COURSE) {
|
||||
count++;
|
||||
}
|
||||
if (typeof(children[i]) === 'object') {
|
||||
branch.addChild(children[i]);
|
||||
}
|
||||
}
|
||||
if ((branch.get('type') == NODETYPE.CATEGORY || branch.get('type') == NODETYPE.MYCATEGORY)
|
||||
if ((branch.get('type') === NODETYPE.CATEGORY || branch.get('type') === NODETYPE.MYCATEGORY)
|
||||
&& count >= M.block_navigation.courselimit) {
|
||||
this.addViewAllCoursesChild(branch);
|
||||
}
|
||||
@ -526,10 +675,13 @@ BRANCH.prototype = {
|
||||
|
||||
/**
|
||||
* Add a link to view all courses in a category
|
||||
*
|
||||
* @method addViewAllCoursesChild
|
||||
* @param {BRANCH} branch
|
||||
*/
|
||||
addViewAllCoursesChild: function(branch) {
|
||||
var url = null;
|
||||
if (branch.get('type') == NODETYPE.ROOTNODE) {
|
||||
if (branch.get('type') === NODETYPE.ROOTNODE) {
|
||||
if (branch.get('key') === 'mycourses') {
|
||||
url = M.cfg.wwwroot + '/my';
|
||||
} else {
|
||||
@ -550,9 +702,22 @@ BRANCH.prototype = {
|
||||
Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
NAME : 'navigation-branch',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The Tree this branch belongs to.
|
||||
* @attribute tree
|
||||
* @type TREE
|
||||
* @required
|
||||
* @writeOnce
|
||||
*/
|
||||
tree : {
|
||||
writeOnce : 'initOnly',
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* The name of this branch.
|
||||
* @attribute name
|
||||
* @type String
|
||||
*/
|
||||
name : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -560,10 +725,21 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val.replace(/\n/g, '<br />');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The title to use for this branch.
|
||||
* @attribute title
|
||||
* @type String
|
||||
*/
|
||||
title : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString
|
||||
},
|
||||
/**
|
||||
* The ID of this branch.
|
||||
* The ID and Type should always form a unique pair.
|
||||
* @attribute id
|
||||
* @type String
|
||||
*/
|
||||
id : {
|
||||
value : '',
|
||||
validator : Y.Lang.isString,
|
||||
@ -575,58 +751,77 @@ Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
|
||||
return val;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The key used to identify this branch easily if there is one.
|
||||
* @attribute key
|
||||
* @type String
|
||||
*/
|
||||
key : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The type of this branch.
|
||||
* @attribute type
|
||||
* @type Int
|
||||
*/
|
||||
type : {
|
||||
value : null
|
||||
value : null,
|
||||
setter : function(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The link to use for this branch.
|
||||
* @attribute link
|
||||
* @type String
|
||||
*/
|
||||
link : {
|
||||
value : false
|
||||
},
|
||||
/**
|
||||
* The Icon to add when displaying this branch.
|
||||
* @attribute icon
|
||||
* @type Object
|
||||
*/
|
||||
icon : {
|
||||
value : false,
|
||||
validator : Y.Lang.isObject
|
||||
},
|
||||
/**
|
||||
* True if this branch is expandable.
|
||||
* @attribute expandable
|
||||
* @type Boolean
|
||||
*/
|
||||
expandable : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch is hidden and should be displayed greyed out.
|
||||
* @attribute hidden
|
||||
* @type Boolean
|
||||
*/
|
||||
hidden : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* True if this branch has any children.
|
||||
* @attribute haschildren
|
||||
* @type Boolean
|
||||
*/
|
||||
haschildren : {
|
||||
value : false,
|
||||
validator : Y.Lang.isBool
|
||||
},
|
||||
/**
|
||||
* An array of other branches that appear as children of this branch.
|
||||
* @attribute children
|
||||
* @type Array
|
||||
*/
|
||||
children : {
|
||||
value : [],
|
||||
validator : Y.Lang.isArray
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This namespace will contain all of the contents of the navigation blocks
|
||||
* global navigation and settings.
|
||||
* @namespace
|
||||
*/
|
||||
M.block_navigation = M.block_navigation || {
|
||||
/** The number of expandable branches in existence */
|
||||
expandablebranchcount:1,
|
||||
courselimit : 20,
|
||||
instance : null,
|
||||
/**
|
||||
* Add new instance of navigation tree to tree collection
|
||||
*/
|
||||
init_add_tree:function(properties) {
|
||||
if (properties.courselimit) {
|
||||
this.courselimit = properties.courselimit;
|
||||
}
|
||||
if (M.core_dock) {
|
||||
M.core_dock.init(Y);
|
||||
}
|
||||
new TREE(properties);
|
||||
}
|
||||
};
|
||||
|
@ -2,11 +2,9 @@
|
||||
"moodle-block_navigation-navigation": {
|
||||
"requires": [
|
||||
"base",
|
||||
"core_dock",
|
||||
"io-base",
|
||||
"node",
|
||||
"dom",
|
||||
"event-custom",
|
||||
"event-synthetic",
|
||||
"event-delegate",
|
||||
"json-parse"
|
||||
]
|
||||
|
@ -91,10 +91,13 @@ class block_settings extends block_base {
|
||||
}
|
||||
|
||||
function get_required_javascript() {
|
||||
global $CFG;
|
||||
$arguments = array('id' => $this->instance->id, 'instance' => $this->instance->id, 'candock' => $this->instance_can_be_docked());
|
||||
$this->page->requires->yui_module(array('core_dock', 'moodle-block_navigation-navigation'), 'M.block_navigation.init_add_tree', array($arguments));
|
||||
user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
|
||||
parent::get_required_javascript();
|
||||
$arguments = array(
|
||||
'id' => $this->instance->id,
|
||||
'instance' => $this->instance->id,
|
||||
'candock' => $this->instance_can_be_docked()
|
||||
);
|
||||
$this->page->requires->yui_module('moodle-block_navigation-navigation', 'M.block_navigation.init_add_tree', array($arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,7 +231,6 @@ $cache = '.var_export($cache, true).';
|
||||
'currencies' => null,
|
||||
'dbtransfer' => null,
|
||||
'debug' => null,
|
||||
'dock' => null,
|
||||
'editor' => $CFG->dirroot.'/lib/editor',
|
||||
'edufields' => null,
|
||||
'enrol' => $CFG->dirroot.'/enrol',
|
||||
|
@ -2438,6 +2438,12 @@ class block_contents {
|
||||
*/
|
||||
public $collapsible = self::NOT_HIDEABLE;
|
||||
|
||||
/**
|
||||
* Set this to true if the block is dockable.
|
||||
* @var bool
|
||||
*/
|
||||
public $dockable = false;
|
||||
|
||||
/**
|
||||
* @var array A (possibly empty) array of editing controls. Each element of
|
||||
* this array should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
|
||||
|
@ -1070,12 +1070,18 @@ class core_renderer extends renderer_base {
|
||||
if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) {
|
||||
$bc->collapsible = block_contents::NOT_HIDEABLE;
|
||||
}
|
||||
if (!empty($bc->blockinstanceid)) {
|
||||
$bc->attributes['data-instanceid'] = $bc->blockinstanceid;
|
||||
}
|
||||
$skiptitle = strip_tags($bc->title);
|
||||
if ($bc->blockinstanceid && !empty($skiptitle)) {
|
||||
$bc->attributes['aria-labelledby'] = 'instance-'.$bc->blockinstanceid.'-header';
|
||||
} else if (!empty($bc->arialabel)) {
|
||||
$bc->attributes['aria-label'] = $bc->arialabel;
|
||||
}
|
||||
if ($bc->dockable) {
|
||||
$bc->attributes['data-dockable'] = 1;
|
||||
}
|
||||
if ($bc->collapsible == block_contents::HIDDEN) {
|
||||
$bc->add_class('hidden');
|
||||
}
|
||||
|
@ -245,7 +245,6 @@ class page_requirements_manager {
|
||||
|
||||
// Every page should include definition of following modules.
|
||||
$this->js_module($this->find_module('core_filepicker'));
|
||||
$this->js_module($this->find_module('core_dock'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -673,15 +672,6 @@ class page_requirements_manager {
|
||||
$module = array('name' => 'core_completion',
|
||||
'fullpath' => '/course/completion.js');
|
||||
break;
|
||||
case 'core_dock':
|
||||
$module = array('name' => 'core_dock',
|
||||
'fullpath' => '/blocks/dock.js',
|
||||
'requires' => array('base', 'node', 'event-custom', 'event-mouseenter', 'event-resize', 'escape'),
|
||||
'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('dockblock', 'block'),
|
||||
array('undockblock', 'block'),array('undockall', 'block'),array('thisdirectionvertical', 'langconfig'),
|
||||
array('hidedockpanel', 'block'),array('hidepanel', 'block')
|
||||
));
|
||||
break;
|
||||
case 'core_message':
|
||||
$module = array('name' => 'core_message',
|
||||
'requires' => array('base', 'node', 'event', 'node-event-simulate'),
|
||||
|
@ -1444,7 +1444,7 @@ class moodle_page {
|
||||
* @return void
|
||||
*/
|
||||
public function initialise_theme_and_output() {
|
||||
global $OUTPUT, $PAGE, $SITE;
|
||||
global $OUTPUT, $PAGE, $SITE, $CFG;
|
||||
|
||||
if (!empty($this->_wherethemewasinitialised)) {
|
||||
return;
|
||||
@ -1465,6 +1465,11 @@ class moodle_page {
|
||||
}
|
||||
|
||||
$this->_theme->setup_blocks($this->pagelayout, $this->blocks);
|
||||
if ($this->_theme->enable_dock && !empty($CFG->allowblockstodock)) {
|
||||
$this->requires->strings_for_js(array('addtodock', 'undockitem', 'dockblock', 'undockblock', 'undockall', 'hidedockpanel', 'hidepanel'), 'block');
|
||||
$this->requires->string_for_js('thisdirectionvertical', 'langconfig');
|
||||
$this->requires->yui_module('moodle-core-dockloader', 'M.core.dock.loader.initLoader');
|
||||
}
|
||||
|
||||
if ($this === $PAGE) {
|
||||
$OUTPUT = $this->get_renderer('core');
|
||||
|
@ -47,7 +47,7 @@ class core_component_testcase extends advanced_testcase {
|
||||
foreach($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertFalse(strpos($subsystem, '_'), 'Core subsystems must be one work without underscores');
|
||||
if ($fulldir === null) {
|
||||
if ($subsystem === 'dock' or $subsystem === 'filepicker' or $subsystem === 'help') {
|
||||
if ($subsystem === 'filepicker' or $subsystem === 'help') {
|
||||
// Arrgghh, let's not introduce more subsystems for no real reason...
|
||||
} else {
|
||||
// Lang strings.
|
||||
|
2116
lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js
vendored
Normal file
2116
lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
lib/yui/build/moodle-core-dock/moodle-core-dock-min.js
vendored
Normal file
4
lib/yui/build/moodle-core-dock/moodle-core-dock-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2098
lib/yui/build/moodle-core-dock/moodle-core-dock.js
vendored
Normal file
2098
lib/yui/build/moodle-core-dock/moodle-core-dock.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
126
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader-debug.js
vendored
Normal file
126
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader-debug.js
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
YUI.add('moodle-core-dockloader', function (Y, NAME) {
|
||||
|
||||
var LOADERNAME = 'moodle-core-dock-loader';
|
||||
|
||||
/**
|
||||
* Core namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M
|
||||
* @class core
|
||||
*/
|
||||
M.core = M.core || {};
|
||||
|
||||
/**
|
||||
* Dock namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core
|
||||
* @class dock
|
||||
*/
|
||||
M.core.dock = M.core.dock || {};
|
||||
|
||||
/**
|
||||
* Creates the move to dock icon for dockable blocks if it doesn't already exist.
|
||||
*
|
||||
* @static
|
||||
* @method ensureMoveToIconExists
|
||||
* @param {Node} blocknode The Blocks node (.block[data-instanceid])
|
||||
*/
|
||||
M.core.dock.ensureMoveToIconExists = function(blocknode) {
|
||||
if (blocknode.one('.moveto')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var commands,
|
||||
moveto = Y.Node.create('<input type="image" class="moveto customcommand requiresjs" />'),
|
||||
blockaction = blocknode.one('.block_action'),
|
||||
icon = 't/block_to_dock';
|
||||
|
||||
// Must set the image src seperatly of we get an error with XML strict headers
|
||||
if (right_to_left()) {
|
||||
icon = icon + '_rtl';
|
||||
}
|
||||
moveto.setAttribute('alt', M.util.get_string('addtodock', 'block'));
|
||||
moveto.setAttribute('title', Y.Escape.html(M.util.get_string('dockblock', 'block', blocknode.one('.header .title h2').getHTML())));
|
||||
moveto.setAttribute('src', M.util.image_url(icon, 'moodle'));
|
||||
|
||||
if (blockaction) {
|
||||
blockaction.prepend(moveto);
|
||||
} else {
|
||||
commands = blocknode.one('.header .title .commands');
|
||||
if (!commands && blocknode.one('.header .title')) {
|
||||
commands = Y.Node.create('<div class="commands"></div>');
|
||||
blocknode.one('.header .title').append(commands);
|
||||
}
|
||||
commands.append(moveto);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dock loader.
|
||||
*
|
||||
* The dock loader is repsponsible for loading and initialising the dock only when required.
|
||||
* By doing this we avoid the need to load unnecessary JavaScript into the page for the dock just incase
|
||||
* it is being used.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core.dock
|
||||
* @class Loader
|
||||
*/
|
||||
M.core.dock.loader = M.core.dock.loader || {};
|
||||
|
||||
/**
|
||||
* Delegation events
|
||||
* @property delegationEvents
|
||||
* @protected
|
||||
* @type {Array}
|
||||
*/
|
||||
M.core.dock.loader.delegationEvents = [];
|
||||
|
||||
/**
|
||||
* Initialises the dock loader.
|
||||
*
|
||||
* The dock loader works by either firing the dock immediately if there are already docked blocks.
|
||||
* Or if there are not any docked blocks delegating two events and then loading and firing the dock when one of
|
||||
* those delegated events is triggered.
|
||||
*
|
||||
* @method initLoader
|
||||
*/
|
||||
M.core.dock.loader.initLoader = function() {
|
||||
Y.log('Dock loader initialising', 'note', LOADERNAME);
|
||||
var dockedblocks = Y.all('.block[data-instanceid][data-dockable]'),
|
||||
body = Y.one(document.body);
|
||||
dockedblocks.each(function() {
|
||||
var id = parseInt(this.getData('instanceid'), 10);
|
||||
Y.log('Dock loader watching block with instance id: '+id, 'note', LOADERNAME);
|
||||
M.core.dock.ensureMoveToIconExists(this);
|
||||
});
|
||||
if (dockedblocks.some(function(node){return node.hasClass('dock_on_load');})) {
|
||||
Y.log('Loading dock module', 'note', LOADERNAME);
|
||||
Y.use('moodle-core-dock', function() {
|
||||
M.core.dock.init();
|
||||
});
|
||||
} else {
|
||||
var callback = function(e) {
|
||||
var i,
|
||||
block = this.ancestor('.block[data-instanceid]'),
|
||||
instanceid = block.getData('instanceid');
|
||||
e.halt();
|
||||
for (i in M.core.dock.loader.delegationEvents) {
|
||||
M.core.dock.loader.delegationEvents[i].detach();
|
||||
}
|
||||
block.addClass('dock_on_load');
|
||||
Y.log('Loading dock module', 'note', LOADERNAME);
|
||||
Y.use('moodle-core-dock', function(){
|
||||
M.util.set_user_preference('docked_block_instance_'+instanceid, 1);
|
||||
M.core.dock.init();
|
||||
});
|
||||
};
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('click', callback, '.moveto'));
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('key', callback, '.moveto', 'enter'));
|
||||
}
|
||||
};
|
||||
|
||||
}, '@VERSION@', {"requires": ["escape"]});
|
1
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader-min.js
vendored
Normal file
1
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader-min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
YUI.add("moodle-core-dockloader",function(e,t){var n="moodle-core-dock-loader";M.core=M.core||{},M.core.dock=M.core.dock||{},M.core.dock.ensureMoveToIconExists=function(t){if(t.one(".moveto"))return!0;var n,r=e.Node.create('<input type="image" class="moveto customcommand requiresjs" />'),i=t.one(".block_action"),s="t/block_to_dock";return right_to_left()&&(s+="_rtl"),r.setAttribute("alt",M.util.get_string("addtodock","block")),r.setAttribute("title",e.Escape.html(M.util.get_string("dockblock","block",t.one(".header .title h2").getHTML()))),r.setAttribute("src",M.util.image_url(s,"moodle")),i?i.prepend(r):(n=t.one(".header .title .commands"),!n&&t.one(".header .title")&&(n=e.Node.create('<div class="commands"></div>'),t.one(".header .title").append(n)),n.append(r)),!0},M.core.dock.loader=M.core.dock.loader||{},M.core.dock.loader.delegationEvents=[],M.core.dock.loader.initLoader=function(){var t=e.all(".block[data-instanceid][data-dockable]"),n=e.one(document.body);t.each(function(){var e=parseInt(this.getData("instanceid"),10);M.core.dock.ensureMoveToIconExists(this)});if(t.some(function(e){return e.hasClass("dock_on_load")}))e.use("moodle-core-dock",function(){M.core.dock.init()});else{var r=function(t){var n,r=this.ancestor(".block[data-instanceid]"),i=r.getData("instanceid");t.halt();for(n in M.core.dock.loader.delegationEvents)M.core.dock.loader.delegationEvents[n].detach();r.addClass("dock_on_load"),e.use("moodle-core-dock",function(){M.util.set_user_preference("docked_block_instance_"+i,1),M.core.dock.init()})};M.core.dock.loader.delegationEvents.push(n.delegate("click",r,".moveto")),M.core.dock.loader.delegationEvents.push(n.delegate("key",r,".moveto","enter"))}}},"@VERSION@",{requires:["escape"]});
|
122
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader.js
vendored
Normal file
122
lib/yui/build/moodle-core-dockloader/moodle-core-dockloader.js
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
YUI.add('moodle-core-dockloader', function (Y, NAME) {
|
||||
|
||||
var LOADERNAME = 'moodle-core-dock-loader';
|
||||
|
||||
/**
|
||||
* Core namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M
|
||||
* @class core
|
||||
*/
|
||||
M.core = M.core || {};
|
||||
|
||||
/**
|
||||
* Dock namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core
|
||||
* @class dock
|
||||
*/
|
||||
M.core.dock = M.core.dock || {};
|
||||
|
||||
/**
|
||||
* Creates the move to dock icon for dockable blocks if it doesn't already exist.
|
||||
*
|
||||
* @static
|
||||
* @method ensureMoveToIconExists
|
||||
* @param {Node} blocknode The Blocks node (.block[data-instanceid])
|
||||
*/
|
||||
M.core.dock.ensureMoveToIconExists = function(blocknode) {
|
||||
if (blocknode.one('.moveto')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var commands,
|
||||
moveto = Y.Node.create('<input type="image" class="moveto customcommand requiresjs" />'),
|
||||
blockaction = blocknode.one('.block_action'),
|
||||
icon = 't/block_to_dock';
|
||||
|
||||
// Must set the image src seperatly of we get an error with XML strict headers
|
||||
if (right_to_left()) {
|
||||
icon = icon + '_rtl';
|
||||
}
|
||||
moveto.setAttribute('alt', M.util.get_string('addtodock', 'block'));
|
||||
moveto.setAttribute('title', Y.Escape.html(M.util.get_string('dockblock', 'block', blocknode.one('.header .title h2').getHTML())));
|
||||
moveto.setAttribute('src', M.util.image_url(icon, 'moodle'));
|
||||
|
||||
if (blockaction) {
|
||||
blockaction.prepend(moveto);
|
||||
} else {
|
||||
commands = blocknode.one('.header .title .commands');
|
||||
if (!commands && blocknode.one('.header .title')) {
|
||||
commands = Y.Node.create('<div class="commands"></div>');
|
||||
blocknode.one('.header .title').append(commands);
|
||||
}
|
||||
commands.append(moveto);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dock loader.
|
||||
*
|
||||
* The dock loader is repsponsible for loading and initialising the dock only when required.
|
||||
* By doing this we avoid the need to load unnecessary JavaScript into the page for the dock just incase
|
||||
* it is being used.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core.dock
|
||||
* @class Loader
|
||||
*/
|
||||
M.core.dock.loader = M.core.dock.loader || {};
|
||||
|
||||
/**
|
||||
* Delegation events
|
||||
* @property delegationEvents
|
||||
* @protected
|
||||
* @type {Array}
|
||||
*/
|
||||
M.core.dock.loader.delegationEvents = [];
|
||||
|
||||
/**
|
||||
* Initialises the dock loader.
|
||||
*
|
||||
* The dock loader works by either firing the dock immediately if there are already docked blocks.
|
||||
* Or if there are not any docked blocks delegating two events and then loading and firing the dock when one of
|
||||
* those delegated events is triggered.
|
||||
*
|
||||
* @method initLoader
|
||||
*/
|
||||
M.core.dock.loader.initLoader = function() {
|
||||
var dockedblocks = Y.all('.block[data-instanceid][data-dockable]'),
|
||||
body = Y.one(document.body);
|
||||
dockedblocks.each(function() {
|
||||
var id = parseInt(this.getData('instanceid'), 10);
|
||||
M.core.dock.ensureMoveToIconExists(this);
|
||||
});
|
||||
if (dockedblocks.some(function(node){return node.hasClass('dock_on_load');})) {
|
||||
Y.use('moodle-core-dock', function() {
|
||||
M.core.dock.init();
|
||||
});
|
||||
} else {
|
||||
var callback = function(e) {
|
||||
var i,
|
||||
block = this.ancestor('.block[data-instanceid]'),
|
||||
instanceid = block.getData('instanceid');
|
||||
e.halt();
|
||||
for (i in M.core.dock.loader.delegationEvents) {
|
||||
M.core.dock.loader.delegationEvents[i].detach();
|
||||
}
|
||||
block.addClass('dock_on_load');
|
||||
Y.use('moodle-core-dock', function(){
|
||||
M.util.set_user_preference('docked_block_instance_'+instanceid, 1);
|
||||
M.core.dock.init();
|
||||
});
|
||||
};
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('click', callback, '.moveto'));
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('key', callback, '.moveto', 'enter'));
|
||||
}
|
||||
};
|
||||
|
||||
}, '@VERSION@', {"requires": ["escape"]});
|
20
lib/yui/src/dock/build.json
Normal file
20
lib/yui/src/dock/build.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "moodle-core-dock",
|
||||
"builds": {
|
||||
"moodle-core-dock": {
|
||||
"jsfiles": [
|
||||
"dock.js",
|
||||
"panel.js",
|
||||
"tabheightmanager.js",
|
||||
"actionkey.js",
|
||||
"block.js",
|
||||
"dockeditem.js"
|
||||
]
|
||||
},
|
||||
"moodle-core-dockloader": {
|
||||
"jsfiles": [
|
||||
"dockloader.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
118
lib/yui/src/dock/js/actionkey.js
vendored
Normal file
118
lib/yui/src/dock/js/actionkey.js
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Dock JS.
|
||||
*
|
||||
* This file contains the action key event definition that is used for accessibility handling within the Dock.
|
||||
*
|
||||
* @module moodle-core-dock
|
||||
*/
|
||||
|
||||
/**
|
||||
* A 'dock:actionkey' Event.
|
||||
* The event consists of the left arrow, right arrow, enter and space keys.
|
||||
* More keys can be mapped to action meanings.
|
||||
* actions: collapse , expand, toggle, enter.
|
||||
*
|
||||
* This event is subscribed to by dockitems.
|
||||
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
|
||||
*
|
||||
* This event can also be delegated if needed.
|
||||
*
|
||||
* @namespace M.core.dock
|
||||
* @class ActionKey
|
||||
*/
|
||||
Y.Event.define("dock:actionkey", {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
/**
|
||||
* The keys to trigger on.
|
||||
* @method _keys
|
||||
*/
|
||||
_keys: {
|
||||
//arrows
|
||||
'37': 'collapse',
|
||||
'39': 'expand',
|
||||
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
|
||||
'32': 'toggle',
|
||||
'13': 'enter'
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles key events
|
||||
* @method _keyHandler
|
||||
* @param {EventFacade} e
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {Object} args
|
||||
*/
|
||||
_keyHandler: function (e, notifier, args) {
|
||||
var actObj;
|
||||
if (!args.actions) {
|
||||
actObj = {collapse:true, expand:true, toggle:true, enter:true};
|
||||
} else {
|
||||
actObj = args.actions;
|
||||
}
|
||||
if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
|
||||
e.action = this._keys[e.keyCode];
|
||||
notifier.fire(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes to events.
|
||||
* @method on
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
*/
|
||||
on: function (node, sub, notifier) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
//no actions given
|
||||
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
|
||||
} else {
|
||||
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches an event listener
|
||||
* @method detach
|
||||
*/
|
||||
detach: function (node, sub, notifier) {
|
||||
//detach our _detacher handle of the subscription made in on()
|
||||
sub._detacher.detach();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a delegated event listener.
|
||||
* @method delegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
delegate: function (node, sub, notifier, filter) {
|
||||
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
|
||||
if (sub.args === null) {
|
||||
//no actions given
|
||||
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
|
||||
} else {
|
||||
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches a delegated event listener.
|
||||
* @method detachDelegate
|
||||
* @param {Node} node The node this subscription was applied to.
|
||||
* @param {Subscription} sub The object tracking this subscription.
|
||||
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
|
||||
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
|
||||
*/
|
||||
detachDelegate: function (node, sub, notifier, filter) {
|
||||
sub._delegateDetacher.detach();
|
||||
}
|
||||
});
|
248
lib/yui/src/dock/js/block.js
vendored
Normal file
248
lib/yui/src/dock/js/block.js
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Dock JS.
|
||||
*
|
||||
* This file contains the block class used to manage blocks (both docked and not) for the dock.
|
||||
*
|
||||
* @module moodle-core-dock
|
||||
*/
|
||||
|
||||
/**
|
||||
* Block.
|
||||
*
|
||||
* @namespace M.core.dock
|
||||
* @class Block
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
var BLOCK = function() {
|
||||
BLOCK.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
BLOCK.prototype = {
|
||||
/**
|
||||
* A content place holder used when the block has been docked.
|
||||
* @property contentplaceholder
|
||||
* @protected
|
||||
* @type Node
|
||||
*/
|
||||
contentplaceholder : null,
|
||||
/**
|
||||
* The skip link associated with this block.
|
||||
* @property contentskipanchor
|
||||
* @protected
|
||||
* @type Node
|
||||
*/
|
||||
contentskipanchor : null,
|
||||
/**
|
||||
* The cached content node for the actual block
|
||||
* @property cachedcontentnode
|
||||
* @protected
|
||||
* @type Node
|
||||
*/
|
||||
cachedcontentnode : null,
|
||||
/**
|
||||
* If true the user preference isn't updated
|
||||
* @property skipsetposition
|
||||
* @protected
|
||||
* @type Boolean
|
||||
*/
|
||||
skipsetposition : true,
|
||||
/**
|
||||
* The dock item associated with this block
|
||||
* @property dockitem
|
||||
* @protected
|
||||
* @type DOCKITEM
|
||||
*/
|
||||
dockitem : null,
|
||||
/**
|
||||
* Called during the initialisation process of the object.
|
||||
* @method initializer
|
||||
*/
|
||||
initializer : function() {
|
||||
var node = Y.one('#inst'+this.get('id'));
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Y.log('Initialised block with instance id:'+this.get('id'), 'note', LOGNS);
|
||||
|
||||
M.core.dock.ensureMoveToIconExists(node);
|
||||
|
||||
// Move the block straight to the dock if required
|
||||
if (node.hasClass(CSS.dockonload)) {
|
||||
node.removeClass(CSS.dockonload);
|
||||
var commands = node.one('.header .title .commands');
|
||||
if (!commands) {
|
||||
commands = Y.Node.create('<div class="commands"></div>');
|
||||
if (node.one('.header .title')) {
|
||||
node.one('.header .title').append(commands);
|
||||
}
|
||||
}
|
||||
this.moveToDock(null, commands);
|
||||
}
|
||||
this.skipsetposition = false;
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* Returns the class associated with this block.
|
||||
* @method _getBlockClass
|
||||
* @private
|
||||
* @param {Node} node
|
||||
* @return String
|
||||
*/
|
||||
_getBlockClass : function(node) {
|
||||
var classes = node.getAttribute('className').toString(),
|
||||
regex = /(^|\s)(block_[a-zA-Z0-9_]+)(\s|$)/,
|
||||
matches = regex.exec(classes);
|
||||
if (matches) {
|
||||
return matches[2];
|
||||
}
|
||||
return matches;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is reponsible for moving a block from the page structure onto the dock.
|
||||
* @method moveToDock
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
moveToDock : function(e) {
|
||||
if (e) {
|
||||
e.halt(true);
|
||||
}
|
||||
|
||||
var dock = M.core.dock.get(),
|
||||
id = this.get('id'),
|
||||
blockcontent = Y.one('#inst'+id).one('.content');
|
||||
|
||||
if (!blockcontent) {
|
||||
return;
|
||||
}
|
||||
|
||||
Y.log('Moving block to the dock:'+this.get('id'), 'note', LOGNS);
|
||||
|
||||
var icon = (right_to_left()) ? 't/dock_to_block_rtl' : 't/dock_to_block',
|
||||
char = (location.href.match(/\?/)) ? '&' : '?',
|
||||
blocktitle,
|
||||
blockcommands,
|
||||
movetoimg,
|
||||
moveto;
|
||||
|
||||
this.recordBlockState();
|
||||
|
||||
blocktitle = this.cachedcontentnode.one('.title h2').cloneNode(true);
|
||||
blockcommands = this.cachedcontentnode.one('.title .commands').cloneNode(true);
|
||||
|
||||
// Must set the image src seperatly of we get an error with XML strict headers
|
||||
movetoimg = Y.Node.create('<img alt="'+Y.Escape.html(M.str.block.undockitem)+'" title="'+
|
||||
Y.Escape.html(M.util.get_string('undockblock', 'block', blocktitle.innerHTML)) +'" />');
|
||||
movetoimg.setAttribute('src', M.util.image_url(icon, 'moodle'));
|
||||
moveto = Y.Node.create('<a class="moveto customcommand requiresjs"></a>').append(movetoimg);
|
||||
moveto.set('href', location.href + char + 'dock='+id);
|
||||
blockcommands.append(moveto);
|
||||
|
||||
// Create a new dock item for the block
|
||||
this.dockitem = new DOCKEDITEM({
|
||||
block : this,
|
||||
dock : dock,
|
||||
blockinstanceid : id,
|
||||
title : blocktitle,
|
||||
contents : blockcontent,
|
||||
commands : blockcommands,
|
||||
blockclass : this._getBlockClass(Y.one('#inst'+id))
|
||||
});
|
||||
// Register an event so that when it is removed we can put it back as a block
|
||||
dock.add(this.dockitem);
|
||||
|
||||
if (!this.skipsetposition) {
|
||||
// save the users preference
|
||||
M.util.set_user_preference('docked_block_instance_'+id, 1);
|
||||
}
|
||||
|
||||
this.set('idDocked', true);
|
||||
},
|
||||
/**
|
||||
* Records the block state and adds it to the docks holding area.
|
||||
* @method recordBlockState
|
||||
*/
|
||||
recordBlockState : function() {
|
||||
var id = this.get('id'),
|
||||
dock = M.core.dock.get(),
|
||||
node = Y.one('#inst'+id),
|
||||
skipanchor = node.previous();
|
||||
// Disable the skip anchor when docking
|
||||
if (skipanchor.hasClass('skip-block')) {
|
||||
this.contentskipanchor = skipanchor;
|
||||
this.contentskipanchor.hide();
|
||||
}
|
||||
this.cachedcontentnode = node;
|
||||
this.contentplaceholder = Y.Node.create('<div class="block_dock_placeholder"></div>');
|
||||
node.replace(this.contentplaceholder);
|
||||
dock.addToHoldingArea(node);
|
||||
node = null;
|
||||
if (!this.cachedcontentnode.one('.title .commands')) {
|
||||
this.cachedcontentnode.one('.title').append(Y.Node.create('<div class="commands"></div>'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function removes a block from the dock and puts it back into the page structure.
|
||||
* @method returnToBlock
|
||||
* @return {Boolean}
|
||||
*/
|
||||
returnToBlock : function() {
|
||||
var id = this.get('id');
|
||||
|
||||
Y.log('Moving block out of the dock:'+this.get('id'), 'note', LOGNS);
|
||||
|
||||
// Enable the skip anchor when going back to block mode
|
||||
if (this.contentskipanchor) {
|
||||
this.contentskipanchor.show();
|
||||
}
|
||||
|
||||
if (this.cachedcontentnode.one('.header')) {
|
||||
this.cachedcontentnode.one('.header').insert(this.dockitem.get('contents'), 'after');
|
||||
} else {
|
||||
this.cachedcontentnode.insert(this.dockitem.get('contents'));
|
||||
}
|
||||
|
||||
this.contentplaceholder.replace(this.cachedcontentnode);
|
||||
this.cachedcontentnode = Y.one('#'+this.cachedcontentnode.get('id'));
|
||||
|
||||
var commands = this.dockitem.get('commands');
|
||||
if (commands) {
|
||||
commands.all('.hidepanelicon').remove();
|
||||
commands.all('.moveto').remove();
|
||||
commands.remove();
|
||||
}
|
||||
this.cachedcontentnode.one('.title').append(commands);
|
||||
this.cachedcontentnode = null;
|
||||
M.util.set_user_preference('docked_block_instance_'+id, 0);
|
||||
this.set('idDocked', false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Y.extend(BLOCK, Y.Base, BLOCK.prototype, {
|
||||
NAME : 'moodle-core-dock-block',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The block instance ID
|
||||
* @attribute id
|
||||
* @writeOnce
|
||||
* @type Number
|
||||
*/
|
||||
id : {
|
||||
writeOnce : 'initOnly',
|
||||
setter : function(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* True if the block has been docked.
|
||||
* @attribute isDocked
|
||||
* @default false
|
||||
* @type Boolean
|
||||
*/
|
||||
isDocked : {
|
||||
value : false
|
||||
}
|
||||
}
|
||||
});
|
1077
lib/yui/src/dock/js/dock.js
vendored
Normal file
1077
lib/yui/src/dock/js/dock.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
315
lib/yui/src/dock/js/dockeditem.js
vendored
Normal file
315
lib/yui/src/dock/js/dockeditem.js
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
/**
|
||||
* Dock JS.
|
||||
*
|
||||
* This file contains the docked item class.
|
||||
*
|
||||
* @module moodle-core-dock
|
||||
*/
|
||||
|
||||
/**
|
||||
* Docked item.
|
||||
*
|
||||
* @namespace M.core.dock
|
||||
* @class DockedItem
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
* @uses Y.EventTarget
|
||||
*/
|
||||
var DOCKEDITEM = function() {
|
||||
DOCKEDITEM.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
DOCKEDITEM.prototype = {
|
||||
/**
|
||||
* Set to true if this item is currently being displayed.
|
||||
* @property active
|
||||
* @protected
|
||||
* @type Boolean
|
||||
*/
|
||||
active : false,
|
||||
/**
|
||||
* Called during the initialisation process of the object.
|
||||
* @method initializer
|
||||
*/
|
||||
initializer : function() {
|
||||
var title = this.get('title'),
|
||||
titlestring;
|
||||
/**
|
||||
* Fired before the docked item has been drawn.
|
||||
* @event dockeditem:drawstart
|
||||
*/
|
||||
this.publish('dockeditem:drawstart', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired after the docked item has been drawn.
|
||||
* @event dockeditem:drawcomplete
|
||||
*/
|
||||
this.publish('dockeditem:drawcomplete', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired before the docked item is to be shown.
|
||||
* @event dockeditem:showstart
|
||||
*/
|
||||
this.publish('dockeditem:showstart', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired after the docked item has been shown.
|
||||
* @event dockeditem:showcomplete
|
||||
*/
|
||||
this.publish('dockeditem:showcomplete', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired before the docked item has been hidden.
|
||||
* @event dockeditem:hidestart
|
||||
*/
|
||||
this.publish('dockeditem:hidestart', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired after the docked item has been hidden.
|
||||
* @event dockeditem:hidecomplete
|
||||
*/
|
||||
this.publish('dockeditem:hidecomplete', {prefix:'dockeditem'});
|
||||
/**
|
||||
* Fired when the docked item is removed from the dock.
|
||||
* @event dockeditem:itemremoved
|
||||
*/
|
||||
this.publish('dockeditem:itemremoved', {prefix:'dockeditem'});
|
||||
if (title) {
|
||||
var type = title.get('nodeName');
|
||||
titlestring = title.cloneNode(true);
|
||||
title = Y.Node.create('<'+type+'></'+type+'>');
|
||||
title = M.core.dock.fixTitleOrientation(title, titlestring.get('text'));
|
||||
this.set('title', title);
|
||||
this.set('titlestring', titlestring);
|
||||
}
|
||||
Y.log('Initialised dockeditem for block with title "'+this._getLogDescription(), 'note', LOGNS);
|
||||
},
|
||||
/**
|
||||
* This function draws the item on the dock.
|
||||
* @method draw
|
||||
* @return Boolean
|
||||
*/
|
||||
draw : function() {
|
||||
var create = Y.Node.create,
|
||||
dock = this.get('dock'),
|
||||
count = dock.count,
|
||||
docktitle,
|
||||
dockitem,
|
||||
closeicon,
|
||||
closeiconimg,
|
||||
id = this.get('id');
|
||||
|
||||
this.fire('dockeditem:drawstart');
|
||||
|
||||
docktitle = create('<div id="dock_item_'+id+'_title" role="menu" aria-haspopup="true" class="'+CSS.dockedtitle+'"></div>');
|
||||
docktitle.append(this.get('title'));
|
||||
dockitem = create('<div id="dock_item_'+id+'" class="'+CSS.dockeditem+'" tabindex="0" rel="'+id+'"></div>');
|
||||
if (count === 1) {
|
||||
dockitem.addClass('firstdockitem');
|
||||
}
|
||||
dockitem.append(docktitle);
|
||||
dock.append(dockitem);
|
||||
|
||||
closeiconimg = create('<img alt="'+M.str.block.hidepanel+'" title="'+M.str.block.hidedockpanel+'" />');
|
||||
closeiconimg.setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
|
||||
closeicon = create('<span class="hidepanelicon" tabindex="0"></span>').append(closeiconimg);
|
||||
closeicon.on('forceclose|click', this.hide, this);
|
||||
closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
|
||||
this.get('commands').append(closeicon);
|
||||
|
||||
this.set('dockTitleNode', docktitle);
|
||||
this.set('dockItemNode', dockitem);
|
||||
|
||||
this.fire('dockeditem:drawcomplete');
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* This function toggles makes the item active and shows it.
|
||||
* @method show
|
||||
* @return Boolean
|
||||
*/
|
||||
show : function() {
|
||||
var dock = this.get('dock'),
|
||||
panel = dock.getPanel(),
|
||||
docktitle = this.get('dockTitleNode');
|
||||
|
||||
dock.hideActive();
|
||||
this.fire('dockeditem:showstart');
|
||||
Y.log('Showing '+this._getLogDescription(), 'info', LOGNS);
|
||||
panel.setHeader(this.get('titlestring'), this.get('commands'));
|
||||
panel.setBody(Y.Node.create('<div class="'+this.get('blockclass')+' block_docked"></div>').append(this.get('contents')));
|
||||
panel.show();
|
||||
panel.correctWidth();
|
||||
|
||||
this.active = true;
|
||||
// Add active item class first up
|
||||
docktitle.addClass(CSS.activeitem);
|
||||
// Set aria-exapanded property to true.
|
||||
docktitle.set('aria-expanded', "true");
|
||||
this.fire('dockeditem:showcomplete');
|
||||
dock.resize();
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* This function hides the item and makes it inactive.
|
||||
* @method hide
|
||||
*/
|
||||
hide : function() {
|
||||
this.fire('dockeditem:hidestart');
|
||||
Y.log('Hiding "'+this._getLogDescription(), 'info', LOGNS);
|
||||
if (this.active) {
|
||||
// No longer active
|
||||
this.active = false;
|
||||
// Hide the panel
|
||||
this.get('dock').getPanel().hide();
|
||||
}
|
||||
// Remove the active class
|
||||
// Set aria-exapanded property to false
|
||||
this.get('dockTitleNode').removeClass(CSS.activeitem).set('aria-expanded', "false");
|
||||
this.fire('dockeditem:hidecomplete');
|
||||
},
|
||||
/**
|
||||
* A toggle between calling show and hide functions based on css.activeitem
|
||||
* Applies rules to key press events (dock:actionkey)
|
||||
* @method toggle
|
||||
* @param {String} action
|
||||
*/
|
||||
toggle : function(action) {
|
||||
var docktitle = this.get('dockTitleNode');
|
||||
if (docktitle.hasClass(CSS.activeitem) && action !== 'expand') {
|
||||
this.hide();
|
||||
} else if (!docktitle.hasClass(CSS.activeitem) && action !== 'collapse') {
|
||||
this.show();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This function removes the node and destroys it's bits.
|
||||
* @method remove.
|
||||
*/
|
||||
remove : function () {
|
||||
this.hide();
|
||||
// Return the block to its original position.
|
||||
this.get('block').returnToBlock();
|
||||
// Remove the dock item node.
|
||||
this.get('dockItemNode').remove();
|
||||
this.fire('dockeditem:itemremoved');
|
||||
},
|
||||
/**
|
||||
* Returns the description of this item to use for log calls.
|
||||
* @method _getLogDescription
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
_getLogDescription : function() {
|
||||
return this.get('titlestring').get('innerHTML')+' ('+this.get('blockinstanceid')+')';
|
||||
}
|
||||
};
|
||||
Y.extend(DOCKEDITEM, Y.Base, DOCKEDITEM.prototype, {
|
||||
NAME : 'moodle-core-dock-dockeditem',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The block this docked item is associated with.
|
||||
* @attribute block
|
||||
* @type BLOCK
|
||||
* @writeOnce
|
||||
* @required
|
||||
*/
|
||||
block : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* The dock itself.
|
||||
* @attribute dock
|
||||
* @type DOCK
|
||||
* @writeOnce
|
||||
* @required
|
||||
*/
|
||||
dock : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* The docked item ID. This will be given by the dock.
|
||||
* @attribute id
|
||||
* @type Number
|
||||
*/
|
||||
id : {},
|
||||
/**
|
||||
* Block instance id.Taken from the associated block.
|
||||
* @attribute blockinstanceid
|
||||
* @type Number
|
||||
* @writeOnce
|
||||
*/
|
||||
blockinstanceid : {
|
||||
writeOnce : 'initOnly',
|
||||
setter : function(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The title nodeof the docked item.
|
||||
* @attribute title
|
||||
* @type Node
|
||||
* @default null
|
||||
*/
|
||||
title : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The title string.
|
||||
* @attribute titlestring
|
||||
* @type String
|
||||
*/
|
||||
titlestring : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The contents of the docked item
|
||||
* @attribute contents
|
||||
* @type Node
|
||||
* @writeOnce
|
||||
* @required
|
||||
*/
|
||||
contents : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* Commands associated with the block.
|
||||
* @attribute commands
|
||||
* @type Node
|
||||
* @writeOnce
|
||||
* @required
|
||||
*/
|
||||
commands : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* The block class.
|
||||
* @attribute blockclass
|
||||
* @type String
|
||||
* @writeOnce
|
||||
* @required
|
||||
*/
|
||||
blockclass : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* The title node for the docked block.
|
||||
* @attribute dockTitleNode
|
||||
* @type Node
|
||||
*/
|
||||
dockTitleNode : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The item node for the docked block.
|
||||
* @attribute dockItemNode
|
||||
* @type Node
|
||||
*/
|
||||
dockItemNode : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The container for the docked item (will contain the block contents when visible)
|
||||
* @attribute dockcontainerNode
|
||||
* @type Node
|
||||
*/
|
||||
dockcontainerNode : {
|
||||
value : null
|
||||
}
|
||||
}
|
||||
});
|
||||
Y.augment(DOCKEDITEM, Y.EventTarget);
|
122
lib/yui/src/dock/js/dockloader.js
vendored
Normal file
122
lib/yui/src/dock/js/dockloader.js
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
var LOADERNAME = 'moodle-core-dock-loader';
|
||||
|
||||
/**
|
||||
* Core namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M
|
||||
* @class core
|
||||
*/
|
||||
M.core = M.core || {};
|
||||
|
||||
/**
|
||||
* Dock namespace.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core
|
||||
* @class dock
|
||||
*/
|
||||
M.core.dock = M.core.dock || {};
|
||||
|
||||
/**
|
||||
* Creates the move to dock icon for dockable blocks if it doesn't already exist.
|
||||
*
|
||||
* @static
|
||||
* @method ensureMoveToIconExists
|
||||
* @param {Node} blocknode The Blocks node (.block[data-instanceid])
|
||||
*/
|
||||
M.core.dock.ensureMoveToIconExists = function(blocknode) {
|
||||
if (blocknode.one('.moveto')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var commands,
|
||||
moveto = Y.Node.create('<input type="image" class="moveto customcommand requiresjs" />'),
|
||||
blockaction = blocknode.one('.block_action'),
|
||||
icon = 't/block_to_dock';
|
||||
|
||||
// Must set the image src seperatly of we get an error with XML strict headers
|
||||
if (right_to_left()) {
|
||||
icon = icon + '_rtl';
|
||||
}
|
||||
moveto.setAttribute('alt', M.util.get_string('addtodock', 'block'));
|
||||
moveto.setAttribute('title', Y.Escape.html(M.util.get_string('dockblock', 'block', blocknode.one('.header .title h2').getHTML())));
|
||||
moveto.setAttribute('src', M.util.image_url(icon, 'moodle'));
|
||||
|
||||
if (blockaction) {
|
||||
blockaction.prepend(moveto);
|
||||
} else {
|
||||
commands = blocknode.one('.header .title .commands');
|
||||
if (!commands && blocknode.one('.header .title')) {
|
||||
commands = Y.Node.create('<div class="commands"></div>');
|
||||
blocknode.one('.header .title').append(commands);
|
||||
}
|
||||
commands.append(moveto);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dock loader.
|
||||
*
|
||||
* The dock loader is repsponsible for loading and initialising the dock only when required.
|
||||
* By doing this we avoid the need to load unnecessary JavaScript into the page for the dock just incase
|
||||
* it is being used.
|
||||
*
|
||||
* @static
|
||||
* @namespace M.core.dock
|
||||
* @class Loader
|
||||
*/
|
||||
M.core.dock.loader = M.core.dock.loader || {};
|
||||
|
||||
/**
|
||||
* Delegation events
|
||||
* @property delegationEvents
|
||||
* @protected
|
||||
* @type {Array}
|
||||
*/
|
||||
M.core.dock.loader.delegationEvents = [];
|
||||
|
||||
/**
|
||||
* Initialises the dock loader.
|
||||
*
|
||||
* The dock loader works by either firing the dock immediately if there are already docked blocks.
|
||||
* Or if there are not any docked blocks delegating two events and then loading and firing the dock when one of
|
||||
* those delegated events is triggered.
|
||||
*
|
||||
* @method initLoader
|
||||
*/
|
||||
M.core.dock.loader.initLoader = function() {
|
||||
Y.log('Dock loader initialising', 'note', LOADERNAME);
|
||||
var dockedblocks = Y.all('.block[data-instanceid][data-dockable]'),
|
||||
body = Y.one(document.body);
|
||||
dockedblocks.each(function() {
|
||||
var id = parseInt(this.getData('instanceid'), 10);
|
||||
Y.log('Dock loader watching block with instance id: '+id, 'note', LOADERNAME);
|
||||
M.core.dock.ensureMoveToIconExists(this);
|
||||
});
|
||||
if (dockedblocks.some(function(node){return node.hasClass('dock_on_load');})) {
|
||||
Y.log('Loading dock module', 'note', LOADERNAME);
|
||||
Y.use('moodle-core-dock', function() {
|
||||
M.core.dock.init();
|
||||
});
|
||||
} else {
|
||||
var callback = function(e) {
|
||||
var i,
|
||||
block = this.ancestor('.block[data-instanceid]'),
|
||||
instanceid = block.getData('instanceid');
|
||||
e.halt();
|
||||
for (i in M.core.dock.loader.delegationEvents) {
|
||||
M.core.dock.loader.delegationEvents[i].detach();
|
||||
}
|
||||
block.addClass('dock_on_load');
|
||||
Y.log('Loading dock module', 'note', LOADERNAME);
|
||||
Y.use('moodle-core-dock', function(){
|
||||
M.util.set_user_preference('docked_block_instance_'+instanceid, 1);
|
||||
M.core.dock.init();
|
||||
});
|
||||
};
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('click', callback, '.moveto'));
|
||||
M.core.dock.loader.delegationEvents.push(body.delegate('key', callback, '.moveto', 'enter'));
|
||||
}
|
||||
};
|
233
lib/yui/src/dock/js/panel.js
vendored
Normal file
233
lib/yui/src/dock/js/panel.js
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Dock JS.
|
||||
*
|
||||
* This file contains the panel class used by the dock to display the content of docked blocks.
|
||||
*
|
||||
* @module moodle-core-dock
|
||||
*/
|
||||
|
||||
/**
|
||||
* Panel.
|
||||
*
|
||||
* @namespace M.core.dock
|
||||
* @class Panel
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
* @uses Y.EventTarget
|
||||
*/
|
||||
var DOCKPANEL = function() {
|
||||
DOCKPANEL.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
DOCKPANEL.prototype = {
|
||||
/**
|
||||
* True once the panel has been created.
|
||||
* @property created
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
*/
|
||||
created : false,
|
||||
/**
|
||||
* Called during the initialisation process of the object.
|
||||
* @method initializer
|
||||
*/
|
||||
initializer : function() {
|
||||
Y.log('Panel initialising', 'note', LOGNS);
|
||||
/**
|
||||
* Fired before the panel is shown.
|
||||
* @event dockpane::beforeshow
|
||||
*/
|
||||
this.publish('dockpanel:beforeshow', {prefix:'dockpanel'});
|
||||
/**
|
||||
* Fired after the panel is shown.
|
||||
* @event dockpanel:shown
|
||||
*/
|
||||
this.publish('dockpanel:shown', {prefix:'dockpanel'});
|
||||
/**
|
||||
* Fired before the panel is hidden.
|
||||
* @event dockpane::beforehide
|
||||
*/
|
||||
this.publish('dockpanel:beforehide', {prefix:'dockpanel'});
|
||||
/**
|
||||
* Fired after the panel is hidden.
|
||||
* @event dockpanel:hidden
|
||||
*/
|
||||
this.publish('dockpanel:hidden', {prefix:'dockpanel'});
|
||||
/**
|
||||
* Fired when ever the dock panel is either hidden or shown.
|
||||
* Always fired after the shown or hidden events.
|
||||
* @event dockpanel:visiblechange
|
||||
*/
|
||||
this.publish('dockpanel:visiblechange', {prefix:'dockpanel'});
|
||||
},
|
||||
/**
|
||||
* Creates the Panel if it has not already been created.
|
||||
* @method create
|
||||
* @return {Boolean}
|
||||
*/
|
||||
create : function() {
|
||||
if (this.created) {
|
||||
return true;
|
||||
}
|
||||
this.created = true;
|
||||
var dock = this.get('dock'),
|
||||
node = dock.get('dockNode');
|
||||
this.set('node', Y.Node.create('<div id="dockeditempanel" class="dockitempanel_hidden"></div>'));
|
||||
this.set('contentNode', Y.Node.create('<div class="dockeditempanel_content"></div>'));
|
||||
this.set('headerNode', Y.Node.create('<div class="dockeditempanel_hd"></div>'));
|
||||
this.set('bodyNode', Y.Node.create('<div class="dockeditempanel_bd"></div>'));
|
||||
node.append(
|
||||
this.get('node').append(this.get('contentNode').append(this.get('headerNode')).append(this.get('bodyNode')))
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Displays the panel.
|
||||
* @method show
|
||||
*/
|
||||
show : function() {
|
||||
this.create();
|
||||
this.fire('dockpanel:beforeshow');
|
||||
this.set('visible', true);
|
||||
this.get('node').removeClass('dockitempanel_hidden');
|
||||
this.fire('dockpanel:shown');
|
||||
this.fire('dockpanel:visiblechange');
|
||||
},
|
||||
/**
|
||||
* Hides the panel
|
||||
* @method hide
|
||||
*/
|
||||
hide : function() {
|
||||
this.fire('dockpanel:beforehide');
|
||||
this.set('visible', false);
|
||||
this.get('node').addClass('dockitempanel_hidden');
|
||||
this.fire('dockpanel:hidden');
|
||||
this.fire('dockpanel:visiblechange');
|
||||
},
|
||||
/**
|
||||
* Sets the panel header.
|
||||
* @method setHeader
|
||||
* @param {Node|String} content
|
||||
*/
|
||||
setHeader : function(content) {
|
||||
this.create();
|
||||
var header = this.get('headerNode');
|
||||
header.setContent(content);
|
||||
if (arguments.length > 1) {
|
||||
for (var i=1;i < arguments.length;i++) {
|
||||
header.append(arguments[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Sets the panel body.
|
||||
* @method setBody
|
||||
* @param {Node|String} content
|
||||
*/
|
||||
setBody : function(content) {
|
||||
this.create();
|
||||
this.get('bodyNode').setContent(content);
|
||||
},
|
||||
/**
|
||||
* Sets the new top mark of the panel.
|
||||
*
|
||||
* @method setTop
|
||||
* @param {Number} newtop
|
||||
*/
|
||||
setTop : function(newtop) {
|
||||
if (Y.UA.ie > 0 && Y.UA.ie < 7) {
|
||||
this.get('node').setY(newtop);
|
||||
} else {
|
||||
this.get('node').setStyle('top', newtop.toString()+'px');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Corrects the width of the panel.
|
||||
* @method correctWidth
|
||||
*/
|
||||
correctWidth : function() {
|
||||
var bodyNode = this.get('bodyNode'),
|
||||
// Width of content.
|
||||
width = bodyNode.get('clientWidth'),
|
||||
// Scrollable width of content.
|
||||
scroll = bodyNode.get('scrollWidth'),
|
||||
// Width of content container with overflow.
|
||||
offsetWidth = bodyNode.get('offsetWidth'),
|
||||
// The new width - defaults to the current width.
|
||||
newWidth = width,
|
||||
// The max width (80% of screen).
|
||||
maxWidth = Math.round(bodyNode.get('winWidth') * 0.8);
|
||||
|
||||
// If the scrollable width is more than the visible width
|
||||
if (scroll > width) {
|
||||
// Content width
|
||||
// + the difference
|
||||
// + any rendering difference (borders, padding)
|
||||
// + 10px to make it look nice.
|
||||
newWidth = width + (scroll - width) + ((offsetWidth - width)*2) + 10;
|
||||
}
|
||||
|
||||
// Make sure its not more then the maxwidth
|
||||
if (newWidth > maxWidth) {
|
||||
newWidth = maxWidth;
|
||||
}
|
||||
|
||||
// Set the new width if its more than the old width.
|
||||
if (newWidth > offsetWidth) {
|
||||
this.get('node').setStyle('width', newWidth+'px');
|
||||
}
|
||||
}
|
||||
};
|
||||
Y.extend(DOCKPANEL, Y.Base, DOCKPANEL.prototype, {
|
||||
NAME : 'moodle-core-dock-panel',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The dock itself.
|
||||
* @attribute dock
|
||||
* @type DOCK
|
||||
* @writeonce
|
||||
*/
|
||||
dock : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* The node that contains the whole panel.
|
||||
* @attribute node
|
||||
* @type Node
|
||||
*/
|
||||
node : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The node that contains the header, body and footer.
|
||||
* @attribute contentNode
|
||||
* @type Node
|
||||
*/
|
||||
contentNode : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The node that contains the header
|
||||
* @attribute headerNode
|
||||
* @type Node
|
||||
*/
|
||||
headerNode : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* The node that contains the body
|
||||
* @attribute bodyNode
|
||||
* @type Node
|
||||
*/
|
||||
bodyNode : {
|
||||
value : null
|
||||
},
|
||||
/**
|
||||
* True if the panel is currently visible.
|
||||
* @attribute visible
|
||||
* @type Boolean
|
||||
*/
|
||||
visible : {
|
||||
value : false
|
||||
}
|
||||
}
|
||||
});
|
||||
Y.augment(DOCKPANEL, Y.EventTarget);
|
111
lib/yui/src/dock/js/tabheightmanager.js
vendored
Normal file
111
lib/yui/src/dock/js/tabheightmanager.js
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Dock JS.
|
||||
*
|
||||
* This file contains the tab height manager.
|
||||
* The tab height manager is responsible for ensure all tabs are visible all the time.
|
||||
*
|
||||
* @module moodle-core-dock
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tab height manager.
|
||||
*
|
||||
* @namespace M.core.dock
|
||||
* @class TabHeightManager
|
||||
* @constructor
|
||||
* @extends Y.Base
|
||||
*/
|
||||
var TABHEIGHTMANAGER = function() {
|
||||
TABHEIGHTMANAGER.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
TABHEIGHTMANAGER.prototype = {
|
||||
/**
|
||||
* Initialises the dock sizer which then attaches itself to the required
|
||||
* events in order to monitor the dock
|
||||
* @method initializer
|
||||
*/
|
||||
initializer : function() {
|
||||
var dock = this.get('dock');
|
||||
dock.on('dock:itemschanged', this.checkSizing, this);
|
||||
Y.on('windowresize', this.checkSizing, this);
|
||||
},
|
||||
/**
|
||||
* Check if the size dock items needs to be adjusted
|
||||
* @method checkSizing
|
||||
*/
|
||||
checkSizing : function() {
|
||||
var dock = this.get('dock'),
|
||||
node = dock.get('dockNode'),
|
||||
items = dock.dockeditems,
|
||||
possibleheight = node.get('offsetHeight') - node.one('.controls').get('offsetHeight') - (dock.get('bufferPanel')*3) - (items.length*2),
|
||||
totalheight = 0,
|
||||
id, dockedtitle;
|
||||
if (items.length > 0) {
|
||||
for (id in items) {
|
||||
dockedtitle = Y.one(items[id].get('title')).ancestor('.'+CSS.dockedtitle);
|
||||
if (dockedtitle) {
|
||||
if (this.get('enabled')) {
|
||||
dockedtitle.setStyle('height', 'auto');
|
||||
}
|
||||
totalheight += dockedtitle.get('offsetHeight') || 0;
|
||||
}
|
||||
}
|
||||
if (totalheight > possibleheight) {
|
||||
this.enable(possibleheight);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Enables the dock sizer and resizes where required.
|
||||
* @method enable
|
||||
* @param {Number} possibleheight
|
||||
*/
|
||||
enable : function(possibleheight) {
|
||||
var dock = this.get('dock'),
|
||||
items = dock.dockeditems,
|
||||
count = dock.count,
|
||||
runningcount = 0,
|
||||
usedheight = 0,
|
||||
id, itemtitle, itemheight, offsetheight;
|
||||
Y.log('Enabling the dock tab sizer.', 'note', LOGNS);
|
||||
this.set('enabled', true);
|
||||
for (id in items) {
|
||||
itemtitle = Y.one(items[id].get('title')).ancestor('.'+CSS.dockedtitle);
|
||||
if (!itemtitle) {
|
||||
continue;
|
||||
}
|
||||
itemheight = Math.floor((possibleheight-usedheight) / (count - runningcount));
|
||||
offsetheight = itemtitle.get('offsetHeight');
|
||||
itemtitle.setStyle('overflow', 'hidden');
|
||||
if (offsetheight > itemheight) {
|
||||
itemtitle.setStyle('height', itemheight+'px');
|
||||
usedheight += itemheight;
|
||||
} else {
|
||||
usedheight += offsetheight;
|
||||
}
|
||||
runningcount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
Y.extend(TABHEIGHTMANAGER, Y.Base, TABHEIGHTMANAGER.prototype, {
|
||||
NAME : 'moodle-core-tabheightmanager',
|
||||
ATTRS : {
|
||||
/**
|
||||
* The dock.
|
||||
* @attribute dock
|
||||
* @type DOCK
|
||||
* @writeOnce
|
||||
*/
|
||||
dock : {
|
||||
writeOnce : 'initOnly'
|
||||
},
|
||||
/**
|
||||
* True if the item_sizer is being used, false otherwise.
|
||||
* @attribute enabled
|
||||
* @type Bool
|
||||
*/
|
||||
enabled : {
|
||||
value : false
|
||||
}
|
||||
}
|
||||
});
|
18
lib/yui/src/dock/meta/dock.json
Normal file
18
lib/yui/src/dock/meta/dock.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"moodle-core-dock": {
|
||||
"requires": [
|
||||
"base",
|
||||
"node",
|
||||
"event-custom",
|
||||
"event-mouseenter",
|
||||
"event-resize",
|
||||
"escape",
|
||||
"moodle-core-dockloader"
|
||||
]
|
||||
},
|
||||
"moodle-core-dockloader": {
|
||||
"requires": [
|
||||
"escape"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user