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