diff --git a/lib/form/form.js b/lib/form/form.js
new file mode 100644
index 00000000000..6136ea3980e
--- /dev/null
+++ b/lib/form/form.js
@@ -0,0 +1,288 @@
+/**
+ * This file contains JS functionality required by mforms and is included automatically
+ * when required.
+ */
+
+// Namespace for the form bits and bobs
+M.form = M.form || {};
+
+/**
+ * Initialises the show advanced functionality and events.
+ * This should only ever happen ONCE per page.
+ *
+ * @param {YUI} Y
+ * @param {object} config
+ */
+M.form.initShowAdvanced = function(Y, config) {
+ if (M.form.showAdvanced) {
+ return M.form.showAdvanced;
+ }
+ var showAdvanced = function(config) {
+ showAdvanced.superclass.constructor.apply(this, arguments);
+ }
+ showAdvanced.prototype = {
+ _advButtons : [],
+ _advAreas : [],
+ _stateInput : null,
+ initializer : function() {
+ this._advAreas = Y.all('form .advanced');
+ this._advButtons = Y.all('.showadvancedbtn');
+ if (this._advButtons.size() > 0) {
+ this._advButtons.item(0).get('form').get('elements').each(function(n){
+ if (this._stateInput == null && n.getAttribute('name') == 'mform_showadvanced_last') {
+ this._stateInput = n;
+ }
+ }, this);
+ this._advButtons.on('click', this.switchState, this);
+ }
+ },
+ /**
+ * Toggles between showing advanced items and hiding them.
+ * Should be fired by an event.
+ */
+ switchState : function(e) {
+ e.preventDefault();
+ if (this._stateInput.get('value')=='1') {
+ this._stateInput.set('value', '0');
+ this._advButtons.setAttribute('value', M.str.form.showadvanced);
+ this._advAreas.addClass('hide');
+ } else {
+ this._stateInput.set('value', '1');
+ this._advButtons.setAttribute('value', M.str.form.hideadvanced);
+ this._advAreas.removeClass('hide');
+ }
+ }
+ }
+ // Extend it with the YUI widget fw.
+ Y.extend(showAdvanced, Y.Base, showAdvanced.prototype, {
+ NAME : 'mform-showAdvanced'
+ })
+ M.form.showAdvanced = new showAdvanced(config);
+ return M.form.showAdvanced;
+}
+
+/**
+ * Initialises a manager for a forms dependencies.
+ * This should happen once per form.
+ */
+M.form.initFormDependencies = function(Y, formid, dependencies) {
+
+ // If the dependencies isn't an array or object we don't want to
+ // know about it
+ if (!Y.Lang.isArray(dependencies) && !Y.Lang.isObject(dependencies)) {
+ return false;
+ }
+
+ /**
+ * Fixes an issue with YUI's processing method of form.elements property
+ * in Internet Explorer.
+ * http://yuilibrary.com/projects/yui3/ticket/2528030
+ */
+ Y.Node.ATTRS.elements = {
+ getter: function() {
+ return Y.all(new Y.Array(this._node.elements, 0, true));
+ }
+ };
+
+ // Define the dependency manager if it hasn't already been defined.
+ M.form.dependencyManager = M.form.dependencyManager || (function(){
+ var dependencyManager = function(config) {
+ dependencyManager.superclass.constructor.apply(this, arguments);
+ }
+ dependencyManager.prototype = {
+ _form : null,
+ _depElements : [],
+ _nameCollections : [],
+ initializer : function(config) {
+ var i = 0, nodeName;
+ this._form = Y.one('#'+formid);
+ for (i in dependencies) {
+ this._depElements[i] = this.elementsByName(i);
+ if (this._depElements[i].size() == 0) {
+ continue;
+ }
+ this._depElements[i].each(function(node){
+ nodeName = node.get('nodeName').toUpperCase();
+ if (nodeName == 'INPUT') {
+ if (node.getAttribute('type').match(/^(button|submit|radio|checkbox)$/)) {
+ node.on('click', this.checkDependencies, this);
+ } else {
+ node.on('blur', this.checkDependencies, this);
+ }
+ node.on('change', this.checkDependencies, this);
+ } else if (nodeName == 'SELECT') {
+ node.on('change', this.checkDependencies, this);
+ } else {
+ node.on('click', this.checkDependencies, this);
+ node.on('blur', this.checkDependencies, this);
+ node.on('change', this.checkDependencies, this);
+ }
+ }, this);
+ }
+ this._form.all('reset').each(function(reset){
+ this._form.reset();
+ reset.on('click', this.checkDependencies, this);
+ }, this);
+
+ return this.checkDependencies(null);
+ },
+ /**
+ * Gets all elements in the form by thier name and returns
+ * a YUI NodeList
+ * @return Y.NodeList
+ */
+ elementsByName : function(name) {
+ if (!this._nameCollections[name]) {
+ var elements = [];
+ this._form.get('elements').each(function(){
+ if (this.getAttribute('name') == name) {
+ elements.push(this);
+ }
+ })
+ this._nameCollections[name] = new Y.NodeList(elements);
+ }
+ return this._nameCollections[name];
+ },
+ /**
+ * Checks the dependencies the form has an makes any changes to the
+ * form that are required.
+ *
+ * Changes are made by functions title _dependency_{dependencytype}
+ * and more can easily be introduced by defining further functions.
+ */
+ checkDependencies : function(e) {
+ var tolock = [],
+ tohide = [],
+ dependon, condition, value,
+ lock, hide, checkfunction, result;
+ for (dependon in dependencies) {
+ if (this._depElements[dependon].size() == 0) {
+ continue;
+ }
+ for (condition in dependencies[dependon]) {
+ for (value in dependencies[dependon][condition]) {
+ lock = false;
+ hide = false;
+ checkfunction = '_dependency_'+condition;
+ if (Y.Lang.isFunction(this[checkfunction])) {
+ result = this[checkfunction].apply(this, [this._depElements[dependon], value]);
+ } else {
+ result = this._dependency_default(this._depElements[dependon], value);
+ }
+ lock = result.lock || false;
+ hide = result.hide || false;
+ for (var ei in dependencies[dependon][condition][value]) {
+ var eltolock = dependencies[dependon][condition][value][ei];
+ if (hide) {
+ tohide[eltolock] = true;
+ }
+ if (tolock[eltolock] != null) {
+ tolock[eltolock] = lock || tolock[eltolock];
+ } else {
+ tolock[eltolock] = lock;
+ }
+ }
+ }
+ }
+ }
+ for (var el in tolock) {
+ this._disableElement(el, tolock[el]);
+ if (tohide.propertyIsEnumerable(el)) {
+ this._hideElement(el, tohide[el]);
+ }
+ }
+ return true;
+ },
+ /**
+ * Disabled all form elements with the given name
+ */
+ _disableElement : function(name, disabled) {
+ var els = this.elementsByName(name);
+ els.each(function(){
+ if (disabled) {
+ this.setAttribute('disabled', 'disabled');
+ } else {
+ this.removeAttribute('disabled');
+ }
+ })
+ },
+ /**
+ * Hides all elements with the given name.
+ */
+ _hideElement : function(name, hidden) {
+ var els = this.elementsByName(name);
+ els.each(function(){
+ var e = els.ancestor('.fitem');
+ if (e) {
+ e.setStyles({
+ display : (hidden)?'none':''
+ })
+ }
+ });
+ },
+ _dependency_notchecked : function(elements, value) {
+ var lock = false;
+ elements.each(function(){
+ lock = lock || !this.get('checked');
+ });
+ return {
+ lock : lock,
+ hide : false
+ }
+ },
+ _dependency_checked : function(elements, value) {
+ var lock = false;
+ elements.each(function(){
+ lock = lock || this.get('checked');
+ });
+ return {
+ lock : lock,
+ hide : false
+ }
+ },
+ _dependency_noitemselected : function(elements, value) {
+ var lock = false;
+ elements.each(function(){
+ lock = lock || this.get('selectedIndex') == -1;
+ });
+ return {
+ lock : lock,
+ hide : false
+ }
+ },
+ _dependency_eq : function(elements, value) {
+ var lock = false;
+ elements.each(function(){
+ lock = lock || this.get('value') == value;
+ });
+ return {
+ lock : lock,
+ hide : false
+ }
+ },
+ _dependency_hide : function(elements, value) {
+ return {
+ lock : false,
+ hide : true
+ }
+ },
+ _dependency_default : function(elements, value) {
+ var lock = false;
+ elements.each(function(){
+ lock = lock || this.get('value') != value;
+ });
+ return {
+ lock : lock,
+ hide : false
+ }
+ }
+ }
+ Y.extend(dependencyManager, Y.Base, dependencyManager.prototype, {
+ NAME : 'mform-dependency-manager'
+ });
+
+ return dependencyManager;
+ })();
+
+ return new M.form.dependencyManager();
+}
\ No newline at end of file
diff --git a/lib/form/yui/dateselector/dateselector.js b/lib/form/yui/dateselector/dateselector.js
index 478a6e51d06..77e63f82d77 100644
--- a/lib/form/yui/dateselector/dateselector.js
+++ b/lib/form/yui/dateselector/dateselector.js
@@ -100,7 +100,7 @@ YUI.add('moodle-form-dateselector', function(Y) {
} else if (node.get('name').match(/\[day]/)) {
this.dayselect = node;
} else {
- node.on('focus', this.cancel_any_timeout, this);
+ node.on('focus', M.form.dateselector.cancel_any_timeout, M.form.dateselector);
node.on('blur', this.blur_event, this);
return;
}
diff --git a/lib/formslib.php b/lib/formslib.php
index 49cc4de187d..13774938f2f 100644
--- a/lib/formslib.php
+++ b/lib/formslib.php
@@ -1075,7 +1075,7 @@ EOS;
global $PAGE;
if (is_string($element)) {
$element = $this->_form->getElement($element);
-}
+ }
if (is_object($element)) {
$element->_generateId();
$elementid = $element->getAttribute('id');
@@ -1091,6 +1091,23 @@ EOS;
}
}
}
+
+ /**
+ * Returns a JS module definition for the mforms JS
+ * @return array
+ */
+ public static function get_js_module() {
+ global $CFG;
+ return array(
+ 'name' => 'mform',
+ 'fullpath' => '/lib/form/form.js',
+ 'requires' => array('base', 'node'),
+ 'strings' => array(
+ array('showadvanced', 'form'),
+ array('hideadvanced', 'form')
+ )
+ );
+ }
}
/**
@@ -1803,22 +1820,14 @@ function validate_' . $this->_formName . '(frm) {
}
}
- /**
- * @return string
- */
- function getLockOptionEndScript(){
-
- $iname = $this->getAttribute('id').'items';
- $js = ''."\n";
- return $js;
+ return array($this->getAttribute('id'), $result);
}
/**
@@ -2238,16 +2243,20 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
}
/**
+ * @global moodle_page $PAGE
* @param object $form Passed by reference
*/
function finishForm(&$form){
+ global $PAGE;
if ($form->isFrozen()){
$this->_hiddenHtml = '';
}
parent::finishForm($form);
- if ((!$form->isFrozen()) && ('' != ($script = $form->getLockOptionEndScript()))) {
- // add a lockoptions script
- $this->_html = $this->_html . "\n" . $script;
+ if (!$form->isFrozen()) {
+ $args = $form->getLockOptionObject();
+ if (count($args[1]) > 0) {
+ $PAGE->requires->js_init_call('M.form.initFormDependencies', $args, false, moodleform::get_js_module());
+ }
}
}
/**
@@ -2256,17 +2265,10 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
* @param object $header An HTML_QuickForm_header element being visited
* @access public
* @return void
+ * @global moodle_page $PAGE
*/
function renderHeader(&$header) {
global $PAGE;
- static $advformcount;
-
- // This ensures that if 2(+) advanced buttons are used
- // that all show/hide buttons appear in the correct place
- // Because of now using $PAGE->requires->js_function_call
- if ($advformcount==null) {
- $advformcount = 1;
- }
$name = $header->getName();
@@ -2282,30 +2284,17 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
if (isset($this->_advancedElements[$name])){
$header_html =str_replace('{advancedimg}', $this->_advancedHTML, $header_html);
+ $elementName='mform_showadvanced';
+ if ($this->_showAdvanced==0){
+ $buttonlabel = get_string('showadvanced', 'form');
+ } else {
+ $buttonlabel = get_string('hideadvanced', 'form');
+ }
+ $button = '';
+ $PAGE->requires->js_init_call('M.form.initShowAdvanced', array(), false, moodleform::get_js_module());
+ $header_html = str_replace('{button}', $button, $header_html);
} else {
$header_html =str_replace('{advancedimg}', '', $header_html);
- }
- $elementName='mform_showadvanced';
- if ($this->_showAdvanced==0){
- $buttonlabel = get_string('showadvanced', 'form');
- } else {
- $buttonlabel = get_string('hideadvanced', 'form');
- }
-
- if (isset($this->_advancedElements[$name])){
- $PAGE->requires->yui2_lib('event');
- // this is tricky - the first submit button on form is "clicked" if user presses enter
- // we do not want to "submit" using advanced button if javascript active
- $button_nojs = '';
-
- $buttonlabel = addslashes_js($buttonlabel);
- $PAGE->requires->string_for_js('showadvanced', 'form');
- $PAGE->requires->string_for_js('hideadvanced', 'form');
- $PAGE->requires->js_function_call('showAdvancedInit', Array($elementName.(string)$advformcount, $elementName, $buttonlabel));
-
- $advformcount++;
- $header_html = str_replace('{button}', $button_nojs, $header_html);
- } else {
$header_html = str_replace('{button}', '', $header_html);
}
diff --git a/lib/javascript-static.js b/lib/javascript-static.js
index 74ed2aa8b7f..c086771f08a 100644
--- a/lib/javascript-static.js
+++ b/lib/javascript-static.js
@@ -675,223 +675,6 @@ function checknone() {
}
}
-function lockoptions(formid, master, subitems) {
- // Subitems is an array of names of sub items.
- // Optionally, each item in subitems may have a
- // companion hidden item in the form with the
- // same name but prefixed by "h".
- var form = document.forms[formid];
-
- if (eval("form."+master+".checked")) {
- for (i=0; i