[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/form/ -> form.js (source)

   1  /**
   2   * This file contains JS functionality required by mforms and is included automatically
   3   * when required.
   4   */
   5  
   6  // Namespace for the form bits and bobs
   7  M.form = M.form || {};
   8  
   9  /**
  10   * Stores a list of the dependencyManager for each form on the page.
  11   */
  12  M.form.dependencyManagers = {};
  13  
  14  /**
  15   * Initialises a manager for a forms dependencies.
  16   * This should happen once per form.
  17   */
  18  M.form.initFormDependencies = function(Y, formid, dependencies) {
  19  
  20      // If the dependencies isn't an array or object we don't want to
  21      // know about it
  22      if (!Y.Lang.isArray(dependencies) && !Y.Lang.isObject(dependencies)) {
  23          return false;
  24      }
  25  
  26      /**
  27       * Fixes an issue with YUI's processing method of form.elements property
  28       * in Internet Explorer.
  29       *     http://yuilibrary.com/projects/yui3/ticket/2528030
  30       */
  31      Y.Node.ATTRS.elements = {
  32          getter: function() {
  33              return Y.all(new Y.Array(this._node.elements, 0, true));
  34          }
  35      };
  36  
  37      // Define the dependency manager if it hasn't already been defined.
  38      M.form.dependencyManager = M.form.dependencyManager || (function(){
  39          var dependencyManager = function(config) {
  40              dependencyManager.superclass.constructor.apply(this, arguments);
  41          };
  42          dependencyManager.prototype = {
  43              _form : null,
  44              _locks : [],
  45              _hides : [],
  46              _dirty : [],
  47              _nameCollections : null,
  48              _fileinputs : null,
  49              initializer : function(config) {
  50                  var i = 0, nodeName;
  51                  this._form = Y.one('#'+formid);
  52                  for (i in dependencies) {
  53                      var elements = this.elementsByName(i);
  54                      if (elements.size() == 0) {
  55                          continue;
  56                      }
  57                      elements.each(function(node){
  58                          nodeName = node.get('nodeName').toUpperCase();
  59                          if (nodeName == 'INPUT') {
  60                              if (node.getAttribute('type').match(/^(button|submit|radio|checkbox)$/)) {
  61                                  node.on('click', this.updateEventDependencies, this);
  62                              } else {
  63                                  node.on('blur', this.updateEventDependencies, this);
  64                              }
  65                              node.on('change', this.updateEventDependencies, this);
  66                          } else if (nodeName == 'SELECT') {
  67                              node.on('change', this.updateEventDependencies, this);
  68                          } else {
  69                              node.on('click', this.updateEventDependencies, this);
  70                              node.on('blur', this.updateEventDependencies, this);
  71                              node.on('change', this.updateEventDependencies, this);
  72                          }
  73                      }, this);
  74                  }
  75                  this._form.get('elements').each(function(input){
  76                      if (input.getAttribute('type')=='reset') {
  77                          input.on('click', function(){
  78                              this._form.reset();
  79                              this.updateAllDependencies();
  80                          }, this);
  81                      }
  82                  }, this);
  83  
  84                  return this.updateAllDependencies();
  85              },
  86              /**
  87               * Initializes the mapping from element name to YUI NodeList
  88               */
  89              initElementsByName : function() {
  90                  var names = [];
  91                  // Collect element names
  92                  for (var i in dependencies) {
  93                      names[i] = new Y.NodeList();
  94                      for (var condition in dependencies[i]) {
  95                          for (var value in dependencies[i][condition]) {
  96                              for (var ei in dependencies[i][condition][value]) {
  97                                  names[dependencies[i][condition][value][ei]] = new Y.NodeList();
  98                              }
  99                          }
 100                      }
 101                  }
 102                  // Locate elements for each name
 103                  this._form.get('elements').each(function(node){
 104                      var name = node.getAttribute('name');
 105                      if (names[name]) {
 106                          names[name].push(node);
 107                      }
 108                  });
 109                  this._nameCollections = names;
 110              },
 111              /**
 112               * Gets all elements in the form by their name and returns
 113               * a YUI NodeList
 114               *
 115               * @param {string} name The form element name.
 116               * @return {Y.NodeList}
 117               */
 118              elementsByName : function(name) {
 119                  if (!this._nameCollections) {
 120                      this.initElementsByName();
 121                  }
 122                  if (!this._nameCollections[name]) {
 123                      return new Y.NodeList();
 124                  }
 125                  return this._nameCollections[name];
 126              },
 127              /**
 128               * Checks the dependencies the form has an makes any changes to the
 129               * form that are required.
 130               *
 131               * Changes are made by functions title _dependency_{dependencytype}
 132               * and more can easily be introduced by defining further functions.
 133               *
 134               * @param {EventFacade | null} e The event, if any.
 135               * @param {string} name The form element name to check dependencies against.
 136               */
 137              checkDependencies : function(e, dependon) {
 138                  var tohide = [],
 139                      tolock = [],
 140                      condition, value, lock, hide,
 141                      checkfunction, result, elements;
 142                  if (!dependencies[dependon]) {
 143                      return true;
 144                  }
 145                  elements = this.elementsByName(dependon);
 146                  for (condition in dependencies[dependon]) {
 147                      for (value in dependencies[dependon][condition]) {
 148                          checkfunction = '_dependency_'+condition;
 149                          if (Y.Lang.isFunction(this[checkfunction])) {
 150                              result = this[checkfunction].apply(this, [elements, value, e]);
 151                          } else {
 152                              result = this._dependency_default(elements, value, e);
 153                          }
 154                          lock = result.lock || false;
 155                          hide = result.hide || false;
 156                          for (var ei in dependencies[dependon][condition][value]) {
 157                              var eltolock = dependencies[dependon][condition][value][ei];
 158                              tohide[eltolock] = tohide[eltolock] || hide;
 159                              tolock[eltolock] = tolock[eltolock] || lock;
 160                          }
 161                      }
 162                  }
 163                  for (var el in tolock) {
 164                      var needsupdate = false;
 165                      if (tolock[el]) {
 166                          this._locks[el] = this._locks[el] || [];
 167                          if (!this._locks[el][dependon]) {
 168                              this._locks[el][dependon] = true;
 169                              needsupdate = true;
 170                          }
 171                      } else if (this._locks[el] && this._locks[el][dependon]) {
 172                          delete this._locks[el][dependon];
 173                          needsupdate = true;
 174                      }
 175                      if (tohide[el]) {
 176                          this._hides[el] = this._hides[el] || [];
 177                          if (!this._hides[el][dependon]) {
 178                              this._hides[el][dependon] = true;
 179                              needsupdate = true;
 180                          }
 181                      } else if (this._hides[el] && this._hides[el][dependon]) {
 182                          delete this._hides[el][dependon];
 183                          needsupdate = true;
 184                      }
 185                      if (needsupdate) {
 186                          this._dirty[el] = true;
 187                      }
 188                  }
 189                  return true;
 190              },
 191              /**
 192               * Update all dependencies in form
 193               */
 194              updateAllDependencies : function() {
 195                  for (var el in dependencies) {
 196                      this.checkDependencies(null, el);
 197                  }
 198                  this.updateForm();
 199              },
 200              /**
 201               * Update dependencies associated with event
 202               *
 203               * @param {Event} e The event.
 204               */
 205              updateEventDependencies : function(e) {
 206                  var el = e.target.getAttribute('name');
 207                  this.checkDependencies(e, el);
 208                  this.updateForm();
 209              },
 210              /**
 211               * Flush pending changes to the form
 212               */
 213              updateForm : function() {
 214                  for (var el in this._dirty) {
 215                      if (this._locks[el]) {
 216                          var locked = !this._isObjectEmpty(this._locks[el]);
 217                          this._disableElement(el, locked);
 218                      }
 219                      if (this._hides[el]) {
 220                          var hidden = !this._isObjectEmpty(this._hides[el]);
 221                          this._hideElement(el, hidden);
 222                      }
 223                  }
 224                  this._dirty = [];
 225              },
 226              /**
 227               * Disables or enables all form elements with the given name
 228               *
 229               * @param {string} name The form element name.
 230               * @param {boolean} disabled True to disable, false to enable.
 231               */
 232              _disableElement : function(name, disabled) {
 233                  var els = this.elementsByName(name);
 234                  var filepicker = this.isFilePicker(name);
 235                  els.each(function(node){
 236                      if (disabled) {
 237                          node.setAttribute('disabled', 'disabled');
 238                      } else {
 239                          node.removeAttribute('disabled');
 240                      }
 241  
 242                      // Extra code to disable filepicker or filemanager form elements
 243                      if (filepicker) {
 244                          var fitem = node.ancestor('.fitem');
 245                          if (fitem) {
 246                              if (disabled){
 247                                  fitem.addClass('disabled');
 248                              } else {
 249                                  fitem.removeClass('disabled');
 250                              }
 251                          }
 252                      }
 253                  })
 254              },
 255              /**
 256               * Hides or shows all form elements with the given name.
 257               *
 258               * @param {string} name The form element name.
 259               * @param {boolean} disabled True to hide, false to show.
 260               */
 261              _hideElement : function(name, hidden) {
 262                  var els = this.elementsByName(name);
 263                  els.each(function(node){
 264                      var e = node.ancestor('.fitem');
 265                      if (e) {
 266                          e.setStyles({
 267                              display : (hidden)?'none':''
 268                          })
 269                      }
 270                  });
 271              },
 272              /**
 273               * Is the form element inside a filepicker or filemanager?
 274               *
 275               * @param {string} el The form element name.
 276               * @return {boolean}
 277               */
 278              isFilePicker : function(el) {
 279                  if (!this._fileinputs) {
 280                      var fileinputs = [];
 281                      var els = this._form.all('.fitem.fitem_ffilepicker input,.fitem.fitem_ffilemanager input');
 282                      els.each(function(node){
 283                          fileinputs[node.getAttribute('name')] = true;
 284                      });
 285                      this._fileinputs = fileinputs;
 286                  }
 287                  return this._fileinputs[el] || false;
 288              },
 289              /**
 290               * Check if the object is empty
 291               *
 292               * @param {object} obj
 293               * @return {boolean}
 294               */
 295              _isObjectEmpty : function(obj) {
 296                  for(var prop in obj) {
 297                      if(obj.hasOwnProperty(prop))
 298                          return false;
 299                  }
 300                  return true;
 301              },
 302              _dependency_notchecked : function(elements, value) {
 303                  var lock = false;
 304                  elements.each(function(){
 305                      if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
 306                          // This is the hidden input that is part of an advcheckbox.
 307                          return;
 308                      }
 309                      if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
 310                          return;
 311                      }
 312                      lock = lock || !Y.Node.getDOMNode(this).checked;
 313                  });
 314                  return {
 315                      lock : lock,
 316                      hide : false
 317                  }
 318              },
 319              _dependency_checked : function(elements, value) {
 320                  var lock = false;
 321                  elements.each(function(){
 322                      if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
 323                          // This is the hidden input that is part of an advcheckbox.
 324                          return;
 325                      }
 326                      if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
 327                          return;
 328                      }
 329                      lock = lock || Y.Node.getDOMNode(this).checked;
 330                  });
 331                  return {
 332                      lock : lock,
 333                      hide : false
 334                  }
 335              },
 336              _dependency_noitemselected : function(elements, value) {
 337                  var lock = false;
 338                  elements.each(function(){
 339                      lock = lock || this.get('selectedIndex') == -1;
 340                  });
 341                  return {
 342                      lock : lock,
 343                      hide : false
 344                  }
 345              },
 346              _dependency_eq : function(elements, value) {
 347                  var lock = false;
 348                  var hidden_val = false;
 349                  var options, v, selected, values;
 350                  elements.each(function(){
 351                      if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
 352                          return;
 353                      } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
 354                          // This is the hidden input that is part of an advcheckbox.
 355                          hidden_val = (this.get('value') == value);
 356                          return;
 357                      } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
 358                          lock = lock || hidden_val;
 359                          return;
 360                      }
 361                      if (this.getAttribute('class').toLowerCase() == 'filepickerhidden') {
 362                          // Check for filepicker status.
 363                          var elementname = this.getAttribute('name');
 364                          if (elementname && M.form_filepicker.instances[elementname].fileadded) {
 365                              lock = false;
 366                          } else {
 367                              lock = true;
 368                          }
 369                      } else if (this.get('nodeName').toUpperCase() === 'SELECT' && this.get('multiple') === true) {
 370                          // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator
 371                          // when multiple values have to be selected at the same time.
 372                          values = value.split('|');
 373                          selected = [];
 374                          options = this.get('options');
 375                          options.each(function() {
 376                              if (this.get('selected')) {
 377                                  selected[selected.length] = this.get('value');
 378                              }
 379                          });
 380                          if (selected.length > 0 && selected.length === values.length) {
 381                              for (var i in selected) {
 382                                  v = selected[i];
 383                                  if (values.indexOf(v) > -1) {
 384                                      lock = true;
 385                                  } else {
 386                                      lock = false;
 387                                      return;
 388                                  }
 389                              }
 390                          } else {
 391                              lock = false;
 392                          }
 393                      } else {
 394                          lock = lock || this.get('value') == value;
 395                      }
 396                  });
 397                  return {
 398                      lock : lock,
 399                      hide : false
 400                  }
 401              },
 402              /**
 403               * Lock the given field if the field value is in the given set of values.
 404               *
 405               * @param elements
 406               * @param values
 407               * @returns {{lock: boolean, hide: boolean}}
 408               * @private
 409               */
 410              _dependency_in : function(elements, values) {
 411                  // A pipe (|) is used as a value separator
 412                  // when multiple values have to be passed on at the same time.
 413                  values = values.split('|');
 414                  var lock = false;
 415                  var hidden_val = false;
 416                  var options, v, selected, value;
 417                  elements.each(function(){
 418                      if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
 419                          return;
 420                      } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
 421                          // This is the hidden input that is part of an advcheckbox.
 422                          hidden_val = (values.indexOf(this.get('value')) > -1);
 423                          return;
 424                      } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
 425                          lock = lock || hidden_val;
 426                          return;
 427                      }
 428                      if (this.getAttribute('class').toLowerCase() == 'filepickerhidden') {
 429                          // Check for filepicker status.
 430                          var elementname = this.getAttribute('name');
 431                          if (elementname && M.form_filepicker.instances[elementname].fileadded) {
 432                              lock = false;
 433                          } else {
 434                              lock = true;
 435                          }
 436                      } else if (this.get('nodeName').toUpperCase() === 'SELECT' && this.get('multiple') === true) {
 437                          // Multiple selects can have one or more value assigned.
 438                          selected = [];
 439                          options = this.get('options');
 440                          options.each(function() {
 441                              if (this.get('selected')) {
 442                                  selected[selected.length] = this.get('value');
 443                              }
 444                          });
 445                          if (selected.length > 0 && selected.length === values.length) {
 446                              for (var i in selected) {
 447                                  v = selected[i];
 448                                  if (values.indexOf(v) > -1) {
 449                                      lock = true;
 450                                  } else {
 451                                      lock = false;
 452                                      return;
 453                                  }
 454                              }
 455                          } else {
 456                              lock = false;
 457                          }
 458                      } else {
 459                          value = this.get('value');
 460                          lock = lock || (values.indexOf(value) > -1);
 461                      }
 462                  });
 463                  return {
 464                      lock : lock,
 465                      hide : false
 466                  }
 467              },
 468              _dependency_hide : function(elements, value) {
 469                  return {
 470                      lock : false,
 471                      hide : true
 472                  }
 473              },
 474              _dependency_default : function(elements, value, ev) {
 475                  var lock = false;
 476                  var hidden_val = false;
 477                  elements.each(function(){
 478                      if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
 479                          return;
 480                      } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
 481                          // This is the hidden input that is part of an advcheckbox.
 482                          hidden_val = (this.get('value') != value);
 483                          return;
 484                      } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
 485                          lock = lock || hidden_val;
 486                          return;
 487                      }
 488                      //check for filepicker status
 489                      if (this.getAttribute('class').toLowerCase() == 'filepickerhidden') {
 490                          var elementname = this.getAttribute('name');
 491                          if (elementname && M.form_filepicker.instances[elementname].fileadded) {
 492                              lock = true;
 493                          } else {
 494                              lock = false;
 495                          }
 496                      } else if (this.get('nodeName').toUpperCase() === 'SELECT' && this.get('multiple') === true) {
 497                          // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator
 498                          // when multiple values have to be selected at the same time.
 499                          values = value.split('|');
 500                          selected = [];
 501                          options = this.get('options');
 502                          options.each(function() {
 503                              if (this.get('selected')) {
 504                                  selected[selected.length] = this.get('value');
 505                              }
 506                          });
 507                          if (selected.length > 0 && selected.length === values.length) {
 508                              for (var i in selected) {
 509                                  v = selected[i];
 510                                  if (values.indexOf(v) > -1) {
 511                                      lock = false;
 512                                  } else {
 513                                      lock = true;
 514                                      return;
 515                                  }
 516                              }
 517                          } else {
 518                              lock = true;
 519                          }
 520                      } else {
 521                          lock = lock || this.get('value') != value;
 522                      }
 523                  });
 524                  return {
 525                      lock : lock,
 526                      hide : false
 527                  }
 528              }
 529          };
 530          Y.extend(dependencyManager, Y.Base, dependencyManager.prototype, {
 531              NAME : 'mform-dependency-manager'
 532          });
 533  
 534          return dependencyManager;
 535      })();
 536  
 537      M.form.dependencyManagers[formid] = new M.form.dependencyManager();
 538      return M.form.dependencyManagers[formid];
 539  };
 540  
 541  /**
 542   * Update the state of a form. You need to call this after, for example, changing
 543   * the state of some of the form input elements in your own code, in order that
 544   * things like the disableIf state of elements can be updated.
 545   */
 546  M.form.updateFormState = function(formid) {
 547      if (formid in M.form.dependencyManagers) {
 548          M.form.dependencyManagers[formid].updateAllDependencies();
 549      }
 550  };


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1