MDL-21695 adding help and link strings
[moodle.git] / lib / javascript-static.js
CommitLineData
47aa42e2 1// Miscellaneous core Javascript functions for Moodle
e50b4c89 2// Global M object is initilised in inline javascript
34f76e9a 3
e50b4c89
PS
4/**
5 * Add module to list of available modules that can be laoded from YUI.
126590b7 6 * @param {Array} modules
e50b4c89
PS
7 */
8M.yui.add_module = function(modules) {
126590b7 9 for (var modname in modules) {
fc367afb
PS
10 M.yui.loader.modules[modname] = modules[modname];
11 }
e50b4c89
PS
12};
13
38224dcb 14
bca09754
PS
15/**
16 * Various utility functions
17 */
d29b8f0f 18M.util = M.util || {};
38224dcb 19
2b728cb5
PS
20/**
21 * Language strings - initialised from page footer.
22 */
d29b8f0f 23M.str = M.str || {};
2b728cb5 24
38224dcb
PS
25/**
26 * Returns url for images.
126590b7
SH
27 * @param {String} imagename
28 * @param {String} component
29 * @return {String}
38224dcb
PS
30 */
31M.util.image_url = function(imagename, component) {
32 var url = M.cfg.wwwroot + '/theme/image.php?theme=' + M.cfg.theme + '&image=' + imagename;
33
34 if (M.cfg.themerev > 0) {
35 url = url + '&rev=' + M.cfg.themerev;
36 }
37
87ad1edc 38 if (component && component != '' && component != 'moodle' && component != 'core') {
38224dcb
PS
39 url = url + '&component=' + component;
40 }
41
42 return url;
43};
44
10eaeca8
PS
45M.util.create_UFO_object = function (eid, FO) {
46 UFO.create(FO, eid);
47}
48
b4430cd6
PS
49M.util.in_array = function(item, array){
50 for( var i = 0; i<array.length; i++){
51 if(item==array[i]){
52 return true;
53 }
54 }
55 return false;
56}
57
38224dcb
PS
58/**
59 * Init a collapsible region, see print_collapsible_region in weblib.php
126590b7
SH
60 * @param {YUI} Y YUI3 instance with all libraries loaded
61 * @param {String} id the HTML id for the div.
62 * @param {String} userpref the user preference that records the state of this box. false if none.
63 * @param {String} strtooltip
38224dcb
PS
64 */
65M.util.init_collapsible_region = function(Y, id, userpref, strtooltip) {
66 Y.use('anim', function(Y) {
3b044ba3 67 new M.util.CollapsibleRegion(Y, id, userpref, strtooltip);
38224dcb
PS
68 });
69};
70
71/**
126590b7 72 * Object to handle a collapsible region : instantiate and forget styled object
3b044ba3 73 *
126590b7 74 * @class
38224dcb 75 * @constructor
126590b7
SH
76 * @param {YUI} Y YUI3 instance with all libraries loaded
77 * @param {String} id The HTML id for the div.
78 * @param {String} userpref The user preference that records the state of this box. false if none.
79 * @param {String} strtooltip
38224dcb
PS
80 */
81M.util.CollapsibleRegion = function(Y, id, userpref, strtooltip) {
38224dcb
PS
82 // Record the pref name
83 this.userpref = userpref;
38224dcb
PS
84
85 // Find the divs in the document.
126590b7
SH
86 this.div = Y.one('#'+id);
87
88 // Get the caption for the collapsible region
89 var caption = this.div.one('#'+id + '_caption');
90 caption.setAttribute('title', strtooltip);
91
92 // Create a link
93 var a = Y.Node.create('<a href="#"></a>');
94 // Create a local scoped lamba function to move nodes to a new link
95 var movenode = function(node){
96 node.remove();
97 a.append(node);
98 };
99 // Apply the lamba function on each of the captions child nodes
100 caption.get('children').each(movenode, this);
101 caption.append(a);
102
103 // Get the height of the div at this point before we shrink it if required
104 var height = this.div.get('offsetHeight');
105 if (this.div.hasClass('collapsed')) {
106 // Add the correct image and record the YUI node created in the process
107 this.icon = Y.Node.create('<img src="'+M.util.image_url('t/collapsed', 'moodle')+'" alt="" />');
108 // Shrink the div as it is collapsed by default
109 this.div.setStyle('height', caption.get('offsetHeight')+'px');
110 } else {
111 // Add the correct image and record the YUI node created in the process
112 this.icon = Y.Node.create('<img src="'+M.util.image_url('t/expanded', 'moodle')+'" alt="" />');
38224dcb 113 }
126590b7 114 a.append(this.icon);
38224dcb
PS
115
116 // Create the animation.
126590b7 117 var animation = new Y.Anim({
38224dcb
PS
118 node: this.div,
119 duration: 0.3,
126590b7
SH
120 easing: Y.Easing.easeBoth,
121 to: {height:caption.get('offsetHeight')},
122 from: {height:height}
38224dcb 123 });
3b044ba3 124
38224dcb 125 // Handler for the animation finishing.
126590b7
SH
126 animation.on('end', function() {
127 this.div.toggleClass('collapsed');
128 if (this.div.hasClass('collapsed')) {
129 this.icon.set('src', M.util.image_url('t/collapsed', 'moodle'));
130 } else {
131 this.icon.set('src', M.util.image_url('t/expanded', 'moodle'));
fc367afb 132 }
38224dcb 133 }, this);
126590b7
SH
134
135 // Hook up the event handler.
136 a.on('click', function(e, animation) {
137 e.preventDefault();
138 // Animate to the appropriate size.
139 if (animation.get('running')) {
140 animation.stop();
141 }
142 animation.set('reverse', this.div.hasClass('collapsed'));
143 // Update the user preference.
144 if (this.userpref) {
41912a26 145 M.util.set_user_preference(this.userpref, !this.div.hasClass('collapsed'));
126590b7
SH
146 }
147 animation.run();
148 }, this, animation);
38224dcb
PS
149};
150
151/**
152 * The user preference that stores the state of this box.
153 * @property userpref
154 * @type String
155 */
156M.util.CollapsibleRegion.prototype.userpref = null;
157
158/**
159 * The key divs that make up this
126590b7
SH
160 * @property div
161 * @type Y.Node
38224dcb
PS
162 */
163M.util.CollapsibleRegion.prototype.div = null;
38224dcb
PS
164
165/**
166 * The key divs that make up this
167 * @property icon
126590b7 168 * @type Y.Node
38224dcb
PS
169 */
170M.util.CollapsibleRegion.prototype.icon = null;
171
41912a26
PS
172/**
173 * Makes a best effort to connect back to Moodle to update a user preference,
174 * however, there is no mechanism for finding out if the update succeeded.
175 *
176 * Before you can use this function in your JavsScript, you must have called
177 * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that
178 * the udpate is allowed, and how to safely clean and submitted values.
179 *
180 * @param String name the name of the setting to udpate.
181 * @param String the value to set it to.
182 */
183M.util.set_user_preference = function(name, value) {
20fb563e 184 YUI(M.yui.loader).use('io', function(Y) {
41912a26
PS
185 var url = M.cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' +
186 M.cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value);
187
188 // If we are a developer, ensure that failures are reported.
189 var cfg = {
20fb563e 190 method: 'get',
8dd14645 191 on: {}
41912a26
PS
192 };
193 if (M.cfg.developerdebug) {
194 cfg.on.failure = function(id, o, args) {
20fb563e 195 alert("Error updating user preference '" + name + "' using ajax. Clicking this link will repeat the Ajax call that failed so you can see the error: ");
41912a26
PS
196 }
197 }
198
199 // Make the request.
200 Y.io(url, cfg);
201 });
202};
203
20fb563e
PS
204/**
205 * Prints a confirmation dialog in the style of DOM.confirm().
206 * @param object event A YUI DOM event or null if launched manually
207 * @param string message The message to show in the dialog
208 * @param string url The URL to forward to if YES is clicked. Disabled if fn is given
209 * @param function fn A JS function to run if YES is clicked.
210 */
211M.util.show_confirm_dialog = function(e, args) {
212 var target = e.target;
213 if (e.preventDefault) {
214 e.preventDefault();
215 }
216
217 YUI(M.yui.loader).use('yui2-container', 'yui2-event', function(Y) {
218 var simpledialog = new YAHOO.widget.SimpleDialog('confirmdialog',
f855a5d2 219 {width: '300px',
20fb563e
PS
220 fixedcenter: true,
221 modal: true,
222 visible: false,
223 draggable: false
224 }
225 );
226
2b728cb5 227 simpledialog.setHeader(M.str.admin.confirmation);
20fb563e
PS
228 simpledialog.setBody(args.message);
229 simpledialog.cfg.setProperty('icon', YAHOO.widget.SimpleDialog.ICON_WARN);
230
231 var handle_cancel = function() {
232 simpledialog.hide();
233 };
234
235 var handle_yes = function() {
236 simpledialog.hide();
237
238 if (args.callback) {
239 // args comes from PHP, so callback will be a string, needs to be evaluated by JS
240 var callback = null;
241 if (Y.Lang.isFunction(args.callback)) {
242 callback = args.callback;
243 } else {
244 callback = eval('('+args.callback+')');
245 }
246
247 if (Y.Lang.isObject(args.scope)) {
248 var sc = args.scope;
249 } else {
250 var sc = e.target;
251 }
252
253 if (args.callbackargs) {
254 callback.apply(sc, args.callbackargs);
255 } else {
256 callback.apply(sc);
257 }
258 return;
259 }
260
261 if (target.get('tagName').toLowerCase() == 'a') {
262 window.location = target.get('href');
263 } else if (target.get('tagName').toLowerCase() == 'input') {
264 var parentelement = target.get('parentNode');
265 while (parentelement.get('tagName').toLowerCase() != 'form' && parentelement.get('tagName').toLowerCase() != 'body') {
266 parentelement = parentelement.get('parentNode');
267 }
268 if (parentelement.get('tagName').toLowerCase() == 'form') {
269 parentelement.submit();
270 }
271 } else if (M.cfg.developerdebug) {
272 alert("Element of type " + target.get('tagName') + " is not supported by the M.util.show_confirm_dialog function. Use A or INPUT");
273 }
274 };
275
f855a5d2
SH
276 var buttons = [ {text: M.str.moodle.cancel, handler: handle_cancel, isDefault: true},
277 {text: M.str.moodle.yes, handler: handle_yes} ];
20fb563e
PS
278
279 simpledialog.cfg.queueProperty('buttons', buttons);
280
281 simpledialog.render(document.body);
282 simpledialog.show();
283 });
284}
285
dd9b1882
PS
286/** Useful for full embedding of various stuff */
287M.util.init_maximised_embed = function(Y, id) {
288 var obj = Y.one('#'+id);
289 if (!obj) {
290 return;
291 }
292
293
294 var get_htmlelement_size = function(el, prop) {
295 if (Y.Lang.isString(el)) {
296 el = Y.one('#' + el);
297 }
298 var val = el.getStyle(prop);
299 if (val == 'auto') {
300 val = el.getComputedStyle(prop);
301 }
302 return parseInt(val);
303 };
304
305 var resize_object = function() {
306 obj.setStyle('width', '0px');
307 obj.setStyle('height', '0px');
308 var newwidth = get_htmlelement_size('content', 'width') - 15;
309
310 if (newwidth > 600) {
311 obj.setStyle('width', newwidth + 'px');
312 } else {
313 obj.setStyle('width', '600px');
314 }
315 var pageheight = get_htmlelement_size('page', 'height');
316 var objheight = get_htmlelement_size(obj, 'height');
317 var newheight = objheight + parseInt(obj.get('winHeight')) - pageheight - 30;
318 if (newheight > 400) {
319 obj.setStyle('height', newheight + 'px');
320 } else {
321 obj.setStyle('height', '400px');
322 }
323 };
324
325 resize_object();
326 // fix layout if window resized too
327 window.onresize = function() {
328 resize_object();
329 };
330};
331
a9967cf5
PS
332/**
333 * Attach handler to single_select
334 */
edc28287 335M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
4aea3cc7
PS
336 YUI(M.yui.loader).use('node', function(Y) {
337 Y.on('change', function() {
338 if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) {
339 Y.one('#'+formid).submit();
340 }
341 },
342 '#'+selectid);
343 });
a9967cf5 344};
dd9b1882 345
4d10e579
PS
346/**
347 * Attach handler to url_select
348 */
349M.util.init_url_select = function(Y, formid, selectid, nothing) {
4aea3cc7
PS
350 YUI(M.yui.loader).use('node', function(Y) {
351 Y.on('change', function() {
352 if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) {
353 window.location = M.cfg.wwwroot+Y.one('#'+selectid).get('value');
354 }
355 },
356 '#'+selectid);
357 });
358};
359
360/**
361 * Breaks out all links to the top frame - used in frametop page layout.
362 */
363M.util.init_frametop = function(Y) {
364 Y.all('a').each(function(node) {
365 node.set('target', '_top');
366 });
367 Y.all('form').each(function(node) {
368 node.set('target', '_top');
369 });
4d10e579
PS
370};
371
38224dcb
PS
372//=== old legacy JS code, hopefully to be replaced soon by M.xx.yy and YUI3 code ===
373
b48fd7b5 374function popupchecker(msg) {
496e3ccd 375 var testwindow = window.open('', '', 'width=1,height=1,left=0,top=0,scrollbars=no');
f6b6861d 376 if (!testwindow) {
377 alert(msg);
378 } else {
b48fd7b5 379 testwindow.close();
380 }
381}
382
63d28811 383function checkall() {
214f5850 384 var inputs = document.getElementsByTagName('input');
385 for (var i = 0; i < inputs.length; i++) {
386 if (inputs[i].type == 'checkbox') {
387 inputs[i].checked = true;
388 }
d2ce367f 389 }
63d28811 390}
391
03f9425f 392function checknone() {
214f5850 393 var inputs = document.getElementsByTagName('input');
394 for (var i = 0; i < inputs.length; i++) {
395 if (inputs[i].type == 'checkbox') {
396 inputs[i].checked = false;
397 }
d2ce367f 398 }
03f9425f 399}
400
d2ce367f 401function lockoptions(formid, master, subitems) {
027d0485 402 // Subitems is an array of names of sub items.
403 // Optionally, each item in subitems may have a
63d28811 404 // companion hidden item in the form with the
027d0485 405 // same name but prefixed by "h".
d2ce367f 406 var form = document.forms[formid];
407
408 if (eval("form."+master+".checked")) {
63d28811 409 for (i=0; i<subitems.length; i++) {
410 unlockoption(form, subitems[i]);
411 }
412 } else {
413 for (i=0; i<subitems.length; i++) {
414 lockoption(form, subitems[i]);
415 }
416 }
417 return(true);
418}
419
f07b9627 420function lockoption(form,item) {
d2ce367f 421 eval("form."+item+".disabled=true");/* IE thing */
422 if(form.elements['h'+item]) {
423 eval("form.h"+item+".value=1");
f07b9627 424 }
425}
426
427function unlockoption(form,item) {
d2ce367f 428 eval("form."+item+".disabled=false");/* IE thing */
429 if(form.elements['h'+item]) {
430 eval("form.h"+item+".value=0");
f07b9627 431 }
432}
dd07bbac 433
b51709c1 434/**
435 * Get the value of the 'virtual form element' with a particular name. That is,
436 * abstracts away the difference between a normal form element, like a select
437 * which is a single HTML element with a .value property, and a set of radio
438 * buttons, which is several HTML elements.
439 *
440 * @param form a HTML form.
441 * @param master the name of an element in that form.
442 * @return the value of that element.
443 */
444function get_form_element_value(form, name) {
445 var element = form[name];
446 if (!element) {
447 return null;
448 }
449 if (element.tagName) {
450 // Ordinarly thing like a select box.
451 return element.value;
452 }
453 // Array of things, like radio buttons.
454 for (var j = 0; j < element.length; j++) {
455 var el = element[j];
456 if (el.checked) {
457 return el.value;
458 }
459 }
460 return null;
461}
462
463
464/**
465 * Set the disabled state of the 'virtual form element' with a particular name.
466 * This abstracts away the difference between a normal form element, like a select
467 * which is a single HTML element with a .value property, and a set of radio
468 * buttons, which is several HTML elements.
469 *
470 * @param form a HTML form.
471 * @param master the name of an element in that form.
472 * @param disabled the disabled state to set.
473 */
474function set_form_element_disabled(form, name, disabled) {
475 var element = form[name];
476 if (!element) {
477 return;
478 }
479 if (element.tagName) {
480 // Ordinarly thing like a select box.
481 element.disabled = disabled;
482 }
483 // Array of things, like radio buttons.
484 for (var j = 0; j < element.length; j++) {
485 var el = element[j];
486 el.disabled = disabled;
487 }
488}
dd07bbac 489
41f23791 490/**
491 * Set the hidden state of the 'virtual form element' with a particular name.
492 * This abstracts away the difference between a normal form element, like a select
493 * which is a single HTML element with a .value property, and a set of radio
494 * buttons, which is several HTML elements.
495 *
496 * @param form a HTML form.
497 * @param master the name of an element in that form.
498 * @param hidden the hidden state to set.
499 */
500function set_form_element_hidden(form, name, hidden) {
501 var element = form[name];
502 if (!element) {
503 return;
504 }
505 if (element.tagName) {
506 var el = findParentNode(element, 'DIV', 'fitem', false);
507 if (el!=null) {
508 el.style.display = hidden ? 'none' : '';
509 el.style.visibility = hidden ? 'hidden' : '';
510 }
511 }
512 // Array of things, like radio buttons.
513 for (var j = 0; j < element.length; j++) {
514 var el = findParentNode(element[j], 'DIV', 'fitem', false);
515 if (el!=null) {
516 el.style.display = hidden ? 'none' : '';
517 el.style.visibility = hidden ? 'hidden' : '';
518 }
519 }
520}
521
50ef8eb9 522function lockoptionsall(formid) {
dd07bbac 523 var form = document.forms[formid];
b51709c1 524 var dependons = eval(formid + 'items');
525 var tolock = [];
41f23791 526 var tohide = [];
dd07bbac 527 for (var dependon in dependons) {
d63ef3b8 528 // change for MooTools compatibility
529 if (!dependons.propertyIsEnumerable(dependon)) {
530 continue;
531 }
b51709c1 532 if (!form[dependon]) {
4bc24867 533 continue;
534 }
dd07bbac 535 for (var condition in dependons[dependon]) {
536 for (var value in dependons[dependon][condition]) {
537 var lock;
41f23791 538 var hide = false;
dd07bbac 539 switch (condition) {
540 case 'notchecked':
f855a5d2 541 lock = !form[dependon].checked;break;
dd07bbac 542 case 'checked':
f855a5d2 543 lock = form[dependon].checked;break;
dd07bbac 544 case 'noitemselected':
f855a5d2 545 lock = form[dependon].selectedIndex == -1;break;
dd07bbac 546 case 'eq':
f855a5d2 547 lock = get_form_element_value(form, dependon) == value;break;
41f23791 548 case 'hide':
549 // hide as well as disable
f855a5d2 550 hide = true;break;
dd07bbac 551 default:
f855a5d2 552 lock = get_form_element_value(form, dependon) != value;break;
dd07bbac 553 }
554 for (var ei in dependons[dependon][condition][value]) {
632b88d5 555 var eltolock = dependons[dependon][condition][value][ei];
41f23791 556 if (hide) {
557 tohide[eltolock] = true;
558 }
b51709c1 559 if (tolock[eltolock] != null) {
560 tolock[eltolock] = lock || tolock[eltolock];
632b88d5 561 } else {
562 tolock[eltolock] = lock;
563 }
dd07bbac 564 }
565 }
50ef8eb9 566 }
dd07bbac 567 }
b51709c1 568 for (var el in tolock) {
d63ef3b8 569 // change for MooTools compatibility
570 if (!tolock.propertyIsEnumerable(el)) {
571 continue;
572 }
b51709c1 573 set_form_element_disabled(form, el, tolock[el]);
41f23791 574 if (tohide.propertyIsEnumerable(el)) {
575 set_form_element_hidden(form, el, tolock[el]);
576 }
632b88d5 577 }
dd07bbac 578 return true;
50ef8eb9 579}
580
d01a38cb 581function lockoptionsallsetup(formid) {
dd07bbac 582 var form = document.forms[formid];
583 var dependons = eval(formid+'items');
584 for (var dependon in dependons) {
d63ef3b8 585 // change for MooTools compatibility
586 if (!dependons.propertyIsEnumerable(dependon)) {
587 continue;
588 }
b51709c1 589 var masters = form[dependon];
590 if (!masters) {
4bc24867 591 continue;
592 }
a4db2e7c 593 if (masters.tagName) {
b51709c1 594 // If master is radio buttons, we get an array, otherwise we don't.
595 // Convert both cases to an array for convinience.
596 masters = [masters];
597 }
598 for (var j = 0; j < masters.length; j++) {
599 master = masters[j];
600 master.formid = formid;
601 master.onclick = function() {return lockoptionsall(this.formid);};
602 master.onblur = function() {return lockoptionsall(this.formid);};
603 master.onchange = function() {return lockoptionsall(this.formid);};
604 }
dd07bbac 605 }
b51709c1 606 for (var i = 0; i < form.elements.length; i++) {
dd07bbac 607 var formelement = form.elements[i];
608 if (formelement.type=='reset') {
c0056e22 609 formelement.formid = formid;
610 formelement.onclick = function() {this.form.reset();return lockoptionsall(this.formid);};
611 formelement.onblur = function() {this.form.reset();return lockoptionsall(this.formid);};
612 formelement.onchange = function() {this.form.reset();return lockoptionsall(this.formid);};
dd07bbac 613 }
614 }
615 return lockoptionsall(formid);
d01a38cb 616}
50ef8eb9 617
45caa363 618/**
619 * Either check, or uncheck, all checkboxes inside the element with id is
620 * @param id the id of the container
621 * @param checked the new state, either '' or 'checked'.
622 */
623function select_all_in_element_with_id(id, checked) {
624 var container = document.getElementById(id);
625 if (!container) {
626 return;
627 }
628 var inputs = container.getElementsByTagName('input');
629 for (var i = 0; i < inputs.length; ++i) {
630 if (inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
631 inputs[i].checked = checked;
632 }
633 }
634}
635
8ceb09e0 636function select_all_in(elTagName, elClass, elId) {
d2ce367f 637 var inputs = document.getElementsByTagName('input');
8ceb09e0 638 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 639 for(var i = 0; i < inputs.length; ++i) {
bee40515 640 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 641 inputs[i].checked = 'checked';
642 }
643 }
644}
645
8ceb09e0 646function deselect_all_in(elTagName, elClass, elId) {
363cb62c 647 var inputs = document.getElementsByTagName('INPUT');
8ceb09e0 648 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 649 for(var i = 0; i < inputs.length; ++i) {
bee40515 650 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 651 inputs[i].checked = '';
652 }
653 }
654}
655
656function confirm_if(expr, message) {
657 if(!expr) {
658 return true;
659 }
660 return confirm(message);
661}
47aa42e2 662
663
664/*
665 findParentNode (start, elementName, elementClass, elementID)
50ef8eb9 666
47aa42e2 667 Travels up the DOM hierarchy to find a parent element with the
668 specified tag name, class, and id. All conditions must be met,
669 but any can be ommitted. Returns the BODY element if no match
670 found.
671*/
672function findParentNode(el, elName, elClass, elId) {
45caa363 673 while (el.nodeName.toUpperCase() != 'BODY') {
674 if ((!elName || el.nodeName.toUpperCase() == elName) &&
47aa42e2 675 (!elClass || el.className.indexOf(elClass) != -1) &&
45caa363 676 (!elId || el.id == elId)) {
47aa42e2 677 break;
678 }
679 el = el.parentNode;
680 }
681 return el;
682}
19194f82 683/*
684 findChildNode (start, elementName, elementClass, elementID)
685
686 Travels down the DOM hierarchy to find all child elements with the
687 specified tag name, class, and id. All conditions must be met,
688 but any can be ommitted.
a23f0aaf 689 Doesn't examine children of matches.
19194f82 690*/
691function findChildNodes(start, tagName, elementClass, elementID, elementName) {
692 var children = new Array();
83c9a8a2 693 for (var i = 0; i < start.childNodes.length; i++) {
a23f0aaf 694 var classfound = false;
83c9a8a2 695 var child = start.childNodes[i];
a23f0aaf 696 if((child.nodeType == 1) &&//element node type
b51709c1 697 (elementClass && (typeof(child.className)=='string'))) {
a23f0aaf 698 var childClasses = child.className.split(/\s+/);
b51709c1 699 for (var childClassIndex in childClasses) {
700 if (childClasses[childClassIndex]==elementClass) {
a23f0aaf 701 classfound = true;
702 break;
703 }
704 }
705 }
f07b9627 706 if(child.nodeType == 1) { //element node type
707 if ( (!tagName || child.nodeName == tagName) &&
708 (!elementClass || classfound)&&
709 (!elementID || child.id == elementID) &&
710 (!elementName || child.name == elementName))
711 {
712 children = children.concat(child);
713 } else {
714 children = children.concat(findChildNodes(child, tagName, elementClass, elementID, elementName));
715 }
19194f82 716 }
19194f82 717 }
718 return children;
719}
720/*
721 elementSetHide (elements, hide)
722
723 Adds or removes the "hide" class for the specified elements depending on boolean hide.
724*/
725function elementShowAdvanced(elements, show) {
b51709c1 726 for (var elementIndex in elements) {
19194f82 727 element = elements[elementIndex];
728 element.className = element.className.replace(new RegExp(' ?hide'), '')
729 if(!show) {
730 element.className += ' hide';
731 }
732 }
733}
734
cd350b53 735function showAdvancedInit(addBefore, nameAttr, buttonLabel, hideText, showText) {
736 var showHideButton = document.createElement("input");
737 showHideButton.type = 'button';
738 showHideButton.value = buttonLabel;
739 showHideButton.name = nameAttr;
740 showHideButton.moodle = {
2b728cb5
PS
741 hideLabel: M.str.form.hideadvanced,
742 showLabel: M.str.form.showadvanced
cd350b53 743 };
744 YAHOO.util.Event.addListener(showHideButton, 'click', showAdvancedOnClick);
745 el = document.getElementById(addBefore);
746 el.parentNode.insertBefore(showHideButton, el);
747}
748
749function showAdvancedOnClick(e) {
750 var button = e.target ? e.target : e.srcElement;
751
19194f82 752 var toSet=findChildNodes(button.form, null, 'advanced');
753 var buttontext = '';
74d15d35 754 if (button.form.elements['mform_showadvanced_last'].value == '0' || button.form.elements['mform_showadvanced_last'].value == '' ) {
19194f82 755 elementShowAdvanced(toSet, true);
cd350b53 756 buttontext = button.moodle.hideLabel;
19194f82 757 button.form.elements['mform_showadvanced_last'].value = '1';
758 } else {
759 elementShowAdvanced(toSet, false);
cd350b53 760 buttontext = button.moodle.showLabel;
19194f82 761 button.form.elements['mform_showadvanced_last'].value = '0';
762 }
763 var formelements = button.form.elements;
4ea054ec 764 // Fixed MDL-10506
b51709c1 765 for (var i = 0; i < formelements.length; i++) {
766 if (formelements[i] && formelements[i].name && (formelements[i].name=='mform_showadvanced')) {
19194f82 767 formelements[i].value = buttontext;
768 }
769 }
770 //never submit the form if js is enabled.
771 return false;
772}
47aa42e2 773
54bb33eb 774function unmaskPassword(id) {
239ade45 775 var pw = document.getElementById(id);
54bb33eb 776 var chb = document.getElementById(id+'unmask');
239ade45 777
778 try {
779 // first try IE way - it can not set name attribute later
780 if (chb.checked) {
781 var newpw = document.createElement('<input type="text" name="'+pw.name+'">');
782 } else {
783 var newpw = document.createElement('<input type="password" name="'+pw.name+'">');
784 }
eba8cd63 785 newpw.attributes['class'].nodeValue = pw.attributes['class'].nodeValue;
239ade45 786 } catch (e) {
787 var newpw = document.createElement('input');
788 newpw.setAttribute('name', pw.name);
789 if (chb.checked) {
790 newpw.setAttribute('type', 'text');
791 } else {
792 newpw.setAttribute('type', 'password');
793 }
eba8cd63 794 newpw.setAttribute('class', pw.getAttribute('class'));
239ade45 795 }
796 newpw.id = pw.id;
797 newpw.size = pw.size;
798 newpw.onblur = pw.onblur;
799 newpw.onchange = pw.onchange;
800 newpw.value = pw.value;
801 pw.parentNode.replaceChild(newpw, pw);
802}
803
8e7cebb0 804/**
805 * Search a Moodle form to find all the fdate_time_selector and fdate_selector
806 * elements, and add date_selector_calendar instance to each.
807 */
808function init_date_selectors(firstdayofweek) {
8e7cebb0 809 var els = YAHOO.util.Dom.getElementsByClassName('fdate_time_selector', 'fieldset');
810 for (var i = 0; i < els.length; i++) {
811 new date_selector_calendar(els[i], firstdayofweek);
812 }
813 els = YAHOO.util.Dom.getElementsByClassName('fdate_selector', 'fieldset');
814 for (i = 0; i < els.length; i++) {
815 new date_selector_calendar(els[i], firstdayofweek);
816 }
817}
818
819/**
9bad31f5 820 * Constructor for a JavaScript object that connects to a fdate_time_selector
8e7cebb0 821 * or a fdate_selector in a Moodle form, and shows a popup calendar whenever
822 * that element has keyboard focus.
823 * @param el the fieldset class="fdate_time_selector" or "fdate_selector".
824 */
825function date_selector_calendar(el, firstdayofweek) {
826 // Ensure that the shared div and calendar exist.
827 if (!date_selector_calendar.panel) {
828 date_selector_calendar.panel = new YAHOO.widget.Panel('date_selector_calendar_panel',
b3a49376 829 {visible: false, draggable: false});
8e7cebb0 830 var div = document.createElement('div');
831 date_selector_calendar.panel.setBody(div);
832 date_selector_calendar.panel.render(document.body);
833
834 YAHOO.util.Event.addListener(document, 'click', date_selector_calendar.document_click);
835 date_selector_calendar.panel.showEvent.subscribe(function() {
836 date_selector_calendar.panel.fireEvent('changeContent');
837 });
b3a49376 838 date_selector_calendar.panel.hideEvent.subscribe(date_selector_calendar.release_current);
8e7cebb0 839
840 date_selector_calendar.calendar = new YAHOO.widget.Calendar(div,
841 {iframe: false, hide_blank_weeks: true, start_weekday: firstdayofweek});
842 date_selector_calendar.calendar.renderEvent.subscribe(function() {
843 date_selector_calendar.panel.fireEvent('changeContent');
bd55319b 844 date_selector_calendar.delayed_reposition();
8e7cebb0 845 });
8e7cebb0 846 }
847
848 this.fieldset = el;
849 var controls = el.getElementsByTagName('select');
850 for (var i = 0; i < controls.length; i++) {
8e7cebb0 851 if (/\[year\]$/.test(controls[i].name)) {
852 this.yearselect = controls[i];
9bad31f5 853 } else if (/\[month\]$/.test(controls[i].name)) {
8e7cebb0 854 this.monthselect = controls[i];
9bad31f5 855 } else if (/\[day\]$/.test(controls[i].name)) {
8e7cebb0 856 this.dayselect = controls[i];
9bad31f5 857 } else {
bd55319b 858 YAHOO.util.Event.addFocusListener(controls[i], date_selector_calendar.cancel_any_timeout, this);
9bad31f5 859 YAHOO.util.Event.addBlurListener(controls[i], this.blur_event, this);
8e7cebb0 860 }
861 }
862 if (!(this.yearselect && this.monthselect && this.dayselect)) {
863 throw 'Failed to initialise calendar.';
864 }
9bad31f5 865 YAHOO.util.Event.addFocusListener([this.yearselect, this.monthselect, this.dayselect], this.focus_event, this);
866 YAHOO.util.Event.addBlurListener([this.yearselect, this.monthselect, this.dayselect], this.blur_event, this);
8e7cebb0 867
868 this.enablecheckbox = el.getElementsByTagName('input')[0];
869 if (this.enablecheckbox) {
870 YAHOO.util.Event.addFocusListener(this.enablecheckbox, this.focus_event, this);
871 YAHOO.util.Event.addListener(this.enablecheckbox, 'change', this.focus_event, this);
872 YAHOO.util.Event.addBlurListener(this.enablecheckbox, this.blur_event, this);
873 }
874}
875
876/** The pop-up calendar that contains the calendar. */
877date_selector_calendar.panel = null;
878
879/** The shared YAHOO.widget.Calendar used by all date_selector_calendars. */
880date_selector_calendar.calendar = null;
881
882/** The date_selector_calendar that currently owns the shared stuff. */
883date_selector_calendar.currentowner = null;
884
885/** Used as a timeout when hiding the calendar on blur - so we don't hide the calendar
886 * if we are just jumping from on of our controls to another. */
887date_selector_calendar.hidetimeout = null;
888
bd55319b 889/** Timeout for repositioning after a delay after a change of months. */
890date_selector_calendar.repositiontimeout = null;
8e7cebb0 891
bd55319b 892/** Member variables. Pointers to various bits of the DOM. */
8e7cebb0 893date_selector_calendar.prototype.fieldset = null;
894date_selector_calendar.prototype.yearselect = null;
895date_selector_calendar.prototype.monthselect = null;
896date_selector_calendar.prototype.dayselect = null;
897date_selector_calendar.prototype.enablecheckbox = null;
898
bd55319b 899date_selector_calendar.cancel_any_timeout = function() {
8e7cebb0 900 if (date_selector_calendar.hidetimeout) {
901 clearTimeout(date_selector_calendar.hidetimeout);
902 date_selector_calendar.hidetimeout = null;
903 }
bd55319b 904 if (date_selector_calendar.repositiontimeout) {
905 clearTimeout(date_selector_calendar.repositiontimeout);
906 date_selector_calendar.repositiontimeout = null;
907 }
908}
909
910date_selector_calendar.delayed_reposition = function() {
911 if (date_selector_calendar.repositiontimeout) {
912 clearTimeout(date_selector_calendar.repositiontimeout);
913 date_selector_calendar.repositiontimeout = null;
914 }
b3a49376 915 date_selector_calendar.repositiontimeout = setTimeout(date_selector_calendar.fix_position, 500);
bd55319b 916}
917
918date_selector_calendar.fix_position = function() {
919 if (date_selector_calendar.currentowner) {
920 date_selector_calendar.panel.cfg.setProperty('context', [date_selector_calendar.currentowner.fieldset, 'bl', 'tl']);
921 }
8e7cebb0 922}
923
b3a49376 924date_selector_calendar.release_current = function() {
925 if (date_selector_calendar.currentowner) {
926 date_selector_calendar.currentowner.release_calendar();
927 }
928}
929
8e7cebb0 930date_selector_calendar.prototype.focus_event = function(e, me) {
bd55319b 931 date_selector_calendar.cancel_any_timeout();
8e7cebb0 932 if (me.enablecheckbox == null || me.enablecheckbox.checked) {
933 me.claim_calendar();
934 } else {
935 if (date_selector_calendar.currentowner) {
936 date_selector_calendar.currentowner.release_calendar();
937 }
938 }
939}
940
941date_selector_calendar.prototype.blur_event = function(e, me) {
b3a49376 942 date_selector_calendar.hidetimeout = setTimeout(date_selector_calendar.release_current, 300);
8e7cebb0 943}
944
945date_selector_calendar.prototype.handle_select_change = function(e, me) {
946 me.set_date_from_selects();
947}
948
949date_selector_calendar.document_click = function(event) {
950 if (date_selector_calendar.currentowner) {
951 var currentcontainer = date_selector_calendar.currentowner.fieldset;
952 var eventarget = YAHOO.util.Event.getTarget(event);
b3a49376 953 if (YAHOO.util.Dom.isAncestor(currentcontainer, eventarget)) {
954 setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
955 } else {
8e7cebb0 956 date_selector_calendar.currentowner.release_calendar();
957 }
958 }
f8065dd2 959}
8e7cebb0 960
961date_selector_calendar.prototype.claim_calendar = function() {
bd55319b 962 date_selector_calendar.cancel_any_timeout();
8e7cebb0 963 if (date_selector_calendar.currentowner == this) {
964 return;
965 }
966 if (date_selector_calendar.currentowner) {
967 date_selector_calendar.currentowner.release_calendar();
968 }
969
bd55319b 970 if (date_selector_calendar.currentowner != this) {
971 this.connect_handlers();
972 }
973 date_selector_calendar.currentowner = this;
974
8e7cebb0 975 date_selector_calendar.calendar.cfg.setProperty('mindate', new Date(this.yearselect.options[0].value, 0, 1));
976 date_selector_calendar.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.options[this.yearselect.options.length - 1].value, 11, 31));
bd55319b 977 this.fieldset.insertBefore(date_selector_calendar.panel.element, this.yearselect.nextSibling);
8e7cebb0 978 this.set_date_from_selects();
979 date_selector_calendar.panel.show();
980 var me = this;
bd55319b 981 setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
8e7cebb0 982}
983
984date_selector_calendar.prototype.set_date_from_selects = function() {
985 var year = parseInt(this.yearselect.value);
986 var month = parseInt(this.monthselect.value) - 1;
987 var day = parseInt(this.dayselect.value);
988 date_selector_calendar.calendar.select(new Date(year, month, day));
989 date_selector_calendar.calendar.setMonth(month);
990 date_selector_calendar.calendar.setYear(year);
bd55319b 991 date_selector_calendar.calendar.render();
992 date_selector_calendar.fix_position();
8e7cebb0 993}
994
995date_selector_calendar.prototype.set_selects_from_date = function(eventtype, args) {
996 var date = args[0][0];
997 var newyear = date[0];
998 var newindex = newyear - this.yearselect.options[0].value;
999 this.yearselect.selectedIndex = newindex;
1000 this.monthselect.selectedIndex = date[1] - this.monthselect.options[0].value;
1001 this.dayselect.selectedIndex = date[2] - this.dayselect.options[0].value;
1002}
1003
1004date_selector_calendar.prototype.connect_handlers = function() {
1005 YAHOO.util.Event.addListener([this.yearselect, this.monthselect, this.dayselect], 'change', this.handle_select_change, this);
1006 date_selector_calendar.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
1007}
1008
1009date_selector_calendar.prototype.release_calendar = function() {
1010 date_selector_calendar.panel.hide();
1011 date_selector_calendar.currentowner = null;
1012 YAHOO.util.Event.removeListener([this.yearselect, this.monthselect, this.dayselect], this.handle_select_change);
1013 date_selector_calendar.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
1014}
1015
47aa42e2 1016function filterByParent(elCollection, parentFinder) {
1017 var filteredCollection = [];
45caa363 1018 for (var i = 0; i < elCollection.length; ++i) {
47aa42e2 1019 var findParent = parentFinder(elCollection[i]);
45caa363 1020 if (findParent.nodeName.toUpperCase != 'BODY') {
47aa42e2 1021 filteredCollection.push(elCollection[i]);
1022 }
1023 }
1024 return filteredCollection;
1025}
1026
7979105c 1027/*
1028 All this is here just so that IE gets to handle oversized blocks
1029 in a visually pleasing manner. It does a browser detect. So sue me.
1030*/
1031
1032function fix_column_widths() {
1033 var agt = navigator.userAgent.toLowerCase();
1034 if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
1035 fix_column_width('left-column');
1036 fix_column_width('right-column');
1037 }
1038}
1039
1040function fix_column_width(colName) {
1041 if(column = document.getElementById(colName)) {
1042 if(!column.offsetWidth) {
1043 setTimeout("fix_column_width('" + colName + "')", 20);
1044 return;
1045 }
1046
1047 var width = 0;
1048 var nodes = column.childNodes;
1049
1050 for(i = 0; i < nodes.length; ++i) {
6605ff8c 1051 if(nodes[i].className.indexOf("block") != -1 ) {
7979105c 1052 if(width < nodes[i].offsetWidth) {
1053 width = nodes[i].offsetWidth;
1054 }
1055 }
1056 }
1057
1058 for(i = 0; i < nodes.length; ++i) {
6605ff8c 1059 if(nodes[i].className.indexOf("block") != -1 ) {
7979105c 1060 nodes[i].style.width = width + 'px';
1061 }
1062 }
1063 }
1064}
d13c5938 1065
1066
1067/*
9f439b17 1068 Insert myValue at current cursor position
1069 */
d13c5938 1070function insertAtCursor(myField, myValue) {
9f439b17 1071 // IE support
1072 if (document.selection) {
1073 myField.focus();
1074 sel = document.selection.createRange();
1075 sel.text = myValue;
1076 }
1077 // Mozilla/Netscape support
1078 else if (myField.selectionStart || myField.selectionStart == '0') {
1079 var startPos = myField.selectionStart;
1080 var endPos = myField.selectionEnd;
1081 myField.value = myField.value.substring(0, startPos)
1082 + myValue + myField.value.substring(endPos, myField.value.length);
1083 } else {
1084 myField.value += myValue;
1085 }
d13c5938 1086}
7470d6de 1087
1088
1089/*
c0056e22 1090 Call instead of setting window.onload directly or setting body onload=.
1091 Adds your function to a chain of functions rather than overwriting anything
1092 that exists.
1093*/
7470d6de 1094function addonload(fn) {
1095 var oldhandler=window.onload;
1096 window.onload=function() {
1097 if(oldhandler) oldhandler();
c0056e22 1098 fn();
7470d6de 1099 }
1100}
7d2a0492 1101/**
1102 * Replacement for getElementsByClassName in browsers that aren't cool enough
6f5e0852 1103 *
7d2a0492 1104 * Relying on the built-in getElementsByClassName is far, far faster than
1105 * using YUI.
6f5e0852 1106 *
7d2a0492 1107 * Note: the third argument used to be an object with odd behaviour. It now
1108 * acts like the 'name' in the HTML5 spec, though the old behaviour is still
1109 * mimicked if you pass an object.
1110 *
1111 * @param {Node} oElm The top-level node for searching. To search a whole
1112 * document, use `document`.
1113 * @param {String} strTagName filter by tag names
1114 * @param {String} name same as HTML5 spec
1115 */
1116function getElementsByClassName(oElm, strTagName, name) {
1117 // for backwards compatibility
1118 if(typeof name == "object") {
1119 var names = new Array();
1120 for(var i=0; i<name.length; i++) names.push(names[i]);
1121 name = names.join('');
1122 }
1123 // use native implementation if possible
1124 if (oElm.getElementsByClassName && Array.filter) {
1125 if (strTagName == '*') {
1126 return oElm.getElementsByClassName(name);
1127 } else {
1128 return Array.filter(oElm.getElementsByClassName(name), function(el) {
1129 return el.nodeName.toLowerCase() == strTagName.toLowerCase();
1130 });
1131 }
1132 }
1133 // native implementation unavailable, fall back to slow method
c849ed1e 1134 var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
1135 var arrReturnElements = new Array();
1136 var arrRegExpClassNames = new Array();
7d2a0492 1137 var names = name.split(' ');
1138 for(var i=0; i<names.length; i++) {
1139 arrRegExpClassNames.push(new RegExp("(^|\\s)" + names[i].replace(/\-/g, "\\-") + "(\\s|$)"));
c849ed1e 1140 }
1141 var oElement;
1142 var bMatchesAll;
b51709c1 1143 for(var j=0; j<arrElements.length; j++) {
c849ed1e 1144 oElement = arrElements[j];
1145 bMatchesAll = true;
b51709c1 1146 for(var k=0; k<arrRegExpClassNames.length; k++) {
1147 if(!arrRegExpClassNames[k].test(oElement.className)) {
c849ed1e 1148 bMatchesAll = false;
1149 break;
1150 }
1151 }
b51709c1 1152 if(bMatchesAll) {
c849ed1e 1153 arrReturnElements.push(oElement);
1154 }
1155 }
1156 return (arrReturnElements)
1157}
77241d9b 1158
f8065dd2 1159function openpopup(event, args) {
1160
2cf81209
SH
1161 if (event) {
1162 YAHOO.util.Event.preventDefault(event);
1163 }
36282d85 1164
2cf81209
SH
1165 var fullurl = args.url;
1166 if (!args.url.match(/https?:\/\//)) {
1167 fullurl = M.cfg.wwwroot + args.url;
77241d9b 1168 }
2cf81209
SH
1169 var windowobj = window.open(fullurl,args.name,args.options);
1170 if (!windowobj) {
1171 return true;
1172 }
1173 if (args.fullscreen) {
1174 windowobj.moveTo(0,0);
1175 windowobj.resizeTo(screen.availWidth,screen.availHeight);
1176 }
1177 windowobj.focus();
1178
1179 return false;
77241d9b 1180}
740939ec 1181
1182/* This is only used on a few help pages. */
1183emoticons_help = {
1184 inputarea: null,
1185
1186 init: function(formname, fieldname, listid) {
1187 if (!opener || !opener.document.forms[formname]) {
1188 return;
1189 }
1190 emoticons_help.inputarea = opener.document.forms[formname][fieldname];
1191 if (!emoticons_help.inputarea) {
1192 return;
1193 }
1194 var emoticons = document.getElementById(listid).getElementsByTagName('li');
1195 for (var i = 0; i < emoticons.length; i++) {
1196 var text = emoticons[i].getElementsByTagName('img')[0].alt;
1197 YAHOO.util.Event.addListener(emoticons[i], 'click', emoticons_help.inserttext, text);
1198 }
1199 },
1200
1201 inserttext: function(e, text) {
1202 text = ' ' + text + ' ';
1203 if (emoticons_help.inputarea.createTextRange && emoticons_help.inputarea.caretPos) {
1204 var caretPos = emoticons_help.inputarea.caretPos;
1205 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
1206 } else {
1207 emoticons_help.inputarea.value += text;
1208 }
1209 emoticons_help.inputarea.focus();
1210 }
bd1884fe 1211}
1212
d4a03c00 1213/**
1214 * Oject to handle expanding and collapsing blocks when an icon is clicked on.
1215 * @constructor
1216 * @param String id the HTML id for the div.
1217 * @param String userpref the user preference that records the state of this block.
1218 * @param String visibletooltip tool tip/alt to show when the block is visible.
1219 * @param String hiddentooltip tool tip/alt to show when the block is hidden.
1220 * @param String visibleicon URL of the icon to show when the block is visible.
1221 * @param String hiddenicon URL of the icon to show when the block is hidden.
1222 */
1223function block_hider(id, userpref, visibletooltip, hiddentooltip, visibleicon, hiddenicon) {
1224 // Find the elemen that is the block.
1225 this.block = document.getElementById(id);
1226 var title_div = YAHOO.util.Dom.getElementsByClassName('title', 'div', this.block);
1227 if (!title_div || !title_div[0]) {
1228 return this;
1229 }
1230 title_div = title_div[0];
1231 this.ishidden = YAHOO.util.Dom.hasClass(this.block, 'hidden');
1232
1233 // Record the pref name
1234 this.userpref = userpref;
1235 this.visibletooltip = visibletooltip;
1236 this.hiddentooltip = hiddentooltip;
1237 this.visibleicon = visibleicon;
1238 this.hiddenicon = hiddenicon;
1239
1240 // Add the icon.
1241 this.icon = document.createElement('input');
1242 this.icon.type = 'image';
d4a03c00 1243 this.update_state();
46de77b6
SH
1244
1245 var blockactions = YAHOO.util.Dom.getElementsByClassName('block_action', 'div', title_div);
1246 if (blockactions && blockactions[0]) {
1247 blockactions[0].insertBefore(this.icon, blockactions[0].firstChild);
1248 }
d4a03c00 1249
1250 // Hook up the event handler.
1251 YAHOO.util.Event.addListener(this.icon, 'click', this.handle_click, null, this);
1252}
1253
1254/** Handle click on a block show/hide icon. */
1255block_hider.prototype.handle_click = function(e) {
1256 YAHOO.util.Event.stopEvent(e);
1257 this.ishidden = !this.ishidden;
1258 this.update_state();
41912a26 1259 M.util.set_user_preference(this.userpref, this.ishidden);
d4a03c00 1260}
1261
1262/** Set the state of the block show/hide icon to this.ishidden. */
1263block_hider.prototype.update_state = function () {
1264 if (this.ishidden) {
1265 YAHOO.util.Dom.addClass(this.block, 'hidden');
1266 this.icon.alt = this.hiddentooltip;
1267 this.icon.title = this.hiddentooltip;
1268 this.icon.src = this.hiddenicon;
1269 } else {
1270 YAHOO.util.Dom.removeClass(this.block, 'hidden');
1271 this.icon.alt = this.visibletooltip;
1272 this.icon.title = this.visibletooltip;
1273 this.icon.src = this.visibleicon;
1274 }
1275}
1276
b166403f 1277/** Close the current browser window. */
7a5c78e0 1278function close_window(e) {
1279 YAHOO.util.Event.preventDefault(e);
1280 self.close();
b166403f 1281}
1282
1283/**
1284 * Close the current browser window, forcing the window/tab that opened this
1285 * popup to reload itself. */
1286function close_window_reloading_opener() {
1287 if (window.opener) {
1288 window.opener.location.reload(1);
1289 close_window();
1290 // Intentionally, only try to close the window if there is some evidence we are in a popup.
1291 }
8e7cebb0 1292}
9f319372 1293
1294/**
1295 * Used in a couple of modules to hide navigation areas when using AJAX
1296 */
34a2777c 1297
2538037f 1298function show_item(itemid) {
1299 var item = document.getElementById(itemid);
1300 if (item) {
1301 item.style.display = "";
1302 }
1303}
1304
1305function destroy_item(itemid) {
1306 var item = document.getElementById(itemid);
1307 if (item) {
1308 item.parentNode.removeChild(item);
1309 }
1310}
34a2777c 1311/**
1312 * Tranfer keyboard focus to the HTML element with the given id, if it exists.
1313 * @param controlid the control id.
1314 */
1315function focuscontrol(controlid) {
1316 var control = document.getElementById(controlid);
1317 if (control) {
1318 control.focus();
1319 }
e29380f3 1320}
1321
e11a8328 1322/**
1323 * Transfers keyboard focus to an HTML element based on the old style style of focus
1324 * This function should be removed as soon as it is no longer used
1325 */
428acddb 1326function old_onload_focus(formid, controlname) {
5f8bce50 1327 if (document.forms[formid] && document.forms[formid].elements && document.forms[formid].elements[controlname]) {
428acddb 1328 document.forms[formid].elements[controlname].focus();
e11a8328 1329 }
1330}
1331
1bcb7eb5 1332function build_querystring(obj) {
1333 if (typeof obj !== 'object') {
1334 return null;
1335 }
1336 var list = [];
1337 for(var k in obj) {
1338 k = encodeURIComponent(k);
1339 var value = obj[k];
1340 if(obj[k] instanceof Array) {
1341 for(var i in value) {
1342 list.push(k+'[]='+encodeURIComponent(value[i]));
1343 }
1344 } else {
1345 list.push(k+'='+encodeURIComponent(value));
1346 }
1347 }
1348 return list.join('&');
1349}
d25e2ca3 1350
1351function stripHTML(str) {
1352 var re = /<\S[^><]*>/g;
1353 var ret = str.replace(re, "");
1354 return ret;
1355}
1356
fd4faf98 1357Number.prototype.fixed=function(n){
1358 with(Math)
1359 return round(Number(this)*pow(10,n))/pow(10,n);
1360}
1361function update_progress_bar (id, width, pt, msg, es){
1362 var percent = pt*100;
1363 var status = document.getElementById("status_"+id);
1364 var percent_indicator = document.getElementById("pt_"+id);
1365 var progress_bar = document.getElementById("progress_"+id);
1366 var time_es = document.getElementById("time_"+id);
1367 status.innerHTML = msg;
1368 percent_indicator.innerHTML = percent.fixed(2) + '%';
1369 if(percent == 100) {
1370 progress_bar.style.background = "green";
1371 time_es.style.display = "none";
1372 } else {
1373 progress_bar.style.background = "#FFCC66";
1374 if (es == Infinity){
1375 time_es.innerHTML = "Initializing...";
1376 }else {
1377 time_es.innerHTML = es.fixed(2)+" sec";
1378 time_es.style.display
1379 = "block";
1380 }
1381 }
1382 progress_bar.style.width = width + "px";
1383
1384}
bf6c37c7 1385
1386function frame_breakout(e, properties) {
1387 this.setAttribute('target', properties.framename);
41f23791 1388}
78946b9b 1389
73b62703
PS
1390
1391// ===== Deprecated core Javascript functions for Moodle ====
1392// DO NOT USE!!!!!!!
e50b4c89 1393// Do not put this stuff in separate file because it only adds extra load on servers!
73b62703 1394
2d65597b
PS
1395/**
1396 * Used in a couple of modules to hide navigation areas when using AJAX
1397 */
1398function hide_item(itemid) {
3b044ba3 1399 // use class='hiddenifjs' instead
2d65597b
PS
1400 var item = document.getElementById(itemid);
1401 if (item) {
1402 item.style.display = "none";
1403 }
1404}
36282d85 1405
2cf81209
SH
1406M.util.help_icon = {
1407 Y : null,
36282d85 1408 instance : null,
2cf81209
SH
1409 add : function(Y, properties) {
1410 this.Y = Y;
61ddb953
RW
1411 properties.node = Y.one('#'+properties.id);
1412 if (properties.node) {
1413 properties.node.on('click', this.display, this, properties);
f855a5d2 1414 }
2cf81209
SH
1415 },
1416 display : function(event, args) {
1417 event.preventDefault();
1418 if (M.util.help_icon.instance === null) {
1419 var Y = M.util.help_icon.Y;
61ddb953 1420 Y.use('overlay', 'io', 'event-mouseenter', 'node', 'event-key', function(Y) {
2cf81209 1421 var help_content_overlay = {
61ddb953 1422 helplink : null,
2cf81209
SH
1423 overlay : null,
1424 init : function() {
1425
61ddb953 1426 var closebtn = Y.Node.create('<a id="closehelpbox" href="#"><img src="'+M.util.image_url('t/delete', 'moodle')+'" /></a>');
2cf81209
SH
1427 // Create an overlay from markup
1428 this.overlay = new Y.Overlay({
1429 headerContent: closebtn,
1430 bodyContent: '',
61ddb953
RW
1431 id: 'helppopupbox',
1432 width:'400px',
2cf81209 1433 visible : false,
61ddb953 1434 constrain : true
2cf81209
SH
1435 });
1436 this.overlay.render(Y.one(document.body));
1437
1438 closebtn.on('click', this.overlay.hide, this.overlay);
61ddb953 1439
2cf81209 1440 var boundingBox = this.overlay.get("boundingBox");
2cf81209
SH
1441
1442 // Hide the menu if the user clicks outside of its content
1443 boundingBox.get("ownerDocument").on("mousedown", function (event) {
1444 var oTarget = event.target;
1445 var menuButton = Y.one("#"+args.id);
61ddb953 1446
2cf81209
SH
1447 if (!oTarget.compareTo(menuButton) &&
1448 !menuButton.contains(oTarget) &&
1449 !oTarget.compareTo(boundingBox) &&
1450 !boundingBox.contains(oTarget)) {
1451 this.overlay.hide();
1452 }
1453 }, this);
61ddb953
RW
1454
1455 Y.on("key", this.close, closebtn , "down:13", this);
1456 closebtn.on('click', this.close, this);
1457 },
1458
1459 close : function(e) {
1460 e.preventDefault();
1461 this.helplink.focus();
1462 this.overlay.hide();
2cf81209
SH
1463 },
1464
1465 display : function(event, args) {
61ddb953 1466 this.helplink = args.node;
2cf81209 1467 this.overlay.set('bodyContent', Y.Node.create('<img src="'+M.cfg.loadingicon+'" class="spinner" />'));
61ddb953 1468 this.overlay.set("align", {node:args.node, points:[Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC]});
2cf81209
SH
1469
1470 var fullurl = args.url;
1471 if (!args.url.match(/https?:\/\//)) {
1472 fullurl = M.cfg.wwwroot + args.url;
36282d85 1473 }
36282d85 1474
2cf81209
SH
1475 var ajaxurl = fullurl + '&ajax=1';
1476
1477 var cfg = {
1478 method: 'get',
1479 context : this,
1480 on: {
1481 success: function(id, o, node) {
1482 this.display_callback(o.responseText);
1483 },
1484 failure: function(id, o, node) {
1485 var debuginfo = o.statusText;
1486 if (M.cfg.developerdebug) {
1487 o.statusText += ' (' + ajaxurl + ')';
1488 }
1489 this.display_callback('bodyContent',debuginfo);
1490 }
1491 }
1492 };
36282d85 1493
2cf81209
SH
1494 Y.io(ajaxurl, cfg);
1495 this.overlay.show();
61ddb953
RW
1496
1497 Y.one('#closehelpbox').focus();
2cf81209 1498 },
36282d85 1499
2cf81209 1500 display_callback : function(content) {
61ddb953 1501 this.overlay.set('bodyContent', content);
2cf81209
SH
1502 },
1503
1504 hideContent : function() {
1505 help = this;
1506 help.overlay.hide();
36282d85 1507 }
2cf81209
SH
1508 }
1509 help_content_overlay.init();
1510 M.util.help_icon.instance = help_content_overlay;
1511 M.util.help_icon.instance.display(event, args);
1512 });
1513 } else {
1514 M.util.help_icon.instance.display(event, args);
1515 }
1516 },
1517 init : function(Y) {
61ddb953 1518 this.Y = Y;
36282d85
RW
1519 }
1520}
d2dbd0c0
SH
1521
1522/**
1523 * Custom menu namespace
1524 */
1525M.core_custom_menu = {
1526 /**
1527 * This method is used to initialise a custom menu given the id that belongs
1528 * to the custom menu's root node.
1529 *
1530 * @param {YUI} Y
1531 * @param {string} nodeid
1532 */
1533 init : function(Y, nodeid) {
242b78f7
SH
1534 var node = Y.one('#'+nodeid);
1535 if (node) {
1536 Y.use('node-menunav', function(Y) {
1537 // Get the node
1538 // Remove the javascript-disabled class.... obviously javascript is enabled.
1539 node.removeClass('javascript-disabled');
1540 // Initialise the menunav plugin
1541 node.plug(Y.Plugin.NodeMenuNav);
1542 });
1543 }
d2dbd0c0
SH
1544 }
1545}