[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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 };
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |