// Miscellaneous core Javascript functions for Moodle // Global M object is initilised in inline javascript /** * Add module to list of available modules that can be laoded from YUI. * @param {Array} modules */ M.yui.add_module = function(modules) { for (var modname in modules) { M.yui.loader.modules[modname] = modules[modname]; } }; /** * Various utility functions */ M.util = {}; /** * Language strings - initialised from page footer. */ M.str = {}; /** * Returns url for images. * @param {String} imagename * @param {String} component * @return {String} */ M.util.image_url = function(imagename, component) { var url = M.cfg.wwwroot + '/theme/image.php?theme=' + M.cfg.theme + '&image=' + imagename; if (M.cfg.themerev > 0) { url = url + '&rev=' + M.cfg.themerev; } if (component && component != '' && component != 'moodle' && component != 'core') { url = url + '&component=' + component; } return url; }; M.util.create_UFO_object = function (eid, FO) { UFO.create(FO, eid); } M.util.in_array = function(item, array){ for( var i = 0; i'); // Create a local scoped lamba function to move nodes to a new link var movenode = function(node){ node.remove(); a.append(node); }; // Apply the lamba function on each of the captions child nodes caption.get('children').each(movenode, this); caption.append(a); // Get the height of the div at this point before we shrink it if required var height = this.div.get('offsetHeight'); if (this.div.hasClass('collapsed')) { // Add the correct image and record the YUI node created in the process this.icon = Y.Node.create(''); // Shrink the div as it is collapsed by default this.div.setStyle('height', caption.get('offsetHeight')+'px'); } else { // Add the correct image and record the YUI node created in the process this.icon = Y.Node.create(''); } a.append(this.icon); // Create the animation. var animation = new Y.Anim({ node: this.div, duration: 0.3, easing: Y.Easing.easeBoth, to: {height:caption.get('offsetHeight')}, from: {height:height} }); // Handler for the animation finishing. animation.on('end', function() { this.div.toggleClass('collapsed'); if (this.div.hasClass('collapsed')) { this.icon.set('src', M.util.image_url('t/collapsed', 'moodle')); } else { this.icon.set('src', M.util.image_url('t/expanded', 'moodle')); } }, this); // Hook up the event handler. a.on('click', function(e, animation) { e.preventDefault(); // Animate to the appropriate size. if (animation.get('running')) { animation.stop(); } animation.set('reverse', this.div.hasClass('collapsed')); // Update the user preference. if (this.userpref) { M.util.set_user_preference(this.userpref, !this.div.hasClass('collapsed')); } animation.run(); }, this, animation); }; /** * The user preference that stores the state of this box. * @property userpref * @type String */ M.util.CollapsibleRegion.prototype.userpref = null; /** * The key divs that make up this * @property div * @type Y.Node */ M.util.CollapsibleRegion.prototype.div = null; /** * The key divs that make up this * @property icon * @type Y.Node */ M.util.CollapsibleRegion.prototype.icon = null; /** * Finds all help icons on the page and initiates YUI tooltips for * each of them, which load a truncated version of the help's content * on-the-fly asynchronously */ M.util.init_help_icons = function(Y) { Y.use('io', 'yui2-dom', 'yui2-container', function(Y) { // remove all titles, they would obscure the YUI tooltip Y.all('span.helplink a').each(function(node) { node.set('title', ''); }); var iconspans = YAHOO.util.Dom.getElementsByClassName('helplink', 'span'); var tooltip = new YAHOO.widget.Tooltip('help_icon_tooltip', { context: iconspans, showdelay: 1000, hidedelay: 150, autodismissdelay: 50000, underlay: 'none', zIndex: '1000' }); tooltip.contextTriggerEvent.subscribe( function(type, args) { // Fetch help page contents asynchronously // Load spinner icon while content is loading var spinner = document.createElement('img'); spinner.src = M.cfg.loadingicon; this.cfg.setProperty('text', spinner); var context = args[0]; var link = context.getElementsByTagName('a')[0]; var thistooltip = this; var ajaxurl = link.href + '&fortooltip=1'; var cfg = { method: 'get', on: { success: function(id, o, args) { thistooltip.cfg.setProperty('text', o.responseText); }, failure: function(id, o, args) { var debuginfo = o.statusText; if (M.cfg.developerdebug) { o.statusText += ' (' + ajaxurl + ')'; } thistooltip.cfg.setProperty('text', debuginfo); } } }; var conn = Y.io(ajaxurl, cfg); } ); }); }; /** * Makes a best effort to connect back to Moodle to update a user preference, * however, there is no mechanism for finding out if the update succeeded. * * Before you can use this function in your JavsScript, you must have called * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that * the udpate is allowed, and how to safely clean and submitted values. * * @param String name the name of the setting to udpate. * @param String the value to set it to. */ M.util.set_user_preference = function(name, value) { YUI(M.yui.loader).use('io', function(Y) { var url = M.cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' + M.cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value); // If we are a developer, ensure that failures are reported. var cfg = { method: 'get', on: {} }; if (M.cfg.developerdebug) { cfg.on.failure = function(id, o, args) { alert("Error updating user preference '" + name + "' using ajax. Clicking this link will repeat the Ajax call that failed so you can see the error: "); } } // Make the request. Y.io(url, cfg); }); }; /** * Prints a confirmation dialog in the style of DOM.confirm(). * @param object event A YUI DOM event or null if launched manually * @param string message The message to show in the dialog * @param string url The URL to forward to if YES is clicked. Disabled if fn is given * @param function fn A JS function to run if YES is clicked. */ M.util.show_confirm_dialog = function(e, args) { var target = e.target; if (e.preventDefault) { e.preventDefault(); } YUI(M.yui.loader).use('yui2-container', 'yui2-event', function(Y) { var simpledialog = new YAHOO.widget.SimpleDialog('confirmdialog', { width: '300px', fixedcenter: true, modal: true, visible: false, draggable: false } ); simpledialog.setHeader(M.str.admin.confirmation); simpledialog.setBody(args.message); simpledialog.cfg.setProperty('icon', YAHOO.widget.SimpleDialog.ICON_WARN); var handle_cancel = function() { simpledialog.hide(); }; var handle_yes = function() { simpledialog.hide(); if (args.callback) { // args comes from PHP, so callback will be a string, needs to be evaluated by JS var callback = null; if (Y.Lang.isFunction(args.callback)) { callback = args.callback; } else { callback = eval('('+args.callback+')'); } if (Y.Lang.isObject(args.scope)) { var sc = args.scope; } else { var sc = e.target; } if (args.callbackargs) { callback.apply(sc, args.callbackargs); } else { callback.apply(sc); } return; } if (target.get('tagName').toLowerCase() == 'a') { window.location = target.get('href'); } else if (target.get('tagName').toLowerCase() == 'input') { var parentelement = target.get('parentNode'); while (parentelement.get('tagName').toLowerCase() != 'form' && parentelement.get('tagName').toLowerCase() != 'body') { parentelement = parentelement.get('parentNode'); } if (parentelement.get('tagName').toLowerCase() == 'form') { parentelement.submit(); } } else if (M.cfg.developerdebug) { alert("Element of type " + target.get('tagName') + " is not supported by the M.util.show_confirm_dialog function. Use A or INPUT"); } }; var buttons = [ { text: M.str.moodle.cancel, handler: handle_cancel, isDefault: true }, { text: M.str.moodle.yes, handler: handle_yes } ]; simpledialog.cfg.queueProperty('buttons', buttons); simpledialog.render(document.body); simpledialog.show(); }); } /** Useful for full embedding of various stuff */ M.util.init_maximised_embed = function(Y, id) { var obj = Y.one('#'+id); if (!obj) { return; } var get_htmlelement_size = function(el, prop) { if (Y.Lang.isString(el)) { el = Y.one('#' + el); } var val = el.getStyle(prop); if (val == 'auto') { val = el.getComputedStyle(prop); } return parseInt(val); }; var resize_object = function() { obj.setStyle('width', '0px'); obj.setStyle('height', '0px'); var newwidth = get_htmlelement_size('content', 'width') - 15; if (newwidth > 600) { obj.setStyle('width', newwidth + 'px'); } else { obj.setStyle('width', '600px'); } var pageheight = get_htmlelement_size('page', 'height'); var objheight = get_htmlelement_size(obj, 'height'); var newheight = objheight + parseInt(obj.get('winHeight')) - pageheight - 30; if (newheight > 400) { obj.setStyle('height', newheight + 'px'); } else { obj.setStyle('height', '400px'); } }; resize_object(); // fix layout if window resized too window.onresize = function() { resize_object(); }; }; /** * Attach handler to single_select */ M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) { YUI(M.yui.loader).use('node', function(Y) { Y.on('change', function() { if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) { Y.one('#'+formid).submit(); } }, '#'+selectid); }); }; /** * Attach handler to url_select */ M.util.init_url_select = function(Y, formid, selectid, nothing) { YUI(M.yui.loader).use('node', function(Y) { Y.on('change', function() { if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) { window.location = M.cfg.wwwroot+Y.one('#'+selectid).get('value'); } }, '#'+selectid); }); }; /** * Breaks out all links to the top frame - used in frametop page layout. */ M.util.init_frametop = function(Y) { Y.all('a').each(function(node) { node.set('target', '_top'); }); Y.all('form').each(function(node) { node.set('target', '_top'); }); }; //=== old legacy JS code, hopefully to be replaced soon by M.xx.yy and YUI3 code === function popupchecker(msg) { var testwindow = window.open('', '', 'width=1,height=1,left=0,top=0,scrollbars=no'); if (!testwindow) { alert(msg); } else { testwindow.close(); } } function checkall() { var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].type == 'checkbox') { inputs[i].checked = true; } } } function checknone() { var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].type == 'checkbox') { inputs[i].checked = false; } } } function lockoptions(formid, master, subitems) { // Subitems is an array of names of sub items. // Optionally, each item in subitems may have a // companion hidden item in the form with the // same name but prefixed by "h". var form = document.forms[formid]; if (eval("form."+master+".checked")) { for (i=0; i