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