# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: moodle/admin/roles/module.js
--- moodle/admin/roles/module.js Base (1.2)
+++ moodle/admin/roles/module.js Locally Modified (Based On 1.2)
@@ -114,15 +114,17 @@
 
 
 M.core_role.init_add_assign_page = function(Y) {
-    var addselect = user_selector.get('addselect');
-    document.getElementById('add').disabled = addselect.is_selection_empty();
-    addselect.subscribe('selectionchanged', function(isempty) {
-        document.getElementById('add').disabled = isempty;
+    var add = Y.one('#add');
+    var addselect = M.core_user.get_user_selector('addselect');
+    add.set('disabled', addselect.is_selection_empty());
+    addselect.on('user_selector:selectionchanged', function(isempty) {
+        add.set('disabled', isempty);
     });
 
-    var removeselect = user_selector.get('removeselect');
-    document.getElementById('remove').disabled = removeselect.is_selection_empty();
-    removeselect.subscribe('selectionchanged', function(isempty) {
-        document.getElementById('remove').disabled = isempty;
+    var remove = Y.one('#remove');
+    var removeselect = M.core_user.get_user_selector('removeselect');
+    remove.set('disabled', removeselect.is_selection_empty());
+    removeselect.on('user_selector:selectionchanged', function(isempty) {
+        remove.set('disabled', isempty);
     });
 };
Index: moodle/group/clientlib.js
--- moodle/group/clientlib.js Base (1.10)
+++ moodle/group/clientlib.js Locally Modified (Based On 1.10)
@@ -224,17 +224,19 @@
      return(true);
 }
 
-function init_add_remove_members_page() {
-    var addselect = user_selector.get('addselect');
-    document.getElementById('add').disabled = addselect.is_selection_empty();
-    addselect.subscribe('selectionchanged', function(isempty) {
-        document.getElementById('add').disabled = isempty;
+function init_add_remove_members_page(Y) {
+    var add = Y.one('#add');
+    var addselect = M.core_user.get_user_selector('addselect');
+    add.set('disabled', addselect.is_selection_empty());
+    addselect.on('user_selector:selectionchanged', function(isempty) {
+        add.set('disabled', isempty);
     });
 
-    var removeselect = user_selector.get('removeselect');
-    document.getElementById('remove').disabled = removeselect.is_selection_empty();
-    removeselect.subscribe('selectionchanged', function(isempty) {
-        document.getElementById('remove').disabled = isempty;
+    var remove = Y.one('#remove');
+    var removeselect = M.core_user.get_user_selector('removeselect');
+    remove.set('disabled', removeselect.is_selection_empty());
+    removeselect.on('user_selector:selectionchanged', function(isempty) {
+        remove.set('disabled', isempty);
     });
 
     addselect = document.getElementById('addselect');
Index: moodle/user/selector/lib.php
--- moodle/user/selector/lib.php Base (1.29)
+++ moodle/user/selector/lib.php Locally Modified (Based On 1.29)
@@ -1,33 +1,26 @@
 <?php
 
-///////////////////////////////////////////////////////////////////////////
-//                                                                       //
-// NOTICE OF COPYRIGHT                                                   //
-//                                                                       //
-// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
-//          http://moodle.org                                            //
-//                                                                       //
-// Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com     //
-//                                                                       //
-// This program is free software; you can redistribute it and/or modify  //
-// it under the terms of the GNU General Public License as published by  //
-// the Free Software Foundation; either version 2 of the License, or     //
-// (at your option) any later version.                                   //
-//                                                                       //
-// This program is distributed in the hope that it will be useful,       //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of        //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
-// GNU General Public License for more details:                          //
-//                                                                       //
-//          http://www.gnu.org/copyleft/gpl.html                         //
-//                                                                       //
-///////////////////////////////////////////////////////////////////////////
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
  * Code for ajax user selectors.
  *
- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
- * @package userselector
+ * @package   user
+ * @copyright 1999 onwards Martin Dougiamas  http://dougiamas.com
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 /**
@@ -43,34 +36,45 @@
  * specified.
  */
 abstract class user_selector_base {
-    /** The control name (and id) in the HTML. */
+    /** @var string The control name (and id) in the HTML. */
     protected $name;
-    /** Extra fields to search on and return in addition to firstname and lastname. */
+    /** @var array Extra fields to search on and return in addition to firstname and lastname. */
     protected $extrafields;
-    /** Whether the conrol should allow selection of many users, or just one. */
+    /** @var boolean Whether the conrol should allow selection of many users, or just one. */
     protected $multiselect = true;
-    /** The height this control should have, in rows. */
+    /** @var int The height this control should have, in rows. */
     protected $rows = USER_SELECTOR_DEFAULT_ROWS;
-    /** A list of userids that should not be returned by this control. */
+    /** @var array A list of userids that should not be returned by this control. */
     protected $exclude = array();
-    /** A list of the users who are selected. */
+    /** @var array|null A list of the users who are selected. */
     protected $selected = null;
-    /** When the search changes, do we keep previously selected options that do
+    /** @var boolean When the search changes, do we keep previously selected options that do
      * not match the new search term? */
     protected $preserveselected = false;
-    /** If only one user matches the search, should we select them automatically. */
+    /** @var boolean If only one user matches the search, should we select them automatically. */
     protected $autoselectunique = false;
-    /** When searching, do we only match the starts of fields (better performace)
+    /** @var boolean When searching, do we only match the starts of fields (better performace)
      * or do we match occurrences anywhere? */
     protected $searchanywhere = false;
-    // This is used by get selected users,
+    /** @var mixed This is used by get selected users */
     protected $validatinguserids = null;
 
-    // Used to ensure we only output the search options for one user selector on
-    // each page.
+    /**  @var boolean Used to ensure we only output the search options for one user selector on
+     * each page. */
     private static $searchoptionsoutput = false;
 
+    /** @var array JavaScript YUI3 Module definition */
+    protected static $jsmodule = array(
+                'name' => 'user_selector',
+                'fullpath' => '/user/selector/module.js',
+                'requires'  => array('node', 'event-custom', 'datasource', 'json'),
+                'strings' => array(
+                    array('previouslyselectedusers', 'moodle', '%%SEARCHTERM%%'),
+                    array('nomatchingusers', 'moodle', '%%SEARCHTERM%%'),
+                    array('none', 'moodle')
+                ));
 
+
     // Public API ==============================================================
 
     /**
@@ -103,12 +107,6 @@
         $this->preserveselected = $this->initialise_option('userselector_preserveselected', $this->preserveselected);
         $this->autoselectunique = $this->initialise_option('userselector_autoselectunique', $this->autoselectunique);
         $this->searchanywhere = $this->initialise_option('userselector_searchanywhere', $this->searchanywhere);
-
-        // Required JavaScript code.
-        $PAGE->requires->yui2_lib('json');
-        $PAGE->requires->yui2_lib('connection');
-        $PAGE->requires->yui2_lib('datasource');
-        $PAGE->requires->js('/user/selector/script.js');
     }
 
     /**
@@ -225,7 +223,7 @@
             $output .= $this->option_checkbox('searchanywhere', $this->searchanywhere, get_string('userselectorsearchanywhere'));
             $output .= print_collapsible_region_end(true);
 
-            $PAGE->requires->js_function_call('new user_selector_options_tracker');
+            $PAGE->requires->js_init_call('M.core_user.init_user_selector_options_tracker', array(), false, self::$jsmodule);
             user_selector_base::$searchoptionsoutput = true;
         }
         $output .= "</div>\n</div>\n\n";
@@ -638,10 +636,7 @@
         $USER->userselectors[$hash] = $options;
 
         // Initialise the selector.
-        $PAGE->requires->js_function_call('new user_selector', array($this->name, $hash, $this->extrafields,
-                $search, get_string('previouslyselectedusers', '', '%%SEARCHTERM%%'),
-                get_string('nomatchingusers', '', '%%SEARCHTERM%%'), get_string('none'), $OUTPUT->pix_url('i/loading')));
-
+        $PAGE->requires->js_init_call('M.core_user.init_user_selector', array($this->name, $hash, $this->extrafields, $search), false, self::$jsmodule);
         return $output;
     }
 }
Index: moodle/user/selector/module.js
--- moodle/user/selector/module.js No Base Revision
+++ moodle/user/selector/module.js Locally New
@@ -0,0 +1,356 @@
+/**
+ * JavaScript for the user selectors.
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package userselector
+ */
+
+// Define the core_user namespace if it has not already been defined
+M.core_user = M.core_user || {};
+// Define a user selectors array for against the cure_user namespace
+M.core_user.user_selectors = [];
+/**
+ * Retrieves an instantiated user selector or null if there isn't one by the requested name
+ * @param {string} name The name of the selector to retrieve
+ * @return bool
+ */
+M.core_user.get_user_selector = function (name) {
+    return this.user_selectors[name] || null;
+}
+
+/**
+ * Initialise a new user selector.
+ * 
+ * @param {YUI} Y The YUI3 instance
+ * @param {string} name the control name/id.
+ * @param {string} hash the hash that identifies this selector in the user's session.
+ * @param {array} extrafields extra fields we are displaying for each user in addition to fullname.
+ * @param {string} lastsearch The last search that took place
+ */
+M.core_user.init_user_selector = function (Y, name, hash, extrafields, lastsearch) {
+    // Creates a new user_selector object
+    var user_selector = {
+        /** This id/name used for this control in the HTML. */
+        name : name,
+        /** Array of fields to display for each user, in addition to fullname. */
+        extrafields: extrafields,
+        /** Number of seconds to delay before submitting a query request */
+        querydelay : 0.5,
+        /** The input element that contains the search term. */
+        searchfield : Y.one('#'+name + '_searchtext'),
+        /** The clear button. */
+        clearbutton : null,
+        /** The select element that contains the list of users. */
+        listbox : Y.one('#'+name),
+        /** Used to hold the timeout id of the timeout that waits before doing a search. */
+        timeoutid : null,
+        /** The last string that we searched for, so we can avoid unnecessary repeat searches. */
+        lastsearch : lastsearch,
+        /** Whether any options where selected last time we checked. Used by
+         *  handle_selection_change to track when this status changes. */
+        selectionempty : true,
+        /**
+         * Initialises the user selector object
+         * @constructor
+         */
+        init : function() {
+            // Hide the search button and replace it with a label.
+            var searchbutton = Y.one('#'+this.name + '_searchbutton');
+            this.searchfield.insert(Y.Node.create('<label for="'+this.name + '_searchtext">'+searchbutton.get('value')+'</label>'), this.searchfield);
+            searchbutton.remove();
+
+            // Hook up the event handler for when the search text changes.
+            this.searchfield.on('keyup', this.handle_keyup, this);
+
+            // Hook up the event handler for when the selection changes.
+            this.listbox.on('keyup', this.handle_selection_change, this);
+            this.listbox.on('click', this.handle_selection_change, this);
+            this.listbox.on('change', this.handle_selection_change, this);
+
+            // And when the search any substring preference changes. Do an immediate re-search.
+            Y.one('#userselector_searchanywhereid').on('click', this.handle_searchanywhere_change, this);
+
+            // Define our custom event.
+            //this.createEvent('selectionchanged');
+            this.selectionempty = this.is_selection_empty();
+
+            // Replace the Clear submit button with a clone that is not a submit button.
+            var clearbtn = Y.one('#'+this.name + '_clearbutton');
+            this.clearbutton = Y.Node.create('<input type="button" value="'+clearbtn.get('value')+'" />');
+            clearbtn.replace(Y.Node.getDOMNode(this.clearbutton));
+            this.clearbutton.set('id',+this.name+"_clearbutton");
+            this.clearbutton.on('click', this.handle_clear, this);
+
+            this.send_query(false);
+        },
+        /**
+         * Key up hander for the search text box.
+         * @param {Y.Event} e the keyup event.
+         */
+        handle_keyup : function(e) {
+            //  Trigger an ajax search after a delay.
+            this.cancel_timeout();
+            this.timeoutid = setTimeout(function(obj){obj.send_query(false)}, this.querydelay*1000, this);
+
+            // Enable or diable the clear button.
+            this.clearbutton.set('disabled', (this.get_search_text() == ''));
+
+            // If enter was pressed, prevent a form submission from happening.
+            if (e.keyCode == 13) {
+                e.halt();
+            }
+        },
+        /**
+         * Handles when the selection has changed. If the selection has changed from
+         * empty to not-empty, or vice versa, then fire the event handlers.
+         */
+        handle_selection_change : function() {
+            var isselectionempty = this.is_selection_empty();
+            if (isselectionempty !== this.selectionempty) {
+                this.fire('user_selector:selectionchanged', isselectionempty);
+            }
+            this.selectionempty = isselectionempty;
+        },
+        /**
+         * Trigger a re-search when the 'search any substring' option is changed.
+         */
+        handle_searchanywhere_change : function() {
+            if (this.lastsearch != '' && this.get_search_text() != '') {
+                this.send_query(true);
+            }
+        },
+        /**
+         * Click handler for the clear button..
+         */
+        handle_clear : function() {
+            this.searchfield.set('value', '');
+            this.clearbutton.set('disabled',true);
+            this.send_query(false);
+        },
+        /**
+         * Fires off the ajax search request.
+         */
+        send_query : function(forceresearch) {
+            // Cancel any pending timeout.
+            this.cancel_timeout();
+
+            var value = this.get_search_text();
+            this.searchfield.set('class', '');
+            if (this.lastsearch == value && !forceresearch) {
+                return;
+            }
+
+            Y.io(M.cfg.wwwroot + '/user/selector/search.php', {
+                method: 'POST',
+                data: 'selectorid='+hash+'&sesskey='+M.cfg.sesskey+'&search='+value + '&userselector_searchanywhere=' + this.get_option('searchanywhere'),
+                on: {
+                    success:this.handle_response,
+                    failure:this.handle_failure
+                },
+                context:this
+            });
+
+            this.lastsearch = value;
+            this.listbox.setStyle('background','url(' + M.util.image_url('i/loading', 'moodle') + ') no-repeat center center');
+        },
+        /**
+         * Handle what happens when we get some data back from the search.
+         * @param {int} requestid not used.
+         * @param {object} response the list of users that was returned.
+         */
+        handle_response : function(requestid, response) {
+            try {
+                this.listbox.setStyle('background','');
+                var data = Y.JSON.parse(response.responseText);
+                this.output_options(data);
+            } catch (e) {
+                this.handle_failure();
+            }
+        },
+        /**
+         * Handles what happens when the ajax request fails.
+         */
+        handle_failure : function() {
+            this.listbox.setStyle('background','');
+            this.searchfield.addClass('error');
+
+            // If we are in developer debug mode, output a link to help debug the failure.
+            if (M.cfg.developerdebug) {
+                this.searchfield.insert(Y.Node.create('<a href="'+M.cfg.wwwroot +'/user/selector/search.php?selectorid='+hash+'&sesskey='+M.cfg.sesskey+'&search='+this.get_search_text()+'&debug=1">Ajax call failed. Click here to try the search call directly.</a>'));
+            }
+        },
+        /**
+         * This method should do the same sort of thing as the PHP method
+         * user_selector_base::output_options.
+         * @param {object} data the list of users to populate the list box with.
+         */
+        output_options : function(data) {
+            // Clear out the existing options, keeping any ones that are already selected.
+            var selectedusers = {};
+            this.listbox.all('optgroup').each(function(optgroup){
+                optgroup.all('option').each(function(option){
+                    if (option.get('selected')) {
+                        selectedusers[option.get('value')] = {
+                            id : option.get('value'),
+                            name : option.get('innerText') || option.get('textContent'),
+                            disabled: option.get('disabled')
+                        }
+                    }
+                    option.remove();
+                }, this);
+                optgroup.remove();
+            }, this);
+
+            // Output each optgroup.
+            var count = 0;
+            for (var groupname in data.results) {
+                this.output_group(groupname, data.results[groupname], selectedusers, true);
+                count++;
+            }
+            if (!count) {
+                var searchstr = (this.lastsearch != '')?this.insert_search_into_str(M.str.moodle.nomatchingusers, this.lastsearch):M.str.moodle.none;
+                this.output_group(searchstr, {}, selectedusers, true)
+            }
+
+            // If there were previously selected users who do not match the search, show them too.
+            if (this.get_option('preserveselected') && selectedusers) {
+                this.output_group(this.insert_search_into_str(M.str.moodle.previouslyselectedusers, this.lastsearch), selectedusers, true, false);
+            }
+            this.handle_selection_change();
+        },
+        /**
+         * This method should do the same sort of thing as the PHP method
+         * user_selector_base::output_optgroup.
+         *
+         * @param {string} groupname the label for this optgroup.v
+         * @param {object} users the users to put in this optgroup.
+         * @param {boolean|object} selectedusers if true, select the users in this group.
+         * @param {boolean} processsingle
+         */
+        output_group : function(groupname, users, selectedusers, processsingle) {
+            var optgroup = Y.Node.create('<optgroup></optgroup>');
+            var count = 0;
+            for (var userid in users) {
+                var user = users[userid];
+                var option = Y.Node.create('<option value="'+userid+'">'+user.name+'</option>');
+                if (user.disabled) {
+                    option.set('disabled', true);
+                } else if (selectedusers===true || selectedusers[userid]) {
+                    option.set('selected', true);
+                } else {
+                    option.set('selected', false);
+                }
+                optgroup.append(option);
+                count++;
+            }
+
+            if (count > 0) {
+                optgroup.set('label', groupname+' ('+count+')');
+                if (processsingle && count===1 && this.get_option('autoselectunique') && option.get('disabled')) {
+                    option.set('selected', true);
+                }
+            } else {
+                optgroup.append(Y.Node.create('<option disabled="disabled">\u00A0</option>'));
+            }
+            this.listbox.append(optgroup);
+        },
+        /**
+         * Replace
+         * @param {string} str
+         * @param {string} search The search term
+         * @return string
+         */
+        insert_search_into_str : function(str, search) {
+            return str.replace("%%SEARCHTERM%%", search);
+        },
+        /**
+         * Gets the search text
+         * @return String the value to search for, with leading and trailing whitespace trimmed.
+         */
+        get_search_text : function() {
+            return this.searchfield.get('value').toString().replace(/^ +| +$/, '');
+        },
+        /**
+         * Returns true if the selection is empty (nothing is selected)
+         * @return Boolean check all the options and return whether any are selected.
+         */
+        is_selection_empty : function() {
+            var selection = false;
+            this.listbox.all('option').each(function(){
+                if (this.get('selected')) {
+                    selection = true;
+                }
+            });
+            return !(selection);
+        },
+        /**
+         * Cancel the search delay timeout, if there is one.
+         */
+        cancel_timeout : function() {
+            if (this.timeoutid) {
+                clearTimeout(this.timeoutid);
+                this.timeoutid = null;
+            }
+        },
+        /**
+         * @param {string} name The name of the option to retrieve
+         * @return the value of one of the option checkboxes.
+         */
+        get_option : function(name) {
+            var checkbox = Y.one('#userselector_' + name + 'id');
+            if (checkbox) {
+                return (checkbox.get('checked'));
+            } else {
+                return false;
+            }
+        }
+    }
+    // Augment the user selector with the EventTarget class so that we can use
+    // custom events
+    Y.augment(user_selector, Y.EventTarget, null, null, {});
+    // Initialise the user selector
+    user_selector.init();
+    // Store the user selector so that it can be retrieved
+    this.user_selectors[name] = user_selector;
+    // Return the user selector
+    return user_selector;
+}
+
+/**
+ * Initialise a class that updates the user's preferences when they change one of
+ * the options checkboxes.
+ * @constructor
+ * @param {YUI} Y
+ * @return Tracker object
+ */
+M.core_user.init_user_selector_options_tracker = function(Y) {
+    // Create a user selector options tracker
+    var user_selector_options_tracker = {
+        /**
+         * Initlises the option tracker and gets everything going.
+         * @constructor
+         */
+        init : function() {
+            var settings = [
+                'userselector_preserveselected',
+                'userselector_autoselectunique',
+                'userselector_searchanywhere'
+            ];
+            for (var s in settings) {
+                var setting = settings[s];
+                Y.one('#'+setting+'id').on('click', this.set_user_preference, this, setting);
+            }
+        },
+        /**
+         * Sets a user preference for the options tracker
+         * @param {Y.Event|null} e
+         * @param {string} name The name of the preference to set
+         */
+        set_user_preference : function(e, name) {
+            M.util.set_user_preference(name, Y.one('#'+name+'id').get('checked'));
+        }
+    }
+    // Initialise the options tracker
+    user_selector_options_tracker.init();
+    // Return it just incase it is ever wanted
+    return user_selector_options_tracker;
+}
Index: moodle/user/selector/script.js
--- moodle/user/selector/script.js Base (1.21)
+++ moodle/user/selector/script.js Locally Deleted
@@ -1,520 +0,0 @@
-// JavaScript for the user selectors.
-// This is somewhat inspired by the autocomplete component in YUI.
-// license: http://www.gnu.org/copyleft/gpl.html GNU Public License
-// package: userselector
-
-/**
- * Initialise a new user selector.
- * @constructor
- * @param String name the control name/id.
- * @param String hash the hash that identifies this selector in the user's session.
- * @param Array extrafields extra fields we are displaying for each user in addition to fullname.
- * @param String label used for the optgroup of users who are selected but who do not match the current search.
- */
-function user_selector(name, hash, extrafields, lastsearch, strprevselected, strnomatchingusers, strnone, iconloading) {
-    this.name = name;
-    this.extrafields = extrafields;
-    this.strprevselected = strprevselected;
-    this.strnomatchingusers = strnomatchingusers;
-    this.strnone = strnone;
-    this.iconloading = iconloading;
-    this.searchurl = M.cfg.wwwroot + '/user/selector/search.php?selectorid=' +
-            hash + '&sesskey=' + M.cfg.sesskey + '&search='
-
-    // Set up the data source.
-    this.datasource = new YAHOO.util.XHRDataSource(this.searchurl);
-    this.datasource.connXhrMode = 'cancelStaleRequests';
-    this.datasource.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
-    this.datasource.responseSchema = {resultsList: 'results'};
-
-    // Find some key HTML elements.
-    this.searchfield = document.getElementById(this.name + '_searchtext');
-    this.listbox = document.getElementById(this.name);
-
-    // Hide the search button and replace it with a label.
-    var searchbutton = document.getElementById(this.name + '_searchbutton');
-    var label = document.createElement('label');
-    label.htmlFor = this.name + '_searchtext';
-    label.appendChild(document.createTextNode(searchbutton.value));
-    this.searchfield.parentNode.insertBefore(label, this.searchfield);
-    searchbutton.parentNode.removeChild(searchbutton);
-
-    // Hook up the event handler for when the search text changes.
-    var oself = this;
-    YAHOO.util.Event.addListener(this.searchfield, "keyup", function(e) { oself.handle_keyup(e) });
-    this.lastsearch = lastsearch;
-
-    // Define our custom event.
-    this.createEvent('selectionchanged');
-    this.selectionempty = this.is_selection_empty();
-    user_selector.allselectors[name] = this;
-
-    // Hook up the event handler for when the selection changes.
-    YAHOO.util.Event.addListener(this.listbox, "keyup", function(e) { oself.handle_selection_change() });
-    YAHOO.util.Event.addListener(this.listbox, "click", function(e) { oself.handle_selection_change() });
-    YAHOO.util.Event.addListener(this.listbox, "change", function(e) { oself.handle_selection_change() });
-
-    // And when the search any substring preference changes. Do an immediate re-search.
-    YAHOO.util.Event.addListener('userselector_searchanywhereid', 'click', function(e) { oself.handle_searchanywhere_change() });
-
-    // Replace the Clear submit button with a clone that is not a submit button.
-    var oldclearbutton = document.getElementById(this.name + '_clearbutton');
-    this.clearbutton = document.createElement('input');
-    this.clearbutton.type = 'button';
-    this.clearbutton.value = oldclearbutton.value;
-    this.clearbutton.id = oldclearbutton.id;
-    oldclearbutton.id = '';
-    oldclearbutton.parentNode.insertBefore(this.clearbutton, oldclearbutton);
-    oldclearbutton.parentNode.removeChild(oldclearbutton);
-
-    // Enable or diable the clear button.
-    this.clearbutton.disabled = this.get_search_text() == '';
-
-    // Hook up the event handler for the clear button.
-    YAHOO.util.Event.addListener(this.clearbutton, "click", function(e) { oself.handle_clear() });
-
-    // If the contents of the search box is different from the search that was
-    // done on the server, reload the options. (This happens, for example,
-    // in Firefox. Go to the role assign page - the search box will be blank.
-    // Type something in the search box. Then click reload. The text will stay
-    // in the search box, but the server does not know about the new search term,
-    // so without this line, the list would contain all the users.
-    this.send_query(false);
-}
-
-/**
- * A list of all the user_selectors on this page. Use by user_selector.get.
- *
- * @property allselectors
- * @type Object
- */
-user_selector.allselectors = {};
-
-/**
- * @param String name the name of a user selector on this page.
- * @return user_selector the named user selector.
- */
-user_selector.get = function(name) {
-    return user_selector.allselectors[name];
-}
-
-// Fields set be the constructor ===============================================
-
-/**
- * This id/name used for this control in the HTML.
- * @property name
- * @type String
- */
-user_selector.prototype.name = null;
-
-/**
- * Array of fields to display for each user, in addition to fullname.
- * @property extrafields
- * @type Array
- */
-user_selector.prototype.extrafields = [];
-
-/**
- * Name of the previously selected users group.
- *
- * @property strprevselected
- * @type String
- */
-user_selector.prototype.strprevselected = '';
-
-/**
- * Name of the no matching users group.
- *
- * @property strnomatchingusers
- * @type String
- */
-user_selector.prototype.strnomatchingusers = '';
-
-/**
- * Name of the no matching users group when empty.
- *
- * @property strnone
- * @type String
- */
-user_selector.prototype.strnone = '';
-
-/**
- * URL of the loading icon.
- *
- * @property iconloading
- * @type String
- */
-user_selector.prototype.iconloading = '';
-
-// Fields that configure the control's behaviour ===============================
-
-/**
- * Number of seconds to delay before submitting a query request. If a query
- * request is received before a previous one has completed its delay, the
- * previous request is cancelled and the new request is set to the delay.
- *
- * @property querydelay
- * @type Number
- * @default 0.2
- */
-user_selector.prototype.querydelay = 0.5;
-
-// Internal fields =============================================================
-
-/**
- * The URL for the datasource.
- * @property searchurl
- * @type String
- */
-user_selector.prototype.searchurl = null;
-
-/**
- * The datasource used to fetch lists of users from Moodle.
- * @property datasource
- * @type YAHOO.widget.DataSource
- */
-user_selector.prototype.datasource = null;
-
-/**
- * The input element that contains the search term.
- *
- * @property searchfield
- * @type HTMLInputElement
- */
-user_selector.prototype.searchfield = null;
-
-/**
- * The clear button.
- *
- * @property clearbutton
- * @type HTMLInputElement
- */
-user_selector.prototype.clearbutton = null;
-
-/**
- * The select element that contains the list of users.
- *
- * @property listbox
- * @type HTMLSelectElement
- */
-user_selector.prototype.listbox = null;
-
-/**
- * Used to hold the timeout id of the timeout that waits before doing a search.
- *
- * @property timeoutid
- * @type Number
- */
-user_selector.prototype.timeoutid = null;
-
-/**
- * The last string that we searched for, so we can avoid unnecessary repeat searches.
- *
- * @property lastsearch
- * @type String
- */
-user_selector.prototype.lastsearch = null;
-
-/**
- * Used while the list of options is being refreshed, to track options that were
- * selected before, so they are not lost if they do not appear in the search results.
- *
- * @property selected
- * @type Object
- */
-user_selector.prototype.selected = null;
-
-/**
- * Used while the list of options is being refreshed to determine if there is only
- * one user matching the search, so they can be automatically selected.
- *
- * @property onlyoption
- * @type HTMLOptionElement
- **/
-user_selector.prototype.onlyoption = null;
-
-/**
- * Whether any options where selected last time we checked. Used by
- * handle_selection_change to track when this status changes.
- *
- * @property selectionempty
- * @type Boolean
- */
-user_selector.prototype.selectionempty = true;
-
-// Methods for handing various events ==========================================
-
-/**
- * Key up hander for the search text box.
- * @param object e the keyup event.
- */
-user_selector.prototype.handle_keyup = function(e) {
-    //  Trigger an ajax search after a delay.
-    this.cancel_timeout();
-    var oself = this;
-    this.timeoutid = setTimeout(function() { oself.send_query(false) }, this.querydelay * 1000);
-
-    // Enable or diable the clear button.
-    this.clearbutton.disabled = this.get_search_text() == '';
-
-    // If enter was pressed, prevent a form submission from happening.
-    var keyCode = e.keyCode ? e.keyCode : e.which;
-    if (keyCode == 13) {
-        YAHOO.util.Event.stopEvent(e);
-    }
-}
-
-/**
- * Cancel the search delay timeout, if there is one.
- */
-user_selector.prototype.cancel_timeout = function() {
-    if (this.timeoutid) {
-        clearTimeout(this.timeoutid);
-        this.timeoutid = null;
-    }
-}
-
-/**
- * Click handler for the clear button..
- */
-user_selector.prototype.handle_clear = function() {
-    this.searchfield.value = '';
-    this.clearbutton.disabled = true;
-    this.send_query(false);
-}
-
-/**
- * Trigger a re-search when the 'search any substring' option is changed.
- */
-user_selector.prototype.handle_searchanywhere_change = function() {
-    if (this.lastsearch != '' && this.get_search_text() != '') {
-        this.send_query(true);
-    }
-}
-
-/**
- * @return String the value to search for, with leading and trailing whitespace trimmed.
- */
-user_selector.prototype.get_search_text = function() {
-    return this.searchfield.value.replace(/^ +| +$/, '');
-}
-
-/**
- * @return the value of one of the option checkboxes.<b>
- */
-user_selector.prototype.get_option = function(name) {
-    var checkbox = document.getElementById('userselector_' + name + 'id');
-    if (checkbox) {
-        return checkbox.checked;
-    } else {
-        return false;
-    }
-}
-
-/**
- * Fires off the ajax search request.
- */
-user_selector.prototype.send_query = function(forceresearch) {
-    // Cancel any pending timeout.
-    this.cancel_timeout();
-
-    var value = this.get_search_text();
-    this.searchfield.className = '';
-    if (this.lastsearch == value && !forceresearch) {
-        return;
-    }
-    this.datasource.sendRequest(value + '&userselector_searchanywhere=' + this.get_option('searchanywhere'), {
-        success: this.handle_response,
-        failure: this.handle_failure,
-        scope: this
-    });
-    this.lastsearch = value;
-    this.listbox.style.background = 'url(' + this.iconloading + ') no-repeat center center';
-}
-
-/**
- * Handle what happens when we get some data back from the search.
- * @param Object request not used.
- * @param Object data the list of users that was returned.
- */
-user_selector.prototype.handle_response = function(request, data) {
-    this.listbox.style.background = '';
-    this.output_options(data);
-}
-
-/**
- * Handles what happens when the ajax request fails.
- */
-user_selector.prototype.handle_failure = function() {
-    this.listbox.style.background = '';
-    this.searchfield.className = 'error';
-
-    // If we are in developer debug mode, output a link to help debug the failure.
-    if (M.cfg.developerdebug) {
-        var link = document.createElement('a');
-        link.href = this.searchurl + this.get_search_text() + '&debug=1';
-        link.appendChild(document.createTextNode('Ajax call failed. Click here to try the search call directly.'))
-        this.searchfield.parentNode.appendChild(link);
-    }
-}
-
-/**
- * @return Boolean check all the options and return whether any are selected.
- */
-user_selector.prototype.is_selection_empty = function() {
-    var options = this.listbox.getElementsByTagName('option');
-    for (i = 0; i < options.length; i++) {
-        var option = options[i];
-        if (option.selected) {
-            return false;
-        }
-    }
-    return true;
-}
-
-/**
- * Handles when the selection has changed. If the selection has changed from
- * empty to not-empty, or vice versa, then fire the event handlers.
- */
-user_selector.prototype.handle_selection_change = function() {
-    var isselectionempty = this.is_selection_empty();
-    if (isselectionempty !== this.selectionempty) {
-        this.fireEvent('selectionchanged', isselectionempty);
-    }
-    this.selectionempty = isselectionempty;
-}
-
-// Methods for refreshing the list of displayed options ========================
-user_selector.prototype.insert_search_into_str = function(string, search) {
-    return string.replace("%%SEARCHTERM%%", search);
-}
-
-/**
- * This method should do the same sort of thing as the PHP method
- * user_selector_base::output_options.
- * @param object data the list of users to populate the list box with.
- */
-user_selector.prototype.output_options = function(data) {
-    // Clear out the existing options, keeping any ones that are already selected.
-    this.selected = {};
-    var groups = this.listbox.getElementsByTagName('optgroup');
-    var preserveselected = this.get_option('preserveselected');
-    while (groups.length > 0) {
-        var optgroup = groups[0]; // Remeber that groups is a live array as we remove optgroups from the select, it updates.
-        var options = optgroup.getElementsByTagName('option');
-        while (options.length > 0) {
-            var option = options[0];
-            if (option.selected) {
-                var optiontext = option.innerText || option.textContent
-                this.selected[option.value] = { id: option.value, name: optiontext, disabled: option.disabled };
-            }
-            optgroup.removeChild(option);
-        }
-        this.listbox.removeChild(optgroup);
-    }
-
-    var results = data.results[0];
-
-    // Output each optgroup.
-    this.onlyoption = null;
-    var nogroups = true;
-    for (groupname in results) {
-        this.output_group(groupname, results[groupname], false);
-        nogroups = false;
-    }
-
-    if (nogroups) {
-        if (this.lastsearch != '') {
-            this.output_group(this.insert_search_into_str(this.strnomatchingusers, this.lastsearch), {}, false)
-        } else {
-            this.output_group(this.strnone, {}, false)
-        }
-    }
-
-    // If there was only one option matching the search results, select it.
-    if (this.get_option('autoselectunique') && this.onlyoption && !this.onlyoption.disabled) {
-        this.onlyoption.selected = true;
-        if (!this.listbox.multiple) {
-            this.selected = {};
-        }
-        this.handle_selection_change();
-    }
-    this.onlyoption = null;
-
-    // If there were previously selected users who do not match the search, show them too.
-    var areprevselected = false;
-    for (user in this.selected) {
-        areprevselected = true;
-        break;
-    }
-    if (preserveselected && areprevselected) {
-        this.output_group(this.insert_search_into_str(this.strprevselected, this.lastsearch), this.selected, true);
-    }
-    this.selected = null;
-}
-
-/**
- * This method should do the same sort of thing as the PHP method
- * user_selector_base::output_optgroup.
- *
- * @param String groupname the label for this optgroup.v
- * @param Object users the users to put in this optgroup.
- * @param Boolean select if true, select the users in this group.
- */
-user_selector.prototype.output_group = function(groupname, users, select) {
-    var optgroup = document.createElement('optgroup');
-    optgroup.label = groupname;
-    var count = 0;
-    for (var userid in users) {
-        var user = users[userid];
-        var option = document.createElement('option');
-        option.value = user.id;
-        option.appendChild(document.createTextNode(user.name));
-        if (user.disabled) {
-            option.disabled = 'disabled';
-        } else if (select || this.selected[user.id]) {
-            option.selected = 'selected';
-        }
-        delete this.selected[user.id];
-        optgroup.appendChild(option);
-        if (this.onlyoption === null) {
-            this.onlyoption = option;
-        } else {
-            this.onlyoption = false;
-        }
-        count++;
-    }
-    if (count > 0) {
-        optgroup.label += ' (' + count + ')';
-    } else {
-        var option = document.createElement('option');
-        option.disabled = 'disabled';
-        option.appendChild(document.createTextNode('\u00A0'));
-        optgroup.appendChild(option);
-    }
-    this.listbox.appendChild(optgroup);
-}
-
-// Say that we want to be a source of custom events.
-YAHOO.lang.augmentProto(user_selector, YAHOO.util.EventProvider);
-
-/**
- * Initialise a class that updates the user's preferences when they change one of
- * the options checkboxes.
- * @constructor
- */
-function user_selector_options_tracker() {
-    var oself = this;
-
-    // Add event listeners.
-    YAHOO.util.Event.addListener('userselector_preserveselectedid', 'click',
-            function(e) { oself.handle_option_change('userselector_preserveselected') });
-    YAHOO.util.Event.addListener('userselector_autoselectuniqueid', 'click',
-            function(e) { oself.handle_option_change('userselector_autoselectunique') });
-    YAHOO.util.Event.addListener('userselector_searchanywhereid', 'click',
-            function(e) { oself.handle_option_change('userselector_searchanywhere') });
-}
-
-user_selector_options_tracker.prototype.handle_option_change = function(option) {
-    M.util.set_user_preference(option, document.getElementById(option + 'id').checked);
-}
