user selector: MDL-16996- followup - change optgroup label escaping funciton to keep...
[moodle.git] / lib / javascript-static.js
CommitLineData
47aa42e2 1// Miscellaneous core Javascript functions for Moodle
2
b48fd7b5 3function popupchecker(msg) {
4 var testwindow = window.open('itestwin.html', '', 'width=1,height=1,left=0,top=0,scrollbars=no');
5 if (testwindow == null)
6 {alert(msg);}
7 else {
8 testwindow.close();
9 }
10}
11
63d28811 12function checkall() {
d2ce367f 13 var el = document.getElementsByTagName('input');
14 for(var i=0; i<el.length; i++) {
15 if(el[i].type == 'checkbox') {
16 el[i].checked = true;
17 }
18 }
63d28811 19}
20
03f9425f 21function checknone() {
d2ce367f 22 var el = document.getElementsByTagName('input');
23 for(var i=0; i<el.length; i++) {
24 if(el[i].type == 'checkbox') {
25 el[i].checked = false;
26 }
27 }
03f9425f 28}
29
d2ce367f 30function lockoptions(formid, master, subitems) {
027d0485 31 // Subitems is an array of names of sub items.
32 // Optionally, each item in subitems may have a
63d28811 33 // companion hidden item in the form with the
027d0485 34 // same name but prefixed by "h".
d2ce367f 35 var form = document.forms[formid];
36
37 if (eval("form."+master+".checked")) {
63d28811 38 for (i=0; i<subitems.length; i++) {
39 unlockoption(form, subitems[i]);
40 }
41 } else {
42 for (i=0; i<subitems.length; i++) {
43 lockoption(form, subitems[i]);
44 }
45 }
46 return(true);
47}
48
f07b9627 49function lockoption(form,item) {
d2ce367f 50 eval("form."+item+".disabled=true");/* IE thing */
51 if(form.elements['h'+item]) {
52 eval("form.h"+item+".value=1");
f07b9627 53 }
54}
55
56function unlockoption(form,item) {
d2ce367f 57 eval("form."+item+".disabled=false");/* IE thing */
58 if(form.elements['h'+item]) {
59 eval("form.h"+item+".value=0");
f07b9627 60 }
61}
dd07bbac 62
63
50ef8eb9 64function lockoptionsall(formid) {
dd07bbac 65 var form = document.forms[formid];
66 var dependons = eval(formid+'items');
632b88d5 67 var tolock = Array();
dd07bbac 68 for (var dependon in dependons) {
d63ef3b8 69 // change for MooTools compatibility
70 if (!dependons.propertyIsEnumerable(dependon)) {
71 continue;
72 }
dd07bbac 73 var master = form[dependon];
d63ef3b8 74 if (master === undefined) {
4bc24867 75 continue;
76 }
dd07bbac 77 for (var condition in dependons[dependon]) {
78 for (var value in dependons[dependon][condition]) {
79 var lock;
80 switch (condition) {
81 case 'notchecked':
82 lock = !master.checked; break;
83 case 'checked':
84 lock = master.checked; break;
85 case 'noitemselected':
86 lock = master.selectedIndex==-1; break;
87 case 'eq':
88 lock = master.value==value; break;
89 default:
90 lock = master.value!=value; break;
91 }
92 for (var ei in dependons[dependon][condition][value]) {
d63ef3b8 93 // change for MooTools compatibility
94 if (!window.webkit && (!dependons[dependon][condition][value].propertyIsEnumerable(ei))) {
95 continue;
96 }
632b88d5 97 var eltolock = dependons[dependon][condition][value][ei];
98 if (tolock[eltolock] != null){
99 tolock[eltolock] =
100 lock || tolock[eltolock];
101 } else {
102 tolock[eltolock] = lock;
103 }
dd07bbac 104 }
105 }
50ef8eb9 106 }
dd07bbac 107 }
632b88d5 108 for (var el in tolock){
d63ef3b8 109 // change for MooTools compatibility
110 if (!tolock.propertyIsEnumerable(el)) {
111 continue;
112 }
632b88d5 113 var formelement = form[el];
d63ef3b8 114 if ((formelement === undefined) || (formelement.disabled === undefined)) {
4bc24867 115 continue;
116 }
632b88d5 117 formelement.disabled = tolock[el];
118 }
dd07bbac 119 return true;
50ef8eb9 120}
121
d01a38cb 122function lockoptionsallsetup(formid) {
dd07bbac 123 var form = document.forms[formid];
124 var dependons = eval(formid+'items');
125 for (var dependon in dependons) {
d63ef3b8 126 // change for MooTools compatibility
127 if (!dependons.propertyIsEnumerable(dependon)) {
128 continue;
129 }
dd07bbac 130 var master = form[dependon];
d63ef3b8 131 if (master === undefined) {
4bc24867 132 continue;
133 }
c0056e22 134 master.formid = formid;
135 master.onclick = function() {return lockoptionsall(this.formid);};
136 master.onblur = function() {return lockoptionsall(this.formid);};
137 master.onchange = function() {return lockoptionsall(this.formid);};
dd07bbac 138 }
139 for (var i = 0; i < form.elements.length; i++){
140 var formelement = form.elements[i];
141 if (formelement.type=='reset') {
c0056e22 142 formelement.formid = formid;
143 formelement.onclick = function() {this.form.reset();return lockoptionsall(this.formid);};
144 formelement.onblur = function() {this.form.reset();return lockoptionsall(this.formid);};
145 formelement.onchange = function() {this.form.reset();return lockoptionsall(this.formid);};
dd07bbac 146 }
147 }
148 return lockoptionsall(formid);
d01a38cb 149}
50ef8eb9 150
7678e65c 151
152function submitFormById(id) {
153 var theform = document.getElementById(id);
154 if(!theform) {
155 return false;
156 }
bb006c95 157 if(theform.tagName.toLowerCase() != 'form') {
7678e65c 158 return false;
159 }
160 if(!theform.onsubmit || theform.onsubmit()) {
161 return theform.submit();
162 }
163}
363cb62c 164
8ceb09e0 165function select_all_in(elTagName, elClass, elId) {
d2ce367f 166 var inputs = document.getElementsByTagName('input');
8ceb09e0 167 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 168 for(var i = 0; i < inputs.length; ++i) {
bee40515 169 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 170 inputs[i].checked = 'checked';
171 }
172 }
173}
174
8ceb09e0 175function deselect_all_in(elTagName, elClass, elId) {
363cb62c 176 var inputs = document.getElementsByTagName('INPUT');
8ceb09e0 177 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 178 for(var i = 0; i < inputs.length; ++i) {
bee40515 179 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 180 inputs[i].checked = '';
181 }
182 }
183}
184
185function confirm_if(expr, message) {
186 if(!expr) {
187 return true;
188 }
189 return confirm(message);
190}
47aa42e2 191
192
193/*
194 findParentNode (start, elementName, elementClass, elementID)
50ef8eb9 195
47aa42e2 196 Travels up the DOM hierarchy to find a parent element with the
197 specified tag name, class, and id. All conditions must be met,
198 but any can be ommitted. Returns the BODY element if no match
199 found.
200*/
201function findParentNode(el, elName, elClass, elId) {
ef4d42fd 202 while(el.nodeName.toUpperCase() != 'BODY') {
47aa42e2 203 if(
ef4d42fd 204 (!elName || el.nodeName.toUpperCase() == elName) &&
47aa42e2 205 (!elClass || el.className.indexOf(elClass) != -1) &&
206 (!elId || el.id == elId))
207 {
208 break;
209 }
210 el = el.parentNode;
211 }
212 return el;
213}
19194f82 214/*
215 findChildNode (start, elementName, elementClass, elementID)
216
217 Travels down the DOM hierarchy to find all child elements with the
218 specified tag name, class, and id. All conditions must be met,
219 but any can be ommitted.
a23f0aaf 220 Doesn't examine children of matches.
19194f82 221*/
222function findChildNodes(start, tagName, elementClass, elementID, elementName) {
223 var children = new Array();
83c9a8a2 224 for (var i = 0; i < start.childNodes.length; i++) {
a23f0aaf 225 var classfound = false;
83c9a8a2 226 var child = start.childNodes[i];
a23f0aaf 227 if((child.nodeType == 1) &&//element node type
228 (elementClass && (typeof(child.className)=='string'))){
229 var childClasses = child.className.split(/\s+/);
230 for (var childClassIndex in childClasses){
231 if (childClasses[childClassIndex]==elementClass){
232 classfound = true;
233 break;
234 }
235 }
236 }
f07b9627 237 if(child.nodeType == 1) { //element node type
238 if ( (!tagName || child.nodeName == tagName) &&
239 (!elementClass || classfound)&&
240 (!elementID || child.id == elementID) &&
241 (!elementName || child.name == elementName))
242 {
243 children = children.concat(child);
244 } else {
245 children = children.concat(findChildNodes(child, tagName, elementClass, elementID, elementName));
246 }
19194f82 247 }
19194f82 248 }
249 return children;
250}
251/*
252 elementSetHide (elements, hide)
253
254 Adds or removes the "hide" class for the specified elements depending on boolean hide.
255*/
256function elementShowAdvanced(elements, show) {
257 for (var elementIndex in elements){
258 element = elements[elementIndex];
259 element.className = element.className.replace(new RegExp(' ?hide'), '')
260 if(!show) {
261 element.className += ' hide';
262 }
263 }
264}
265
cd350b53 266function showAdvancedInit(addBefore, nameAttr, buttonLabel, hideText, showText) {
267 var showHideButton = document.createElement("input");
268 showHideButton.type = 'button';
269 showHideButton.value = buttonLabel;
270 showHideButton.name = nameAttr;
271 showHideButton.moodle = {
272 hideLabel: hideText,
273 showLabel: showText
274 };
275 YAHOO.util.Event.addListener(showHideButton, 'click', showAdvancedOnClick);
276 el = document.getElementById(addBefore);
277 el.parentNode.insertBefore(showHideButton, el);
278}
279
280function showAdvancedOnClick(e) {
281 var button = e.target ? e.target : e.srcElement;
282
19194f82 283 var toSet=findChildNodes(button.form, null, 'advanced');
284 var buttontext = '';
74d15d35 285 if (button.form.elements['mform_showadvanced_last'].value == '0' || button.form.elements['mform_showadvanced_last'].value == '' ) {
19194f82 286 elementShowAdvanced(toSet, true);
cd350b53 287 buttontext = button.moodle.hideLabel;
19194f82 288 button.form.elements['mform_showadvanced_last'].value = '1';
289 } else {
290 elementShowAdvanced(toSet, false);
cd350b53 291 buttontext = button.moodle.showLabel;
19194f82 292 button.form.elements['mform_showadvanced_last'].value = '0';
293 }
294 var formelements = button.form.elements;
4ea054ec 295 // Fixed MDL-10506
296 for (var i = 0; i < formelements.length; i++){
9bf930bd 297 if (formelements[i] && formelements[i].name && (formelements[i].name=='mform_showadvanced')){
19194f82 298 formelements[i].value = buttontext;
299 }
300 }
301 //never submit the form if js is enabled.
302 return false;
303}
47aa42e2 304
54bb33eb 305function unmaskPassword(id) {
239ade45 306 var pw = document.getElementById(id);
54bb33eb 307 var chb = document.getElementById(id+'unmask');
239ade45 308
309 try {
310 // first try IE way - it can not set name attribute later
311 if (chb.checked) {
312 var newpw = document.createElement('<input type="text" name="'+pw.name+'">');
313 } else {
314 var newpw = document.createElement('<input type="password" name="'+pw.name+'">');
315 }
eba8cd63 316 newpw.attributes['class'].nodeValue = pw.attributes['class'].nodeValue;
239ade45 317 } catch (e) {
318 var newpw = document.createElement('input');
319 newpw.setAttribute('name', pw.name);
320 if (chb.checked) {
321 newpw.setAttribute('type', 'text');
322 } else {
323 newpw.setAttribute('type', 'password');
324 }
eba8cd63 325 newpw.setAttribute('class', pw.getAttribute('class'));
239ade45 326 }
327 newpw.id = pw.id;
328 newpw.size = pw.size;
329 newpw.onblur = pw.onblur;
330 newpw.onchange = pw.onchange;
331 newpw.value = pw.value;
332 pw.parentNode.replaceChild(newpw, pw);
333}
334
47aa42e2 335/*
336 elementToggleHide (element, elementFinder)
337
338 If elementFinder is not provided, toggles the "hidden" class for the specified element.
339 If elementFinder is provided, then the "hidden" class will be toggled for the object
340 returned by the function call elementFinder(element).
341
342 If persistent == true, also sets a cookie for this.
343*/
c076f4be 344function elementToggleHide(el, persistent, elementFinder, strShow, strHide) {
47aa42e2 345 if(!elementFinder) {
c076f4be 346 var obj = el; //el:container
347 el = document.getElementById('togglehide_'+obj.id);
47aa42e2 348 }
349 else {
c076f4be 350 var obj = elementFinder(el); //el:button.
47aa42e2 351 }
352 if(obj.className.indexOf('hidden') == -1) {
353 obj.className += ' hidden';
c076f4be 354 if (el.src) {
355 el.src = el.src.replace('switch_minus', 'switch_plus');
356 el.alt = strShow;
357 el.title = strShow;
358 }
19194f82 359 var shown = 0;
47aa42e2 360 }
361 else {
c076f4be 362 obj.className = obj.className.replace(new RegExp(' ?hidden'), '');
363 if (el.src) {
364 el.src = el.src.replace('switch_plus', 'switch_minus');
365 el.alt = strHide;
366 el.title = strHide;
367 }
19194f82 368 var shown = 1;
47aa42e2 369 }
19194f82 370
47aa42e2 371 if(persistent == true) {
372 new cookie('hide:' + obj.id, 1, (shown ? -1 : 356), '/').set();
373 }
374}
375
c076f4be 376function elementCookieHide(id, strShow, strHide) {
47aa42e2 377 var obj = document.getElementById(id);
378 var cook = new cookie('hide:' + id).read();
379 if(cook != null) {
c076f4be 380 elementToggleHide(obj, false, null, strShow, strHide);
47aa42e2 381 }
382}
383
384function filterByParent(elCollection, parentFinder) {
385 var filteredCollection = [];
386 for(var i = 0; i < elCollection.length; ++i) {
387 var findParent = parentFinder(elCollection[i]);
388 if(findParent.nodeName != 'BODY') {
389 filteredCollection.push(elCollection[i]);
390 }
391 }
392 return filteredCollection;
393}
394
7979105c 395/*
396 All this is here just so that IE gets to handle oversized blocks
397 in a visually pleasing manner. It does a browser detect. So sue me.
398*/
399
400function fix_column_widths() {
401 var agt = navigator.userAgent.toLowerCase();
402 if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
403 fix_column_width('left-column');
404 fix_column_width('right-column');
405 }
406}
407
408function fix_column_width(colName) {
409 if(column = document.getElementById(colName)) {
410 if(!column.offsetWidth) {
411 setTimeout("fix_column_width('" + colName + "')", 20);
412 return;
413 }
414
415 var width = 0;
416 var nodes = column.childNodes;
417
418 for(i = 0; i < nodes.length; ++i) {
419 if(nodes[i].className.indexOf("sideblock") != -1 ) {
420 if(width < nodes[i].offsetWidth) {
421 width = nodes[i].offsetWidth;
422 }
423 }
424 }
425
426 for(i = 0; i < nodes.length; ++i) {
427 if(nodes[i].className.indexOf("sideblock") != -1 ) {
428 nodes[i].style.width = width + 'px';
429 }
430 }
431 }
432}
d13c5938 433
434
435/*
9f439b17 436 Insert myValue at current cursor position
437 */
d13c5938 438function insertAtCursor(myField, myValue) {
9f439b17 439 // IE support
440 if (document.selection) {
441 myField.focus();
442 sel = document.selection.createRange();
443 sel.text = myValue;
444 }
445 // Mozilla/Netscape support
446 else if (myField.selectionStart || myField.selectionStart == '0') {
447 var startPos = myField.selectionStart;
448 var endPos = myField.selectionEnd;
449 myField.value = myField.value.substring(0, startPos)
450 + myValue + myField.value.substring(endPos, myField.value.length);
451 } else {
452 myField.value += myValue;
453 }
d13c5938 454}
7470d6de 455
456
457/*
c0056e22 458 Call instead of setting window.onload directly or setting body onload=.
459 Adds your function to a chain of functions rather than overwriting anything
460 that exists.
461*/
7470d6de 462function addonload(fn) {
463 var oldhandler=window.onload;
464 window.onload=function() {
465 if(oldhandler) oldhandler();
c0056e22 466 fn();
7470d6de 467 }
468}
c849ed1e 469
470function uncheckall() {
471 void(d=document);
472 void(el=d.getElementsByTagName('INPUT'));
473 for(i=0;i<el.length;i++) {
474 void(el[i].checked=0);
475 }
476}
477
478function checkall() {
479 void(d=document);
480 void(el=d.getElementsByTagName('INPUT'));
481 for(i=0;i<el.length;i++) {
482 void(el[i].checked=1);
483 }
484}
485
486function getElementsByClassName(oElm, strTagName, oClassNames){
487 var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
488 var arrReturnElements = new Array();
489 var arrRegExpClassNames = new Array();
490 if(typeof oClassNames == "object"){
491 for(var i=0; i<oClassNames.length; i++){
492 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)"));
493 }
494 }
495 else{
496 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)"));
497 }
498 var oElement;
499 var bMatchesAll;
500 for(var j=0; j<arrElements.length; j++){
501 oElement = arrElements[j];
502 bMatchesAll = true;
503 for(var k=0; k<arrRegExpClassNames.length; k++){
504 if(!arrRegExpClassNames[k].test(oElement.className)){
505 bMatchesAll = false;
506 break;
507 }
508 }
509 if(bMatchesAll){
510 arrReturnElements.push(oElement);
511 }
512 }
513 return (arrReturnElements)
514}
77241d9b 515
516function openpopup(url, name, options, fullscreen) {
740939ec 517 var fullurl = moodle_cfg.wwwroot + url;
77241d9b 518 var windowobj = window.open(fullurl,name,options);
519 if (fullscreen) {
520 windowobj.moveTo(0,0);
521 windowobj.resizeTo(screen.availWidth,screen.availHeight);
522 }
523 windowobj.focus();
524 return false;
525}
740939ec 526
527/* This is only used on a few help pages. */
528emoticons_help = {
529 inputarea: null,
530
531 init: function(formname, fieldname, listid) {
532 if (!opener || !opener.document.forms[formname]) {
533 return;
534 }
535 emoticons_help.inputarea = opener.document.forms[formname][fieldname];
536 if (!emoticons_help.inputarea) {
537 return;
538 }
539 var emoticons = document.getElementById(listid).getElementsByTagName('li');
540 for (var i = 0; i < emoticons.length; i++) {
541 var text = emoticons[i].getElementsByTagName('img')[0].alt;
542 YAHOO.util.Event.addListener(emoticons[i], 'click', emoticons_help.inserttext, text);
543 }
544 },
545
546 inserttext: function(e, text) {
547 text = ' ' + text + ' ';
548 if (emoticons_help.inputarea.createTextRange && emoticons_help.inputarea.caretPos) {
549 var caretPos = emoticons_help.inputarea.caretPos;
550 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
551 } else {
552 emoticons_help.inputarea.value += text;
553 }
554 emoticons_help.inputarea.focus();
555 }
bd1884fe 556}
557
558/**
559 * Makes a best effort to connect back to Moodle to update a user preference,
560 * however, there is no mechanism for finding out if the update succeeded.
561 *
562 * Before you can use this function in your JavsScript, you must have called
563 * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that
564 * the udpate is allowed, and how to safely clean and submitted values.
565 *
566 * @param String name the name of the setting to udpate.
567 * @param String the value to set it to.
568 */
569function set_user_preference(name, value) {
570 // Don't generate a script error if the library has not been loaded,
571 // unless we are a Developer, in which case we want the error.
572 if (YAHOO && YAHOO.util && YAHOO.util.Connect || moodle_cfg.developerdebug) {
573 var url = moodle_cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' +
574 moodle_cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value);
575
576 // If we are a developer, ensure that failures are reported.
577 var callback = {};
578 if (moodle_cfg.developerdebug) {
579 callback.failure = function() {
580 var a = document.createElement('a');
581 a.href = url;
582 a.classname = 'error';
583 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."));
584 document.body.insertBefore(a, document.body.firstChild);
585 }
586 }
587
588 // Make the request.
589 YAHOO.util.Connect.asyncRequest('GET', url, callback);
590 }
3b0bf2e4 591}
592
593function moodle_initialise_body() {
594 document.body.className += ' jsenabled';
740939ec 595}