navigation MDL-25596 Improvements for the navigation blocks JS

The biggest change is that the navigation block has been converted to a proper YUI module.
The following are the other changes made at the same time:
 * A loading icon is displayed when a branch is being loaded by AJAX.
 * Fixed a bug where you could trigger multiple AJAX requests by rapidly clicking an unloaded branch.
 * Fixed a bug where empty branches weren't being marked as such after a successful AJAX load.
 * When docked the width of the blocks dock panel is now inspected an increased if required to try avoid horizontal scrolling.
 * Removed the no longer needed inclusion of the YUI2 dom library from the navigation and settings block.
 * Expandable nodes are now passed as JS data allowing the navigation JS to be initialised through block_navigation::get_required_javascript.
 * AJAX is now focused around the branch in question rather than the tree in general.
 * Expansion of branches is now delegated to the tree rather than being an individual event on all branches.
 * Tidied up the code in general removing unneeded-unused parameters.
This commit is contained in:
Sam Hemelryk 2010-12-23 11:21:07 +08:00
parent f056cb544d
commit 48d8d09063
7 changed files with 479 additions and 410 deletions

View File

@ -268,6 +268,43 @@ M.core_dock.getPanel = function() {
}
return;
};
/**
* Increases the width of the panel to avoid horizontal scrolling
* if possible.
*/
dockpanel.correctWidth = function() {
var bd = this.one('.dockeditempanel_bd');
// Width of content
var w = bd.get('clientWidth');
// Scrollable width of content
var s = bd.get('scrollWidth');
// Width of content container with overflow
var ow = this.get('offsetWidth');
// The new width
var nw = w;
// The max width (80% of screen)
var mw = Math.round(this.get('winWidth') * 0.8);
// If the scrollable width is more than the visible width
if (s > w) {
// Content width
// + the difference
// + any rendering difference (borders, padding)
// + 10px to make it look nice.
nw = w + (s-w) + ((ow-w)*2) + 10;
}
// Make sure its not more then the maxwidth
if (nw > mw) {
nw = mw;
}
// Set the new width if its more than the old width.
if (nw > ow) {
this.setStyle('width', nw+'px');
}
}
// Put the dockpanel in the body
parent.append(dockpanel);
// Return it
@ -520,7 +557,7 @@ M.core_dock.init_genericblock = function(Y, id) {
if (!this.initialised) {
this.init(Y);
}
new this.genericblock(id).init(Y, Y.one('#inst'+id));
new this.genericblock(id).initialise_block(Y, Y.one('#inst'+id));
};
/**
* Removes the node at the given index and puts it back into conventional page sturcture
@ -700,7 +737,7 @@ M.core_dock.genericblock.prototype = {
* @param {YUI.Node} node The node that contains all of the block's content
* @return {M.core_dock.genericblock}
*/
init : function(Y, node) {
initialise_block : function(Y, node) {
M.core_dock.init(Y);
this.Y = Y;
@ -939,6 +976,7 @@ M.core_dock.item.prototype = {
panel.setHeader(this.titlestring, this.commands);
panel.setBody(Y.Node.create('<div class="'+this.blockclass+' block_docked"></div>').append(this.contents));
panel.show();
panel.correctWidth();
this.active = true;
// Add active item class first up

View File

@ -102,6 +102,13 @@ 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');
$limit = 20;
if (!empty($CFG->navcourselimit)) {
$limit = $CFG->navcourselimit;
}
$arguments = array('id'=>$this->instance->id, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked(), 'courselimit'=>$limit);
$this->page->requires->yui_module(array('core_dock', 'moodle-block_navigation-navigation'), 'M.block_navigation.init_add_tree', array($arguments));
}
/**
@ -113,7 +120,6 @@ class block_navigation extends block_base {
if ($this->contentgenerated === true) {
return $this->content;
}
$this->page->requires->yui2_lib('dom');
// JS for navigation moved to the standard theme, the code will probably have to depend on the actual page structure
// $this->page->requires->js('/lib/javascript-navigation.js');
// Navcount is used to allow us to have multiple trees although I dont' know why
@ -171,14 +177,7 @@ class block_navigation extends block_base {
}
}
// Initialise the JS tree object
$module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'), 'strings'=>array(array('viewallcourses','moodle')));
$limit = 20;
if (!empty($CFG->navcourselimit)) {
$limit = $CFG->navcourselimit;
}
$arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked(), 'courselimit'=>$limit));
$this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module);
$this->page->requires->data_for_js('navtreeexpansions'.$this->instance->id, $expandable);
$options = array();
$options['linkcategories'] = (!empty($this->config->linkcategories) && $this->config->linkcategories == 'yes');

View File

@ -1,393 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains classes used to manage the navigation structures in Moodle
* and was introduced as part of the changes occuring in Moodle 2.0
*
* @since 2.0
* @package javascript
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* 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:0,
/** An array of initialised trees */
treecollection:[],
/**
* Will contain all of the classes for the navigation blocks
* @namespace
*/
classes:{},
courselimit : 20,
/**
* This function gets called when the module is first loaded as required by
* the YUI.add statement at the bottom of the page.
*
* NOTE: This will only be executed ONCE
* @function
*/
init:function(Y) {
M.core_dock.init(Y);
if (M.core_dock.genericblock) {
// Give the tree class the dock block properties
Y.augment(M.block_navigation.classes.tree, M.core_dock.genericblock);
}
},
/**
* Add new instance of navigation tree to tree collection
*/
init_add_tree:function(Y, id, properties) {
if (properties.courselimit) {
this.courselimit = properties.courselimit;
}
M.block_navigation.treecollection[id] = new M.block_navigation.classes.tree(Y, id, properties);
}
};
/**
* @class tree
* @constructor
* @base M.core_dock.genericblock
* @param {YUI} Y A yui instance to use with the navigation
* @param {string} id The name of the tree
* @param {object} properties Object containing tree properties
*/
M.block_navigation.classes.tree = function(Y, id, properties) {
this.Y = Y;
this.id = id;
this.key = id;
this.errorlog = [];
this.ajaxbranches = 0;
this.expansions = [];
this.instance = id;
this.cachedcontentnode = null;
this.cachedfooter = null;
this.position = 'block';
this.skipsetposition = false;
this.candock = false;
if (properties.expansions) {
this.expansions = properties.expansions;
}
if (properties.instance) {
this.instance = properties.instance;
}
if (properties.candock) {
this.candock = true;
}
var node = this.Y.one('#inst'+this.id);
// Can't find the block instance within the page
if (node === null) {
return;
}
// Attach event to toggle expansion
node.all('.tree_item.branch').on('click', this.toggleexpansion , this);
// Attach events to expand by AJAX
//var expandablenode;
for (var i in this.expansions) {
var expandablenode = Y.one('#'+this.expansions[i].id);
if (expandablenode) {
expandablenode.on('ajaxload|click', this.init_load_ajax, this, this.expansions[i]);
M.block_navigation.expandablebranchcount++;
} else if (M.cfg.debug) {
Y.one(document.body).append(Y.Node.create('<div class="notification" style="font-size:6pt;">Expandable node within navigation was missing [#'+this.expansions[i].id+']</div>'));
} else {
// Failing over silently
}
}
if (node.hasClass('block_js_expansion')) {
node.on('mouseover', function(e){this.toggleClass('mouseover');}, node);
node.on('mouseout', function(e){this.toggleClass('mouseover');}, node);
}
// Call the generic blocks init method to add all the generic stuff
if (this.candock) {
this.init(Y, node);
}
};
/**
* Loads a branch via AJAX
* @param {event} e The event object
* @param {object} branch A branch to load via ajax
*/
M.block_navigation.classes.tree.prototype.init_load_ajax = function(e, branch) {
e.stopPropagation();
var target = e.target;
if (target.test('span')) {
target = target.ancestor('p');
}
if (!target || !target.test('p')) {
return true;
}
var cfginstance = '', Y = this.Y;
if (this.instance != null) {
cfginstance = '&instance='+this.instance
}
Y.io(M.cfg.wwwroot+'/lib/ajax/getnavbranch.php', {
method:'POST',
data:'elementid='+branch.id+'&id='+branch.branchid+'&type='+branch.type+'&sesskey='+M.cfg.sesskey+cfginstance,
on: {
complete:this.load_ajax,
success:function() {Y.detach('click', this.init_load_ajax, target);}
},
context:this,
arguments:{
target:target
}
});
return true;
};
/**
* Takes an branch provided through ajax and loads it into the tree
* @param {int} tid The transaction id
* @param {object} outcome
* @param {mixed} args
* @return bool
*/
M.block_navigation.classes.tree.prototype.load_ajax = function(tid, outcome, args) {
try {
var object = this.Y.JSON.parse(outcome.responseText);
if (this.add_branch(object, args.target.ancestor('li') ,1)) {
if (this.candock) {
M.core_dock.resize();
}
return true;
}
} catch (e) {
// If we got here then there was an error parsing the result
}
// The branch is empty so class it accordingly
args.target.replaceClass('branch', 'emptybranch');
return true;
};
/**
* Adds a branch into the tree provided with some XML
* @param {object} branchobj
* @param {Y.Node} target
* @param {int} depth
* @return bool
*/
M.block_navigation.classes.tree.prototype.add_branch = function(branchobj, target, depth) {
// Make the new branch into an object
var branch = new M.block_navigation.classes.branch(this, branchobj);
var childrenul = false, Y = this.Y;
if (depth === 1) {
if (!branch.children) {
return false;
}
childrenul = Y.Node.create('<ul></ul>');
target.appendChild(childrenul);
} else {
childrenul = branch.inject_into_dom(target);
}
if (childrenul) {
var count = 0;
for (var i in branch.children) {
// Add each branch to the tree
if (branch.children[i].type == 20) {
count++;
}
if (typeof(branch.children[i])=='object') {
this.add_branch(branch.children[i], childrenul, depth+1);
}
}
if (branch.type == 10 && count >= M.block_navigation.courselimit) {
var properties = Array();
properties['name'] = M.str.moodle.viewallcourses;
properties['title'] = M.str.moodle.viewallcourses;
properties['link'] = M.cfg.wwwroot+'/course/category.php?id='+branch.key;
properties['haschildren'] = false;
properties['icon'] = {'pix':"i/navigationitem",'component':'moodle'};
this.add_branch(properties, childrenul, depth+1);
}
}
return true;
};
/**
* Toggle a branch as expanded or collapsed
* @param {Event} e
*/
M.block_navigation.classes.tree.prototype.toggleexpansion = function(e) {
// First check if they managed to click on the li iteslf, then find the closest
// LI ancestor and use that
if (e.target.get('nodeName').toUpperCase() == 'A') {
// A link has been clicked don't fire any more events just do the default.
e.stopPropagation();
return;
}
var target = e.target;
if (!target.test('li')) {
target = target.ancestor('li')
}
if (target && !target.hasClass('depth_1')) {
target.toggleClass('collapsed');
}
if (this.candock) {
M.core_dock.resize();
}
};
/**
* This class represents a branch for a tree
* @class branch
* @constructor
* @param {M.block_navigation.classes.tree} tree
* @param {object|null} obj
*/
M.block_navigation.classes.branch = function(tree, obj) {
this.tree = tree;
this.name = null;
this.title = null;
this.classname = null;
this.id = null;
this.key = null;
this.type = null;
this.link = null;
this.icon = null;
this.expandable = null;
this.expansionceiling = null;
this.hidden = false;
this.haschildren = false;
this.children = false;
if (obj !== null) {
// Construct from the provided xml
this.construct_from_json(obj);
}
};
/**
* Populates this branch from a JSON object
* @param {object} obj
*/
M.block_navigation.classes.branch.prototype.construct_from_json = function(obj) {
for (var i in obj) {
this[i] = obj[i];
}
if (this.children && this.children.length > 0) {
this.haschildren = true;
} else {
this.children = [];
}
if (this.id && this.id.match(/^expandable_branch_\d+$/)) {
// Assign a new unique id for this new expandable branch
M.block_navigation.expandablebranchcount++;
this.id = 'expandable_branch_'+M.block_navigation.expandablebranchcount;
}
};
/**
* Injects a branch into the tree at the given location
* @param {element} element
*/
M.block_navigation.classes.branch.prototype.inject_into_dom = function(element) {
var Y = this.tree.Y;
var isbranch = ((this.expandable !== null || this.haschildren) && this.expansionceiling===null);
var branchli = Y.Node.create('<li></li>');
var branchp = Y.Node.create('<p class="tree_item"></p>');
if (isbranch) {
branchli.addClass('collapsed');
branchli.addClass('contains_branch');
branchp.addClass('branch');
branchp.on('click', this.tree.toggleexpansion, this.tree);
if (this.expandable) {
branchp.on('ajaxload|click', this.tree.init_load_ajax, this.tree, {branchid:this.key,id:this.id,type:this.type});
}
}
if (this.myclass !== null) {
branchp.addClass(this.myclass);
}
if (this.id !== null) {
branchp.setAttribute('id', this.id);
}
// Prepare the icon, should be an object representing a pix_icon
var branchicon = false;
if (this.icon != null && (!isbranch || this.type == 40)) {
branchicon = Y.Node.create('<img alt="" />');
branchicon.setAttribute('src', M.util.image_url(this.icon.pix, this.icon.component));
branchli.addClass('item_with_icon');
if (this.icon.alt) {
branchicon.setAttribute('alt', this.icon.alt);
}
if (this.icon.title) {
branchicon.setAttribute('alt', this.icon.title);
}
if (this.icon.classes) {
for (var i in this.icon.classes) {
branchicon.addClass(this.icon.classes[i]);
}
}
}
if (this.link === null) {
if (branchicon) {
branchp.appendChild(branchicon);
}
branchp.append(this.name.replace(/\n/g, '<br />'));
} else {
var branchlink = Y.Node.create('<a title="'+this.title+'" href="'+this.link+'"></a>');
if (branchicon) {
branchlink.appendChild(branchicon);
}
branchlink.append(this.name.replace(/\n/g, '<br />'));
if (this.hidden) {
branchlink.addClass('dimmed');
}
branchp.appendChild(branchlink);
}
branchli.appendChild(branchp);
if (this.haschildren) {
var childrenul = Y.Node.create('<ul></ul>');
branchli.appendChild(childrenul);
element.appendChild(branchli);
return childrenul
} else {
element.appendChild(branchli);
return false;
}
};
/**
* Causes the navigation block module to initalise the first time the module
* is used!
*
* NOTE: Never convert the second argument to a function reference...
* doing so causes scoping issues
*/
YUI.add('block_navigation', function(Y){M.block_navigation.init(Y);}, '0.0.0.1', M.yui.loader.modules.block_navigation.requires);

View File

@ -16,10 +16,13 @@
.block_navigation .block_tree .active_tree_node {font-weight:bold;}
.block_navigation .block_tree .depth_1.current_branch ul {font-weight:normal;}
.dock .block_navigation .tree_item {white-space: nowrap;}
.jsenabled .block_navigation .block_tree .tree_item.branch {cursor:pointer;}
.jsenabled .block_navigation .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: 0% 5%;background-repeat: no-repeat;}
.jsenabled .block_navigation .block_tree .collapsed ul {display: none;}
.jsenabled .block_navigation .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);}
.jsenabled .block_navigation .block_tree .tree_item.branch.loadingbranch {background-image:url([[pix:i/loading_small]]);}
/** JavaScript state rules **/
.jsenabled .block_navigation.dock_on_load,

