[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /** 2 * JavaScript for the user selectors. 3 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4 * @package userselector 5 */ 6 7 // Define the core_user namespace if it has not already been defined 8 M.core_user = M.core_user || {}; 9 // Define a user selectors array for against the cure_user namespace 10 M.core_user.user_selectors = []; 11 /** 12 * Retrieves an instantiated user selector or null if there isn't one by the requested name 13 * @param {string} name The name of the selector to retrieve 14 * @return bool 15 */ 16 M.core_user.get_user_selector = function (name) { 17 return this.user_selectors[name] || null; 18 }; 19 20 /** 21 * Initialise a new user selector. 22 * 23 * @param {YUI} Y The YUI3 instance 24 * @param {string} name the control name/id. 25 * @param {string} hash the hash that identifies this selector in the user's session. 26 * @param {array} extrafields extra fields we are displaying for each user in addition to fullname. 27 * @param {string} lastsearch The last search that took place 28 */ 29 M.core_user.init_user_selector = function (Y, name, hash, extrafields, lastsearch) { 30 // Creates a new user_selector object 31 var user_selector = { 32 /** This id/name used for this control in the HTML. */ 33 name : name, 34 /** Array of fields to display for each user, in addition to fullname. */ 35 extrafields: extrafields, 36 /** Number of seconds to delay before submitting a query request */ 37 querydelay : 0.5, 38 /** The input element that contains the search term. */ 39 searchfield : Y.one('#' + name + '_searchtext'), 40 /** The clear button. */ 41 clearbutton : null, 42 /** The select element that contains the list of users. */ 43 listbox : Y.one('#' + name), 44 /** Used to hold the timeout id of the timeout that waits before doing a search. */ 45 timeoutid : null, 46 /** Stores any in-progress remote requests. */ 47 iotransactions : {}, 48 /** The last string that we searched for, so we can avoid unnecessary repeat searches. */ 49 lastsearch : lastsearch, 50 /** Whether any options where selected last time we checked. Used by 51 * handle_selection_change to track when this status changes. */ 52 selectionempty : true, 53 /** 54 * Initialises the user selector object 55 * @constructor 56 */ 57 init : function() { 58 // Hide the search button and replace it with a label. 59 var searchbutton = Y.one('#' + this.name + '_searchbutton'); 60 this.searchfield.insert(Y.Node.create('<label for="' + this.name + '_searchtext">' + searchbutton.get('value') + '</label>'), this.searchfield); 61 searchbutton.remove(); 62 63 // Hook up the event handler for when the search text changes. 64 this.searchfield.on('keyup', this.handle_keyup, this); 65 66 // Hook up the event handler for when the selection changes. 67 this.listbox.on('keyup', this.handle_selection_change, this); 68 this.listbox.on('click', this.handle_selection_change, this); 69 this.listbox.on('change', this.handle_selection_change, this); 70 71 // And when the search any substring preference changes. Do an immediate re-search. 72 Y.one('#userselector_searchanywhereid').on('click', this.handle_searchanywhere_change, this); 73 74 // Define our custom event. 75 //this.createEvent('selectionchanged'); 76 this.selectionempty = this.is_selection_empty(); 77 78 // Replace the Clear submit button with a clone that is not a submit button. 79 var clearbtn = Y.one('#' + this.name + '_clearbutton'); 80 this.clearbutton = Y.Node.create('<input type="button" value="' + clearbtn.get('value') + '" />'); 81 clearbtn.replace(Y.Node.getDOMNode(this.clearbutton)); 82 this.clearbutton.set('id', this.name + "_clearbutton"); 83 this.clearbutton.on('click', this.handle_clear, this); 84 this.clearbutton.set('disabled', (this.get_search_text() == '')); 85 86 this.send_query(false); 87 }, 88 /** 89 * Key up hander for the search text box. 90 * @param {Y.Event} e the keyup event. 91 */ 92 handle_keyup : function(e) { 93 // Trigger an ajax search after a delay. 94 this.cancel_timeout(); 95 this.timeoutid = Y.later(this.querydelay * 1000, e, function(obj){obj.send_query(false)}, this); 96 97 // Enable or diable the clear button. 98 this.clearbutton.set('disabled', (this.get_search_text() == '')); 99 100 // If enter was pressed, prevent a form submission from happening. 101 if (e.keyCode == 13) { 102 e.halt(); 103 } 104 }, 105 /** 106 * Handles when the selection has changed. If the selection has changed from 107 * empty to not-empty, or vice versa, then fire the event handlers. 108 */ 109 handle_selection_change : function() { 110 var isselectionempty = this.is_selection_empty(); 111 if (isselectionempty !== this.selectionempty) { 112 this.fire('user_selector:selectionchanged', isselectionempty); 113 } 114 this.selectionempty = isselectionempty; 115 }, 116 /** 117 * Trigger a re-search when the 'search any substring' option is changed. 118 */ 119 handle_searchanywhere_change : function() { 120 if (this.lastsearch != '' && this.get_search_text() != '') { 121 this.send_query(true); 122 } 123 }, 124 /** 125 * Click handler for the clear button.. 126 */ 127 handle_clear : function() { 128 this.searchfield.set('value', ''); 129 this.clearbutton.set('disabled',true); 130 this.send_query(false); 131 }, 132 /** 133 * Fires off the ajax search request. 134 */ 135 send_query : function(forceresearch) { 136 // Cancel any pending timeout. 137 this.cancel_timeout(); 138 139 var value = this.get_search_text(); 140 this.searchfield.set('class', ''); 141 if (this.lastsearch == value && !forceresearch) { 142 return; 143 } 144 145 // Try to cancel existing transactions. 146 Y.Object.each(this.iotransactions, function(trans) { 147 trans.abort(); 148 }); 149 150 var iotrans = Y.io(M.cfg.wwwroot + '/user/selector/search.php', { 151 method: 'POST', 152 data: 'selectorid=' + hash + '&sesskey=' + M.cfg.sesskey + '&search=' + value + '&userselector_searchanywhere=' + this.get_option('searchanywhere'), 153 on: { 154 complete: this.handle_response 155 }, 156 context:this 157 }); 158 this.iotransactions[iotrans.id] = iotrans; 159 160 this.lastsearch = value; 161 this.listbox.setStyle('background','url(' + M.util.image_url('i/loading', 'moodle') + ') no-repeat center center'); 162 }, 163 /** 164 * Handle what happens when we get some data back from the search. 165 * @param {int} requestid not used. 166 * @param {object} response the list of users that was returned. 167 */ 168 handle_response : function(requestid, response) { 169 try { 170 delete this.iotransactions[requestid]; 171 if (!Y.Object.isEmpty(this.iotransactions)) { 172 // More searches pending. Wait until they are all done. 173 return; 174 } 175 this.listbox.setStyle('background',''); 176 var data = Y.JSON.parse(response.responseText); 177 if (data.error) { 178 this.searchfield.addClass('error'); 179 return new M.core.ajaxException(data); 180 } 181 this.output_options(data); 182 } catch (e) { 183 this.listbox.setStyle('background',''); 184 this.searchfield.addClass('error'); 185 return new M.core.exception(e); 186 } 187 }, 188 /** 189 * This method should do the same sort of thing as the PHP method 190 * user_selector_base::output_options. 191 * @param {object} data the list of users to populate the list box with. 192 */ 193 output_options : function(data) { 194 // Clear out the existing options, keeping any ones that are already selected. 195 var selectedusers = {}; 196 this.listbox.all('optgroup').each(function(optgroup){ 197 optgroup.all('option').each(function(option){ 198 if (option.get('selected')) { 199 selectedusers[option.get('value')] = { 200 id : option.get('value'), 201 name : option.get('innerText') || option.get('textContent'), 202 disabled: option.get('disabled') 203 } 204 } 205 option.remove(); 206 }, this); 207 optgroup.remove(); 208 }, this); 209 210 // Output each optgroup. 211 var count = 0; 212 for (var key in data.results) { 213 var groupdata = data.results[key]; 214 this.output_group(groupdata.name, groupdata.users, selectedusers, true); 215 count ++; 216 } 217 if (!count) { 218 var searchstr = (this.lastsearch != '') ? this.insert_search_into_str(M.util.get_string('nomatchingusers', 'moodle'), this.lastsearch) : M.util.get_string('none', 'moodle'); 219 this.output_group(searchstr, {}, selectedusers, true) 220 } 221 222 // If there were previously selected users who do not match the search, show them too. 223 if (this.get_option('preserveselected') && selectedusers) { 224 this.output_group(this.insert_search_into_str(M.util.get_string('previouslyselectedusers', 'moodle'), this.lastsearch), selectedusers, true, false); 225 } 226 this.handle_selection_change(); 227 }, 228 /** 229 * This method should do the same sort of thing as the PHP method 230 * user_selector_base::output_optgroup. 231 * 232 * @param {string} groupname the label for this optgroup.v 233 * @param {object} users the users to put in this optgroup. 234 * @param {boolean|object} selectedusers if true, select the users in this group. 235 * @param {boolean} processsingle 236 */ 237 output_group : function(groupname, users, selectedusers, processsingle) { 238 var optgroup = Y.Node.create('<optgroup></optgroup>'); 239 var count = 0; 240 for (var key in users) { 241 var user = users[key]; 242 var option = Y.Node.create('<option value="' + user.id + '">' + user.name + '</option>'); 243 if (user.disabled) { 244 option.set('disabled', true); 245 } else if (selectedusers === true || selectedusers[user.id]) { 246 option.set('selected', true); 247 delete selectedusers[user.id]; 248 } else { 249 option.set('selected', false); 250 } 251 optgroup.append(option); 252 if (user.infobelow) { 253 extraoption = Y.Node.create('<option disabled="disabled" class="userselector-infobelow"/>'); 254 extraoption.appendChild(document.createTextNode(user.infobelow)); 255 optgroup.append(extraoption); 256 } 257 count ++; 258 } 259 260 if (count > 0) { 261 optgroup.set('label', groupname + ' (' + count + ')'); 262 if (processsingle && count === 1 && this.get_option('autoselectunique') && option.get('disabled') == false) { 263 option.set('selected', true); 264 } 265 } else { 266 optgroup.set('label', groupname); 267 optgroup.append(Y.Node.create('<option disabled="disabled">\u00A0</option>')); 268 } 269 this.listbox.append(optgroup); 270 }, 271 /** 272 * Replace 273 * @param {string} str 274 * @param {string} search The search term 275 * @return string 276 */ 277 insert_search_into_str : function(str, search) { 278 return str.replace("%%SEARCHTERM%%", search); 279 }, 280 /** 281 * Gets the search text 282 * @return String the value to search for, with leading and trailing whitespace trimmed. 283 */ 284 get_search_text : function() { 285 return this.searchfield.get('value').toString().replace(/^ +| +$/, ''); 286 }, 287 /** 288 * Returns true if the selection is empty (nothing is selected) 289 * @return Boolean check all the options and return whether any are selected. 290 */ 291 is_selection_empty : function() { 292 var selection = false; 293 this.listbox.all('option').each(function(){ 294 if (this.get('selected')) { 295 selection = true; 296 } 297 }); 298 return !(selection); 299 }, 300 /** 301 * Cancel the search delay timeout, if there is one. 302 */ 303 cancel_timeout : function() { 304 if (this.timeoutid) { 305 clearTimeout(this.timeoutid); 306 this.timeoutid = null; 307 } 308 }, 309 /** 310 * @param {string} name The name of the option to retrieve 311 * @return the value of one of the option checkboxes. 312 */ 313 get_option : function(name) { 314 var checkbox = Y.one('#userselector_' + name + 'id'); 315 if (checkbox) { 316 return (checkbox.get('checked')); 317 } else { 318 return false; 319 } 320 } 321 }; 322 // Augment the user selector with the EventTarget class so that we can use 323 // custom events 324 Y.augment(user_selector, Y.EventTarget, null, null, {}); 325 // Initialise the user selector 326 user_selector.init(); 327 // Store the user selector so that it can be retrieved 328 this.user_selectors[name] = user_selector; 329 // Return the user selector 330 return user_selector; 331 }; 332 333 /** 334 * Initialise a class that updates the user's preferences when they change one of 335 * the options checkboxes. 336 * @constructor 337 * @param {YUI} Y 338 * @return Tracker object 339 */ 340 M.core_user.init_user_selector_options_tracker = function(Y) { 341 // Create a user selector options tracker 342 var user_selector_options_tracker = { 343 /** 344 * Initlises the option tracker and gets everything going. 345 * @constructor 346 */ 347 init : function() { 348 var settings = [ 349 'userselector_preserveselected', 350 'userselector_autoselectunique', 351 'userselector_searchanywhere' 352 ]; 353 for (var s in settings) { 354 var setting = settings[s]; 355 Y.one('#' + setting + 'id').on('click', this.set_user_preference, this, setting); 356 } 357 }, 358 /** 359 * Sets a user preference for the options tracker 360 * @param {Y.Event|null} e 361 * @param {string} name The name of the preference to set 362 */ 363 set_user_preference : function(e, name) { 364 M.util.set_user_preference(name, Y.one('#' + name + 'id').get('checked')); 365 } 366 }; 367 // Initialise the options tracker 368 user_selector_options_tracker.init(); 369 // Return it just incase it is ever wanted 370 return user_selector_options_tracker; 371 };
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |