Fix whitespace.
[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() {
214f5850 13 var inputs = document.getElementsByTagName('input');
14 for (var i = 0; i < inputs.length; i++) {
15 if (inputs[i].type == 'checkbox') {
16 inputs[i].checked = true;
17 }
d2ce367f 18 }
63d28811 19}
20
03f9425f 21function checknone() {
214f5850 22 var inputs = document.getElementsByTagName('input');
23 for (var i = 0; i < inputs.length; i++) {
24 if (inputs[i].type == 'checkbox') {
25 inputs[i].checked = false;
26 }
d2ce367f 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
b51709c1 63/**
64 * Get the value of the 'virtual form element' with a particular name. That is,
65 * abstracts away the difference between a normal form element, like a select
66 * which is a single HTML element with a .value property, and a set of radio
67 * buttons, which is several HTML elements.
68 *
69 * @param form a HTML form.
70 * @param master the name of an element in that form.
71 * @return the value of that element.
72 */
73function get_form_element_value(form, name) {
74 var element = form[name];
75 if (!element) {
76 return null;
77 }
78 if (element.tagName) {
79 // Ordinarly thing like a select box.
80 return element.value;
81 }
82 // Array of things, like radio buttons.
83 for (var j = 0; j < element.length; j++) {
84 var el = element[j];
85 if (el.checked) {
86 return el.value;
87 }
88 }
89 return null;
90}
91
92
93/**
94 * Set the disabled state of the 'virtual form element' with a particular name.
95 * This abstracts away the difference between a normal form element, like a select
96 * which is a single HTML element with a .value property, and a set of radio
97 * buttons, which is several HTML elements.
98 *
99 * @param form a HTML form.
100 * @param master the name of an element in that form.
101 * @param disabled the disabled state to set.
102 */
103function set_form_element_disabled(form, name, disabled) {
104 var element = form[name];
105 if (!element) {
106 return;
107 }
108 if (element.tagName) {
109 // Ordinarly thing like a select box.
110 element.disabled = disabled;
111 }
112 // Array of things, like radio buttons.
113 for (var j = 0; j < element.length; j++) {
114 var el = element[j];
115 el.disabled = disabled;
116 }
117}
dd07bbac 118
50ef8eb9 119function lockoptionsall(formid) {
dd07bbac 120 var form = document.forms[formid];
b51709c1 121 var dependons = eval(formid + 'items');
122 var tolock = [];
dd07bbac 123 for (var dependon in dependons) {
d63ef3b8 124 // change for MooTools compatibility
125 if (!dependons.propertyIsEnumerable(dependon)) {
126 continue;
127 }
b51709c1 128 if (!form[dependon]) {
4bc24867 129 continue;
130 }
dd07bbac 131 for (var condition in dependons[dependon]) {
132 for (var value in dependons[dependon][condition]) {
133 var lock;
134 switch (condition) {
135 case 'notchecked':
b51709c1 136 lock = !form[dependon].checked; break;
dd07bbac 137 case 'checked':
b51709c1 138 lock = form[dependon].checked; break;
dd07bbac 139 case 'noitemselected':
b51709c1 140 lock = form[dependon].selectedIndex == -1; break;
dd07bbac 141 case 'eq':
b51709c1 142 lock = get_form_element_value(form, dependon) == value; break;
dd07bbac 143 default:
b51709c1 144 lock = get_form_element_value(form, dependon) != value; break;
dd07bbac 145 }
146 for (var ei in dependons[dependon][condition][value]) {
d63ef3b8 147 // change for MooTools compatibility
148 if (!window.webkit && (!dependons[dependon][condition][value].propertyIsEnumerable(ei))) {
149 continue;
150 }
632b88d5 151 var eltolock = dependons[dependon][condition][value][ei];
b51709c1 152 if (tolock[eltolock] != null) {
153 tolock[eltolock] = lock || tolock[eltolock];
632b88d5 154 } else {
155 tolock[eltolock] = lock;
156 }
dd07bbac 157 }
158 }
50ef8eb9 159 }
dd07bbac 160 }
b51709c1 161 for (var el in tolock) {
d63ef3b8 162 // change for MooTools compatibility
163 if (!tolock.propertyIsEnumerable(el)) {
164 continue;
165 }
b51709c1 166 set_form_element_disabled(form, el, tolock[el]);
632b88d5 167 }
dd07bbac 168 return true;
50ef8eb9 169}
170
d01a38cb 171function lockoptionsallsetup(formid) {
dd07bbac 172 var form = document.forms[formid];
173 var dependons = eval(formid+'items');
174 for (var dependon in dependons) {
d63ef3b8 175 // change for MooTools compatibility
176 if (!dependons.propertyIsEnumerable(dependon)) {
177 continue;
178 }
b51709c1 179 var masters = form[dependon];
180 if (!masters) {
4bc24867 181 continue;
182 }
a4db2e7c 183 if (masters.tagName) {
b51709c1 184 // If master is radio buttons, we get an array, otherwise we don't.
185 // Convert both cases to an array for convinience.
186 masters = [masters];
187 }
188 for (var j = 0; j < masters.length; j++) {
189 master = masters[j];
190 master.formid = formid;
191 master.onclick = function() {return lockoptionsall(this.formid);};
192 master.onblur = function() {return lockoptionsall(this.formid);};
193 master.onchange = function() {return lockoptionsall(this.formid);};
194 }
dd07bbac 195 }
b51709c1 196 for (var i = 0; i < form.elements.length; i++) {
dd07bbac 197 var formelement = form.elements[i];
198 if (formelement.type=='reset') {
c0056e22 199 formelement.formid = formid;
200 formelement.onclick = function() {this.form.reset();return lockoptionsall(this.formid);};
201 formelement.onblur = function() {this.form.reset();return lockoptionsall(this.formid);};
202 formelement.onchange = function() {this.form.reset();return lockoptionsall(this.formid);};
dd07bbac 203 }
204 }
205 return lockoptionsall(formid);
d01a38cb 206}
50ef8eb9 207
7678e65c 208
209function submitFormById(id) {
210 var theform = document.getElementById(id);
211 if(!theform) {
212 return false;
213 }
bb006c95 214 if(theform.tagName.toLowerCase() != 'form') {
7678e65c 215 return false;
216 }
217 if(!theform.onsubmit || theform.onsubmit()) {
218 return theform.submit();
219 }
220}
363cb62c 221
8ceb09e0 222function select_all_in(elTagName, elClass, elId) {
d2ce367f 223 var inputs = document.getElementsByTagName('input');
8ceb09e0 224 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 225 for(var i = 0; i < inputs.length; ++i) {
bee40515 226 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 227 inputs[i].checked = 'checked';
228 }
229 }
230}
231
8ceb09e0 232function deselect_all_in(elTagName, elClass, elId) {
363cb62c 233 var inputs = document.getElementsByTagName('INPUT');
8ceb09e0 234 inputs = filterByParent(inputs, function(el) {return findParentNode(el, elTagName, elClass, elId);});
363cb62c 235 for(var i = 0; i < inputs.length; ++i) {
bee40515 236 if(inputs[i].type == 'checkbox' || inputs[i].type == 'radio') {
363cb62c 237 inputs[i].checked = '';
238 }
239 }
240}
241
242function confirm_if(expr, message) {
243 if(!expr) {
244 return true;
245 }
246 return confirm(message);
247}
47aa42e2 248
249
250/*
251 findParentNode (start, elementName, elementClass, elementID)
50ef8eb9 252
47aa42e2 253 Travels up the DOM hierarchy to find a parent element with the
254 specified tag name, class, and id. All conditions must be met,
255 but any can be ommitted. Returns the BODY element if no match
256 found.
257*/
258function findParentNode(el, elName, elClass, elId) {
ef4d42fd 259 while(el.nodeName.toUpperCase() != 'BODY') {
47aa42e2 260 if(
ef4d42fd 261 (!elName || el.nodeName.toUpperCase() == elName) &&
47aa42e2 262 (!elClass || el.className.indexOf(elClass) != -1) &&
263 (!elId || el.id == elId))
264 {
265 break;
266 }
267 el = el.parentNode;
268 }
269 return el;
270}
19194f82 271/*
272 findChildNode (start, elementName, elementClass, elementID)
273
274 Travels down the DOM hierarchy to find all child elements with the
275 specified tag name, class, and id. All conditions must be met,
276 but any can be ommitted.
a23f0aaf 277 Doesn't examine children of matches.
19194f82 278*/
279function findChildNodes(start, tagName, elementClass, elementID, elementName) {
280 var children = new Array();
83c9a8a2 281 for (var i = 0; i < start.childNodes.length; i++) {
a23f0aaf 282 var classfound = false;
83c9a8a2 283 var child = start.childNodes[i];
a23f0aaf 284 if((child.nodeType == 1) &&//element node type
b51709c1 285 (elementClass && (typeof(child.className)=='string'))) {
a23f0aaf 286 var childClasses = child.className.split(/\s+/);
b51709c1 287 for (var childClassIndex in childClasses) {
288 if (childClasses[childClassIndex]==elementClass) {
a23f0aaf 289 classfound = true;
290 break;
291 }
292 }
293 }
f07b9627 294 if(child.nodeType == 1) { //element node type
295 if ( (!tagName || child.nodeName == tagName) &&
296 (!elementClass || classfound)&&
297 (!elementID || child.id == elementID) &&
298 (!elementName || child.name == elementName))
299 {
300 children = children.concat(child);
301 } else {
302 children = children.concat(findChildNodes(child, tagName, elementClass, elementID, elementName));
303 }
19194f82 304 }
19194f82 305 }
306 return children;
307}
308/*
309 elementSetHide (elements, hide)
310
311 Adds or removes the "hide" class for the specified elements depending on boolean hide.
312*/
313function elementShowAdvanced(elements, show) {
b51709c1 314 for (var elementIndex in elements) {
19194f82 315 element = elements[elementIndex];
316 element.className = element.className.replace(new RegExp(' ?hide'), '')
317 if(!show) {
318 element.className += ' hide';
319 }
320 }
321}
322
cd350b53 323function showAdvancedInit(addBefore, nameAttr, buttonLabel, hideText, showText) {
324 var showHideButton = document.createElement("input");
325 showHideButton.type = 'button';
326 showHideButton.value = buttonLabel;
327 showHideButton.name = nameAttr;
328 showHideButton.moodle = {
329 hideLabel: hideText,
330 showLabel: showText
331 };
332 YAHOO.util.Event.addListener(showHideButton, 'click', showAdvancedOnClick);
333 el = document.getElementById(addBefore);
334 el.parentNode.insertBefore(showHideButton, el);
335}
336
337function showAdvancedOnClick(e) {
338 var button = e.target ? e.target : e.srcElement;
339
19194f82 340 var toSet=findChildNodes(button.form, null, 'advanced');
341 var buttontext = '';
74d15d35 342 if (button.form.elements['mform_showadvanced_last'].value == '0' || button.form.elements['mform_showadvanced_last'].value == '' ) {
19194f82 343 elementShowAdvanced(toSet, true);
cd350b53 344 buttontext = button.moodle.hideLabel;
19194f82 345 button.form.elements['mform_showadvanced_last'].value = '1';
346 } else {
347 elementShowAdvanced(toSet, false);
cd350b53 348 buttontext = button.moodle.showLabel;
19194f82 349 button.form.elements['mform_showadvanced_last'].value = '0';
350 }
351 var formelements = button.form.elements;
4ea054ec 352 // Fixed MDL-10506
b51709c1 353 for (var i = 0; i < formelements.length; i++) {
354 if (formelements[i] && formelements[i].name && (formelements[i].name=='mform_showadvanced')) {
19194f82 355 formelements[i].value = buttontext;
356 }
357 }
358 //never submit the form if js is enabled.
359 return false;
360}
47aa42e2 361
54bb33eb 362function unmaskPassword(id) {
239ade45 363 var pw = document.getElementById(id);
54bb33eb 364 var chb = document.getElementById(id+'unmask');
239ade45 365
366 try {
367 // first try IE way - it can not set name attribute later
368 if (chb.checked) {
369 var newpw = document.createElement('<input type="text" name="'+pw.name+'">');
370 } else {
371 var newpw = document.createElement('<input type="password" name="'+pw.name+'">');
372 }
eba8cd63 373 newpw.attributes['class'].nodeValue = pw.attributes['class'].nodeValue;
239ade45 374 } catch (e) {
375 var newpw = document.createElement('input');
376 newpw.setAttribute('name', pw.name);
377 if (chb.checked) {
378 newpw.setAttribute('type', 'text');
379 } else {
380 newpw.setAttribute('type', 'password');
381 }
eba8cd63 382 newpw.setAttribute('class', pw.getAttribute('class'));
239ade45 383 }
384 newpw.id = pw.id;
385 newpw.size = pw.size;
386 newpw.onblur = pw.onblur;
387 newpw.onchange = pw.onchange;
388 newpw.value = pw.value;
389 pw.parentNode.replaceChild(newpw, pw);
390}
391
8e7cebb0 392/**
393 * Search a Moodle form to find all the fdate_time_selector and fdate_selector
394 * elements, and add date_selector_calendar instance to each.
395 */
396function init_date_selectors(firstdayofweek) {
397 YAHOO.util.Dom.addClass(document.body, 'yui-skin-sam');
398 var els = YAHOO.util.Dom.getElementsByClassName('fdate_time_selector', 'fieldset');
399 for (var i = 0; i < els.length; i++) {
400 new date_selector_calendar(els[i], firstdayofweek);
401 }
402 els = YAHOO.util.Dom.getElementsByClassName('fdate_selector', 'fieldset');
403 for (i = 0; i < els.length; i++) {
404 new date_selector_calendar(els[i], firstdayofweek);
405 }
406}
407
408/**
9bad31f5 409 * Constructor for a JavaScript object that connects to a fdate_time_selector
8e7cebb0 410 * or a fdate_selector in a Moodle form, and shows a popup calendar whenever
411 * that element has keyboard focus.
412 * @param el the fieldset class="fdate_time_selector" or "fdate_selector".
413 */
414function date_selector_calendar(el, firstdayofweek) {
415 // Ensure that the shared div and calendar exist.
416 if (!date_selector_calendar.panel) {
417 date_selector_calendar.panel = new YAHOO.widget.Panel('date_selector_calendar_panel',
b3a49376 418 {visible: false, draggable: false});
8e7cebb0 419 var div = document.createElement('div');
420 date_selector_calendar.panel.setBody(div);
421 date_selector_calendar.panel.render(document.body);
422
423 YAHOO.util.Event.addListener(document, 'click', date_selector_calendar.document_click);
424 date_selector_calendar.panel.showEvent.subscribe(function() {
425 date_selector_calendar.panel.fireEvent('changeContent');
426 });
b3a49376 427 date_selector_calendar.panel.hideEvent.subscribe(date_selector_calendar.release_current);
8e7cebb0 428
429 date_selector_calendar.calendar = new YAHOO.widget.Calendar(div,
430 {iframe: false, hide_blank_weeks: true, start_weekday: firstdayofweek});
431 date_selector_calendar.calendar.renderEvent.subscribe(function() {
432 date_selector_calendar.panel.fireEvent('changeContent');
bd55319b 433 date_selector_calendar.delayed_reposition();
8e7cebb0 434 });
8e7cebb0 435 }
436
437 this.fieldset = el;
438 var controls = el.getElementsByTagName('select');
439 for (var i = 0; i < controls.length; i++) {
8e7cebb0 440 if (/\[year\]$/.test(controls[i].name)) {
441 this.yearselect = controls[i];
9bad31f5 442 } else if (/\[month\]$/.test(controls[i].name)) {
8e7cebb0 443 this.monthselect = controls[i];
9bad31f5 444 } else if (/\[day\]$/.test(controls[i].name)) {
8e7cebb0 445 this.dayselect = controls[i];
9bad31f5 446 } else {
bd55319b 447 YAHOO.util.Event.addFocusListener(controls[i], date_selector_calendar.cancel_any_timeout, this);
9bad31f5 448 YAHOO.util.Event.addBlurListener(controls[i], this.blur_event, this);
8e7cebb0 449 }
450 }
451 if (!(this.yearselect && this.monthselect && this.dayselect)) {
452 throw 'Failed to initialise calendar.';
453 }
9bad31f5 454 YAHOO.util.Event.addFocusListener([this.yearselect, this.monthselect, this.dayselect], this.focus_event, this);
455 YAHOO.util.Event.addBlurListener([this.yearselect, this.monthselect, this.dayselect], this.blur_event, this);
8e7cebb0 456
457 this.enablecheckbox = el.getElementsByTagName('input')[0];
458 if (this.enablecheckbox) {
459 YAHOO.util.Event.addFocusListener(this.enablecheckbox, this.focus_event, this);
460 YAHOO.util.Event.addListener(this.enablecheckbox, 'change', this.focus_event, this);
461 YAHOO.util.Event.addBlurListener(this.enablecheckbox, this.blur_event, this);
462 }
463}
464
465/** The pop-up calendar that contains the calendar. */
466date_selector_calendar.panel = null;
467
468/** The shared YAHOO.widget.Calendar used by all date_selector_calendars. */
469date_selector_calendar.calendar = null;
470
471/** The date_selector_calendar that currently owns the shared stuff. */
472date_selector_calendar.currentowner = null;
473
474/** Used as a timeout when hiding the calendar on blur - so we don't hide the calendar
475 * if we are just jumping from on of our controls to another. */
476date_selector_calendar.hidetimeout = null;
477
bd55319b 478/** Timeout for repositioning after a delay after a change of months. */
479date_selector_calendar.repositiontimeout = null;
8e7cebb0 480
bd55319b 481/** Member variables. Pointers to various bits of the DOM. */
8e7cebb0 482date_selector_calendar.prototype.fieldset = null;
483date_selector_calendar.prototype.yearselect = null;
484date_selector_calendar.prototype.monthselect = null;
485date_selector_calendar.prototype.dayselect = null;
486date_selector_calendar.prototype.enablecheckbox = null;
487
bd55319b 488date_selector_calendar.cancel_any_timeout = function() {
8e7cebb0 489 if (date_selector_calendar.hidetimeout) {
490 clearTimeout(date_selector_calendar.hidetimeout);
491 date_selector_calendar.hidetimeout = null;
492 }
bd55319b 493 if (date_selector_calendar.repositiontimeout) {
494 clearTimeout(date_selector_calendar.repositiontimeout);
495 date_selector_calendar.repositiontimeout = null;
496 }
497}
498
499date_selector_calendar.delayed_reposition = function() {
500 if (date_selector_calendar.repositiontimeout) {
501 clearTimeout(date_selector_calendar.repositiontimeout);
502 date_selector_calendar.repositiontimeout = null;
503 }
b3a49376 504 date_selector_calendar.repositiontimeout = setTimeout(date_selector_calendar.fix_position, 500);
bd55319b 505}
506
507date_selector_calendar.fix_position = function() {
508 if (date_selector_calendar.currentowner) {
509 date_selector_calendar.panel.cfg.setProperty('context', [date_selector_calendar.currentowner.fieldset, 'bl', 'tl']);
510 }
8e7cebb0 511}
512
b3a49376 513date_selector_calendar.release_current = function() {
514 if (date_selector_calendar.currentowner) {
515 date_selector_calendar.currentowner.release_calendar();
516 }
517}
518
8e7cebb0 519date_selector_calendar.prototype.focus_event = function(e, me) {
bd55319b 520 date_selector_calendar.cancel_any_timeout();
8e7cebb0 521 if (me.enablecheckbox == null || me.enablecheckbox.checked) {
522 me.claim_calendar();
523 } else {
524 if (date_selector_calendar.currentowner) {
525 date_selector_calendar.currentowner.release_calendar();
526 }
527 }
528}
529
530date_selector_calendar.prototype.blur_event = function(e, me) {
b3a49376 531 date_selector_calendar.hidetimeout = setTimeout(date_selector_calendar.release_current, 300);
8e7cebb0 532}
533
534date_selector_calendar.prototype.handle_select_change = function(e, me) {
535 me.set_date_from_selects();
536}
537
538date_selector_calendar.document_click = function(event) {
539 if (date_selector_calendar.currentowner) {
540 var currentcontainer = date_selector_calendar.currentowner.fieldset;
541 var eventarget = YAHOO.util.Event.getTarget(event);
b3a49376 542 if (YAHOO.util.Dom.isAncestor(currentcontainer, eventarget)) {
543 setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
544 } else {
8e7cebb0 545 date_selector_calendar.currentowner.release_calendar();
546 }
547 }
548}
549
550date_selector_calendar.prototype.claim_calendar = function() {
bd55319b 551 date_selector_calendar.cancel_any_timeout();
8e7cebb0 552 if (date_selector_calendar.currentowner == this) {
553 return;
554 }
555 if (date_selector_calendar.currentowner) {
556 date_selector_calendar.currentowner.release_calendar();
557 }
558
bd55319b 559 if (date_selector_calendar.currentowner != this) {
560 this.connect_handlers();
561 }
562 date_selector_calendar.currentowner = this;
563
8e7cebb0 564 date_selector_calendar.calendar.cfg.setProperty('mindate', new Date(this.yearselect.options[0].value, 0, 1));
565 date_selector_calendar.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.options[this.yearselect.options.length - 1].value, 11, 31));
bd55319b 566 this.fieldset.insertBefore(date_selector_calendar.panel.element, this.yearselect.nextSibling);
8e7cebb0 567 this.set_date_from_selects();
568 date_selector_calendar.panel.show();
569 var me = this;
bd55319b 570 setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
8e7cebb0 571}
572
573date_selector_calendar.prototype.set_date_from_selects = function() {
574 var year = parseInt(this.yearselect.value);
575 var month = parseInt(this.monthselect.value) - 1;
576 var day = parseInt(this.dayselect.value);
577 date_selector_calendar.calendar.select(new Date(year, month, day));
578 date_selector_calendar.calendar.setMonth(month);
579 date_selector_calendar.calendar.setYear(year);
bd55319b 580 date_selector_calendar.calendar.render();
581 date_selector_calendar.fix_position();
8e7cebb0 582}
583
584date_selector_calendar.prototype.set_selects_from_date = function(eventtype, args) {
585 var date = args[0][0];
586 var newyear = date[0];
587 var newindex = newyear - this.yearselect.options[0].value;
588 this.yearselect.selectedIndex = newindex;
589 this.monthselect.selectedIndex = date[1] - this.monthselect.options[0].value;
590 this.dayselect.selectedIndex = date[2] - this.dayselect.options[0].value;
591}
592
593date_selector_calendar.prototype.connect_handlers = function() {
594 YAHOO.util.Event.addListener([this.yearselect, this.monthselect, this.dayselect], 'change', this.handle_select_change, this);
595 date_selector_calendar.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
596}
597
598date_selector_calendar.prototype.release_calendar = function() {
599 date_selector_calendar.panel.hide();
600 date_selector_calendar.currentowner = null;
601 YAHOO.util.Event.removeListener([this.yearselect, this.monthselect, this.dayselect], this.handle_select_change);
602 date_selector_calendar.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
603}
604
605/**
47aa42e2 606 elementToggleHide (element, elementFinder)
607
608 If elementFinder is not provided, toggles the "hidden" class for the specified element.
609 If elementFinder is provided, then the "hidden" class will be toggled for the object
610 returned by the function call elementFinder(element).
611
612 If persistent == true, also sets a cookie for this.
613*/
c076f4be 614function elementToggleHide(el, persistent, elementFinder, strShow, strHide) {
47aa42e2 615 if(!elementFinder) {
c076f4be 616 var obj = el; //el:container
617 el = document.getElementById('togglehide_'+obj.id);
47aa42e2 618 }
619 else {
c076f4be 620 var obj = elementFinder(el); //el:button.
47aa42e2 621 }
622 if(obj.className.indexOf('hidden') == -1) {
623 obj.className += ' hidden';
c076f4be 624 if (el.src) {
625 el.src = el.src.replace('switch_minus', 'switch_plus');
626 el.alt = strShow;
627 el.title = strShow;
628 }
19194f82 629 var shown = 0;
47aa42e2 630 }
631 else {
c076f4be 632 obj.className = obj.className.replace(new RegExp(' ?hidden'), '');
633 if (el.src) {
634 el.src = el.src.replace('switch_plus', 'switch_minus');
635 el.alt = strHide;
636 el.title = strHide;
637 }
19194f82 638 var shown = 1;
47aa42e2 639 }
19194f82 640
47aa42e2 641 if(persistent == true) {
642 new cookie('hide:' + obj.id, 1, (shown ? -1 : 356), '/').set();
643 }
644}
645
c076f4be 646function elementCookieHide(id, strShow, strHide) {
47aa42e2 647 var obj = document.getElementById(id);
648 var cook = new cookie('hide:' + id).read();
649 if(cook != null) {
c076f4be 650 elementToggleHide(obj, false, null, strShow, strHide);
47aa42e2 651 }
652}
653
654function filterByParent(elCollection, parentFinder) {
655 var filteredCollection = [];
656 for(var i = 0; i < elCollection.length; ++i) {
657 var findParent = parentFinder(elCollection[i]);
658 if(findParent.nodeName != 'BODY') {
659 filteredCollection.push(elCollection[i]);
660 }
661 }
662 return filteredCollection;
663}
664
7979105c 665/*
666 All this is here just so that IE gets to handle oversized blocks
667 in a visually pleasing manner. It does a browser detect. So sue me.
668*/
669
670function fix_column_widths() {
671 var agt = navigator.userAgent.toLowerCase();
672 if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
673 fix_column_width('left-column');
674 fix_column_width('right-column');
675 }
676}
677
678function fix_column_width(colName) {
679 if(column = document.getElementById(colName)) {
680 if(!column.offsetWidth) {
681 setTimeout("fix_column_width('" + colName + "')", 20);
682 return;
683 }
684
685 var width = 0;
686 var nodes = column.childNodes;
687
688 for(i = 0; i < nodes.length; ++i) {
689 if(nodes[i].className.indexOf("sideblock") != -1 ) {
690 if(width < nodes[i].offsetWidth) {
691 width = nodes[i].offsetWidth;
692 }
693 }
694 }
695
696 for(i = 0; i < nodes.length; ++i) {
697 if(nodes[i].className.indexOf("sideblock") != -1 ) {
698 nodes[i].style.width = width + 'px';
699 }
700 }
701 }
702}
d13c5938 703
704
705/*
9f439b17 706 Insert myValue at current cursor position
707 */
d13c5938 708function insertAtCursor(myField, myValue) {
9f439b17 709 // IE support
710 if (document.selection) {
711 myField.focus();
712 sel = document.selection.createRange();
713 sel.text = myValue;
714 }
715 // Mozilla/Netscape support
716 else if (myField.selectionStart || myField.selectionStart == '0') {
717 var startPos = myField.selectionStart;
718 var endPos = myField.selectionEnd;
719 myField.value = myField.value.substring(0, startPos)
720 + myValue + myField.value.substring(endPos, myField.value.length);
721 } else {
722 myField.value += myValue;
723 }
d13c5938 724}
7470d6de 725
726
727/*
c0056e22 728 Call instead of setting window.onload directly or setting body onload=.
729 Adds your function to a chain of functions rather than overwriting anything
730 that exists.
731*/
7470d6de 732function addonload(fn) {
733 var oldhandler=window.onload;
734 window.onload=function() {
735 if(oldhandler) oldhandler();
c0056e22 736 fn();
7470d6de 737 }
738}
c849ed1e 739
b51709c1 740function getElementsByClassName(oElm, strTagName, oClassNames) {
c849ed1e 741 var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
742 var arrReturnElements = new Array();
743 var arrRegExpClassNames = new Array();
b51709c1 744 if(typeof oClassNames == "object") {
745 for(var i=0; i<oClassNames.length; i++) {
c849ed1e 746 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)"));
747 }
748 }
749 else{
750 arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)"));
751 }
752 var oElement;
753 var bMatchesAll;
b51709c1 754 for(var j=0; j<arrElements.length; j++) {
c849ed1e 755 oElement = arrElements[j];
756 bMatchesAll = true;
b51709c1 757 for(var k=0; k<arrRegExpClassNames.length; k++) {
758 if(!arrRegExpClassNames[k].test(oElement.className)) {
c849ed1e 759 bMatchesAll = false;
760 break;
761 }
762 }
b51709c1 763 if(bMatchesAll) {
c849ed1e 764 arrReturnElements.push(oElement);
765 }
766 }
767 return (arrReturnElements)
768}
77241d9b 769
770function openpopup(url, name, options, fullscreen) {
bed9cec8 771 var fullurl = url;
772 if (!url.match(/https?:\/\//)) {
214f5850 773 fullurl = moodle_cfg.wwwroot + url;
bed9cec8 774 }
77241d9b 775 var windowobj = window.open(fullurl,name,options);
214f5850 776 if (!windowobj) {
777 return true;
778 }
77241d9b 779 if (fullscreen) {
780 windowobj.moveTo(0,0);
781 windowobj.resizeTo(screen.availWidth,screen.availHeight);
782 }
783 windowobj.focus();
784 return false;
785}
740939ec 786
787/* This is only used on a few help pages. */
788emoticons_help = {
789 inputarea: null,
790
791 init: function(formname, fieldname, listid) {
792 if (!opener || !opener.document.forms[formname]) {
793 return;
794 }
795 emoticons_help.inputarea = opener.document.forms[formname][fieldname];
796 if (!emoticons_help.inputarea) {
797 return;
798 }
799 var emoticons = document.getElementById(listid).getElementsByTagName('li');
800 for (var i = 0; i < emoticons.length; i++) {
801 var text = emoticons[i].getElementsByTagName('img')[0].alt;
802 YAHOO.util.Event.addListener(emoticons[i], 'click', emoticons_help.inserttext, text);
803 }
804 },
805
806 inserttext: function(e, text) {
807 text = ' ' + text + ' ';
808 if (emoticons_help.inputarea.createTextRange && emoticons_help.inputarea.caretPos) {
809 var caretPos = emoticons_help.inputarea.caretPos;
810 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
811 } else {
812 emoticons_help.inputarea.value += text;
813 }
814 emoticons_help.inputarea.focus();
815 }
bd1884fe 816}
817
818/**
819 * Makes a best effort to connect back to Moodle to update a user preference,
820 * however, there is no mechanism for finding out if the update succeeded.
821 *
822 * Before you can use this function in your JavsScript, you must have called
823 * user_preference_allow_ajax_update from moodlelib.php to tell Moodle that
824 * the udpate is allowed, and how to safely clean and submitted values.
825 *
826 * @param String name the name of the setting to udpate.
827 * @param String the value to set it to.
828 */
829function set_user_preference(name, value) {
830 // Don't generate a script error if the library has not been loaded,
831 // unless we are a Developer, in which case we want the error.
832 if (YAHOO && YAHOO.util && YAHOO.util.Connect || moodle_cfg.developerdebug) {
833 var url = moodle_cfg.wwwroot + '/lib/ajax/setuserpref.php?sesskey=' +
834 moodle_cfg.sesskey + '&pref=' + encodeURI(name) + '&value=' + encodeURI(value);
835
836 // If we are a developer, ensure that failures are reported.
837 var callback = {};
838 if (moodle_cfg.developerdebug) {
839 callback.failure = function() {
840 var a = document.createElement('a');
841 a.href = url;
842 a.classname = 'error';
843 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."));
844 document.body.insertBefore(a, document.body.firstChild);
845 }
846 }
847
848 // Make the request.
849 YAHOO.util.Connect.asyncRequest('GET', url, callback);
850 }
3b0bf2e4 851}
852
853function moodle_initialise_body() {
854 document.body.className += ' jsenabled';
f2eb5002 855}
856
857/**
858 * Oject to handle a collapsible region, see print_collapsible_region in weblib.php
859 * @constructor
860 * @param String id the HTML id for the div.
861 * @param String userpref the user preference that records the state of this box. false if none.
862 * @param Boolean startcollapsed whether the box should start collapsed.
863 */
864function collapsible_region(id, userpref, strtooltip) {
865 // Record the pref name
866 this.userpref = userpref;
867
868 // Find the divs in the document.
869 this.div = document.getElementById(id);
904998d8 870 this.innerdiv = document.getElementById(id + '_sizer');
f2eb5002 871 this.caption = document.getElementById(id + '_caption');
872 this.caption.title = strtooltip;
873
874 // Put the contents of caption in an <a> to make it focussable.
875 var a = document.createElement('a');
876 while (e = this.caption.firstChild) {
877 a.appendChild(e);
878 }
879 a.href = '#';
880 this.caption.appendChild(a);
881
882 // Create the animation.
883 this.animation = new YAHOO.util.Anim(this.div, {}, 0.3, YAHOO.util.Easing.easeBoth);
884
885 // Get to the right initial state.
886 if (this.div.className.match(/\bcollapsed\b/)) {
887 this.collapsed = true;
e7b7583c 888 var self = this;
889 setTimeout(function() {
890 var region = YAHOO.util.Region.getRegion(self.caption);
891 self.div.style.height = (region.bottom - region.top + 3) + 'px';
892 }, 10);
f2eb5002 893 }
894
895 // Add the appropriate image.
896 this.icon = document.createElement('img');
897 this.icon.id = id + '_icon';
898 this.icon.alt = '';
899 if (this.collapsed) {
900 this.icon.src = moodle_cfg.pixpath + '/t/collapsed.png';
901 } else {
902 this.icon.src = moodle_cfg.pixpath + '/t/expanded.png';
903 }
c9f8e118 904 a.appendChild(this.icon);
f2eb5002 905
906 // Hook up the event handler.
904998d8 907 var self = this;
f2eb5002 908 YAHOO.util.Event.addListener(a, 'click', function(e) {self.handle_click(e);});
904998d8 909
910 // Handler for the animation finishing.
911 this.animation.onComplete.subscribe(function() {self.handle_animation_complete();});
f2eb5002 912}
913
914/**
915 * The user preference that stores the state of this box.
6a208406 916 * @property userpref
f2eb5002 917 * @type String
918 */
919collapsible_region.prototype.userpref = null;
920
921/**
922 * The key divs that make up this
923 * @property div, innerdiv, captiondiv
924 * @type HTMLDivElement
925 */
926collapsible_region.prototype.div = null;
927collapsible_region.prototype.innerdiv = null;
928collapsible_region.prototype.captiondiv = null;
929
930/**
931 * The key divs that make up this
932 * @property icon
933 * @type HTMLImageElement
934 */
935collapsible_region.prototype.icon = null;
936
937/**
938 * Whether the region is currently collapsed.
939 * @property collapsed
940 * @type Boolean
941 */
942collapsible_region.prototype.collapsed = false;
943
944/**
945 * @property animation
946 * @type YAHOO.util.Anim
947 */
948collapsible_region.prototype.animation = null;
949
904998d8 950/** When clicked, toggle the collapsed state, and trigger the animation. */
f2eb5002 951collapsible_region.prototype.handle_click = function(e) {
952 // Toggle the state.
953 this.collapsed = !this.collapsed;
954
955 // Stop the click following the link.
956 YAHOO.util.Event.stopEvent(e);
957
958 // Animate to the appropriate size.
959 if (this.animation.isAnimated()) {
960 this.animation.stop();
961 }
962 if (this.collapsed) {
c679952e 963 var region = YAHOO.util.Region.getRegion(this.caption);
964 var targetheight = region.bottom - region.top + 3;
f2eb5002 965 } else {
c679952e 966 var region = YAHOO.util.Region.getRegion(this.innerdiv);
967 var targetheight = region.bottom - region.top + 2;
f2eb5002 968 this.div.className = this.div.className.replace(/\s*\bcollapsed\b\s*/, ' ');
969 }
c679952e 970 this.animation.attributes.height = { to: targetheight, unit: 'px' };
f2eb5002 971 this.animation.animate();
972
973 // Set the appropriate icon.
974 if (this.collapsed) {
975 this.icon.src = moodle_cfg.pixpath + '/t/collapsed.png';
976 } else {
977 this.icon.src = moodle_cfg.pixpath + '/t/expanded.png';
978 }
979
980 // Update the user preference.
981 if (this.userpref) {
982 set_user_preference(this.userpref, this.collapsed);
983 }
984}
904998d8 985
986/** When when the animation is finished, add the collapsed class name in relevant. */
987collapsible_region.prototype.handle_animation_complete = function() {
988 if (this.collapsed) {
989 this.div.className += ' collapsed';
990 }
b166403f 991}
992
993/** Close the current browser window. */
994function close_window() {
995 window.close();
996}
997
998/**
999 * Close the current browser window, forcing the window/tab that opened this
1000 * popup to reload itself. */
1001function close_window_reloading_opener() {
1002 if (window.opener) {
1003 window.opener.location.reload(1);
1004 close_window();
1005 // Intentionally, only try to close the window if there is some evidence we are in a popup.
1006 }
8e7cebb0 1007}