MDL-40006 searchableselector does not work in IE or Safari.
authorTim Hunt <T.J.Hunt@open.ac.uk>
Sat, 6 Sep 2014 21:27:11 +0000 (22:27 +0100)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Fri, 19 Sep 2014 20:59:34 +0000 (21:59 +0100)
This fix is a horrible hack, but one I have used for years in tool_editrolesbycap
https://github.com/moodleou/moodle-tool_editrolesbycap/blob/master/yui/capabilityformfield/capabilityformfield.js
and it seems to be reliable.

As an added bonus, I removed the use of YUI2 here.

Also, I fixed the related button in quiz to be GET not POST.

lib/form/searchableselector.js
mod/quiz/overrides.php

index dbd3f51..13632b2 100644 (file)
@@ -25,12 +25,22 @@ selector = {
     select: null,
     input: null,
     button: null,
+    goodbrowser: false,
+    alloptions: [],
 
     filter_init: function(strsearch, selectinputid) {
+        selector.goodbrowser = !(' ' + document.body.className + ' ').match(/ ie | safari /);
+
         // selector.id = selectinputid
         selector.select = document.getElementById(selectinputid);
         selector.button = document.getElementById('settingssubmit');
 
+        // Copy all selector options into a plain array. selector.select.options
+        // is linked live to the document, which causes problems in IE and Safari.
+        for (var i = 0; i < selector.select.options.length; i++) {
+            selector.alloptions[i] = selector.select.options[i];
+        }
+
         // Create a div to hold the search UI.
         var div = document.createElement('div');
         div.id = 'searchui';
@@ -50,37 +60,60 @@ selector = {
         div.appendChild(label);
         div.appendChild(input);
         selector.select.parentNode.insertBefore(div, selector.select);
-        YUI().use('yui2-event', function(Y) {
-            Y.YUI2.util.Event.addListener(input, 'keyup', selector.filter_change);
-        });
+        input.addEventListener('keyup', selector.filter_change);
     },
 
     filter_change: function() {
         var searchtext = selector.input.value.toLowerCase();
-        var options = selector.select.options;
-        var matchingoption = -1;
-        for (var i = 0; i < options.length; i++) {
-            var optiontext = options[i].text.toLowerCase();
-            if (optiontext.indexOf(searchtext) >= 0) { //the option is matching the search text
-                options[i].disabled = false; //the option must be visible
-                options[i].style.display = 'block';
-                if (matchingoption == -1) { //we found at least one
-                    matchingoption = i;
-                }
+        var found = false;
+        for (var i = 0; i < selector.alloptions.length; i++) {
+            var option = selector.alloptions[i];
+            if (option.text.toLowerCase().indexOf(searchtext) >= 0) {
+                // The option is matching the search text.
+                selector.set_visible(option, true);
+                found = true;
             } else {
-                options[i].disabled = true;
-                options[i].selected = false;
-                options[i].style.display = 'none';
+                selector.set_visible(option, false);
             }
         }
 
-        if (matchingoption == -1) { //the search didn't find any matching, color the search text in red
-            selector.input.className = "error";
-        } else {
+        if (found) {
+            // No error.
             selector.input.className = "";
+        } else {
+            // The search didn't find any matching, color the search text in red.
+            selector.input.className = "error";
         }
+    },
 
+    set_visible: function(element, visible) {
+        if (selector.goodbrowser) {
+            if (visible) {
+                element.style.display = 'block';
+            } else {
+                element.style.display = 'none';
+                element.selected = false;
+            }
+        } else {
+            // This is a deeply evil hack to make the filtering work in IE.
+            // IE ignores display: none; on select options, but wrapping the
+            // option in a span does seem to hide the option.
+            // Thanks http://work.arounds.org/issue/96/option-elements-do-not-hide-in-IE/
+            if (visible) {
+                if (element.parentNode.tagName.toLowerCase() === 'span') {
+                    element.parentNode.parentNode.replaceChild(element, element.parentNode); // New, old.
+                }
+                element.enabled = true;
+            } else {
+                if (element.parentNode.tagName.toLowerCase() !== 'span') {
+                    var span = document.createElement('span');
+                    element.parentNode.replaceChild(span, element); // New, old.
+                    span.appendChild(element);
+                    span.style.display = 'none';
+                }
+                element.enabled = false;
+                element.selected = false;
+            }
+        }
     }
-
 };
-
index 4b904f7..7814b10 100644 (file)
@@ -272,7 +272,7 @@ if ($groupmode) {
     }
     echo $OUTPUT->single_button($overrideediturl->out(true,
             array('action' => 'adduser', 'cmid' => $cm->id)),
-            get_string('addnewuseroverride', 'quiz'), 'post', $options);
+            get_string('addnewuseroverride', 'quiz'), 'get', $options);
 }
 echo html_writer::end_tag('div');
 echo html_writer::end_tag('div');