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