View File

@ -0,0 +1,424 @@
YUI.add('moodle-block_navigation-navigation', function(Y){
/**
* Navigation tree class.
*
* This class establishes the tree initially, creating expandable branches as
* required, and delegating the expand/collapse event.
*/
var TREE = function(config) {
TREE.superclass.constructor.apply(this, arguments);
}
TREE.prototype = {
/**
* The tree's ID, normally its block instance id.
*/
id : null,
/**
* Initialise the tree object when its first created.
*/
initializer : function(config) {
this.id = config.id;
var node = Y.one('#inst'+config.id);
// Can't find the block instance within the page
if (node === null) {
return;
}
// Delegate event to toggle expansion
var self = this;
Y.delegate('click', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
// Gather the expandable branches ready for initialisation.
var expansions = [];
if (config.expansions) {
expansions = config.expansions;
} else if (window['navtreeexpansions'+config.id]) {
expansions = window['navtreeexpansions'+config.id];
}
// Establish each expandable branch as a tree branch.
for (var i in expansions) {
new BRANCH({
tree:this,
branchobj:expansions[i],
overrides : {
expandable : true,
children : [],
haschildren : true
}
}).wire();
M.block_navigation.expandablebranchcount++;
}
// Call the generic blocks init method to add all the generic stuff
if (this.get('candock')) {
this.initialise_block(Y, node);
}
},
/**
* This is a callback function responsible for expanding and collapsing the
* branches of the tree. It is delegated to rather than multiple event handles.
*/
toggleExpansion : function(e) {
// First check if they managed to click on the li iteslf, then find the closest
// LI ancestor and use that
if (e.target.test('a')) {
// A link has been clicked don't fire any more events just do the default.
e.stopPropagation();
return;
}
// Makes sure we can get to the LI containing the branch.
var target = e.target;
if (!target.test('li')) {
target = target.ancestor('li')
}
if (!target) {
return;
}
// Toggle expand/collapse providing its not a root level branch.
if (!target.hasClass('depth_1')) {
target.toggleClass('collapsed');
}
// If the accordian feature has been enabled collapse all siblings.
if (this.get('accordian')) {
target.siblings('li').each(function(){
if (this.get('id') !== target.get('id') && !this.hasClass('collapsed')) {
this.addClass('collapsed');
}
});
}
// 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();
}
}
}
}
// The tree extends the YUI base foundation.
Y.extend(TREE, Y.Base, TREE.prototype, {
NAME : 'navigation-tree',
ATTRS : {
instance : {
value : null
},
candock : {
validator : Y.Lang.isBool,
value : false
},
accordian : {
validator : Y.Lang.isBool,
value : false
}
}
});
if (M.core_dock && M.core_dock.genericblock) {
Y.augment(TREE, M.core_dock.genericblock);
}
/**
* The tree branch class.
* This class is used to manage a tree branch, in particular its ability to load
* its contents by AJAX.
*/
var BRANCH = function(config) {
BRANCH.superclass.constructor.apply(this, arguments);
}
BRANCH.prototype = {
/**
* The node for this branch (p)
*/
node : null,
/**
* A reference to the ajax load event handle when created.
*/
event_ajaxload : null,
/**
* Initialises the branch when it is first created.
*/
initializer : function(config) {
if (config.branchobj !== null) {
// Construct from the provided xml
for (var i in config.branchobj) {
this.set(i, config.branchobj[i]);
}
var children = this.get('children');
this.set('haschildren', (children.length > 0));
}
if (config.overrides !== null) {
// Construct from the provided xml
for (var i in config.overrides) {
this.set(i, config.overrides[i]);
}
}
this.node = Y.one('#', this.get('id'));
},
/**
* Draws the branch within the tree.
*
* This function creates a DOM structure for the branch and then injects
* it into the navigation tree at the correct point.
*/
draw : function(element) {
var isbranch = (this.get('expandable') || this.get('haschildren'));
var branchli = Y.Node.create('<li></li>');
var branchp = Y.Node.create('<p class="tree_item"></p>').setAttribute('id', this.get('id'));
if (isbranch) {
branchli.addClass('collapsed').addClass('contains_branch');
branchp.addClass('branch');
}
// 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') == 40)) {
branchicon = Y.Node.create('<img alt="" />');
branchicon.setAttribute('src', M.util.image_url(icon.pix, icon.component));
branchli.addClass('item_with_icon');
if (icon.alt) {
branchicon.setAttribute('alt', icon.alt);
}
if (icon.title) {
branchicon.setAttribute('title', icon.title);
}
if (icon.classes) {
for (var i in icon.classes) {
branchicon.addClass(icon.classes[i]);
}
}
}
var link = this.get('link');
if (!link) {
if (branchicon) {
branchp.appendChild(branchicon);
}
branchp.append(this.get('name'));
} else {
var branchlink = Y.Node.create('<a title="'+this.get('title')+'" href="'+link+'"></a>');
if (branchicon) {
branchlink.appendChild(branchicon);
}
branchlink.append(this.get('name'));
if (this.get('hidden')) {
branchlink.addClass('dimmed');
}
branchp.appendChild(branchlink);
}
branchli.appendChild(branchp);
element.appendChild(branchli);
this.node = branchp;
return this;
},
/**
* Attaches required events to the branch structure.
*/
wire : function() {
this.node = this.node || Y.one('#'+this.get('id'));
if (!this.node) {
return false;
}
if (this.get('expandable')) {
this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
}
return this;
},
/**
* Gets the UL element that children for this branch should be inserted into.
*/
getChildrenUL : function() {
var ul = this.node.next('ul');
if (!ul) {
ul = Y.Node.create('<ul></ul>');
this.node.ancestor().append(ul);
}
return ul;
},
/**
* Load the content of the branch via AJAX.
*
* This function calls ajaxProcessResponse with the result of the AJAX
* request made here.
*/
ajaxLoad : function(e) {
e.stopPropagation();
if (this.node.hasClass('loadingbranch')) {
return true;
}
this.node.addClass('loadingbranch');
var params = {
elementid : this.get('id'),
id : this.get('key'),
type : this.get('type'),
sesskey : M.cfg.sesskey,
instance : this.get('tree').get('instance')
};
Y.io(M.cfg.wwwroot+'/lib/ajax/getnavbranch.php', {
method:'POST',
data: build_querystring(params),
on: {
complete: this.ajaxProcessResponse
},
context:this
});
return true;
},
/**
* Processes an AJAX request to load the content of this branch through
* AJAX.
*/
ajaxProcessResponse : function(tid, outcome) {
this.node.removeClass('loadingbranch');
this.event_ajaxload.detach();
try {
var object = Y.JSON.parse(outcome.responseText);
if (object.children && object.children.length > 0) {
for (var i in object.children) {
if (typeof(object.children[i])=='object') {
this.addChild(object.children[i]);
}
}
this.get('tree').toggleExpansion({target:this.node});
return true;
}
} catch (ex) {
// 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');
return true;
},
/**
* Turns the branch object passed to the method into a proper branch object
* and then adds it as a child of this branch.
*/
addChild : function(branchobj) {
// Make the new branch into an object
var branch = new BRANCH({tree:this.get('tree'), branchobj:branchobj});
if (branch.draw(this.getChildrenUL())) {
branch.wire();
var count = 0, i, children = branch.get('children');
for (i in children) {
// Add each branch to the tree
if (children[i].type == 20) {
count++;
}
if (typeof(children[i])=='object') {
branch.addChild(children[i]);
}
}
if (branch.get('type') == 10 && count >= M.block_navigation.courselimit) {
branch.addChild({
name : M.str.moodle.viewallcourses,
title : M.str.moodle.viewallcourses,
link : M.cfg.wwwroot+'/course/category.php?id='+branch.get('key'),
haschildren : false,
icon : {'pix':"i/navigationitem",'component':'moodle'}
}, branch);
}
}
return true;
}
}
Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
NAME : 'navigation-branch',
ATTRS : {
tree : {
validator : Y.Lang.isObject
},
name : {
value : '',
validator : Y.Lang.isString,
setter : function(val) {
return val.replace(/\n/g, '<br />');
}
},
title : {
value : '',
validator : Y.Lang.isString
},
id : {
value : '',
validator : Y.Lang.isString,
getter : function(val) {
if (val == '') {
val = 'expandable_branch_'+M.block_navigation.expandablebranchcount;
M.block_navigation.expandablebranchcount++;
}
return val;
}
},
key : {
value : null
},
type : {
value : null
},
link : {
value : false
},
icon : {
value : false,
validator : Y.Lang.isObject
},
expandable : {
value : false,
validator : Y.Lang.isBool
},
hidden : {
value : false,
validator : Y.Lang.isBool
},
haschildren : {
value : false,
validator : Y.Lang.isBool
},
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);
}
};
}, '@VERSION@', {requires:['base', 'core_dock', 'io', 'node', 'dom', 'event-custom', 'event-delegate', 'json-parse']});

