2010-03-23 07:38:32 +00:00
/ * *
* 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 ;
2010-09-17 19:59:16 +00:00
} ;
2010-03-23 07:38:32 +00:00
/ * *
* Initialise a new user selector .
2010-09-17 19:59:16 +00:00
*
2010-03-23 07:38:32 +00:00
* @ 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 ,
2012-09-27 19:03:34 +01:00
/** Stores any in-progress remote requests. */
iotransactions : { } ,
2010-03-23 07:38:32 +00:00
/** The last string that we searched for, so we can avoid unnecessary repeat searches. */
lastsearch : lastsearch ,
/ * * W h e t h e r a n y o p t i o n s w h e r e s e l e c t e d l a s t t i m e w e c h e c k e d . U s e d b y
* 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 ) ) ;
2012-08-27 14:55:02 +08:00
this . clearbutton . set ( 'id' , this . name + "_clearbutton" ) ;
2010-03-23 07:38:32 +00:00
this . clearbutton . on ( 'click' , this . handle _clear , this ) ;
2012-08-27 14:55:02 +08:00
this . clearbutton . set ( 'disabled' , ( this . get _search _text ( ) == '' ) ) ;
2010-03-23 07:38:32 +00:00
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 ( ) ;
2011-03-18 15:02:55 +08:00
this . timeoutid = Y . later ( this . querydelay * 1000 , e , function ( obj ) { obj . send _query ( false ) } , this ) ;
2010-03-23 07:38:32 +00:00
// 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 ;
}
2012-09-27 19:03:34 +01:00
// Try to cancel existing transactions.
Y . Object . each ( this . iotransactions , function ( trans ) {
trans . abort ( ) ;
} ) ;
var iotrans = Y . io ( M . cfg . wwwroot + '/user/selector/search.php' , {
2010-03-23 07:38:32 +00:00
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
} ) ;
2012-09-27 19:03:34 +01:00
this . iotransactions [ iotrans . id ] = iotrans ;
2010-03-23 07:38:32 +00:00
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 {
2012-09-27 19:03:34 +01:00
delete this . iotransactions [ requestid ] ;
if ( ! Y . Object . isEmpty ( this . iotransactions ) ) {
// More searches pending. Wait until they are all done.
return ;
}
2010-03-23 07:38:32 +00:00
this . listbox . setStyle ( 'background' , '' ) ;
var data = Y . JSON . parse ( response . responseText ) ;
this . output _options ( data ) ;
} catch ( e ) {
2012-09-27 19:03:34 +01:00
this . handle _failure ( requestid ) ;
2010-03-23 07:38:32 +00:00
}
} ,
/ * *
* Handles what happens when the ajax request fails .
* /
2012-09-27 19:03:34 +01:00
handle _failure : function ( requestid ) {
delete this . iotransactions [ requestid ] ;
if ( ! Y . Object . isEmpty ( this . iotransactions ) ) {
// More searches pending. Wait until they are all done.
return ;
}
2010-03-23 07:38:32 +00:00
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 ;
2012-10-03 18:46:07 +01:00
for ( var key in data . results ) {
var groupdata = data . results [ key ] ;
this . output _group ( groupdata . name , groupdata . users , selectedusers , true ) ;
2010-03-23 07:38:32 +00:00
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 ;
2012-10-03 18:46:07 +01:00
for ( var key in users ) {
var user = users [ key ] ;
var option = Y . Node . create ( '<option value="' + user . id + '">' + user . name + '</option>' ) ;
2010-03-23 07:38:32 +00:00
if ( user . disabled ) {
option . set ( 'disabled' , true ) ;
2012-10-03 18:46:07 +01:00
} else if ( selectedusers === true || selectedusers [ user . id ] ) {
2010-03-23 07:38:32 +00:00
option . set ( 'selected' , true ) ;
2012-10-03 18:46:07 +01:00
delete selectedusers [ user . id ] ;
2010-03-23 07:38:32 +00:00
} else {
option . set ( 'selected' , false ) ;
}
optgroup . append ( option ) ;
2012-03-20 17:41:33 +00:00
if ( user . infobelow ) {
extraoption = Y . Node . create ( '<option disabled="disabled" class="userselector-infobelow"/>' ) ;
extraoption . appendChild ( document . createTextNode ( user . infobelow ) ) ;
optgroup . append ( extraoption ) ;
}
2010-03-23 07:38:32 +00:00
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 ;
}
}
2010-09-17 19:59:16 +00:00
} ;
2010-03-23 07:38:32 +00:00
// 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 ;
2010-09-17 19:59:16 +00:00
} ;
2010-03-23 07:38:32 +00:00
/ * *
* 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' ) ) ;
}
2010-09-17 19:59:16 +00:00
} ;
2010-03-23 07:38:32 +00:00
// Initialise the options tracker
user _selector _options _tracker . init ( ) ;
// Return it just incase it is ever wanted
return user _selector _options _tracker ;
2010-09-17 19:59:16 +00:00
} ;