From 48d8d09063607428d82b596fdad40e45d4d9aae9 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Thu, 23 Dec 2010 11:21:07 +0800 Subject: [PATCH] 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. --- blocks/dock.js | 42 +- blocks/navigation/block_navigation.php | 17 +- blocks/navigation/navigation.js | 393 ---------------- blocks/navigation/styles.css | 3 + .../navigation/yui/navigation/navigation.js | 424 ++++++++++++++++++ blocks/settings/block_settings.php | 6 +- lib/navigationlib.php | 4 +- 7 files changed, 479 insertions(+), 410 deletions(-) delete mode 100644 blocks/navigation/navigation.js create mode 100644 blocks/navigation/yui/navigation/navigation.js diff --git a/blocks/dock.js b/blocks/dock.js index 5a2d56b87ad..225c163af84 100644 --- a/blocks/dock.js +++ b/blocks/dock.js @@ -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('
').append(this.contents)); panel.show(); + panel.correctWidth(); this.active = true; // Add active item class first up diff --git a/blocks/navigation/block_navigation.php b/blocks/navigation/block_navigation.php index 73522fee8ff..8d671962ea5 100644 --- a/blocks/navigation/block_navigation.php +++ b/blocks/navigation/block_navigation.php @@ -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'); diff --git a/blocks/navigation/navigation.js b/blocks/navigation/navigation.js deleted file mode 100644 index e5e4fae7d35..00000000000 --- a/blocks/navigation/navigation.js +++ /dev/null @@ -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 . - -/** - * 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('
Expandable node within navigation was missing [#'+this.expansions[i].id+']
')); - } 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(''); - 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('
  • '); - var branchp = Y.Node.create('

    '); - - 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(''); - 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, '
    ')); - } else { - var branchlink = Y.Node.create(''); - if (branchicon) { - branchlink.appendChild(branchicon); - } - branchlink.append(this.name.replace(/\n/g, '
    ')); - if (this.hidden) { - branchlink.addClass('dimmed'); - } - branchp.appendChild(branchlink); - } - - branchli.appendChild(branchp); - if (this.haschildren) { - var childrenul = Y.Node.create(''); - 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); \ No newline at end of file diff --git a/blocks/navigation/styles.css b/blocks/navigation/styles.css index c6aecd3c510..8e204ddf08d 100644 --- a/blocks/navigation/styles.css +++ b/blocks/navigation/styles.css @@ -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, diff --git a/blocks/navigation/yui/navigation/navigation.js b/blocks/navigation/yui/navigation/navigation.js new file mode 100644 index 00000000000..d35d8d7b564 --- /dev/null +++ b/blocks/navigation/yui/navigation/navigation.js @@ -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('
  • '); + var branchp = Y.Node.create('

    ').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(''); + 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(''); + 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(''); + 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, '
    '); + } + }, + 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']}); \ No newline at end of file diff --git a/blocks/settings/block_settings.php b/blocks/settings/block_settings.php index bab697d547e..675200ea6e2 100644 --- a/blocks/settings/block_settings.php +++ b/blocks/settings/block_settings.php @@ -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++; diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 67e5241a130..31d2a37f9d9 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -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; } } /**