formslib durations: MDL-18500 Let the form control the default unit.
[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');
f6b6861d 5 if (!testwindow) {
6 alert(msg);
7 } else {
b48fd7b5 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
8e7cebb0 335/**
336 * Search a Moodle form to find all the fdate_time_selector and fdate_selector
337 * elements, and add date_selector_calendar instance to each.
338 */
339function init_date_selectors(firstdayofweek) {
340 YAHOO.util.Dom.addClass(document.body, 'yui-skin-sam');
341 var els = YAHOO.util.Dom.getElementsByClassName('fdate_time_selector', 'fieldset');
342 for (var i = 0; i < els.length; i++) {
343 new date_selector_calendar(els[i], firstdayofweek);
344 }
345 els = YAHOO.util.Dom.getElementsByClassName('fdate_selector', 'fieldset');
346 for (i = 0; i < els.length; i++) {
347 new date_selector_calendar(els[i], firstdayofweek);
348 }
349}
350
351/**
9bad31f5 352 * Constructor for a JavaScript object that connects to a fdate_time_selector
8e7cebb0 353 * or a fdate_selector in a Moodle form, and shows a popup calendar whenever
354 * that element has keyboard focus.
355 * @param el the fieldset class="fdate_time_selector" or "fdate_selector".
356 */
357function date_selector_calendar(el, firstdayofweek) {
358 // Ensure that the shared div and calendar exist.
359 if (!date_selector_calendar.panel) {
360 date_selector_calendar.panel = new YAHOO.widget.Panel('date_selector_calendar_panel',
361 {visible: false, close: false, draggable: false});
362 var div = document.createElement('div');
363 date_selector_calendar.panel.setBody(div);
364 date_selector_calendar.panel.render(document.body);
365
366 YAHOO.util.Event.addListener(document, 'click', date_selector_calendar.document_click);
367 date_selector_calendar.panel.showEvent.subscribe(function() {
368 date_selector_calendar.panel.fireEvent('changeContent');
369 });
370
371 date_selector_calendar.calendar = new YAHOO.widget.Calendar(div,
372 {iframe: false, hide_blank_weeks: true, start_weekday: firstdayofweek});
373 date_selector_calendar.calendar.renderEvent.subscribe(function() {
374 date_selector_calendar.panel.fireEvent('changeContent');
bd55319b 375 date_selector_calendar.delayed_reposition();
8e7cebb0 376 });
8e7cebb0 377 }
378
379 this.fieldset = el;
380 var controls = el.getElementsByTagName('select');
381 for (var i = 0; i < controls.length; i++) {
8e7cebb0 382 if (/\[year\]$/.test(controls[i].name)) {
383 this.yearselect = controls[i];
9bad31f5 384 } else if (/\[month\]$/.test(controls[i].name)) {
8e7cebb0 385 this.monthselect = controls[i];
9bad31f5 386 } else if (/\[day\]$/.test(controls[i].name)) {
8e7cebb0 387 this.dayselect = controls[i];
9bad31f5 388 } else {
bd55319b 389 YAHOO.util.Event.addFocusListener(controls[i], date_selector_calendar.cancel_any_timeout, this);
9bad31f5 390 YAHOO.util.Event.addBlurListener(controls[i], this.blur_event, this);
8e7cebb0 391 }
392 }
393 if (!(this.yearselect && this.monthselect && this.dayselect)) {
394 throw 'Failed to initialise calendar.';
395 }
9bad31f5 396 YAHOO.util.Event.addFocusListener([this.yearselect, this.monthselect, this.dayselect], this.focus_event, this);
397 YAHOO.util.Event.addBlurListener([this.yearselect, this.monthselect, this.dayselect], this.blur_event, this);
8e7cebb0 398
399 this.enablecheckbox = el.getElementsByTagName('input')[0];
400 if (this.enablecheckbox) {
401 YAHOO.util.Event.addFocusListener(this.enablecheckbox, this.focus_event, this);
402 YAHOO.util.Event.addListener(this.enablecheckbox, 'change', this.focus_event, this);
403 YAHOO.util.Event.addBlurListener(this.enablecheckbox, this.blur_event, this);
404 }
405}
406
407/** The pop-up calendar that contains the calendar. */
408date_selector_calendar.panel = null;
409
410/** The shared YAHOO.widget.Calendar used by all date_selector_calendars. */
411date_selector_calendar.calendar = null;
412
413/** The date_selector_calendar that currently owns the shared stuff. */
414date_selector_calendar.currentowner = null;
415
416/** Used as a timeout when hiding the calendar on blur - so we don't hide the calendar
417 * if we are just jumping from on of our controls to another. */
418date_selector_calendar.hidetimeout = null;
419
bd55319b 420/** Timeout for repositioning after a delay after a change of months. */
421date_selector_calendar.repositiontimeout = null;
8e7cebb0 422
bd55319b 423/** Member variables. Pointers to various bits of the DOM. */
8e7cebb0 424date_selector_calendar.prototype.fieldset = null;
425date_selector_calendar.prototype.yearselect = null;
426date_selector_calendar.prototype.monthselect = null;
427date_selector_calendar.prototype.dayselect = null;
428date_selector_calendar.prototype.enablecheckbox = null;
429
bd55319b 430date_selector_calendar.cancel_any_timeout = function() {
8e7cebb0 431 if (date_selector_calendar.hidetimeout) {
432 clearTimeout(date_selector_calendar.hidetimeout);
433 date_selector_calendar.hidetimeout = null;
434 }
bd55319b 435 if (date_selector_calendar.repositiontimeout) {
436 clearTimeout(date_selector_calendar.repositiontimeout);
437 date_selector_calendar.repositiontimeout = null;
438 }
439}
440
441date_selector_calendar.delayed_reposition = function() {
442 if (date_selector_calendar.repositiontimeout) {
443 clearTimeout(date_selector_calendar.repositiontimeout);
444 date_selector_calendar.repositiontimeout = null;
445 }
446 date_selector_calendar.repositiontimeout = setTimeout(
447 function() {date_selector_calendar.fix_position();}, 500);
448}
449
450date_selector_calendar.fix_position = function() {
451 if (date_selector_calendar.currentowner) {
452 date_selector_calendar.panel.cfg.setProperty('context', [date_selector_calendar.currentowner.fieldset, 'bl', 'tl']);
453 }
8e7cebb0 454}
455
456date_selector_calendar.prototype.focus_event = function(e, me) {
bd55319b 457 date_selector_calendar.cancel_any_timeout();
8e7cebb0 458 if (me.enablecheckbox == null || me.enablecheckbox.checked) {
459 me.claim_calendar();
460 } else {
461 if (date_selector_calendar.currentowner) {
462 date_selector_calendar.currentowner.release_calendar();
463 }
464 }
465}
466
467date_selector_calendar.prototype.blur_event = function(e, me) {
9bad31f5 468 date_selector_calendar.hidetimeout = setTimeout(function() {me.release_calendar();}, 200);
8e7cebb0 469}
470
471date_selector_calendar.prototype.handle_select_change = function(e, me) {
472 me.set_date_from_selects();
473}
474
475date_selector_calendar.document_click = function(event) {
476 if (date_selector_calendar.currentowner) {
477 var currentcontainer = date_selector_calendar.currentowner.fieldset;
478 var eventarget = YAHOO.util.Event.getTarget(event);
479 if (!YAHOO.util.Dom.isAncestor(currentcontainer, eventarget)) {
480 date_selector_calendar.currentowner.release_calendar();
481 }
482 }
483}
484
485date_selector_calendar.prototype.claim_calendar = function() {
bd55319b 486 date_selector_calendar.cancel_any_timeout();
8e7cebb0 487 if (date_selector_calendar.currentowner == this) {
488 return;
489 }
490 if (date_selector_calendar.currentowner) {
491 date_selector_calendar.currentowner.release_calendar();
492 }
493
bd55319b 494 if (date_selector_calendar.currentowner != this) {
495 this.connect_handlers();
496 }
497 date_selector_calendar.currentowner = this;
498
8e7cebb0 499 date_selector_calendar.calendar.cfg.setProperty('mindate', new Date(this.yearselect.options[0].value, 0, 1));
500 date_selector_calendar.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.options[this.yearselect.options.length - 1].value, 11, 31));
bd55319b 501 this.fieldset.insertBefore(date_selector_calendar.panel.element, this.yearselect.nextSibling);
8e7cebb0 502 this.set_date_from_selects();
503 date_selector_calendar.panel.show();
504 var me = this;
bd55319b 505 setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
8e7cebb0 506}
507
508date_selector_calendar.prototype.set_date_from_selects = function() {
509 var year = parseInt(this.yearselect.value);
510 var month = parseInt(this.monthselect.value) - 1;
511 var day = parseInt(this.dayselect.value);
512 date_selector_calendar.calendar.select(new Date(year, month, day));
513 date_selector_calendar.calendar.setMonth(month);
514 date_selector_calendar.calendar.setYear(year);
bd55319b 515 date_selector_calendar.calendar.render();
516 date_selector_calendar.fix_position();
8e7cebb0 517}
518
519date_selector_calendar.prototype.set_selects_from_date = function(eventtype, args) {
520 var date = args[0][0];
521 var newyear = date[0];
522 var newindex = newyear - this.yearselect.options[0].value;
523 this.yearselect.selectedIndex = newindex;
524 this.monthselect.selectedIndex = date[1] - this.monthselect.options[0].value;
525 this.dayselect.selectedIndex = date[2] - this.dayselect.options[0].value;
526}
527
528date_selector_calendar.prototype.connect_handlers = function() {
529 YAHOO.util.Event.addListener([this.yearselect, this.monthselect, this.dayselect], 'change', this.handle_select_change, this);
530 date_selector_calendar.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
531}
532
533date_selector_calendar.prototype.release_calendar = function() {
534 date_selector_calendar.panel.hide();
535 date_selector_calendar.currentowner = null;
536 YAHOO.util.Event.removeListener([this.yearselect, this.monthselect, this.dayselect], this.handle_select_change);
537 date_selector_calendar.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
538}
539
540/**
47aa42e2 541 elementToggleHide (element, elementFinder)
542
543 If elementFinder is not provided, toggles the "hidden" class for the specified element.
544 If elementFinder is provided, then the "hidden" class will be toggled for the object
545 returned by the function call elementFinder(element).
546
547 If persistent == true, also sets a cookie for this.
548*/
c076f4be 549function elementToggleHide(el, persistent, elementFinder, strShow, strHide) {
47aa42e2 550 if(!elementFinder) {
c076f4be 551 var obj = el; //el:container
552 el = document.getElementById('togglehide_'+obj.id);
47aa42e2 553 }
554 else {
c076f4be 555 var obj = elementFinder(el); //el:button.
47aa42e2 556 }
557 if(obj.className.indexOf('hidden') == -1) {
558 obj.className += ' hidden';
c076f4be 559 if (el.src) {
560 el.src = el.src.replace('switch_minus', 'switch_plus');
561 el.alt = strShow;
562 el.title = strShow;
563 }
19194f82 564 var shown = 0;
47aa42e2 565 }
566 else {
c076f4be 567 obj.className = obj.className.replace(new RegExp(' ?hidden'), '');
568 if (el.src) {
569 el.src = el.src.replace('switch_plus', 'switch_minus');
570 el.alt = strHide;
571 el.title = strHide;
572 }
19194f82 573 var shown = 1;
47aa42e2 574 }
19194f82 575
47aa42e2 576 if(persistent == true) {
577 new cookie('hide:' + obj.id, 1, (shown ? -1 : 356), '/').set();
578 }
579}
580
c076f4be 581function elementCookieHide(id, strShow, strHide) {
47aa42e2 582 var obj = document.getElementById(id);
583 var cook = new cookie('hide:' + id).read();
584 if(cook != null) {
c076f4be 585 elementToggleHide(obj, false, null, strShow, strHide);
47aa42e2 586 }
587}
588
589function filterByParent(elCollection, parentFinder) {
590 var filteredCollection = [];
591 for(var i = 0; i < elCollection.length; ++i) {
592 var findParent = parentFinder(elCollection[i]);
593 if(findParent.nodeName != 'BODY') {
594 filteredCollection.push(elCollection[i]);
595 }
596 }
597 return filteredCollection;
598}
599
7979105c 600/*
601 All this is here just so that IE gets to handle oversized blocks
602 in a visually pleasing manner. It does a browser detect. So sue me.
603*/
604
605function fix_column_widths() {
606 var agt = navigator.userAgent.toLowerCase();
607 if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
608 fix_column_width('left-column');
609 fix_column_width('right-column');
610 }
611}
612
613function fix_column_width(colName) {
614 if(column = document.getElementById(colName)) {
615 if(!column.offsetWidth) {
616 setTimeout("fix_column_width('" + colName + "')", 20);
617 return;
618 }
619
620 var width = 0;
621 var nodes = column.childNodes;
622
623 for(i = 0; i < nodes.length; ++i) {
624 if(nodes[i].className.indexOf("sideblock") != -1 ) {
625 if(width < nodes[i].offsetWidth) {
626 width = nodes[i].offsetWidth;
627 }
628 }
629 }
630
631 for(i = 0; i < nodes.length; ++i) {
632 if(nodes[i].className.indexOf("sideblock") != -1 ) {
633 nodes[i].style.width = width + 'px';
634 }
635 }
636 }
637}
d13c5938 638
639
640/*
9f439b17 641 Insert myValue at current cursor position
642 */
d13c5938 643function insertAtCursor(myField, myValue) {
9f439b17 644 // IE support
645 if (document.selection) {
646 myField.focus();
647 sel = document.selection.createRange();
648 sel.text = myValue;
649 }
650 // Mozilla/Netscape support
651 else if (myField.selectionStart || myField.selectionStart == '0') {
652 var startPos = myField.selectionStart;
653 var endPos = myField.selectionEnd;
654 myField.value = myField.value.substring(0, startPos)
655 + myValue + myField.value.substring(endPos, myField.value.length);
656 } else {
657 myField.value += myValue;
658 }
d13c5938 659}
7470d6de 660
661
662/*
c0056e22 663 Call instead of setting window.onload directly or setting body onload=.
664 Adds your function to a chain of functions rather than overwriting anything
665 that exists.
666*/
7470d6de 667function addonload(fn) {
668 var oldhandler=window.onload;
669 window.onload=function() {
670 if(oldhandler) oldhandler();
c0056e22 671 fn();
7470d6de 672 }
673}
c849ed1e 674
c849ed1e 675function getElementsByClassName(oElm, strTagName, oClassNames){
676 var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
677 var arrReturnElements = new Array();
678 var arrRegExpClassNames = new Array();
679 if(typeof oClassNames == "object"){
680 for(var i=0; i<oClassNames.length; i++){
681 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)"));
682 }
683 }
684 else{
685 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)"));
686 }
687 var oElement;
688 var bMatchesAll;
689 for(var j=0; j<arrElements.length; j++){
690 oElement = arrElements[j];
691 bMatchesAll = true;
692 for(var k=0; k<arrRegExpClassNames.length; k++){
693 if(!arrRegExpClassNames[k].test(oElement.className)){
694 bMatchesAll = false;
695 break;
696 }
697 }
698 if(bMatchesAll){
699 arrReturnElements.push(oElement);
700 }
701 }
702 return (arrReturnElements)
703}
77241d9b 704
705function openpopup(url, name, options, fullscreen) {
bed9cec8 706 var fullurl = url;
707 if (!url.match(/https?:\/\//)) {
708 var fullurl = moodle_cfg.wwwroot + url;
709 }
77241d9b 710 var windowobj = window.open(fullurl,name,options);
711 if (fullscreen) {
712 windowobj.moveTo(0,0);
713 windowobj.resizeTo(screen.availWidth,screen.availHeight);
714 }
715 windowobj.focus();
716 return false;
717}
740939ec 718
719/* This is only used on a few help pages. */
720emoticons_help = {
721 inputarea: null,
722
723 init: function(formname, fieldname, listid) {
724 if (!opener || !opener.document.forms[formname]) {
725 return;
726 }
727 emoticons_help.inputarea = opener.document.forms[formname][fieldname];
728 if (!emoticons_help.inputarea) {
729 return;
730 }
731 var emoticons = document.getElementById(listid).getElementsByTagName('li');
732 for (var i = 0; i < emoticons.length; i++) {
733 var text = emoticons[i].getElementsByTagName('img')[0].alt;
734 YAHOO.util.Event.addListener(emoticons[i], 'click', emoticons_help.inserttext, text);
735 }
736 },
737
738 inserttext: function(e, text) {
739 text = ' ' + text + ' ';
740 if (emoticons_help.inputarea.createTextRange && emoticons_help.inputarea.caretPos) {
741 var caretPos = emoticons_help.inputarea.caretPos;
742 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
743 } else {
744 emoticons_help.inputarea.value += text;
745 }
746 emoticons_help.inputarea.focus();
747 }
bd1884fe 748}
749
750/**
751 * Makes a best effort to connect back to Moodle to update a user preference,
752 * however, there is no mechanism for finding out if the update succeeded.
753 *
754 * Before you can use this function in your JavsScript, you must have called
755 * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that
756 * the udpate is allowed, and how to safely clean and submitted values.
757 *
758 * @param String name the name of the setting to udpate.
759 * @param String the value to set it to.
760 */
761function set_user_preference(name, value) {
762 // Don't generate a script error if the library has not been loaded,
763 // unless we are a Developer, in which case we want the error.
764 if (YAHOO && YAHOO.util && YAHOO.util.Connect || moodle_cfg.developerdebug) {
765 var url = moodle_cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' +
766 moodle_cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value);
767
768 // If we are a developer, ensure that failures are reported.
769 var callback = {};
770 if (moodle_cfg.developerdebug) {
771 callback.failure = function() {
772 var a = document.createElement('a');
773 a.href = url;
774 a.classname = 'error';
775 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."));
776 document.body.insertBefore(a, document.body.firstChild);
777 }
778 }
779
780 // Make the request.
781 YAHOO.util.Connect.asyncRequest('GET', url, callback);
782 }
3b0bf2e4 783}
784
785function moodle_initialise_body() {
786 document.body.className += ' jsenabled';
f2eb5002 787}
788
789/**
790 * Oject to handle a collapsible region, see print_collapsible_region in weblib.php
791 * @constructor
792 * @param String id the HTML id for the div.
793 * @param String userpref the user preference that records the state of this box. false if none.
794 * @param Boolean startcollapsed whether the box should start collapsed.
795 */
796function collapsible_region(id, userpref, strtooltip) {
797 // Record the pref name
798 this.userpref = userpref;
799
800 // Find the divs in the document.
801 this.div = document.getElementById(id);
904998d8 802 this.innerdiv = document.getElementById(id + '_sizer');
f2eb5002 803 this.caption = document.getElementById(id + '_caption');
804 this.caption.title = strtooltip;
805
806 // Put the contents of caption in an <a> to make it focussable.
807 var a = document.createElement('a');
808 while (e = this.caption.firstChild) {
809 a.appendChild(e);
810 }
811 a.href = '#';
812 this.caption.appendChild(a);
813
814 // Create the animation.
815 this.animation = new YAHOO.util.Anim(this.div, {}, 0.3, YAHOO.util.Easing.easeBoth);
816
817 // Get to the right initial state.
818 if (this.div.className.match(/\bcollapsed\b/)) {
819 this.collapsed = true;
e7b7583c 820 var self = this;
821 setTimeout(function() {
822 var region = YAHOO.util.Region.getRegion(self.caption);
823 self.div.style.height = (region.bottom - region.top + 3) + 'px';
824 }, 10);
f2eb5002 825 }
826
827 // Add the appropriate image.
828 this.icon = document.createElement('img');
829 this.icon.id = id + '_icon';
830 this.icon.alt = '';
831 if (this.collapsed) {
832 this.icon.src = moodle_cfg.pixpath + '/t/collapsed.png';
833 } else {
834 this.icon.src = moodle_cfg.pixpath + '/t/expanded.png';
835 }
c9f8e118 836 a.appendChild(this.icon);
f2eb5002 837
838 // Hook up the event handler.
904998d8 839 var self = this;
f2eb5002 840 YAHOO.util.Event.addListener(a, 'click', function(e) {self.handle_click(e);});
904998d8 841
842 // Handler for the animation finishing.
843 this.animation.onComplete.subscribe(function() {self.handle_animation_complete();});
f2eb5002 844}
845
846/**
847 * The user preference that stores the state of this box.
6a208406 848 * @property userpref
f2eb5002 849 * @type String
850 */
851collapsible_region.prototype.userpref = null;
852
853/**
854 * The key divs that make up this
855 * @property div, innerdiv, captiondiv
856 * @type HTMLDivElement
857 */
858collapsible_region.prototype.div = null;
859collapsible_region.prototype.innerdiv = null;
860collapsible_region.prototype.captiondiv = null;
861
862/**
863 * The key divs that make up this
864 * @property icon
865 * @type HTMLImageElement
866 */
867collapsible_region.prototype.icon = null;
868
869/**
870 * Whether the region is currently collapsed.
871 * @property collapsed
872 * @type Boolean
873 */
874collapsible_region.prototype.collapsed = false;
875
876/**
877 * @property animation
878 * @type YAHOO.util.Anim
879 */
880collapsible_region.prototype.animation = null;
881
904998d8 882/** When clicked, toggle the collapsed state, and trigger the animation. */
f2eb5002 883collapsible_region.prototype.handle_click = function(e) {
884 // Toggle the state.
885 this.collapsed = !this.collapsed;
886
887 // Stop the click following the link.
888 YAHOO.util.Event.stopEvent(e);
889
890 // Animate to the appropriate size.
891 if (this.animation.isAnimated()) {
892 this.animation.stop();
893 }
894 if (this.collapsed) {
c679952e 895 var region = YAHOO.util.Region.getRegion(this.caption);
896 var targetheight = region.bottom - region.top + 3;
f2eb5002 897 } else {
c679952e 898 var region = YAHOO.util.Region.getRegion(this.innerdiv);
899 var targetheight = region.bottom - region.top + 2;
f2eb5002 900 this.div.className = this.div.className.replace(/\s*\bcollapsed\b\s*/, ' ');
901 }
c679952e 902 this.animation.attributes.height = { to: targetheight, unit: 'px' };
f2eb5002 903 this.animation.animate();
904
905 // Set the appropriate icon.
906 if (this.collapsed) {
907 this.icon.src = moodle_cfg.pixpath + '/t/collapsed.png';
908 } else {
909 this.icon.src = moodle_cfg.pixpath + '/t/expanded.png';
910 }
911
912 // Update the user preference.
913 if (this.userpref) {
914 set_user_preference(this.userpref, this.collapsed);
915 }
916}
904998d8 917
918/** When when the animation is finished, add the collapsed class name in relevant. */
919collapsible_region.prototype.handle_animation_complete = function() {
920 if (this.collapsed) {
921 this.div.className += ' collapsed';
922 }
b166403f 923}
924
925/** Close the current browser window. */
926function close_window() {
927 window.close();
928}
929
930/**
931 * Close the current browser window, forcing the window/tab that opened this
932 * popup to reload itself. */
933function close_window_reloading_opener() {
934 if (window.opener) {
935 window.opener.location.reload(1);
936 close_window();
937 // Intentionally, only try to close the window if there is some evidence we are in a popup.
938 }
8e7cebb0 939}