1
1
YUI.add('moodle-block_navigation-navigation', function (Y, NAME) {
6
* This file contains the Navigation block JS..
8
* @module moodle-block_navigation-navigation
12
* This namespace will contain all of the contents of the navigation blocks
13
* global navigation and settings.
15
* @class block_navigation
18
M.block_navigation = M.block_navigation || {};
20
* The number of expandable branches in existence.
22
* @property expandablebranchcount
26
M.block_navigation.expandablebranchcount = 1;
28
* The maximum number of courses to show as part of a branch.
30
* @property courselimit
34
M.block_navigation.courselimit = 20;
36
* Add new instance of navigation tree to tree collection
38
* @method init_add_tree
40
* @param {Object} properties
42
M.block_navigation.init_add_tree = function(properties) {
43
if (properties.courselimit) {
44
this.courselimit = properties.courselimit;
4
50
* A 'actionkey' Event to help with Y.delegate().
5
51
* The event consists of the left arrow, right arrow, enter and space keys.
6
52
* More keys can be mapped to action meanings.
9
55
* This event is delegated to branches in the navigation tree.
10
56
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
12
* Todo: This could be centralised, a similar Event is defined in blocks/dock.js
58
* @namespace M.block_navigation
14
61
Y.Event.define("actionkey", {
15
// Webkit and IE repeat keydown when you hold down arrow keys.
62
// Webkit and IE repeat keydown when you hold down arrow keys.
16
63
// Opera links keypress to page scroll; others keydown.
17
64
// Firefox prevents page scroll via preventDefault() on either
18
65
// keydown or keypress.
19
66
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
69
* The keys to trigger on.
25
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
83
* @param {EventFacade} e
84
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
85
* @param {Object} args
30
87
_keyHandler: function (e, notifier, args) {
32
89
if (!args.actions) {
101
* Subscribes to events.
103
* @param {Node} node The node this subscription was applied to.
104
* @param {Subscription} sub The object tracking this subscription.
105
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
43
107
on: function (node, sub, notifier) {
44
108
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
45
109
if (sub.args === null) {
118
* Detaches an event listener
53
121
detach: function (node, sub) {
54
122
//detach our _detacher handle of the subscription made in on()
55
123
sub._detacher.detach();
127
* Creates a delegated event listener.
129
* @param {Node} node The node this subscription was applied to.
130
* @param {Subscription} sub The object tracking this subscription.
131
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
132
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
58
134
delegate: function (node, sub, notifier, filter) {
59
135
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
60
136
if (sub.args === null) {
145
* Detaches a delegated event listener.
146
* @method detachDelegate
147
* @param {Node} node The node this subscription was applied to.
148
* @param {Subscription} sub The object tracking this subscription.
149
* @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
150
* @param {String|function} filter Selector string or function that accpets an event object and returns null.
68
152
detachDelegate: function (node, sub) {
69
153
sub._delegateDetacher.detach();
73
157
var EXPANSIONLIMIT_EVERYTHING = 0,
74
//EXPANSIONLIMIT_COURSE = 20,
75
//EXPANSIONLIMIT_SECTION = 30,
158
EXPANSIONLIMIT_COURSE = 20,
159
EXPANSIONLIMIT_SECTION = 30,
76
160
EXPANSIONLIMIT_ACTIVITY = 40;
79
* Mappings for the different types of nodes coming from the navigation.
80
* Copied from lib/navigationlib.php navigation_node constants.
162
// Mappings for the different types of nodes coming from the navigation.
163
// Copied from lib/navigationlib.php navigation_node constants.
84
/** @type int Root node = 0 */
165
// @type int Root node = 0
86
/** @type int System context = 1 */
167
// @type int System context = 1
88
/** @type int Course category = 10 */
169
// @type int Course category = 10
90
/** @type int MYCATEGORY = 11 */
171
// @type int MYCATEGORY = 11
92
/** @type int Course = 20 */
173
// @type int Course = 20
94
/** @type int Course section = 30 */
175
// @type int Course section = 30
96
/** @type int Activity (course module) = 40 */
177
// @type int Activity (course module) = 40
98
/** @type int Resource (course module = 50 */
179
// @type int Resource (course module = 50
100
/** @type int Custom node (could be anything) = 60 */
181
// @type int Custom node (could be anything) = 60
102
/** @type int Setting = 70 */
183
// @type int Setting = 70
104
/** @type int User context = 80 */
185
// @type int site administration = 71
187
// @type int User context = 80
106
/** @type int Container = 90 */
189
// @type int Container = 90
113
196
* This class establishes the tree initially, creating expandable branches as
114
197
* required, and delegating the expand/collapse event.
199
* @namespace M.block_navigation
116
204
var TREE = function() {
117
205
TREE.superclass.constructor.apply(this, arguments);
119
207
TREE.prototype = {
121
209
* The tree's ID, normally its block instance id.
125
216
* An array of initialised branches.
129
223
* Initialise the tree object when its first created.
224
* @method initializer
225
* @param {Object} config
131
227
initializer : function(config) {
228
Y.log('Initialising navigation block tree', 'note', 'moodle-block_navigation');
230
this.id = parseInt(config.id, 10);
134
232
var node = Y.one('#inst'+config.id);
163
261
M.block_navigation.expandablebranchcount++;
164
262
this.branches[branch.get('id')] = branch;
264
// Create siteadmin branch.
265
if (window.siteadminexpansion) {
266
var siteadminbranch = new BRANCH({
268
branchobj: window.siteadminexpansion,
275
M.block_navigation.expandablebranchcount++;
276
this.branches[siteadminbranch.get('id')] = siteadminbranch;
277
// Remove link on site admin with JS to keep old UI.
278
if (siteadminbranch.node) {
279
var siteadminlinknode = siteadminbranch.node.get('childNodes').item(0);
280
if (siteadminlinknode) {
281
var siteadminnode = Y.Node.create('<span tabindex="0">'+siteadminlinknode.get('innerHTML')+'</span>');
282
siteadminbranch.node.replaceChild(siteadminnode, siteadminlinknode);
166
286
if (M.block_navigation.expandablebranchcount > 0) {
167
287
// Delegate some events to handle AJAX loading.
168
288
Y.delegate('click', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
169
289
Y.delegate('actionkey', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
172
// Call the generic blocks init method to add all the generic stuff
173
if (this.get('candock')) {
174
this.initialise_block(Y, node);
178
293
* Fire actions for a branch when an event occurs.
294
* @method fire_branch_action
295
* @param {EventFacade} event
180
297
fire_branch_action : function(event) {
181
298
var id = event.currentTarget.getAttribute('id');
186
303
* This is a callback function responsible for expanding and collapsing the
187
304
* branches of the tree. It is delegated to rather than multiple event handles.
305
* @method toggleExpansion
306
* @param {EventFacade} e
189
309
toggleExpansion : function(e) {
190
310
// First check if they managed to click on the li iteslf, then find the closest
241
361
// If this block can dock tell the dock to resize if required and check
242
362
// the width on the dock panel in case it is presently in use.
243
if (this.get('candock')) {
244
M.core_dock.resize();
245
var panel = M.core_dock.getPanel();
247
panel.correctWidth();
363
if (this.get('candock') && M.core.dock.notifyBlockChange) {
364
M.core.dock.notifyBlockChange(this.id);
252
370
// The tree extends the YUI base foundation.
253
371
Y.extend(TREE, Y.Base, TREE.prototype, {
254
372
NAME : 'navigation-tree',
375
* True if the block can dock.
260
380
validator : Y.Lang.isBool,
384
* If set to true nodes will be opened/closed in an accordian fashion.
385
* @attribute accordian
264
389
validator : Y.Lang.isBool,
393
* The nodes that get shown.
394
* @attribute expansionlimit
267
397
expansionlimit : {
269
399
setter : function(val) {
400
val = parseInt(val, 10);
401
if (val !== EXPANSIONLIMIT_EVERYTHING &&
402
val !== EXPANSIONLIMIT_COURSE &&
403
val !== EXPANSIONLIMIT_SECTION &&
404
val !== EXPANSIONLIMIT_ACTIVITY) {
405
val = EXPANSIONLIMIT_EVERYTHING;
411
* The navigation tree block instance.
415
setter : function(val) {
270
416
return parseInt(val, 10);
275
if (M.core_dock && M.core_dock.genericblock) {
276
Y.augment(TREE, M.core_dock.genericblock);
280
* The tree branch class.
281
425
* This class is used to manage a tree branch, in particular its ability to load
282
426
* its contents by AJAX.
428
* @namespace M.block_navigation
284
433
BRANCH = function() {
285
434
BRANCH.superclass.constructor.apply(this, arguments);
312
466
// Get the node for this branch
313
this.node = Y.one('#', this.get('id'));
314
// Now check whether the branch is not expandable because of the expansionlimit
467
this.node = Y.one('#'+this.get('id'));
315
468
var expansionlimit = this.get('tree').get('expansionlimit');
316
469
var type = this.get('type');
317
470
if (expansionlimit !== EXPANSIONLIMIT_EVERYTHING && type >= expansionlimit && type <= EXPANSIONLIMIT_ACTIVITY) {
431
595
e.stopPropagation();
433
if (e.type === 'actionkey' && e.action === 'enter' && e.target.test('A')) {
597
if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
434
598
// No ajaxLoad for enter.
435
599
this.node.setAttribute('data-expandable', '0');
436
600
this.node.setAttribute('data-loaded', '1');
456
621
instance : this.get('tree').get('instance')
459
Y.io(M.cfg.wwwroot+'/lib/ajax/getnavbranch.php', {
624
var ajaxfile = '/lib/ajax/getnavbranch.php';
625
// For siteadmin navigation get tree from getsiteadminbranch.php.
626
if (this.get('type') === NODETYPE.SITEADMIN) {
627
ajaxfile = '/lib/ajax/getsiteadminbranch.php';
630
Y.io(M.cfg.wwwroot + ajaxfile, {
461
632
data: build_querystring(params),
470
641
* Processes an AJAX request to load the content of this branch through
644
* @method ajaxProcessResponse
645
* @param {Int} tid The transaction id.
646
* @param {Object} outcome
473
649
ajaxProcessResponse : function(tid, outcome) {
474
650
this.node.removeClass('loadingbranch');
475
651
this.node.setAttribute('data-loaded', '1');
477
653
var object = Y.JSON.parse(outcome.responseText);
655
Y.use('moodle-core-notification-ajaxexception', function () {
656
return new M.core.ajaxException(object).show();
478
660
if (object.children && object.children.length > 0) {
479
661
var coursecount = 0;
480
662
for (var i in object.children) {
481
if (typeof(object.children[i]) ==='object') {
663
if (typeof(object.children[i])==='object') {
482
664
if (object.children[i].type === NODETYPE.COURSE) {
489
671
&& coursecount >= M.block_navigation.courselimit) {
490
672
this.addViewAllCoursesChild(this);
674
Y.log('AJAX loading complete.', 'note', 'moodle-block_navigation');
675
// If this block can dock tell the dock to resize if required and check
676
// the width on the dock panel in case it is presently in use.
677
if (this.get('tree').get('candock') && M.core.dock.notifyBlockChange) {
678
M.core.dock.notifyBlockChange(this.get('tree').id);
495
// If we got here then there was an error parsing the result
682
Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
684
if (outcome && outcome.status && outcome.status > 0) {
685
// If we got here then there was an error parsing the result.
686
Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
687
Y.use('moodle-core-notification-exception', function () {
688
return new M.core.exception(error).show();
497
694
// The branch is empty so class it accordingly
498
695
this.node.replaceClass('branch', 'emptybranch');
552
756
Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
553
757
NAME : 'navigation-branch',
760
* The Tree this branch belongs to.
767
writeOnce : 'initOnly',
556
768
validator : Y.Lang.isObject
771
* The name of this branch.
560
777
validator : Y.Lang.isString,
562
779
return val.replace(/\n/g, '<br />');
783
* The title to use for this branch.
567
789
validator : Y.Lang.isString
792
* The ID of this branch.
793
* The ID and Type should always form a unique pair.
571
799
validator : Y.Lang.isString,
809
* The key used to identify this branch easily if there is one.
817
* The type of this branch.
823
setter : function(value) {
824
return parseInt(value, 10);
828
* The link to use for this branch.
836
* The Icon to add when displaying this branch.
591
842
validator : Y.Lang.isObject
845
* True if this branch is expandable.
846
* @attribute expandable
595
851
validator : Y.Lang.isBool
854
* True if this branch is hidden and should be displayed greyed out.
599
860
validator : Y.Lang.isBool
863
* True if this branch has any children.
864
* @attribute haschildren
603
869
validator : Y.Lang.isBool
872
* An array of other branches that appear as children of this branch.
873
* @attribute children
607
878
validator : Y.Lang.isArray
613
* This namespace will contain all of the contents of the navigation blocks
614
* global navigation and settings.
617
M.block_navigation = M.block_navigation || {
618
/** The number of expandable branches in existence */
619
expandablebranchcount:1,
623
* Add new instance of navigation tree to tree collection
625
init_add_tree:function(properties) {
626
if (properties.courselimit) {
627
this.courselimit = properties.courselimit;
632
new TREE(properties);
884
}, '@VERSION@', {"requires": ["base", "io-base", "node", "event-synthetic", "event-delegate", "json-parse"]});