MDL-25998 fix default manual enrol role
[moodle.git] / lib / javascript-static.js
blob184ef84978c8c63fbdd7209375858875a23c572c
1 // Miscellaneous core Javascript functions for Moodle
2 // Global M object is initilised in inline javascript
4 /**
5  * Add module to list of available modules that can be laoded from YUI.
6  * @param {Array} modules
7  */
8 M.yui.add_module = function(modules) {
9     for (var modname in modules) {
10         M.yui.loader.modules[modname] = modules[modname];
11     }
13 /**
14  * The gallery version to use when loading YUI modules from the gallery.
15  * Will be changed every time when using local galleries.
16  */
17 M.yui.galleryversion = '2010.04.21-21-51';
19 /**
20  * Various utility functions
21  */
22 M.util = M.util || {};
24 /**
25  * Language strings - initialised from page footer.
26  */
27 M.str = M.str || {};
29 /**
30  * Returns url for images.
31  * @param {String} imagename
32  * @param {String} component
33  * @return {String}
34  */
35 M.util.image_url = function(imagename, component) {
36     var url = M.cfg.wwwroot + '/theme/image.php?theme=' + M.cfg.theme + '&image=' + imagename;
38     if (M.cfg.themerev > 0) {
39         url = url + '&rev=' + M.cfg.themerev;
40     }
42     if (component && component != '' && component != 'moodle' && component != 'core') {
43         url = url + '&component=' + component;
44     }
46     return url;
49 M.util.create_UFO_object = function (eid, FO) {
50     UFO.create(FO, eid);
53 M.util.in_array = function(item, array){
54     for( var i = 0; i<array.length; i++){
55         if(item==array[i]){
56             return true;
57         }
58     }
59     return false;
62 /**
63  * Init a collapsible region, see print_collapsible_region in weblib.php
64  * @param {YUI} Y YUI3 instance with all libraries loaded
65  * @param {String} id the HTML id for the div.
66  * @param {String} userpref the user preference that records the state of this box. false if none.
67  * @param {String} strtooltip
68  */
69 M.util.init_collapsible_region = function(Y, id, userpref, strtooltip) {
70     Y.use('anim', function(Y) {
71         new M.util.CollapsibleRegion(Y, id, userpref, strtooltip);
72     });
75 /**
76  * Object to handle a collapsible region : instantiate and forget styled object
77  *
78  * @class
79  * @constructor
80  * @param {YUI} Y YUI3 instance with all libraries loaded
81  * @param {String} id The HTML id for the div.
82  * @param {String} userpref The user preference that records the state of this box. false if none.
83  * @param {String} strtooltip
84  */
85 M.util.CollapsibleRegion = function(Y, id, userpref, strtooltip) {
86     // Record the pref name
87     this.userpref = userpref;
89     // Find the divs in the document.
90     this.div = Y.one('#'+id);
92     // Get the caption for the collapsible region
93     var caption = this.div.one('#'+id + '_caption');
94     caption.setAttribute('title', strtooltip);
96     // Create a link
97     var a = Y.Node.create('<a href="#"></a>');
98     // Create a local scoped lamba function to move nodes to a new link
99     var movenode = function(node){
100         node.remove();
101         a.append(node);
102     };
103     // Apply the lamba function on each of the captions child nodes
104     caption.get('children').each(movenode, this);
105     caption.append(a);
107     // Get the height of the div at this point before we shrink it if required
108     var height = this.div.get('offsetHeight');
109     if (this.div.hasClass('collapsed')) {
110         // Add the correct image and record the YUI node created in the process
111         this.icon = Y.Node.create('<img src="'+M.util.image_url('t/collapsed', 'moodle')+'" alt="" />');
112         // Shrink the div as it is collapsed by default
113         this.div.setStyle('height', caption.get('offsetHeight')+'px');
114     } else {
115         // Add the correct image and record the YUI node created in the process
116         this.icon = Y.Node.create('<img src="'+M.util.image_url('t/expanded', 'moodle')+'" alt="" />');
117     }
118     a.append(this.icon);
120     // Create the animation.
121     var animation = new Y.Anim({
122         node: this.div,
123         duration: 0.3,
124         easing: Y.Easing.easeBoth,
125         to: {height:caption.get('offsetHeight')},
126         from: {height:height}
127     });
129     // Handler for the animation finishing.
130     animation.on('end', function() {
131         this.div.toggleClass('collapsed');
132         if (this.div.hasClass('collapsed')) {
133             this.icon.set('src', M.util.image_url('t/collapsed', 'moodle'));
134         } else {
135             this.icon.set('src', M.util.image_url('t/expanded', 'moodle'));
136         }
137     }, this);
139     // Hook up the event handler.
140     a.on('click', function(e, animation) {
141         e.preventDefault();
142         // Animate to the appropriate size.
143         if (animation.get('running')) {
144             animation.stop();
145         }
146         animation.set('reverse', this.div.hasClass('collapsed'));
147         // Update the user preference.
148         if (this.userpref) {
149             M.util.set_user_preference(this.userpref, !this.div.hasClass('collapsed'));
150         }
151         animation.run();
152     }, this, animation);
156  * The user preference that stores the state of this box.
157  * @property userpref
158  * @type String
159  */
160 M.util.CollapsibleRegion.prototype.userpref = null;
163  * The key divs that make up this
164  * @property div
165  * @type Y.Node
166  */
167 M.util.CollapsibleRegion.prototype.div = null;
170  * The key divs that make up this
171  * @property icon
172  * @type Y.Node
173  */
174 M.util.CollapsibleRegion.prototype.icon = null;
177  * Makes a best effort to connect back to Moodle to update a user preference,
178  * however, there is no mechanism for finding out if the update succeeded.
180  * Before you can use this function in your JavsScript, you must have called
181  * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that
182  * the udpate is allowed, and how to safely clean and submitted values.
184  * @param String name the name of the setting to udpate.
185  * @param String the value to set it to.
186  */
187 M.util.set_user_preference = function(name, value) {
188     YUI(M.yui.loader).use('io', function(Y) {
189         var url = M.cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' +
190                 M.cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value);
192         // If we are a developer, ensure that failures are reported.
193         var cfg = {
194                 method: 'get',
195                 on: {}
196             };
197         if (M.cfg.developerdebug) {
198             cfg.on.failure = function(id, o, args) {
199                 alert("Error updating user preference '" + name + "' using ajax. Clicking this link will repeat the Ajax call that failed so you can see the error: ");
200             }
201         }
203         // Make the request.
204         Y.io(url, cfg);
205     });
209  * Prints a confirmation dialog in the style of DOM.confirm().
210  * @param object event A YUI DOM event or null if launched manually
211  * @param string message The message to show in the dialog
212  * @param string url The URL to forward to if YES is clicked. Disabled if fn is given
213  * @param function fn A JS function to run if YES is clicked.
214  */
215 M.util.show_confirm_dialog = function(e, args) {
216     var target = e.target;
217     if (e.preventDefault) {
218         e.preventDefault();
219     }
221     YUI(M.yui.loader).use('yui2-container', 'yui2-event', function(Y) {
222         var simpledialog = new YAHOO.widget.SimpleDialog('confirmdialog',
223             {width: '300px',
224               fixedcenter: true,
225               modal: true,
226               visible: false,
227               draggable: false
228             }
229         );
231         simpledialog.setHeader(M.str.admin.confirmation);
232         simpledialog.setBody(args.message);
233         simpledialog.cfg.setProperty('icon', YAHOO.widget.SimpleDialog.ICON_WARN);
235         var handle_cancel = function() {
236             simpledialog.hide();
237         };
239         var handle_yes = function() {
240             simpledialog.hide();
242             if (args.callback) {
243                 // args comes from PHP, so callback will be a string, needs to be evaluated by JS
244                 var callback = null;
245                 if (Y.Lang.isFunction(args.callback)) {
246                     callback = args.callback;
247                 } else {
248                     callback = eval('('+args.callback+')');
249                 }
251                 if (Y.Lang.isObject(args.scope)) {
252                     var sc = args.scope;
253                 } else {
254                     var sc = e.target;
255                 }
257                 if (args.callbackargs) {
258                     callback.apply(sc, args.callbackargs);
259                 } else {
260                     callback.apply(sc);
261                 }
262                 return;
263             }
265             var targetancestor = null,
266                 targetform = null;
268             if (target.test('a')) {
269                 window.location = target.get('href');
270             } else if ((targetancestor = target.ancestor('a')) !== null) {
271                 window.location = targetancestor.get('href');
272             } else if (target.test('input')) {
273                 targetform = target.ancestor('form');
274                 if (targetform && targetform.submit) {
275                     targetform.submit();
276                 }
277             } else if (M.cfg.developerdebug) {
278                 alert("Element of type " + target.get('tagName') + " is not supported by the M.util.show_confirm_dialog function. Use A or INPUT");
279             }
280         };
282         var buttons = [ {text: M.str.moodle.cancel, handler: handle_cancel, isDefault: true},
283                         {text: M.str.moodle.yes, handler: handle_yes} ];
285         simpledialog.cfg.queueProperty('buttons', buttons);
287         simpledialog.render(document.body);
288         simpledialog.show();
289     });
292 /** Useful for full embedding of various stuff */
293 M.util.init_maximised_embed = function(Y, id) {
294     var obj = Y.one('#'+id);
295     if (!obj) {
296         return;
297     }
300     var get_htmlelement_size = function(el, prop) {
301         if (Y.Lang.isString(el)) {
302             el = Y.one('#' + el);
303         }
304         var val = el.getStyle(prop);
305         if (val == 'auto') {
306             val = el.getComputedStyle(prop);
307         }
308         return parseInt(val);
309     };
311     var resize_object = function() {
312         obj.setStyle('width', '0px');
313         obj.setStyle('height', '0px');
314         var newwidth = get_htmlelement_size('maincontent', 'width') - 15;
316         if (newwidth > 600) {
317             obj.setStyle('width', newwidth  + 'px');
318         } else {
319             obj.setStyle('width', '600px');
320         }
322         var headerheight = get_htmlelement_size('page-header', 'height');
323         var footerheight = get_htmlelement_size('page-footer', 'height');
324         var newheight = parseInt(YAHOO.util.Dom.getViewportHeight()) - footerheight - headerheight - 20;
325         if (newheight < 400) {
326             newheight = 400;
327         }
328         obj.setStyle('height', newheight+'px');
329     };
331     resize_object();
332     // fix layout if window resized too
333     window.onresize = function() {
334         resize_object();
335     };
339  * Attach handler to single_select
340  */
341 M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
342     Y.use('event-key', function() {
343         var select = Y.one('#'+selectid);
344         if (select) {
345             // Try to get the form by id
346             var form = Y.one('#'+formid) || (function(){
347                 // Hmmm the form's id may have been overriden by an internal input
348                 // with the name id which will KILL IE.
349                 // We need to manually iterate at this point because if the case
350                 // above is true YUI's ancestor method will also kill IE!
351                 var form = select;
352                 while (form && form.get('nodeName').toUpperCase() !== 'FORM') {
353                     form = form.ancestor();
354                 }
355                 return form;
356             })();
357             // Make sure we have the form
358             if (form) {
359                 // Create a function to handle our change event
360                 var processchange = function(e, lastindex) {
361                     if ((nothing===false || select.get('value') != nothing) && lastindex != select.get('selectedIndex')) {
362                         this.submit();
363                     }
364                 };
365                 // Attach the change event to the keypress, blur, and click actions.
366                 // We don't use the change event because IE fires it on every arrow up/down
367                 // event.... usability
368                 Y.on('key', processchange, select, 'press:13', form, select.get('selectedIndex'));
369                 select.on('blur', processchange, form, select.get('selectedIndex'));
370                 //little hack for chrome that need onChange event instead of onClick - see MDL-23224
371                 if (Y.UA.webkit) {
372                     select.on('change', processchange, form, select.get('selectedIndex'));
373                 } else {
374                     select.on('click', processchange, form, select.get('selectedIndex'));
375                 }
376             }
377         }
378     });
382  * Attach handler to url_select
383  */
384 M.util.init_url_select = function(Y, formid, selectid, nothing) {
385     YUI(M.yui.loader).use('node', function(Y) {
386         Y.on('change', function() {
387             if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) {
388                 window.location = M.cfg.wwwroot+Y.one('#'+selectid).get('value');
389             }
390         },
391         '#'+selectid);
392     });
396  * Breaks out all links to the top frame - used in frametop page layout.
397  */
398 M.util.init_frametop = function(Y) {
399     Y.all('a').each(function(node) {
400         node.set('target', '_top');
401     });
402     Y.all('form').each(function(node) {
403         node.set('target', '_top');
404     });
408  * Finds all nodes that match the given CSS selector and attaches events to them
409  * so that they toggle a given classname when clicked.
411  * @param {YUI} Y
412  * @param {string} id An id containing elements to target
413  * @param {string} cssselector A selector to use to find targets
414  * @param {string} toggleclassname A classname to toggle
415  */
416 M.util.init_toggle_class_on_click = function(Y, id, cssselector, toggleclassname, togglecssselector) {
418     if (togglecssselector == '') {
419         togglecssselector = cssselector;
420     }
422     var node = Y.one('#'+id);
423     node.all(cssselector).each(function(n){
424         n.on('click', function(e){
425             e.stopPropagation();
426             if (e.target.test(cssselector) && !e.target.test('a') && !e.target.test('img')) {
427                 if (this.test(togglecssselector)) {
428                     this.toggleClass(toggleclassname);
429                 } else {
430                     this.ancestor(togglecssselector).toggleClass(toggleclassname);
431             }
432             }
433         }, n);
434     });
435     // Attach this click event to the node rather than all selectors... will be much better
436     // for performance
437     node.on('click', function(e){
438         if (e.target.hasClass('addtoall')) {
439             this.all(togglecssselector).addClass(toggleclassname);
440         } else if (e.target.hasClass('removefromall')) {
441             this.all(togglecssselector+'.'+toggleclassname).removeClass(toggleclassname);
442         }
443     }, node);
447  * Initialises a colour picker
449  * Designed to be used with admin_setting_configcolourpicker although could be used
450  * anywhere, just give a text input an id and insert a div with the class admin_colourpicker
451  * above or below the input (must have the same parent) and then call this with the
452  * id.
454  * This code was mostly taken from my [Sam Hemelryk] css theme tool available in
455  * contrib/blocks. For better docs refer to that.
457  * @param {YUI} Y
458  * @param {int} id
459  * @param {object} previewconf
460  */
461 M.util.init_colour_picker = function(Y, id, previewconf) {
462     /**
463      * We need node and event-mouseenter
464      */
465     Y.use('node', 'event-mouseenter', function(){
466         /**
467          * The colour picker object
468          */
469         var colourpicker = {
470             box : null,
471             input : null,
472             image : null,
473             preview : null,
474             current : null,
475             eventClick : null,
476             eventMouseEnter : null,
477             eventMouseLeave : null,
478             eventMouseMove : null,
479             width : 300,
480             height :  100,
481             factor : 5,
482             /**
483              * Initalises the colour picker by putting everything together and wiring the events
484              */
485             init : function() {
486                 this.input = Y.one('#'+id);
487                 this.box = this.input.ancestor().one('.admin_colourpicker');
488                 this.image = Y.Node.create('<img alt="" class="colourdialogue" />');
489                 this.image.setAttribute('src', M.util.image_url('i/colourpicker', 'moodle'));
490                 this.preview = Y.Node.create('<div class="previewcolour"></div>');
491                 this.preview.setStyle('width', this.height/2).setStyle('height', this.height/2).setStyle('backgroundColor', this.input.get('value'));
492                 this.current = Y.Node.create('<div class="currentcolour"></div>');
493                 this.current.setStyle('width', this.height/2).setStyle('height', this.height/2 -1).setStyle('backgroundColor', this.input.get('value'));
494                 this.box.setContent('').append(this.image).append(this.preview).append(this.current);
496                 if (typeof(previewconf) === 'object' && previewconf !== null) {
497                     Y.one('#'+id+'_preview').on('click', function(e){
498                         if (Y.Lang.isString(previewconf.selector)) {
499                             Y.all(previewconf.selector).setStyle(previewconf.style, this.input.get('value'));
500                         } else {
501                             for (var i in previewconf.selector) {
502                                 Y.all(previewconf.selector[i]).setStyle(previewconf.style, this.input.get('value'));
503                             }
504                         }
505                     }, this);
506                 }
508                 this.eventClick = this.image.on('click', this.pickColour, this);
509                 this.eventMouseEnter = Y.on('mouseenter', this.startFollow, this.image, this);
510             },
511             /**
512              * Starts to follow the mouse once it enter the image
513              */
514             startFollow : function(e) {
515                 this.eventMouseEnter.detach();
516                 this.eventMouseLeave = Y.on('mouseleave', this.endFollow, this.image, this);
517                 this.eventMouseMove = this.image.on('mousemove', function(e){
518                     this.preview.setStyle('backgroundColor', this.determineColour(e));
519                 }, this);
520             },
521             /**
522              * Stops following the mouse
523              */
524             endFollow : function(e) {
525                 this.eventMouseMove.detach();
526                 this.eventMouseLeave.detach();
527                 this.eventMouseEnter = Y.on('mouseenter', this.startFollow, this.image, this);
528             },
529             /**
530              * Picks the colour the was clicked on
531              */
532             pickColour : function(e) {
533                 var colour = this.determineColour(e);
534                 this.input.set('value', colour);
535                 this.current.setStyle('backgroundColor', colour);
536             },
537             /**
538              * Calculates the colour fromthe given co-ordinates
539              */
540             determineColour : function(e) {
541                 var eventx = Math.floor(e.pageX-e.target.getX());
542                 var eventy = Math.floor(e.pageY-e.target.getY());
544                 var imagewidth = this.width;
545                 var imageheight = this.height;
546                 var factor = this.factor;
547                 var colour = [255,0,0];
549                 var matrices = [
550                     [  0,  1,  0],
551                     [ -1,  0,  0],
552                     [  0,  0,  1],
553                     [  0, -1,  0],
554                     [  1,  0,  0],
555                     [  0,  0, -1]
556                 ];
558                 var matrixcount = matrices.length;
559                 var limit = Math.round(imagewidth/matrixcount);
560                 var heightbreak = Math.round(imageheight/2);
562                 for (var x = 0; x < imagewidth; x++) {
563                     var divisor = Math.floor(x / limit);
564                     var matrix = matrices[divisor];
566                     colour[0] += matrix[0]*factor;
567                     colour[1] += matrix[1]*factor;
568                     colour[2] += matrix[2]*factor;
570                     if (eventx==x) {
571                         break;
572                     }
573                 }
575                 var pixel = [colour[0], colour[1], colour[2]];
576                 if (eventy < heightbreak) {
577                     pixel[0] += Math.floor(((255-pixel[0])/heightbreak) * (heightbreak - eventy));
578                     pixel[1] += Math.floor(((255-pixel[1])/heightbreak) * (heightbreak - eventy));
579                     pixel[2] += Math.floor(((255-pixel[2])/heightbreak) * (heightbreak - eventy));
580                 } else if (eventy > heightbreak) {
581                     pixel[0] = Math.floor((imageheight-eventy)*(pixel[0]/heightbreak));
582                     pixel[1] = Math.floor((imageheight-eventy)*(pixel[1]/heightbreak));
583                     pixel[2] = Math.floor((imageheight-eventy)*(pixel[2]/heightbreak));
584                 }
586                 return this.convert_rgb_to_hex(pixel);
587             },
588             /**
589              * Converts an RGB value to Hex
590              */
591             convert_rgb_to_hex : function(rgb) {
592                 var hex = '#';
593                 var hexchars = "0123456789ABCDEF";
594                 for (var i=0; i<3; i++) {
595                     var number = Math.abs(rgb[i]);
596                     if (number == 0 || isNaN(number)) {
597                         hex += '00';
598                     } else {
599                         hex += hexchars.charAt((number-number%16)/16)+hexchars.charAt(number%16);
600                     }
601                 }
602                 return hex;
603             }
604         };
605         /**
606          * Initialise the colour picker :) Hoorah
607          */
608         colourpicker.init();
609     });
612 M.util.init_block_hider = function(Y, config) {
613     Y.use('base', 'node', function(Y) {
614         M.util.block_hider = M.util.block_hider || (function(){
615             var blockhider = function() {
616                 blockhider.superclass.constructor.apply(this, arguments);
617             };
618             blockhider.prototype = {
619                 initializer : function(config) {
620                     this.set('block', '#'+this.get('id'));
621                     var b = this.get('block'),
622                         t = b.one('.title'),
623                         a = null;
624                     if (t && (a = t.one('.block_action'))) {
625                         var hide = Y.Node.create('<img class="block-hider-hide" alt="'+config.tooltipVisible+'" title="'+config.tooltipVisible+'" />');
626                         hide.setAttribute('src', this.get('iconVisible')).on('click', this.updateState, this, true);
627                         var show = Y.Node.create('<img class="block-hider-show" alt="'+config.tooltipHidden+'" title="'+config.tooltipHidden+'" />');
628                         show.setAttribute('src', this.get('iconHidden')).on('click', this.updateState, this, false);
629                         a.insert(show, 0).insert(hide, 0);
630                     }
631                 },
632                 updateState : function(e, hide) {
633                     M.util.set_user_preference(this.get('preference'), hide);
634                     if (hide) {
635                         this.get('block').addClass('hidden');
636                     } else {
637                         this.get('block').removeClass('hidden');
638                     }
639                 }
640             };
641             Y.extend(blockhider, Y.Base, blockhider.prototype, {
642                 NAME : 'blockhider',
643                 ATTRS : {
644                     id : {},
645                     preference : {},
646                     iconVisible : {
647                         value : M.util.image_url('t/switch_minus', 'moodle')
648                     },
649                     iconHidden : {
650                         value : M.util.image_url('t/switch_plus', 'moodle')
651                     },
652                     block : {
653                         setter : function(node) {
654                             return Y.one(node);
655                         }
656                     }
657                 }
658             });
659             return blockhider;
660         })();
661         new M.util.block_hider(config);
662     });
666  * Returns a string registered in advance for usage in JavaScript
668  * If you do not pass the third parameter, the function will just return
669  * the corresponding value from the M.str object. If the third parameter is
670  * provided, the function performs {$a} placeholder substitution in the
671  * same way as PHP get_string() in Moodle does.
673  * @param {String} identifier string identifier
674  * @param {String} component the component providing the string
675  * @param {Object|String} a optional variable to populate placeholder with
676  */
677 M.util.get_string = function(identifier, component, a) {
678     var stringvalue;
680     if (M.cfg.developerdebug) {
681         // creating new instance if YUI is not optimal but it seems to be better way then
682         // require the instance via the function API - note that it is used in rare cases
683         // for debugging only anyway
684         var Y = new YUI({ debug : true });
685     }
687     if (!M.str.hasOwnProperty(component) || !M.str[component].hasOwnProperty(identifier)) {
688         stringvalue = '[[' + identifier + ',' + component + ']]';
689         if (M.cfg.developerdebug) {
690             Y.log('undefined string ' + stringvalue, 'warn', 'M.util.get_string');
691         }
692         return stringvalue;
693     }
695     stringvalue = M.str[component][identifier];
697     if (typeof a == 'undefined') {
698         // no placeholder substitution requested
699         return stringvalue;
700     }
702     if (typeof a == 'number' || typeof a == 'string') {
703         // replace all occurrences of {$a} with the placeholder value
704         stringvalue = stringvalue.replace(/\{\$a\}/g, a);
705         return stringvalue;
706     }
708     if (typeof a == 'object') {
709         // replace {$a->key} placeholders
710         for (var key in a) {
711             if (typeof a[key] != 'number' && typeof a[key] != 'string') {
712                 if (M.cfg.developerdebug) {
713                     Y.log('invalid value type for $a->' + key, 'warn', 'M.util.get_string');
714                 }
715                 continue;
716             }
717             var search = '{$a->' + key + '}';
718             search = search.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
719             search = new RegExp(search, 'g');
720             stringvalue = stringvalue.replace(search, a[key]);
721         }
722         return stringvalue;
723     }
725     if (M.cfg.developerdebug) {
726         Y.log('incorrect placeholder type', 'warn', 'M.util.get_string');
727     }
728     return stringvalue;
731 //=== old legacy JS code, hopefully to be replaced soon by M.xx.yy and YUI3 code ===
733 function checkall() {
734     var inputs = document.getElementsByTagName('input');
735     for (var i = 0; i < inputs.length; i++) {
736         if (inputs[i].type == 'checkbox') {
737             inputs[i].checked = true;
738         }
739     }
742 function checknone() {
743     var inputs = document.getElementsByTagName('input');
744     for (var i = 0; i < inputs.length; i++) {
745         if (inputs[i].type == 'checkbox') {
746             inputs[i].checked = false;
747         }
748     }
752  * Either check, or uncheck, all checkboxes inside the element with id is
753  * @param id the id of the container
754  * @param checked the new state, either '' or 'checked'.
755  */
756 function select_all_in_element_with_id(id, checked) {
757     var container = document.getElementById(id);
758     if (!container) {
759         return;
760     }
761     var inputs = container.getElementsByTagName('input');
762     for (var i = 0; i < inputs.length; ++i) {
763         if (inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
764             inputs[i].checked = checked;
765         }
766     }
769 function select_all_in(elTagName, elClass, elId) {
770     var inputs = document.getElementsByTagName('input');
771     inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
772     for(var i = 0; i < inputs.length; ++i) {
773         if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
774             inputs[i].checked = 'checked';
775         }
776     }
779 function deselect_all_in(elTagName, elClass, elId) {
780     var inputs = document.getElementsByTagName('INPUT');
781     inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
782     for(var i = 0; i < inputs.length; ++i) {
783         if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
784             inputs[i].checked = '';
785         }
786     }
789 function confirm_if(expr, message) {
790     if(!expr) {
791         return true;
792     }
793     return confirm(message);
798     findParentNode (start, elementName, elementClass, elementID)
800     Travels up the DOM hierarchy to find a parent element with the
801     specified tag name, class, and id. All conditions must be met,
802     but any can be ommitted. Returns the BODY element if no match
803     found.
805 function findParentNode(el, elName, elClass, elId) {
806     while (el.nodeName.toUpperCase() != 'BODY') {
807         if ((!elName || el.nodeName.toUpperCase() == elName) &&
808             (!elClass || el.className.indexOf(elClass) != -1) &&
809             (!elId || el.id == elId)) {
810             break;
811         }
812         el = el.parentNode;
813     }
814     return el;
817     findChildNode (start, elementName, elementClass, elementID)
819     Travels down the DOM hierarchy to find all child elements with the
820     specified tag name, class, and id. All conditions must be met,
821     but any can be ommitted.
822     Doesn't examine children of matches.
824 function findChildNodes(start, tagName, elementClass, elementID, elementName) {
825     var children = new Array();
826     for (var i = 0; i < start.childNodes.length; i++) {
827         var classfound = false;
828         var child = start.childNodes[i];
829         if((child.nodeType == 1) &&//element node type
830                   (elementClass && (typeof(child.className)=='string'))) {
831             var childClasses = child.className.split(/\s+/);
832             for (var childClassIndex in childClasses) {
833                 if (childClasses[childClassIndex]==elementClass) {
834                     classfound = true;
835                     break;
836                 }
837             }
838         }
839         if(child.nodeType == 1) { //element node type
840             if  ( (!tagName || child.nodeName == tagName) &&
841                 (!elementClass || classfound)&&
842                 (!elementID || child.id == elementID) &&
843                 (!elementName || child.name == elementName))
844             {
845                 children = children.concat(child);
846             } else {
847                 children = children.concat(findChildNodes(child, tagName, elementClass, elementID, elementName));
848             }
849         }
850     }
851     return children;
854 function unmaskPassword(id) {
855   var pw = document.getElementById(id);
856   var chb = document.getElementById(id+'unmask');
858   try {
859     // first try IE way - it can not set name attribute later
860     if (chb.checked) {
861       var newpw = document.createElement('<input type="text" name="'+pw.name+'">');
862     } else {
863       var newpw = document.createElement('<input type="password" name="'+pw.name+'">');
864     }
865     newpw.attributes['class'].nodeValue = pw.attributes['class'].nodeValue;
866   } catch (e) {
867     var newpw = document.createElement('input');
868     newpw.setAttribute('name', pw.name);
869     if (chb.checked) {
870       newpw.setAttribute('type', 'text');
871     } else {
872       newpw.setAttribute('type', 'password');
873     }
874     newpw.setAttribute('class', pw.getAttribute('class'));
875   }
876   newpw.id = pw.id;
877   newpw.size = pw.size;
878   newpw.onblur = pw.onblur;
879   newpw.onchange = pw.onchange;
880   newpw.value = pw.value;
881   pw.parentNode.replaceChild(newpw, pw);
884 function filterByParent(elCollection, parentFinder) {
885     var filteredCollection = [];
886     for (var i = 0; i < elCollection.length; ++i) {
887         var findParent = parentFinder(elCollection[i]);
888         if (findParent.nodeName.toUpperCase != 'BODY') {
889             filteredCollection.push(elCollection[i]);
890         }
891     }
892     return filteredCollection;
896     All this is here just so that IE gets to handle oversized blocks
897     in a visually pleasing manner. It does a browser detect. So sue me.
900 function fix_column_widths() {
901     var agt = navigator.userAgent.toLowerCase();
902     if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
903         fix_column_width('left-column');
904         fix_column_width('right-column');
905     }
908 function fix_column_width(colName) {
909     if(column = document.getElementById(colName)) {
910         if(!column.offsetWidth) {
911             setTimeout("fix_column_width('" + colName + "')", 20);
912             return;
913         }
915         var width = 0;
916         var nodes = column.childNodes;
918         for(i = 0; i < nodes.length; ++i) {
919             if(nodes[i].className.indexOf("block") != -1 ) {
920                 if(width < nodes[i].offsetWidth) {
921                     width = nodes[i].offsetWidth;
922                 }
923             }
924         }
926         for(i = 0; i < nodes.length; ++i) {
927             if(nodes[i].className.indexOf("block") != -1 ) {
928                 nodes[i].style.width = width + 'px';
929             }
930         }
931     }
936    Insert myValue at current cursor position
937  */
938 function insertAtCursor(myField, myValue) {
939     // IE support
940     if (document.selection) {
941         myField.focus();
942         sel = document.selection.createRange();
943         sel.text = myValue;
944     }
945     // Mozilla/Netscape support
946     else if (myField.selectionStart || myField.selectionStart == '0') {
947         var startPos = myField.selectionStart;
948         var endPos = myField.selectionEnd;
949         myField.value = myField.value.substring(0, startPos)
950             + myValue + myField.value.substring(endPos, myField.value.length);
951     } else {
952         myField.value += myValue;
953     }
958         Call instead of setting window.onload directly or setting body onload=.
959         Adds your function to a chain of functions rather than overwriting anything
960         that exists.
962 function addonload(fn) {
963     var oldhandler=window.onload;
964     window.onload=function() {
965         if(oldhandler) oldhandler();
966             fn();
967     }
970  * Replacement for getElementsByClassName in browsers that aren't cool enough
972  * Relying on the built-in getElementsByClassName is far, far faster than
973  * using YUI.
975  * Note: the third argument used to be an object with odd behaviour. It now
976  * acts like the 'name' in the HTML5 spec, though the old behaviour is still
977  * mimicked if you pass an object.
979  * @param {Node} oElm The top-level node for searching. To search a whole
980  *                    document, use `document`.
981  * @param {String} strTagName filter by tag names
982  * @param {String} name same as HTML5 spec
983  */
984 function getElementsByClassName(oElm, strTagName, name) {
985     // for backwards compatibility
986     if(typeof name == "object") {
987         var names = new Array();
988         for(var i=0; i<name.length; i++) names.push(names[i]);
989         name = names.join('');
990     }
991     // use native implementation if possible
992     if (oElm.getElementsByClassName && Array.filter) {
993         if (strTagName == '*') {
994             return oElm.getElementsByClassName(name);
995         } else {
996             return Array.filter(oElm.getElementsByClassName(name), function(el) {
997                 return el.nodeName.toLowerCase() == strTagName.toLowerCase();
998             });
999         }
1000     }
1001     // native implementation unavailable, fall back to slow method
1002     var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
1003     var arrReturnElements = new Array();
1004     var arrRegExpClassNames = new Array();
1005     var names = name.split(' ');
1006     for(var i=0; i<names.length; i++) {
1007         arrRegExpClassNames.push(new RegExp("(^|\\s)" + names[i].replace(/\-/g, "\\-") + "(\\s|$)"));
1008     }
1009     var oElement;
1010     var bMatchesAll;
1011     for(var j=0; j<arrElements.length; j++) {
1012         oElement = arrElements[j];
1013         bMatchesAll = true;
1014         for(var k=0; k<arrRegExpClassNames.length; k++) {
1015             if(!arrRegExpClassNames[k].test(oElement.className)) {
1016                 bMatchesAll = false;
1017                 break;
1018             }
1019         }
1020         if(bMatchesAll) {
1021             arrReturnElements.push(oElement);
1022         }
1023     }
1024     return (arrReturnElements)
1027 function openpopup(event, args) {
1029     if (event) {
1030         if (event.preventDefault) {
1031             event.preventDefault();
1032         } else {
1033             event.returnValue = false;
1034         }
1035     }
1037     var fullurl = args.url;
1038     if (!args.url.match(/https?:\/\//)) {
1039         fullurl = M.cfg.wwwroot + args.url;
1040     }
1041     var windowobj = window.open(fullurl,args.name,args.options);
1042     if (!windowobj) {
1043         return true;
1044     }
1045     if (args.fullscreen) {
1046         windowobj.moveTo(0,0);
1047         windowobj.resizeTo(screen.availWidth,screen.availHeight);
1048     }
1049     windowobj.focus();
1051     return false;
1054 /** Close the current browser window. */
1055 function close_window(e) {
1056     if (e.preventDefault) {
1057         e.preventDefault();
1058     } else {
1059         e.returnValue = false;
1060     }
1061     window.close();
1065  * Close the current browser window, forcing the window/tab that opened this
1066  * popup to reload itself. */
1067 function close_window_reloading_opener() {
1068     if (window.opener) {
1069         window.opener.location.reload(1);
1070         close_window({});
1071         // Intentionally, only try to close the window if there is some evidence we are in a popup.
1072     }
1076  * Used in a couple of modules to hide navigation areas when using AJAX
1077  */
1079 function show_item(itemid) {
1080     var item = document.getElementById(itemid);
1081     if (item) {
1082         item.style.display = "";
1083     }
1086 function destroy_item(itemid) {
1087     var item = document.getElementById(itemid);
1088     if (item) {
1089         item.parentNode.removeChild(item);
1090     }
1093  * Tranfer keyboard focus to the HTML element with the given id, if it exists.
1094  * @param controlid the control id.
1095  */
1096 function focuscontrol(controlid) {
1097     var control = document.getElementById(controlid);
1098     if (control) {
1099         control.focus();
1100     }
1104  * Transfers keyboard focus to an HTML element based on the old style style of focus
1105  * This function should be removed as soon as it is no longer used
1106  */
1107 function old_onload_focus(formid, controlname) {
1108     if (document.forms[formid] && document.forms[formid].elements && document.forms[formid].elements[controlname]) {
1109         document.forms[formid].elements[controlname].focus();
1110     }
1113 function build_querystring(obj) {
1114     return convert_object_to_string(obj, '&');
1117 function build_windowoptionsstring(obj) {
1118     return convert_object_to_string(obj, ',');
1121 function convert_object_to_string(obj, separator) {
1122     if (typeof obj !== 'object') {
1123         return null;
1124     }
1125     var list = [];
1126     for(var k in obj) {
1127         k = encodeURIComponent(k);
1128         var value = obj[k];
1129         if(obj[k] instanceof Array) {
1130             for(var i in value) {
1131                 list.push(k+'[]='+encodeURIComponent(value[i]));
1132             }
1133         } else {
1134             list.push(k+'='+encodeURIComponent(value));
1135         }
1136     }
1137     return list.join(separator);
1140 function stripHTML(str) {
1141     var re = /<\S[^><]*>/g;
1142     var ret = str.replace(re, "");
1143     return ret;
1146 Number.prototype.fixed=function(n){
1147     with(Math)
1148         return round(Number(this)*pow(10,n))/pow(10,n);
1150 function update_progress_bar (id, width, pt, msg, es){
1151     var percent = pt;
1152     var status = document.getElementById("status_"+id);
1153     var percent_indicator = document.getElementById("pt_"+id);
1154     var progress_bar = document.getElementById("progress_"+id);
1155     var time_es = document.getElementById("time_"+id);
1156     status.innerHTML = msg;
1157     percent_indicator.innerHTML = percent.fixed(2) + '%';
1158     if(percent == 100) {
1159         progress_bar.style.background = "green";
1160         time_es.style.display = "none";
1161     } else {
1162         progress_bar.style.background = "#FFCC66";
1163         if (es == '?'){
1164             time_es.innerHTML = "";
1165         }else {
1166             time_es.innerHTML = es.fixed(2)+" sec";
1167             time_es.style.display
1168                 = "block";
1169         }
1170     }
1171     progress_bar.style.width = width + "px";
1175 function frame_breakout(e, properties) {
1176     this.setAttribute('target', properties.framename);
1180 // ===== Deprecated core Javascript functions for Moodle ====
1181 //       DO NOT USE!!!!!!!
1182 // Do not put this stuff in separate file because it only adds extra load on servers!
1185  * Used in a couple of modules to hide navigation areas when using AJAX
1186  */
1187 function hide_item(itemid) {
1188     // use class='hiddenifjs' instead
1189     var item = document.getElementById(itemid);
1190     if (item) {
1191         item.style.display = "none";
1192     }
1195 M.util.help_icon = {
1196     Y : null,
1197     instance : null,
1198     add : function(Y, properties) {
1199         this.Y = Y;
1200         properties.node = Y.one('#'+properties.id);
1201         if (properties.node) {
1202             properties.node.on('click', this.display, this, properties);
1203         }
1204     },
1205     display : function(event, args) {
1206         event.preventDefault();
1207         if (M.util.help_icon.instance === null) {
1208             var Y = M.util.help_icon.Y;
1209             Y.use('overlay', 'io', 'event-mouseenter', 'node', 'event-key', function(Y) {
1210                 var help_content_overlay = {
1211                     helplink : null,
1212                     overlay : null,
1213                     init : function() {
1215                         var closebtn = Y.Node.create('<a id="closehelpbox" href="#"><img  src="'+M.util.image_url('t/delete', 'moodle')+'" /></a>');
1216                         // Create an overlay from markup
1217                         this.overlay = new Y.Overlay({
1218                             headerContent: closebtn,
1219                             bodyContent: '',
1220                             id: 'helppopupbox',
1221                             width:'400px',
1222                             visible : false,
1223                             constrain : true
1224                         });
1225                         this.overlay.render(Y.one(document.body));
1227                         closebtn.on('click', this.overlay.hide, this.overlay);
1229                         var boundingBox = this.overlay.get("boundingBox");
1231                         //  Hide the menu if the user clicks outside of its content
1232                         boundingBox.get("ownerDocument").on("mousedown", function (event) {
1233                             var oTarget = event.target;
1234                             var menuButton = Y.one("#"+args.id);
1236                             if (!oTarget.compareTo(menuButton) &&
1237                                 !menuButton.contains(oTarget) &&
1238                                 !oTarget.compareTo(boundingBox) &&
1239                                 !boundingBox.contains(oTarget)) {
1240                                 this.overlay.hide();
1241                             }
1242                         }, this);
1244                         Y.on("key", this.close, closebtn , "down:13", this);
1245                         closebtn.on('click', this.close, this);
1246                     },
1248                     close : function(e) {
1249                         e.preventDefault();
1250                         this.helplink.focus();
1251                         this.overlay.hide();
1252                     },
1254                     display : function(event, args) {
1255                         this.helplink = args.node;
1256                         this.overlay.set('bodyContent', Y.Node.create('<img src="'+M.cfg.loadingicon+'" class="spinner" />'));
1257                         this.overlay.set("align", {node:args.node, points:[Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC]});
1259                         var fullurl = args.url;
1260                         if (!args.url.match(/https?:\/\//)) {
1261                             fullurl = M.cfg.wwwroot + args.url;
1262                         }
1264                         var ajaxurl = fullurl + '&ajax=1';
1266                         var cfg = {
1267                             method: 'get',
1268                             context : this,
1269                             on: {
1270                                 success: function(id, o, node) {
1271                                     this.display_callback(o.responseText);
1272                                 },
1273                                 failure: function(id, o, node) {
1274                                     var debuginfo = o.statusText;
1275                                     if (M.cfg.developerdebug) {
1276                                         o.statusText += ' (' + ajaxurl + ')';
1277                                     }
1278                                     this.display_callback('bodyContent',debuginfo);
1279                                 }
1280                             }
1281                         };
1283                         Y.io(ajaxurl, cfg);
1284                         this.overlay.show();
1286                         Y.one('#closehelpbox').focus();
1287                     },
1289                     display_callback : function(content) {
1290                         this.overlay.set('bodyContent', content);
1291                     },
1293                     hideContent : function() {
1294                         help = this;
1295                         help.overlay.hide();
1296                     }
1297                 };
1298                 help_content_overlay.init();
1299                 M.util.help_icon.instance = help_content_overlay;
1300                 M.util.help_icon.instance.display(event, args);
1301             });
1302         } else {
1303             M.util.help_icon.instance.display(event, args);
1304         }
1305     },
1306     init : function(Y) {
1307         this.Y = Y;
1308     }
1312  * Custom menu namespace
1313  */
1314 M.core_custom_menu = {
1315     /**
1316      * This method is used to initialise a custom menu given the id that belongs
1317      * to the custom menu's root node.
1318      *
1319      * @param {YUI} Y
1320      * @param {string} nodeid
1321      */
1322     init : function(Y, nodeid) {
1323         var node = Y.one('#'+nodeid);
1324         if (node) {
1325             Y.use('node-menunav', function(Y) {
1326                 // Get the node
1327                 // Remove the javascript-disabled class.... obviously javascript is enabled.
1328                 node.removeClass('javascript-disabled');
1329                 // Initialise the menunav plugin
1330                 node.plug(Y.Plugin.NodeMenuNav);
1331             });
1332         }
1333     }
1337  * Used to store form manipulation methods and enhancments
1338  */
1339 M.form = M.form || {};
1342  * Converts a nbsp indented select box into a multi drop down custom control much
1343  * like the custom menu. It also selectable categories on or off.
1345  * $form->init_javascript_enhancement('elementname','smartselect', array('selectablecategories'=>true|false, 'mode'=>'compact'|'spanning'));
1347  * @param {YUI} Y
1348  * @param {string} id
1349  * @param {Array} options
1350  */
1351 M.form.init_smartselect = function(Y, id, options) {
1352     if (!id.match(/^id_/)) {
1353         id = 'id_'+id;
1354     }
1355     var select = Y.one('select#'+id);
1356     if (!select) {
1357         return false;
1358     }
1359     Y.use('event-delegate',function(){
1360         var smartselect = {
1361             id : id,
1362             structure : [],
1363             options : [],
1364             submenucount : 0,
1365             currentvalue : null,
1366             currenttext : null,
1367             shownevent : null,
1368             cfg : {
1369                 selectablecategories : true,
1370                 mode : null
1371             },
1372             nodes : {
1373                 select : null,
1374                 loading : null,
1375                 menu : null
1376             },
1377             init : function(Y, id, args, nodes) {
1378                 if (typeof(args)=='object') {
1379                     for (var i in this.cfg) {
1380                         if (args[i] || args[i]===false) {
1381                             this.cfg[i] = args[i];
1382                         }
1383                     }
1384                 }
1386                 // Display a loading message first up
1387                 this.nodes.select = nodes.select;
1389                 this.currentvalue = this.nodes.select.get('selectedIndex');
1390                 this.currenttext = this.nodes.select.all('option').item(this.currentvalue).get('innerHTML');
1392                 var options = Array();
1393                 options[''] = {text:this.currenttext,value:'',depth:0,children:[]};
1394                 this.nodes.select.all('option').each(function(option, index) {
1395                     var rawtext = option.get('innerHTML');
1396                     var text = rawtext.replace(/^(&nbsp;)*/, '');
1397                     if (rawtext === text) {
1398                         text = rawtext.replace(/^(\s)*/, '');
1399                         var depth = (rawtext.length - text.length ) + 1;
1400                     } else {
1401                         var depth = ((rawtext.length - text.length )/12)+1;
1402                     }
1403                     option.set('innerHTML', text);
1404                     options['i'+index] = {text:text,depth:depth,index:index,children:[]};
1405                 }, this);
1407                 this.structure = [];
1408                 var structcount = 0;
1409                 for (var i in options) {
1410                     var o = options[i];
1411                     if (o.depth == 0) {
1412                         this.structure.push(o);
1413                         structcount++;
1414                     } else {
1415                         var d = o.depth;
1416                         var current = this.structure[structcount-1];
1417                         for (var j = 0; j < o.depth-1;j++) {
1418                             if (current && current.children) {
1419                                 current = current.children[current.children.length-1];
1420                             }
1421                         }
1422                         if (current && current.children) {
1423                             current.children.push(o);
1424                         }
1425                     }
1426                 }
1428                 this.nodes.menu = Y.Node.create(this.generate_menu_content());
1429                 this.nodes.menu.one('.smartselect_mask').setStyle('opacity', 0.01);
1430                 this.nodes.menu.one('.smartselect_mask').setStyle('width', (this.nodes.select.get('offsetWidth')+5)+'px');
1431                 this.nodes.menu.one('.smartselect_mask').setStyle('height', (this.nodes.select.get('offsetHeight'))+'px');
1433                 if (this.cfg.mode == null) {
1434                     var formwidth = this.nodes.select.ancestor('form').get('offsetWidth');
1435                     if (formwidth < 400 || this.nodes.menu.get('offsetWidth') < formwidth*2) {
1436                         this.cfg.mode = 'compact';
1437                     } else {
1438                         this.cfg.mode = 'spanning';
1439                     }
1440                 }
1442                 if (this.cfg.mode == 'compact') {
1443                     this.nodes.menu.addClass('compactmenu');
1444                 } else {
1445                     this.nodes.menu.addClass('spanningmenu');
1446                     this.nodes.menu.delegate('mouseover', this.show_sub_menu, '.smartselect_submenuitem', this);
1447                 }
1449                 Y.one(document.body).append(this.nodes.menu);
1450                 var pos = this.nodes.select.getXY();
1451                 pos[0] += 1;
1452                 this.nodes.menu.setXY(pos);
1453                 this.nodes.menu.on('click', this.handle_click, this);
1455                 Y.one(window).on('resize', function(){
1456                      var pos = this.nodes.select.getXY();
1457                     pos[0] += 1;
1458                     this.nodes.menu.setXY(pos);
1459                  }, this);
1460             },
1461             generate_menu_content : function() {
1462                 var content = '<div id="'+this.id+'_smart_select" class="smartselect">';
1463                 content += this.generate_submenu_content(this.structure[0], true);
1464                 content += '</ul></div>';
1465                 return content;
1466             },
1467             generate_submenu_content : function(item, rootelement) {
1468                 this.submenucount++;
1469                 var content = '';
1470                 if (item.children.length > 0) {
1471                     if (rootelement) {
1472                         content += '<div class="smartselect_mask" href="#ss_submenu'+this.submenucount+'">&nbsp;</div>';
1473                         content += '<div id="ss_submenu'+this.submenucount+'" class="smartselect_menu">';
1474                         content += '<div class="smartselect_menu_content">';
1475                     } else {
1476                         content += '<li class="smartselect_submenuitem">';
1477                         var categoryclass = (this.cfg.selectablecategories)?'selectable':'notselectable';
1478                         content += '<a class="smartselect_menuitem_label '+categoryclass+'" href="#ss_submenu'+this.submenucount+'" value="'+item.index+'">'+item.text+'</a>';
1479                         content += '<div id="ss_submenu'+this.submenucount+'" class="smartselect_submenu">';
1480                         content += '<div class="smartselect_submenu_content">';
1481                     }
1482                     content += '<ul>';
1483                     for (var i in item.children) {
1484                         content += this.generate_submenu_content(item.children[i],false);
1485                     }
1486                     content += '</ul>';
1487                     content += '</div>';
1488                     content += '</div>';
1489                     if (rootelement) {
1490                     } else {
1491                         content += '</li>';
1492                     }
1493                 } else {
1494                     content += '<li class="smartselect_menuitem">';
1495                     content += '<a class="smartselect_menuitem_content selectable" href="#" value="'+item.index+'">'+item.text+'</a>';
1496                     content += '</li>';
1497                 }
1498                 return content;
1499             },
1500             select : function(e) {
1501                 var t = e.target;
1502                 e.halt();
1503                 this.currenttext = t.get('innerHTML');
1504                 this.currentvalue = t.getAttribute('value');
1505                 this.nodes.select.set('selectedIndex', this.currentvalue);
1506                 this.hide_menu();
1507             },
1508             handle_click : function(e) {
1509                 var target = e.target;
1510                 if (target.hasClass('smartselect_mask')) {
1511                     this.show_menu(e);
1512                 } else if (target.hasClass('selectable') || target.hasClass('smartselect_menuitem')) {
1513                     this.select(e);
1514                 } else if (target.hasClass('smartselect_menuitem_label') || target.hasClass('smartselect_submenuitem')) {
1515                     this.show_sub_menu(e);
1516                 }
1517             },
1518             show_menu : function(e) {
1519                 e.halt();
1520                 var menu = e.target.ancestor().one('.smartselect_menu');
1521                 menu.addClass('visible');
1522                 this.shownevent = Y.one(document.body).on('click', this.hide_menu, this);
1523             },
1524             show_sub_menu : function(e) {
1525                 e.halt();
1526                 var target = e.target;
1527                 if (!target.hasClass('smartselect_submenuitem')) {
1528                     target = target.ancestor('.smartselect_submenuitem');
1529                 }
1530                 if (this.cfg.mode == 'compact' && target.one('.smartselect_submenu').hasClass('visible')) {
1531                     target.ancestor('ul').all('.smartselect_submenu.visible').removeClass('visible');
1532                     return;
1533                 }
1534                 target.ancestor('ul').all('.smartselect_submenu.visible').removeClass('visible');
1535                 target.one('.smartselect_submenu').addClass('visible');
1536             },
1537             hide_menu : function() {
1538                 this.nodes.menu.all('.visible').removeClass('visible');
1539                 if (this.shownevent) {
1540                     this.shownevent.detach();
1541                 }
1542             }
1543         };
1544         smartselect.init(Y, id, options, {select:select});
1545     });
1548 M.util.init_flvflowplayer = function (id, playerpath, fileurl) {
1549     $f(id, playerpath, {
1550         plugins: {
1551                 controls: {
1552                        autoHide: true
1553                 }
1554         },
1555         clip: {
1556             url: fileurl,
1557             autoPlay: false,
1558             autoBuffering: true
1559         }
1560     });
1563 M.util.init_mp3flowplayer = function (id, playerpath, audioplayerpath, fileurl, color) {
1565     $f(id, playerpath, {
1567             plugins: {
1568                     controls: {
1569                             fullscreen: false,
1570                             height: 25,
1571                             autoHide: false,
1572                             background: '#'+color['bgColour'],
1573                             buttonColor: '#'+color['btnColour'],
1574                             sliderColor: '#'+color['handleColour'],
1575                             volumeSliderColor: '#'+color['handleColour'],
1576                             volumeColor: '#'+color['trackColour'],
1577                             durationColor: '#'+color['fontColour'],
1578                             buttonOverColor: '#'+color['iconOverColour'],
1579                             progressColor: '#'+color['handleColour']
1580                     },
1581                     audio: {url: audioplayerpath}
1582             },
1583             clip: {url: fileurl,
1584                     provider: "audio",
1585                     autoPlay: false
1586             }
1587     });
1590 M.util.init_mp3flowplayerplugin = function (id, playerpath, audioplayerpath, fileurl, color) {
1591     $f(id, playerpath, {
1592             plugins: {
1593                     controls: {
1594                             all: false,
1595                             play: true,
1596                             pause: true,
1597                             scrubber: true,
1598                             autoHide: false,
1599                             height: 15,
1600                             background: '#'+color['bgColour'],
1601                             buttonColor: '#'+color['btnColour'],
1602                             sliderColor: '#'+color['handleColour'],
1603                             volumeSliderColor: '#'+color['handleColour'],
1604                             progressColor: '#'+color['handleColour'],
1605                             volumeColor: '#'+color['trackColour'],
1606                             buttonOverColor: '#'+color['iconOverColour']
1607                     },
1608                     audio: {url: audioplayerpath}
1609             },
1610             clip: {url: fileurl,
1611                     provider: "audio",
1612                     autoPlay: false
1613             }
1614     });