From 6759dc35fe2eecd73b3b82ce59d132b0d7f24c4d Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Fri, 22 Jan 2016 17:54:38 +0800 Subject: [PATCH] MDL-35590 block_navigation: fix remaining issues --- .../amd/build/ajax_response_renderer.min.js | 1 + blocks/navigation/amd/build/nav_loader.min.js | 1 + blocks/navigation/amd/build/navblock.min.js | 1 + .../amd/build/site_admin_loader.min.js | 1 + .../amd/src/ajax_response_renderer.js | 37 ++++---- blocks/navigation/amd/src/nav_loader.js | 18 ++-- blocks/navigation/amd/src/navblock.js | 2 +- .../navigation/amd/src/site_admin_loader.js | 2 +- blocks/navigation/block_navigation.php | 2 +- blocks/navigation/renderer.php | 3 +- .../settings/amd/build/settingsblock.min.js | 1 + blocks/settings/renderer.php | 15 +++- blocks/upgrade.txt | 6 ++ lib/amd/build/tree.min.js | 1 + lib/amd/src/tree.js | 85 +++++++++---------- 15 files changed, 101 insertions(+), 75 deletions(-) create mode 100644 blocks/navigation/amd/build/ajax_response_renderer.min.js create mode 100644 blocks/navigation/amd/build/nav_loader.min.js create mode 100644 blocks/navigation/amd/build/navblock.min.js create mode 100644 blocks/navigation/amd/build/site_admin_loader.min.js create mode 100644 blocks/settings/amd/build/settingsblock.min.js create mode 100644 lib/amd/build/tree.min.js diff --git a/blocks/navigation/amd/build/ajax_response_renderer.min.js b/blocks/navigation/amd/build/ajax_response_renderer.min.js new file mode 100644 index 00000000000..3f5162d9cdb --- /dev/null +++ b/blocks/navigation/amd/build/ajax_response_renderer.min.js @@ -0,0 +1 @@ +define(["jquery"],function(a){function b(d,e){var f=a("");f.attr("role","group"),a.each(e,function(d,e){if("object"==typeof e){var g=a("
  • "),h=a("

    "),i=null,j=e.expandable||e.haschildren?!0:!1;if(h.addClass("tree_item"),h.attr("id",e.id),g.attr("role","treeitem"),e.requiresajaxloading&&(g.attr("data-requires-ajax",!0),g.attr("data-node-id",e.id),g.attr("data-node-key",e.key),g.attr("data-node-type",e.type)),j&&(g.addClass("collapsed contains_branch"),g.attr("aria-expanded",!1),h.addClass("branch")),!e.icon||j&&e.type!==c.ACTIVITY&&e.type!==c.RESOURCE||(g.addClass("item_with_icon"),h.addClass("hasicon"),i=a(""),i.attr("alt",e.icon.alt),i.attr("title",e.icon.title),i.attr("src",M.util.image_url(e.icon.pix,e.icon.component)),a.each(e.icon.classes,function(a,b){i.addClass(b)})),e.link){var k=a("");k.attr("title",e.title),k.attr("href",e.link),i?(k.append(i),k.append(''+e.name+"")):k.text(e.name),e.hidden&&k.addClass("dimmed"),h.append(k)}else{var l=a("");i?(l.append(i),l.append(''+e.name+"")):l.text(e.name),e.hidden&&l.addClass("dimmed"),h.append(l)}g.append(h),f.append(g),e.children&&e.children.length?b(g,e.children):j&&!e.requiresajaxloading&&(g.removeClass("contains_branch"),g.addClass("emptybranch"))}}),d.append(f)}var c={ROOTNODE:0,SYSTEM:1,CATEGORY:10,MYCATEGORY:11,COURSE:20,SECTION:30,ACTIVITY:40,RESOURCE:50,CUSTOM:60,SETTING:70,SITEADMIN:71,USER:80,CONTAINER:90};return{render:function(a,c){c.children&&c.children.length?b(a,c.children):a.hasClass("contains_branch")&&a.removeClass("contains_branch").addClass("emptybranch")}}}); \ No newline at end of file diff --git a/blocks/navigation/amd/build/nav_loader.min.js b/blocks/navigation/amd/build/nav_loader.min.js new file mode 100644 index 00000000000..183a19f43b7 --- /dev/null +++ b/blocks/navigation/amd/build/nav_loader.min.js @@ -0,0 +1 @@ +define(["jquery","core/ajax","core/config","block_navigation/ajax_response_renderer"],function(a,b,c,d){function e(a){return a.closest("[data-block]").attr("data-instanceid")}var f=c.wwwroot+"/lib/ajax/getnavbranch.php";return{load:function(b){b=a(b);var g=a.Deferred(),h={elementid:b.attr("data-node-id"),id:b.attr("data-node-key"),type:b.attr("data-node-type"),sesskey:c.sesskey,instance:e(b)},i={type:"POST",dataType:"json",data:h};return a.ajax(f,i).done(function(a){d.render(b,a),g.resolve()}),g}}}); \ No newline at end of file diff --git a/blocks/navigation/amd/build/navblock.min.js b/blocks/navigation/amd/build/navblock.min.js new file mode 100644 index 00000000000..5a5d7b90d80 --- /dev/null +++ b/blocks/navigation/amd/build/navblock.min.js @@ -0,0 +1 @@ +define(["jquery","core/tree"],function(a,b){return{init:function(){new b(".block_navigation .block_tree")}}}); \ No newline at end of file diff --git a/blocks/navigation/amd/build/site_admin_loader.min.js b/blocks/navigation/amd/build/site_admin_loader.min.js new file mode 100644 index 00000000000..9bd76cbae61 --- /dev/null +++ b/blocks/navigation/amd/build/site_admin_loader.min.js @@ -0,0 +1 @@ +define(["jquery","core/ajax","core/config","block_navigation/ajax_response_renderer"],function(a,b,c,d){var e=71,f=c.wwwroot+"/lib/ajax/getsiteadminbranch.php";return{load:function(b){b=a(b);var g=a.Deferred(),h={type:e,sesskey:c.sesskey},i={type:"POST",dataType:"json",data:h};return a.ajax(f,i).done(function(a){d.render(b,a),g.resolve()}),g}}}); \ No newline at end of file diff --git a/blocks/navigation/amd/src/ajax_response_renderer.js b/blocks/navigation/amd/src/ajax_response_renderer.js index 5112b7ce972..1416899cb69 100644 --- a/blocks/navigation/amd/src/ajax_response_renderer.js +++ b/blocks/navigation/amd/src/ajax_response_renderer.js @@ -27,34 +27,42 @@ define(['jquery'], function($) { // Mappings for the different types of nodes coming from the navigation. // Copied from lib/navigationlib.php navigation_node constants. var NODETYPE = { - // @type int Root node = 0 + // @type int Root node = 0. ROOTNODE : 0, - // @type int System context = 1 + // @type int System context = 1. SYSTEM : 1, - // @type int Course category = 10 + // @type int Course category = 10. CATEGORY : 10, - // @type int MYCATEGORY = 11 + // @type int MYCATEGORY = 11. MYCATEGORY : 11, - // @type int Course = 20 + // @type int Course = 20. COURSE : 20, - // @type int Course section = 30 + // @type int Course section = 30. SECTION : 30, - // @type int Activity (course module) = 40 + // @type int Activity (course module) = 40. ACTIVITY : 40, - // @type int Resource (course module = 50 + // @type int Resource (course module = 50. RESOURCE : 50, - // @type int Custom node (could be anything) = 60 + // @type int Custom node (could be anything) = 60. CUSTOM : 60, - // @type int Setting = 70 + // @type int Setting = 70. SETTING : 70, - // @type int site administration = 71 + // @type int site administration = 71. SITEADMIN : 71, - // @type int User context = 80 + // @type int User context = 80. USER : 80, - // @type int Container = 90 + // @type int Container = 90. CONTAINER : 90 }; + /** + * Build DOM. + * + * @method buildDOM + * @param {Object} rootElement the root element of DOM. + * @param {object} nodes jquery object representing the nodes to be build. + * @return + */ function buildDOM(rootElement, nodes) { var ul = $(''); ul.attr('role', 'group'); @@ -149,8 +157,7 @@ define(['jquery'], function($) { return { render: function(element, nodes) { - // The first element of the response is the existing node - // so we start with processing the children. + // The first element of the response is the existing node so we start with processing the children. if (nodes.children && nodes.children.length) { buildDOM(element, nodes.children); } else { diff --git a/blocks/navigation/amd/src/nav_loader.js b/blocks/navigation/amd/src/nav_loader.js index ff707b28ed9..2cb51372ac2 100644 --- a/blocks/navigation/amd/src/nav_loader.js +++ b/blocks/navigation/amd/src/nav_loader.js @@ -22,13 +22,19 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_renderer'], - function($, ajax, config, renderer) { + function($, ajax, config, renderer) { + var URL = config.wwwroot + '/lib/ajax/getnavbranch.php'; - var URL = config.wwwroot + '/lib/ajax/getnavbranch.php'; - - function getBlockInstanceId(element) { - return element.closest('[data-block]').attr('data-instanceid'); - } + /** + * Get the block instance id. + * + * @function getBlockInstanceId + * @param element + * @returns {*} + */ + function getBlockInstanceId(element) { + return element.closest('[data-block]').attr('data-instanceid'); + } return { load: function(element) { diff --git a/blocks/navigation/amd/src/navblock.js b/blocks/navigation/amd/src/navblock.js index 49707689d81..14b14bbf61d 100644 --- a/blocks/navigation/amd/src/navblock.js +++ b/blocks/navigation/amd/src/navblock.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . /** - * Load the navtree javscript + * Load the navigation tree javascript. * * @module block_navigation/navblock * @package core diff --git a/blocks/navigation/amd/src/site_admin_loader.js b/blocks/navigation/amd/src/site_admin_loader.js index d0ca39cfc6f..b203aac0199 100644 --- a/blocks/navigation/amd/src/site_admin_loader.js +++ b/blocks/navigation/amd/src/site_admin_loader.js @@ -33,7 +33,7 @@ define(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_re var promise = $.Deferred(); var data = { type: SITE_ADMIN_NODE_TYPE, - sesskey: config.sesskey, + sesskey: config.sesskey }; var settings = { type: 'POST', diff --git a/blocks/navigation/block_navigation.php b/blocks/navigation/block_navigation.php index 1e38c8f60b3..31a3fc8e49e 100644 --- a/blocks/navigation/block_navigation.php +++ b/blocks/navigation/block_navigation.php @@ -107,7 +107,6 @@ class block_navigation extends block_base { * Gets Javascript that may be required for navigation */ function get_required_javascript() { - global $CFG; parent::get_required_javascript(); $this->page->requires->string_for_js('viewallcourses', 'moodle'); $this->page->requires->js_call_amd('block_navigation/navblock', 'init', array()); @@ -119,6 +118,7 @@ class block_navigation extends block_base { * @return object $this->content */ function get_content() { + global $CFG; // First check if we have already generated, don't waste cycles if ($this->contentgenerated === true) { return $this->content; diff --git a/blocks/navigation/renderer.php b/blocks/navigation/renderer.php index 780bdb3a2f5..3c8932c306b 100644 --- a/blocks/navigation/renderer.php +++ b/blocks/navigation/renderer.php @@ -182,7 +182,8 @@ class block_navigation_renderer extends plugin_renderer_base { // Create the structure. $content = html_writer::tag('p', $content, $divattr); if ($isexpandable) { - $content .= $this->navigation_node($item->children, array('role' => 'group'), $expansionlimit, $options, $depth+1); + $content .= $this->navigation_node($item->children, array('role' => 'group'), $expansionlimit, + $options, $depth + 1); } if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) { $content = html_writer::empty_tag('hr') . $content; diff --git a/blocks/settings/amd/build/settingsblock.min.js b/blocks/settings/amd/build/settingsblock.min.js new file mode 100644 index 00000000000..58a9fbcc6a4 --- /dev/null +++ b/blocks/settings/amd/build/settingsblock.min.js @@ -0,0 +1 @@ +define(["jquery","core/tree"],function(a,b){return{init:function(){new b(".block_settings .block_tree")}}}); \ No newline at end of file diff --git a/blocks/settings/renderer.php b/blocks/settings/renderer.php index f8683da8daf..d5572e85277 100644 --- a/blocks/settings/renderer.php +++ b/blocks/settings/renderer.php @@ -43,6 +43,14 @@ class block_settings_renderer extends plugin_renderer_base { return $content; } + /** + * Build the navigation node. + * + * @param navigation_node $node the navigation node object. + * @param array $attrs list of attributes. + * @param int $depth the depth, default to 1. + * @return string the navigation node code. + */ protected function navigation_node(navigation_node $node, $attrs=array(), $depth = 1) { $items = $node->children; @@ -73,7 +81,8 @@ class block_settings_renderer extends plugin_renderer_base { $liexpandable = array(); if ($isbranch) { $liclasses[] = 'contains_branch'; - if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count()==0 && $item->nodetype==navigation_node::NODETYPE_BRANCH)) { + if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count() == 0 + && $item->nodetype == navigation_node::NODETYPE_BRANCH)) { $liexpandable = array('aria-expanded' => 'false'); } else { $liexpandable = array('aria-expanded' => 'true'); @@ -90,7 +99,7 @@ class block_settings_renderer extends plugin_renderer_base { $liclasses[] = 'current_branch'; } $nodetextid = 'label_' . $depth . '_' . $number; - $liattr = array('class' => join(' ',$liclasses), 'tabindex' => '-1', 'role' => 'treeitem') + $liexpandable; + $liattr = array('class' => join(' ', $liclasses), 'tabindex' => '-1', 'role' => 'treeitem') + $liexpandable; // class attribute on the div item which only contains the item content $divclasses = array('tree_item'); if ($isbranch) { @@ -105,7 +114,7 @@ class block_settings_renderer extends plugin_renderer_base { if (!empty($item->id)) { $divattr['id'] = $item->id; } - $content = html_writer::tag('p', $content, $divattr) . $this->navigation_node($item, array(), $depth+1); + $content = html_writer::tag('p', $content, $divattr) . $this->navigation_node($item, array(), $depth + 1); if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) { $content = html_writer::empty_tag('hr') . $content; } diff --git a/blocks/upgrade.txt b/blocks/upgrade.txt index 34305e16d89..cdda9f345a4 100644 --- a/blocks/upgrade.txt +++ b/blocks/upgrade.txt @@ -1,6 +1,12 @@ This files describes API changes in /blocks/* - activity modules, information provided here is intended especially for developers. +=== 3.1 === + +* The collapsed class was removed from the navigation block to make it compatible with aria. +* New aria attributes were added on the navigation block [aria-expanded="false"]. +* The tree JS handling were moved from YUI to AMD module (Jquery). + === 2.9 === * The obsolete method preferred_width() was removed (it was not doing anything) diff --git a/lib/amd/build/tree.min.js b/lib/amd/build/tree.min.js new file mode 100644 index 00000000000..abc9f1d60b6 --- /dev/null +++ b/lib/amd/build/tree.min.js @@ -0,0 +1 @@ +define(["jquery"],function(a){var b={ITEM:"[role=treeitem]",GROUP:"[role=treeitem]:has([role=group]), [role=treeitem][data-requires-ajax=true]",CLOSED_GROUP:"[role=treeitem]:has([role=group])[aria-expanded=false], [role=treeitem][data-requires-ajax=true][aria-expanded=false]",FIRST_ITEM:"[role=treeitem]:first",VISIBLE_ITEM:"[role=treeitem]:visible",UNLOADED_AJAX_ITEM:"[role=treeitem][data-requires-ajax=true][data-loaded=false][aria-expanded=true]"},c=function(c,d){this.treeRoot=a(c),this.treeRoot.data("activeItem",null),this.selectCallback=d,this.keys={tab:9,enter:13,space:32,pageup:33,pagedown:34,end:35,home:36,left:37,up:38,right:39,down:40,asterisk:106},this.initialiseNodes(this.treeRoot),this.setActiveItem(this.treeRoot.find(b.FIRST_ITEM)),this.refreshVisibleItemsCache(),this.bindEventHandlers()};return c.prototype.refreshVisibleItemsCache=function(){this.treeRoot.data("visibleItems",this.treeRoot.find(b.VISIBLE_ITEM))},c.prototype.getVisibleItems=function(){return this.treeRoot.data("visibleItems")},c.prototype.setActiveItem=function(a){var b=this.treeRoot.data("activeItem");a!==b&&(null!==b&&(b.attr("tabindex","-1"),b.attr("aria-selected","false")),a.attr("tabindex","0"),a.attr("aria-selected","true"),this.treeRoot.data("activeItem",a),"function"==typeof this.selectCallback&&this.selectCallback(a))},c.prototype.isGroupItem=function(a){return a.is(b.GROUP)},c.prototype.initialiseNodes=function(c){this.removeAllFromTabOrder(c),this.setAriaSelectedFalseOnItems(c);var d=this;c.find(b.UNLOADED_AJAX_ITEM).each(function(){var b=a(this);d.collapseGroup(b),d.expandGroup(b)})},c.prototype.removeAllFromTabOrder=function(a){a.find("*").attr("tabindex","-1")},c.prototype.setAriaSelectedFalseOnItems=function(a){a.find(b.ITEM).attr("aria-selected","false")},c.prototype.expandAllGroups=function(){this.expandAllChildGroups(this.treeRoot)},c.prototype.expandAllChildGroups=function(c){var d=this;c.find(b.CLOSED_GROUP).each(function(){var b=a(this);d.expandGroup(b).done(function(){d.expandAllChildGroups(b)})})},c.prototype.expandGroup=function(b){var c=a.Deferred();if("false"!==b.attr("data-expandable")&&"true"!==b.attr("aria-expanded"))if("true"===b.attr("data-requires-ajax")&&"true"!==b.attr("data-loaded")){b.attr("data-loaded",!1);var d=b.closest("[data-ajax-loader]").attr("data-ajax-loader"),e=this;b.addClass("loading"),require([d],function(a){a.load(b).done(function(){b.attr("data-loaded",!0),e.initialiseNodes(b),e.finishExpandingGroup(b),b.removeClass("loading"),c.resolve()})})}else this.finishExpandingGroup(b),c.resolve();else c.resolve();return c},c.prototype.finishExpandingGroup=function(a){var c=a.children(b.GROUP);c.show().attr("aria-hidden","false"),a.attr("aria-expanded","true"),this.refreshVisibleItemsCache()},c.prototype.collapseGroup=function(a){if("false"!==a.attr("aria-expanded")){var c=a.children(b.GROUP);c.hide().attr("aria-hidden","true"),a.attr("aria-expanded","false"),this.refreshVisibleItemsCache()}},c.prototype.toggleGroup=function(a){"true"===a.attr("aria-expanded")?this.collapseGroup(a):this.expandGroup(a)},c.prototype.handleKeyDown=function(a,c){var d=this.getVisibleItems().index(a);if(c.altKey||c.ctrlKey||c.shiftKey&&c.keyCode!=this.keys.tab)return!0;switch(c.keyCode){case this.keys.home:return this.getVisibleItems().first().focus(),c.stopPropagation(),!1;case this.keys.end:return this.getVisibleItems().last().focus(),c.stopPropagation(),!1;case this.keys.enter:var e=a.children().not(b.GROUP).children("a");return e.length?window.location.href=e.first().attr("href"):this.isGroupItem(a)&&this.toggleGroup(a,!0),c.stopPropagation(),!1;case this.keys.space:return this.isGroupItem(a)&&this.toggleGroup(a,!0),c.stopPropagation(),!1;case this.keys.left:if(this.isGroupItem(a)){this.collapseGroup(a);var f=this.getVisibleItems().filter(b.GROUP),g=f.index(a),h=g-1>0?g-1:0;f.eq(h).focus()}return c.stopPropagation(),!1;case this.keys.right:return this.isGroupItem(a)&&this.expandGroup(a).done(function(){a.find(b.ITEM).first().focus()}),c.stopPropagation(),!1;case this.keys.up:if(d>0){var i=this.getVisibleItems().eq(d-1);i.focus()}return c.stopPropagation(),!1;case this.keys.down:if(d