View File

@ -92,9 +92,8 @@ class block_settings extends block_base {
function get_required_javascript() {
global $CFG;
$module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'));
$arguments = array($this->instance->id, array('instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
$this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module);
$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);
}
@ -107,7 +106,6 @@ class block_settings extends block_base {
if ($this->contentgenerated === true) {
return true;
}
$this->page->requires->yui2_lib('dom');
// JS for navigation moved to the standard theme, the code will probably have to depend on the actual page structure
// $this->page->requires->js('/lib/javascript-navigation.js');
block_settings::$navcount++;

View File

@ -540,7 +540,7 @@ class navigation_node implements renderable {
if ($child->nodetype == self::NODETYPE_BRANCH && $child->children->count()==0 && $child->display) {
$child->id = 'expandable_branch_'.(count($expandable)+1);
$this->add_class('canexpand');
$expandable[] = array('id'=>$child->id,'branchid'=>$child->key,'type'=>$child->type);
$expandable[] = array('id'=>$child->id,'key'=>$child->key,'type'=>$child->type);
}
$child->find_expandable($expandable);
}
@ -3699,7 +3699,7 @@ class navigation_json {
*/
public function set_expandable($expandable) {
foreach ($expandable as $node) {
$this->expandable[$node['branchid'].':'.$node['type']] = $node;
$this->expandable[$node['key'].':'.$node['type']] = $node;
}
}
/**