[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/admin/tool/lp/amd/src/ -> competencypicker.js (source)

   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  });


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1