mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
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:
parent
f056cb544d
commit
48d8d09063
@ -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
|
||||
|
@ -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');
|
||||
|
@ -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);
|
@ -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,
|
||||
|
424
blocks/navigation/yui/navigation/navigation.js
vendored
Normal file
424
blocks/navigation/yui/navigation/navigation.js
vendored
Normal 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']});
|
@ -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++;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user