[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * Competency picker. 18 * 19 * To handle 'save' events use: picker.on('save') 20 * This will receive a object with either a single 'competencyId', or an array in 'competencyIds' 21 * depending on the value of multiSelect. 22 * 23 * @package tool_lp 24 * @copyright 2015 Frédéric Massart - FMCorz.net 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 define(['jquery', 29 'core/notification', 30 'core/ajax', 31 'core/templates', 32 'tool_lp/dialogue', 33 'core/str', 34 'tool_lp/tree'], 35 function($, Notification, Ajax, Templates, Dialogue, Str, Tree) { 36 37 /** 38 * Competency picker class. 39 * @param {Number} pageContextId The page context ID. 40 * @param {Number|false} singleFramework The ID of the framework when limited to one. 41 * @param {String} pageContextIncludes One of 'children', 'parents', 'self'. 42 * @param {Boolean} multiSelect Support multi-select in the tree. 43 */ 44 var Picker = function(pageContextId, singleFramework, pageContextIncludes, multiSelect) { 45 var self = this; 46 self._eventNode = $('<div></div>'); 47 self._frameworks = []; 48 self._reset(); 49 50 self._pageContextId = pageContextId; 51 self._pageContextIncludes = pageContextIncludes || 'children'; 52 self._multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true); 53 if (singleFramework) { 54 self._frameworkId = singleFramework; 55 self._singleFramework = true; 56 } 57 }; 58 59 /** @type {Array} The competencies fetched. */ 60 Picker.prototype._competencies = null; 61 /** @type {Array} The competencies that cannot be picked. */ 62 Picker.prototype._disallowedCompetencyIDs = null; 63 /** @type {Node} The node we attach the events to. */ 64 Picker.prototype._eventNode = null; 65 /** @type {Array} The list of frameworks fetched. */ 66 Picker.prototype._frameworks = null; 67 /** @type {Number} The current framework ID. */ 68 Picker.prototype._frameworkId = null; 69 /** @type {Number} The page context ID. */ 70 Picker.prototype._pageContextId = null; 71 /** @type {Number} Relevant contexts inclusion. */ 72 Picker.prototype._pageContextIncludes = null; 73 /** @type {Dialogue} The reference to the dialogue. */ 74 Picker.prototype._popup = null; 75 /** @type {String} The string we filter the competencies with. */ 76 Picker.prototype._searchText = ''; 77 /** @type {Object} The competency that was selected. */ 78 Picker.prototype._selectedCompetencies = null; 79 /** @type {Boolean} Whether we can browse frameworks or not. */ 80 Picker.prototype._singleFramework = false; 81 /** @type {Boolean} Do we allow multi select? */ 82 Picker.prototype._multiSelect = true; 83 /** @type {Boolean} Do we allow to display hidden framework? */ 84 Picker.prototype._onlyVisible = true; 85 86 /** 87 * Hook to executed after the view is rendered. 88 * 89 * @method _afterRender 90 */ 91 Picker.prototype._afterRender = function() { 92 var self = this; 93 94 // Initialise the tree. 95 var tree = new Tree(self._find('[data-enhance=linktree]'), self._multiSelect); 96 97 // To prevent jiggling we only show the tree after it is enhanced. 98 self._find('[data-enhance=linktree]').show(); 99 100 tree.on('selectionchanged', function(evt, params) { 101 var selected = params.selected; 102 evt.preventDefault(); 103 var validIds = []; 104 $.each(selected, function(index, item) { 105 var compId = $(item).data('id'), 106 valid = true; 107 108 if (typeof compId === 'undefined') { 109 // Do not allow picking nodes with no id. 110 valid = false; 111 } else { 112 $.each(self._disallowedCompetencyIDs, function(i, id) { 113 if (id == compId) { 114 valid = false; 115 } 116 }); 117 } 118 if (valid) { 119 validIds.push(compId); 120 } 121 }); 122 123 self._selectedCompetencies = validIds; 124 125 // TODO Implement disabling of nodes in the tree module somehow. 126 if (!self._selectedCompetencies.length) { 127 self._find('[data-region="competencylinktree"] [data-action="add"]').attr('disabled', 'disabled'); 128 } else { 129 self._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr('disabled'); 130 } 131 }); 132 133 // Add listener for framework change. 134 if (!self._singleFramework) { 135 self._find('[data-action="chooseframework"]').change(function(e) { 136 self._frameworkId = $(e.target).val(); 137 self._loadCompetencies().then(self._refresh.bind(self)); 138 }); 139 } 140 141 // Add listener for search. 142 self._find('[data-region="filtercompetencies"] button').click(function(e) { 143 e.preventDefault(); 144 $(e.target).attr('disabled', 'disabled'); 145 self._searchText = self._find('[data-region="filtercompetencies"] input').val() || ''; 146 return self._refresh().always(function() { 147 $(e.target).removeAttr('disabled'); 148 }); 149 }); 150 151 // Add listener for cancel. 152 self._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) { 153 e.preventDefault(); 154 self.close(); 155 }); 156 157 // Add listener for add. 158 self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) { 159 e.preventDefault(); 160 if (!self._selectedCompetencies.length) { 161 return; 162 } 163 164 if (self._multiSelect) { 165 self._trigger('save', {competencyIds: self._selectedCompetencies}); 166 } else { 167 // We checked above that the array has at least one value. 168 self._trigger('save', {competencyId: self._selectedCompetencies[0]}); 169 } 170 171 self.close(); 172 }); 173 174 // The list of selected competencies will be modified while looping (because of the listeners above). 175 var currentItems = self._selectedCompetencies.slice(0); 176 177 $.each(currentItems, function(index, id) { 178 var node = self._find('[data-id=' + id + ']'); 179 if (node.length) { 180 tree.toggleItem(node); 181 tree.updateFocus(node); 182 } 183 }); 184 185 }; 186 187 /** 188 * Close the dialogue. 189 * 190 * @method close 191 */ 192 Picker.prototype.close = function() { 193 var self = this; 194 self._popup.close(); 195 self._reset(); 196 }; 197 198 /** 199 * Opens the picker. 200 * 201 * @method display 202 * @return {Promise} 203 */ 204 Picker.prototype.display = function() { 205 var self = this; 206 return self._render().then(function(html) { 207 return Str.get_string('competencypicker', 'tool_lp').then(function(title) { 208 self._popup = new Dialogue( 209 title, 210 html, 211 self._afterRender.bind(self) 212 ); 213 }); 214 }).fail(Notification.exception); 215 }; 216 217 /** 218 * Fetch the competencies. 219 * 220 * @param {Number} frameworkId The frameworkId. 221 * @param {String} searchText Limit the competencies to those matching the text. 222 * @method _fetchCompetencies 223 * @return {Promise} 224 */ 225 Picker.prototype._fetchCompetencies = function(frameworkId, searchText) { 226 var self = this; 227 228 return Ajax.call([ 229 {methodname: 'core_competency_search_competencies', args: { 230 searchtext: searchText, 231 competencyframeworkid: frameworkId 232 }} 233 ])[0].done(function(competencies) { 234 /** 235 * @param {Object} parent 236 * @param {Array} competencies 237 */ 238 function addCompetencyChildren(parent, competencies) { 239 for (var i = 0; i < competencies.length; i++) { 240 if (competencies[i].parentid == parent.id) { 241 parent.haschildren = true; 242 competencies[i].children = []; 243 competencies[i].haschildren = false; 244 parent.children[parent.children.length] = competencies[i]; 245 addCompetencyChildren(competencies[i], competencies); 246 } 247 } 248 } 249 250 // Expand the list of competencies into a tree. 251 var i, comp; 252 var tree = []; 253 for (i = 0; i < competencies.length; i++) { 254 comp = competencies[i]; 255 if (comp.parentid == "0") { // Loose check for now, because WS returns a string. 256 comp.children = []; 257 comp.haschildren = 0; 258 tree[tree.length] = comp; 259 addCompetencyChildren(comp, competencies); 260 } 261 } 262 263 self._competencies = tree; 264 265 }).fail(Notification.exception); 266 }; 267 268 /** 269 * Find a node in the dialogue. 270 * 271 * @param {String} selector 272 * @return {JQuery} 273 * @method _find 274 */ 275 Picker.prototype._find = function(selector) { 276 return $(this._popup.getContent()).find(selector); 277 }; 278 279 /** 280 * Convenience method to get a framework object. 281 * 282 * @param {Number} fid The framework ID. 283 * @return {Object} 284 * @method _getFramework 285 */ 286 Picker.prototype._getFramework = function(fid) { 287 var frm; 288 $.each(this._frameworks, function(i, f) { 289 if (f.id == fid) { 290 frm = f; 291 return; 292 } 293 }); 294 return frm; 295 }; 296 297 /** 298 * Load the competencies. 299 * 300 * @method _loadCompetencies 301 * @return {Promise} 302 */ 303 Picker.prototype._loadCompetencies = function() { 304 return this._fetchCompetencies(this._frameworkId, this._searchText); 305 }; 306 307 /** 308 * Load the frameworks. 309 * 310 * @method _loadFrameworks 311 * @return {Promise} 312 */ 313 Picker.prototype._loadFrameworks = function() { 314 var promise, 315 self = this; 316 317 // Quit early because we already have the data. 318 if (self._frameworks.length > 0) { 319 return $.when(); 320 } 321 322 if (self._singleFramework) { 323 promise = Ajax.call([ 324 {methodname: 'core_competency_read_competency_framework', args: { 325 id: this._frameworkId 326 }} 327 ])[0].then(function(framework) { 328 return [framework]; 329 }); 330 } else { 331 promise = Ajax.call([ 332 {methodname: 'core_competency_list_competency_frameworks', args: { 333 sort: 'shortname', 334 context: {contextid: self._pageContextId}, 335 includes: self._pageContextIncludes, 336 onlyvisible: self._onlyVisible 337 }} 338 ])[0]; 339 } 340 341 return promise.done(function(frameworks) { 342 self._frameworks = frameworks; 343 }).fail(Notification.exception); 344 }; 345 346 /** 347 * Register an event listener. 348 * 349 * @param {String} type The event type. 350 * @param {Function} handler The event listener. 351 * @method on 352 */ 353 Picker.prototype.on = function(type, handler) { 354 this._eventNode.on(type, handler); 355 }; 356 357 /** 358 * Hook to executed before render. 359 * 360 * @method _preRender 361 * @return {Promise} 362 */ 363 Picker.prototype._preRender = function() { 364 var self = this; 365 return self._loadFrameworks().then(function() { 366 if (!self._frameworkId && self._frameworks.length > 0) { 367 self._frameworkId = self._frameworks[0].id; 368 } 369 370 // We could not set a framework ID, that probably means there are no frameworks accessible. 371 if (!self._frameworkId) { 372 self._frameworks = []; 373 return $.when(); 374 } 375 376 return self._loadCompetencies(); 377 }); 378 }; 379 380 /** 381 * Refresh the view. 382 * 383 * @method _refresh 384 * @return {Promise} 385 */ 386 Picker.prototype._refresh = function() { 387 var self = this; 388 return self._render().then(function(html) { 389 self._find('[data-region="competencylinktree"]').replaceWith(html); 390 self._afterRender(); 391 }); 392 }; 393 394 /** 395 * Render the dialogue. 396 * 397 * @method _render 398 * @return {Promise} 399 */ 400 Picker.prototype._render = function() { 401 var self = this; 402 return self._preRender().then(function() { 403 404 if (!self._singleFramework) { 405 $.each(self._frameworks, function(i, framework) { 406 if (framework.id == self._frameworkId) { 407 framework.selected = true; 408 } else { 409 framework.selected = false; 410 } 411 }); 412 } 413 414 var context = { 415 competencies: self._competencies, 416 framework: self._getFramework(self._frameworkId), 417 frameworks: self._frameworks, 418 search: self._searchText, 419 singleFramework: self._singleFramework, 420 }; 421 422 return Templates.render('tool_lp/competency_picker', context); 423 }); 424 }; 425 426 /** 427 * Reset the dialogue properties. 428 * 429 * This does not reset everything, just enough to reset the UI. 430 * 431 * @method _reset 432 */ 433 Picker.prototype._reset = function() { 434 this._competencies = []; 435 this._disallowedCompetencyIDs = []; 436 this._popup = null; 437 this._searchText = ''; 438 this._selectedCompetencies = []; 439 }; 440 441 /** 442 * Set what competencies cannot be picked. 443 * 444 * This needs to be set after reset/close. 445 * 446 * @param {Number[]} ids The IDs. 447 * @method _setDisallowedCompetencyIDs 448 */ 449 Picker.prototype.setDisallowedCompetencyIDs = function(ids) { 450 this._disallowedCompetencyIDs = ids; 451 }; 452 453 /** 454 * Trigger an event. 455 * 456 * @param {String} type The type of event. 457 * @param {Object} data The data to pass to the listeners. 458 * @method _reset 459 */ 460 Picker.prototype._trigger = function(type, data) { 461 this._eventNode.trigger(type, [data]); 462 }; 463 464 return /** @alias module:tool_lp/competencypicker */ Picker; 465 466 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |