Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / mod / hotpot / template / v6.php
blobb0dcf640550e377de9f50d3ba64cbdedf27fb8b2
1 <?PHP
3 class hotpot_xml_quiz_template extends hotpot_xml_template_default {
4 // left and right items for JMatch
5 var $l_items = array();
6 var $r_items = array();
8 // constructor function for this class
9 function hotpot_xml_quiz_template(&$parent) {
11 $this->parent = &$parent;
13 $get_js = optional_param('js', false);
14 $get_css = optional_param('css', false);
16 if (!empty($get_css)) {
17 // set $this->css
18 $this->v6_expand_StyleSheet();
20 } else if (!empty($get_js)) {
21 // set $this->js
22 $this->read_template($this->parent->draganddrop.$this->parent->quiztype.'6.js_', 'js');
24 } else {
25 // set $this->html
26 $this->read_template($this->parent->draganddrop.$this->parent->quiztype.'6.ht_', 'html');
29 // expand special strings, if any
30 $pattern = '';
31 switch ($this->parent->quiztype) {
32 case 'jcloze':
33 $pattern = '/\[(PreloadImageList)\]/';
34 break;
35 case 'jcross':
36 $pattern = '/\[(PreloadImageList|ShowHideClueList)\]/';
37 break;
38 case 'jmatch':
39 $pattern = '/\[(PreloadImageList|QsToShow|FixedArray|DragArray)\]/';
40 break;
41 case 'jmix':
42 $pattern = '/\[(PreloadImageList|SegmentArray|AnswerArray)\]/';
43 break;
44 case 'jquiz':
45 $pattern = '/\[(PreloadImageList|QsToShow)\]/';
46 break;
48 if (!empty($pattern)) {
49 $this->expand_strings('html', $pattern);
53 // captions and messages
55 function v6_expand_AlsoCorrect() {
56 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',also-correct');
58 function v6_expand_CapitalizeFirst() {
59 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',capitalize-first-letter');
61 function v6_expand_CheckCaption() {
62 return $this->parent->xml_value('hotpot-config-file,global,check-caption');
64 function v6_expand_CorrectIndicator() {
65 return $this->js_value('hotpot-config-file,global,correct-indicator');
67 function v6_expand_Back() {
68 return $this->int_value('hotpot-config-file,global,include-back');
70 function v6_expand_BackCaption() {
71 return $this->parent->xml_value('hotpot-config-file,global,back-caption');
73 function v6_expand_ClickToAdd() {
74 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',click-to-add');
76 function v6_expand_ClueCaption() {
77 return $this->parent->xml_value('hotpot-config-file,global,clue-caption');
79 function v6_expand_Clues() {
80 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-clues');
82 function v6_expand_Contents() {
83 return $this->int_value('hotpot-config-file,global,include-contents');
85 function v6_expand_ContentsCaption() {
86 return $this->parent->xml_value('hotpot-config-file,global,contents-caption');
88 function v6_expand_GuessCorrect() {
89 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guess-correct');
91 function v6_expand_GuessIncorrect() {
92 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guess-incorrect');
94 function v6_expand_Hint() {
95 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-hint');
97 function v6_expand_HintCaption() {
98 return $this->parent->xml_value('hotpot-config-file,global,hint-caption');
100 function v6_expand_IncorrectIndicator() {
101 return $this->js_value('hotpot-config-file,global,incorrect-indicator');
103 function v6_expand_LastQCaption() {
104 $caption = $this->parent->xml_value('hotpot-config-file,global,last-q-caption');
105 return ($caption=='<=' ? '&lt;=' : $caption);
107 function v6_expand_NextCorrect() {
108 $value = $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-part');
109 if (empty($value)) { // jquiz
110 $value = $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-letter');
112 return $value;
114 function v6_expand_NextEx() {
115 return $this->int_value('hotpot-config-file,global,include-next-ex');
117 function v6_expand_NextExCaption() {
118 $caption = $this->parent->xml_value('hotpot-config-file,global,next-ex-caption');
119 return ($caption=='=>' ? '=&gt;' : $caption);
121 function v6_expand_NextQCaption() {
122 return $this->parent->xml_value('hotpot-config-file,global,next-q-caption');
124 function v6_expand_OKCaption() {
125 return $this->parent->xml_value('hotpot-config-file,global,ok-caption');
127 function v6_expand_Restart() {
128 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-restart');
130 function v6_expand_RestartCaption() {
131 return $this->parent->xml_value('hotpot-config-file,global,restart-caption');
133 function v6_expand_ShowAllQuestionsCaption() {
134 return $this->js_value('hotpot-config-file,global,show-all-questions-caption');
136 function v6_expand_ShowOneByOneCaption() {
137 return $this->js_value('hotpot-config-file,global,show-one-by-one-caption');
139 function v6_expand_TheseAnswersToo() {
140 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',also-correct');
142 function v6_expand_ThisMuch() {
143 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',this-much-correct');
145 function v6_expand_Undo() {
146 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-undo');
148 function v6_expand_UndoCaption() {
149 return $this->parent->xml_value('hotpot-config-file,global,undo-caption');
151 function v6_expand_YourScoreIs() {
152 return $this->js_value('hotpot-config-file,global,your-score-is');
155 // reading
157 function v6_expand_Reading() {
158 return $this->int_value('data,reading,include-reading');
160 function v6_expand_ReadingText() {
161 $title = $this->v6_expand_ReadingTitle();
162 $value = $this->parent->xml_value('data,reading,reading-text');
163 $value = empty($value) ? '' : ('<div class="ReadingText">'.$value.'</div>');
164 return $title.$value;
166 function v6_expand_ReadingTitle() {
167 $value = $this->parent->xml_value('data,reading,reading-title');
168 return empty($value) ? '' : ('<h3 class="ExerciseSubtitle">'.$value.'</h3>');
171 // timer
173 function v6_expand_Timer() {
174 return $this->int_value('data,timer,include-timer');
176 function v6_expand_JSTimer() {
177 return $this->read_template('hp6timer.js_');
179 function v6_expand_Seconds() {
180 return $this->parent->xml_value('data,timer,seconds');
183 // send results
185 function v6_expand_SendResults() {
186 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',send-email');
188 function v6_expand_JSSendResults() {
189 return $this->read_template('hp6sendresults.js_');
191 function v6_expand_FormMailURL() {
192 return $this->parent->xml_value('hotpot-config-file,global,formmail-url');
194 function v6_expand_EMail() {
195 return $this->parent->xml_value('hotpot-config-file,global,email');
197 function v6_expand_NamePlease() {
198 return $this->js_value('hotpot-config-file,global,name-please');
201 // preload images
203 function v6_expand_PreloadImages() {
204 $value = $this->v6_expand_PreloadImageList();
205 return empty($value) ? false : true;
207 function v6_expand_PreloadImageList() {
209 // check it has not been set already
210 if (!isset($this->PreloadImageList)) {
212 // the list of image urls
213 $list = array();
215 // extract <img> tags
216 $img_tag = htmlspecialchars('|&#x003C;img.*?src="(.*?)".*?&#x003E;|is');
217 if (preg_match_all($img_tag, $this->parent->source, $matches)) {
218 $list = $matches[1];
220 // remove duplicates
221 $list = array_unique($list);
224 // convert to comma delimited string
225 $this->PreloadImageList = empty($list) ? '' : "'".implode(',', $list)."'";
227 return $this->PreloadImageList;
230 // html files (all quiz types)
232 function v6_expand_PlainTitle() {
233 return $this->parent->xml_value('data,title');
235 function v6_expand_ExerciseSubtitle() {
236 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',exercise-subtitle');
238 function v6_expand_Instructions() {
239 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',instructions');
241 function v6_expand_DublinCoreMetadata() {
242 $dc = '<link rel="schema.DC" href="'.$this->parent->xml_value('', "['rdf:RDF'][0]['@']['xmlns:dc']").'" />'."\n";
243 if (is_string($this->parent->xml_value('rdf:RDF,rdf:Description'))) {
244 // do nothing (there is no more dc info)
245 } else {
246 $dc .= '<meta name="DC:Creator" content="'.$this->parent->xml_value('rdf:RDF,rdf:Description,dc:creator').'" />'."\n";
247 $dc .= '<meta name="DC:Title" content="'.strip_tags($this->parent->xml_value('rdf:RDF,rdf:Description,dc:title')).'" />'."\n";
249 return $dc;
251 function v6_expand_FullVersionInfo() {
252 global $CFG;
253 require_once($CFG->hotpotroot.DIRECTORY_SEPARATOR.'version.php'); // set $module
254 return $this->parent->xml_value('version').'.x (Moodle '.$CFG->release.', hotpot-module '.$this->parent->obj_value($module, 'release').')';
256 function v6_expand_HeaderCode() {
257 return $this->parent->xml_value('hotpot-config-file,global,header-code');
259 function v6_expand_StyleSheet() {
260 $this->read_template('hp6.cs_', 'css');
261 $this->css = hotpot_convert_stylesheets_urls($this->parent->get_baseurl(), $this->parent->reference, $this->css);
262 return $this->css;
265 // stylesheet (hp6.cs_)
267 function v6_expand_PageBGColor() {
268 return $this->parent->xml_value('hotpot-config-file,global,page-bg-color');
270 function v6_expand_GraphicURL() {
271 return $this->parent->xml_value('hotpot-config-file,global,graphic-url');
273 function v6_expand_ExBGColor() {
274 return $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
277 function v6_expand_FontFace() {
278 return $this->parent->xml_value('hotpot-config-file,global,font-face');
280 function v6_expand_FontSize() {
281 $value = $this->parent->xml_value('hotpot-config-file,global,font-size');
282 return (empty($value) ? 'small' : $value);
284 function v6_expand_TextColor() {
285 return $this->parent->xml_value('hotpot-config-file,global,text-color');
287 function v6_expand_TitleColor() {
288 return $this->parent->xml_value('hotpot-config-file,global,title-color');
290 function v6_expand_LinkColor() {
291 return $this->parent->xml_value('hotpot-config-file,global,link-color');
293 function v6_expand_VLinkColor() {
294 return $this->parent->xml_value('hotpot-config-file,global,vlink-color');
297 function v6_expand_NavTextColor() {
298 return $this->parent->xml_value('hotpot-config-file,global,page-bg-color');
300 function v6_expand_NavBarColor() {
301 return $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
303 function v6_expand_NavLightColor() {
304 $color = $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
305 return $this->get_halfway_color($color, '#ffffff');
307 function v6_expand_NavShadeColor() {
308 $color = $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
309 return $this->get_halfway_color($color, '#000000');
312 function v6_expand_FuncLightColor() { // top-left of buttons
313 $color = $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
314 return $this->get_halfway_color($color, '#ffffff');
316 function v6_expand_FuncShadeColor() { // bottom right of buttons
317 $color = $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
318 return $this->get_halfway_color($color, '#000000');
321 // navigation buttons
323 function v6_expand_NavButtons() {
324 $back = $this->v6_expand_Back();
325 $next_ex = $this->v6_expand_NextEx();
326 $contents = $this->v6_expand_Contents();
327 return (empty($back) && empty($next_ex) && empty($contents) ? false : true);
329 function v6_expand_NavBarJS() {
330 return $this->v6_expand_NavButtons();
333 // switch off scorm
334 function v6_expand_Scorm12() {
335 return false;
338 // js files (all quiz types)
340 function v6_expand_JSBrowserCheck() {
341 return $this->read_template('hp6browsercheck.js_');
343 function v6_expand_JSButtons() {
344 return $this->read_template('hp6buttons.js_');
346 function v6_expand_JSCard() {
347 return $this->read_template('hp6card.js_');
349 function v6_expand_JSCheckShortAnswer() {
350 return $this->read_template('hp6checkshortanswer.js_');
352 function v6_expand_JSHotPotNet() {
353 return $this->read_template('hp6hotpotnet.js_');
355 function v6_expand_JSShowMessage() {
356 return $this->read_template('hp6showmessage.js_');
358 function v6_expand_JSUtilities() {
359 return $this->read_template('hp6utilities.js_');
362 // js files
364 function v6_expand_JSJCloze6() {
365 return $this->read_template('jcloze6.js_');
367 function v6_expand_JSJCross6() {
368 return $this->read_template('jcross6.js_');
370 function v6_expand_JSJMatch6() {
371 return $this->read_template('jmatch6.js_');
373 function v6_expand_JSJMix6() {
374 return $this->read_template('jmix6.js_');
376 function v6_expand_JSJQuiz6() {
377 return $this->read_template('jquiz6.js_');
380 // drag and drop
382 function v6_expand_JSDJMatch6() {
383 return $this->read_template('djmatch6.js_');
385 function v6_expand_JSDJMix6() {
386 return $this->read_template('djmix6.js_');
389 // what are these for?
391 function v6_expand_JSFJMatch6() {
392 return $this->read_template('fjmatch6.js_');
394 function v6_expand_JSFJMix6() {
395 return $this->read_template('fjmix6.js_');
398 // jmatch6.js_
400 function v6_expand_ShuffleQs() {
401 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',shuffle-questions');
403 function v6_expand_QsToShow() {
404 $i = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',show-limited-questions');
405 if ($i) {
406 $i = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',questions-to-show');
408 if (empty($i)) {
409 $i = 0;
410 switch ($this->parent->quiztype) {
411 case 'jmatch':
412 $values = $this->parent->xml_values('data,matching-exercise,pair');
413 $i = count($values);
414 break;
415 case 'jquiz':
416 $tags = 'data,questions,question-record';
417 while (($question="[$i]['#']") && $this->parent->xml_value($tags, $question)) {
418 $i++;
420 break;
421 } // end switch
423 return $i;
425 function v6_expand_MatchDivItems() {
426 $this->set_jmatch_items();
428 $l_keys = $this->shuffle_jmatch_items($this->l_items);
429 $r_keys = $this->shuffle_jmatch_items($this->r_items);
431 $options = '<option value="x">'.$this->parent->xml_value('data,matching-exercise,default-right-item').'</option>';
432 foreach ($r_keys as $key) {
433 if (! $this->r_items[$key]['fixed']) {
434 $options .= '<option value="'.$key.'">'.$this->r_items[$key]['text'].'</option>'."\n";
438 $str = '';
439 foreach ($l_keys as $key) {
440 $str .= '<tr><td class="LeftItem">'.$this->l_items[$key]['text'].'</td>';
441 $str .= '<td class="RightItem">';
442 if ($this->r_items[$key]['fixed']) {
443 $str .= $this->r_items[$key]['text'];
444 } else {
445 $str .= '<select id="s'.$key.'_'.$key.'">'.$options.'</select>';
447 $str .= '</td><td></td></tr>';
449 return $str;
452 // jmix6.js_
454 function v6_expand_Punctuation() {
455 $tags = 'data,jumbled-order-exercise';
456 $chars = array_merge(
457 $this->jmix_Punctuation("$tags,main-order,segment"),
458 $this->jmix_Punctuation("$tags,alternate")
460 $chars = array_unique($chars);
461 $chars = implode('', $chars);
462 $chars = $this->js_safe($chars, true);
463 return $chars;
465 function jmix_Punctuation($tags) {
466 $chars = array();
468 // all punctutation except '&#;' (because they are used in html entities)
469 $ENTITIES = $this->jmix_encode_punctuation('!"$%'."'".'()*+,-./:<=>?@[\]^_`{|}~');
470 $pattern = "/&#x([0-9A-F]+);/i";
471 $i = 0;
473 // get next segment (or alternate answer)
474 while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
476 // convert low-ascii punctuation to entities
477 $value = strtr($value, $ENTITIES);
479 // extract all hex HTML entities
480 if (preg_match_all($pattern, $value, $matches)) {
482 // loop through hex entities
483 $m_max = count($matches[0]);
484 for ($m=0; $m<$m_max; $m++) {
486 // convert to hex number
487 eval('$hex=0x'.$matches[1][$m].';');
489 // is this a punctuation character?
490 if (
491 ($hex>=0x0020 && $hex<=0x00BF) || // ascii punctuation
492 ($hex>=0x2000 && $hex<=0x206F) || // general punctuation
493 ($hex>=0x3000 && $hex<=0x303F) || // CJK punctuation
494 ($hex>=0xFE30 && $hex<=0xFE4F) || // CJK compatability
495 ($hex>=0xFE50 && $hex<=0xFE6F) || // small form variants
496 ($hex>=0xFF00 && $hex<=0xFF40) || // halfwidth and fullwidth forms (1)
497 ($hex>=0xFF5B && $hex<=0xFF65) || // halfwidth and fullwidth forms (2)
498 ($hex>=0xFFE0 && $hex<=0xFFEE) // halfwidth and fullwidth forms (3)
500 // add this character
501 $chars[] = $matches[0][$m];
505 $i++;
508 return $chars;
510 function v6_expand_OpenPunctuation() {
511 $tags = 'data,jumbled-order-exercise';
512 $chars = array_merge(
513 $this->jmix_OpenPunctuation("$tags,main-order,segment"),
514 $this->jmix_OpenPunctuation("$tags,alternate")
516 $chars = array_unique($chars);
517 $chars = implode('', $chars);
518 $chars = $this->js_safe($chars, true);
519 return $chars;
521 function jmix_OpenPunctuation($tags) {
522 $chars = array();
524 // unicode punctuation designations (pi="initial quote", ps="open")
525 // http://www.sql-und-xml.de/unicode-database/pi.html
526 // http://www.sql-und-xml.de/unicode-database/ps.html
527 $pi = '0022|0027|00AB|2018|201B|201C|201F|2039';
528 $ps = '0028|005B|007B|0F3A|0F3C|169B|201A|201E|2045|207D|208D|2329|23B4|2768|276A|276C|276E|2770|2772|2774|27E6|27E8|27EA|2983|2985|2987|2989|298B|298D|298F|2991|2993|2995|2997|29D8|29DA|29FC|3008|300A|300C|300E|3010|3014|3016|3018|301A|301D|FD3E|FE35|FE37|FE39|FE3B|FE3D|FE3F|FE41|FE43|FE47|FE59|FE5B|FE5D|FF08|FF3B|FF5B|FF5F|FF62';
529 $pattern = "/(&#x($pi|$ps);)/i";
531 $ENTITIES = $this->jmix_encode_punctuation('"'."'".'(<[{');
533 $i = 0;
534 while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
535 $value = strtr($value, $ENTITIES);
536 if (preg_match_all($pattern, $value, $matches)) {
537 $chars = array_merge($chars, $matches[0]);
539 $i++;
542 return $chars;
544 function jmix_encode_punctuation($str) {
545 $ENTITIES = array();
546 $i_max = strlen($str);
547 for ($i=0; $i<$i_max; $i++) {
548 $ENTITIES[$str{$i}] = '&#x'.sprintf('%04X', ord($str{$i})).';';
550 return $ENTITIES;
552 function v6_expand_ExerciseTitle() {
553 return $this->parent->xml_value('data,title');
556 // Jmix specials
558 function v6_expand_SegmentArray() {
560 $segments = array();
561 $values = array();
562 $VALUES = array();
564 // XML tags to the start of a segment
565 $tags = 'data,jumbled-order-exercise,main-order,segment';
567 $i = 0;
568 while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
569 $VALUE = strtoupper($value);
570 $key = array_search($VALUE, $VALUES);
571 if (is_numeric($key)) {
572 $segments[] = $key;
573 } else {
574 $segments[] = $i;
575 $values[$i] = $value;
576 $VALUES[$i] = $VALUE;
578 $i++;
581 $this->seed_random_number_generator();
582 $keys = array_keys($segments);
583 shuffle($keys);
585 $str = '';
586 for($i=0; $i<count($keys); $i++) {
587 $key = $segments[$keys[$i]];
588 $str .= "Segments[$i] = new Array();\n";
589 $str .= "Segments[$i][0] = '".$this->js_safe($values[$key], true)."';\n";
590 $str .= "Segments[$i][1] = ".($key+1).";\n";
591 $str .= "Segments[$i][2] = 0;\n";
593 return $str;
595 function v6_expand_AnswerArray() {
597 $segments = array();
598 $values = array();
599 $VALUES = array();
600 $escapedvalues = array();
602 // XML tags to the start of a segment
603 $tags = 'data,jumbled-order-exercise,main-order,segment';
605 $i = 0;
606 while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
607 $VALUE = strtoupper($value);
608 $key = array_search($VALUE, $VALUES);
609 if (is_numeric($key)) {
610 $segments[] = $key+1;
611 } else {
612 $segments[] = $i+1;
613 $values[$i] = $value;
614 $VALUES[$i] = $VALUE;
615 $escapedvalues[] = preg_quote($value, '/');
617 $i++;
620 // start the answers array
621 $a = 0;
622 $str = 'Answers['.($a++).'] = new Array('.implode(',', $segments).");\n";
624 // pattern to match the next part of an alternate answer
625 $pattern = '/^('.implode('|', $escapedvalues).')\\s*/i';
627 // XML tags to the start of an alternate answer
628 $tags = 'data,jumbled-order-exercise,alternate';
630 $i = 0;
631 while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
632 $segments = array();
633 while (strlen($value) && preg_match($pattern, $value, $matches)) {
634 $key = array_search($matches[1], $values);
635 if (is_numeric($key)) {
636 $segments[] = $key+1;
637 $value = substr($value, strlen($matches[0]));
638 } else {
639 // invalid alternate sequence
640 $segments = array();
641 break;
644 if (count($segments)) {
645 $str .= 'Answers['.($a++).'] = new Array('.implode(',', $segments).");\n";
647 $i++;
649 return $str;
652 // ===============================================================
654 // JMix (jmix6.js_)
656 function v6_expand_RemainingWords() {
657 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',remaining-words');
659 function v6_expand_TimesUp() {
660 return $this->js_safe($this->parent->xml_value('hotpot-config-file,global,times-up'));
663 // nav bar
665 function v6_expand_NavBar($navbarid='') {
666 $this->navbarid = $navbarid;
668 $tag = 'navbar';
669 $this->read_template('hp6navbar.ht_', $tag);
671 unset($this->navbarid);
673 return $this->$tag;
675 function v6_expand_TopNavBar() {
676 return $this->v6_expand_NavBar('TopNavBar');
678 function v6_expand_BottomNavBar() {
679 return $this->v6_expand_NavBar('BottomNavBar');
682 // hp6navbar.ht_
684 function v6_expand_NavBarID() {
685 // $this->navbarid is set in "$this->v6_expand_NavBar"
686 return empty($this->navbarid) ? '' : $this->navbarid;
688 function v6_expand_ContentsURL() {
689 $url = $this->parent->xml_value('hotpot-config-file,global,contents-url');
690 if ($url) {
691 $url = hotpot_convert_navbutton_url($this->parent->get_baseurl(), $this->parent->reference, $url, $this->parent->course);
693 return $url;
695 function v6_expand_NextExURL() {
696 $url = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',next-ex-url');
697 if ($url) {
698 $url = hotpot_convert_navbutton_url($this->parent->get_baseurl(), $this->parent->reference, $url, $this->parent->course);
700 return $url;
703 // conditional blocks
705 function v6_expand_ShowAnswer() {
706 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-show-answer');
708 function v6_expand_Slide() {
709 return true; // whats's this (JMatch drag and drop)
712 // specials (JMatch)
714 function v6_expand_FixedArray() {
715 $this->set_jmatch_items();
716 $str = '';
717 foreach ($this->l_items as $i=>$item) {
718 for ($ii=0; $ii<$i; $ii++) {
719 if ($this->r_items[$ii]['text']==$this->r_items[$i]['text']) {
720 break;
723 $str .= "F[$i] = new Array();\n";
724 $str .= "F[$i][0] = '".$this->js_safe($item['text'], true)."';\n";
725 $str .= "F[$i][1] = ".($ii+1).";\n";
727 return $str;
729 function v6_expand_DragArray() {
730 $this->set_jmatch_items();
731 $str = '';
732 foreach ($this->r_items as $i=>$item) {
733 for ($ii=0; $ii<$i; $ii++) {
734 if ($this->r_items[$ii]['text']==$this->r_items[$i]['text']) {
735 break;
738 $str .= "D[$i] = new Array();\n";
739 $str .= "D[$i][0] = '".$this->js_safe($item['text'], true)."';\n";
740 $str .= "D[$i][1] = ".($ii+1).";\n";
741 $str .= "D[$i][2] = ".$item['fixed'].";\n";
743 return $str;
746 function set_jmatch_items() {
747 if (count($this->l_items)) {
748 return;
750 $tags = 'data,matching-exercise,pair';
751 $i = 0;
752 while (($item = "[$i]['#']") && $this->parent->xml_value($tags, $item)) {
753 $leftitem = $item."['left-item'][0]['#']";
754 $lefttext = $this->parent->xml_value($tags, $leftitem."['text'][0]['#']");
756 $rightitem = $item."['right-item'][0]['#']";
757 $righttext = $this->parent->xml_value($tags, $rightitem."['text'][0]['#']");
759 if (strlen($righttext)) {
760 $addright = true;
761 } else {
762 $addright = false;
764 if (strlen($lefttext)) {
765 $this->l_items[] = array(
766 'text' => $lefttext,
767 'fixed' => $this->int_value($tags, $leftitem."['fixed'][0]['#']")
769 $addright = true; // force right item to be added
771 if ($addright) {
772 $this->r_items[] = array(
773 'text' => $righttext,
774 'fixed' => $this->int_value($tags, $rightitem."['fixed'][0]['#']")
777 $i++;
780 function shuffle_jmatch_items(&$items) {
781 // get moveable items
782 $moveable_keys = array();
783 for($i=0; $i<count($items); $i++) {
784 if(empty($items[$i]['fixed'])) {
785 $moveable_keys[] = $i;
788 // shuffle moveable items
789 $this->seed_random_number_generator();
790 shuffle($moveable_keys);
792 $keys = array();
793 for($i=0, $ii=0; $i<count($items); $i++) {
794 if(empty($items[$i]['fixed'])) {
795 // moveable items are inserted in a shuffled order
796 $keys[] = $moveable_keys[$ii++];
797 } else {
798 // fixed items stay where they are
799 $keys[] = $i;
802 return $keys;
804 function seed_random_number_generator() {
805 static $seeded_RNG = FALSE;
806 if (!$seeded_RNG) {
807 srand((double) microtime() * 1000000);
808 $seeded_RNG = TRUE;
812 // specials (JMix)
815 // specials (JCloze)
817 function v6_expand_ItemArray() {
818 $q = 0;
819 $str = '';
820 switch ($this->parent->quiztype) {
821 case 'jcloze':
822 $tags = 'data,gap-fill,question-record';
823 while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
824 $a = 0;
825 $aa = 0;
826 while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
827 $text = $this->js_value($tags, $answer."['text'][0]['#']", true);
828 if (strlen($text)) {
829 if ($aa==0) { // first time only
830 $str .= "\n";
831 $str .= "I[$q] = new Array();\n";
832 $str .= "I[$q][1] = new Array();\n";
834 $str .= "I[$q][1][$aa] = new Array();\n";
835 $str .= "I[$q][1][$aa][0] = '$text';\n";
836 $aa++;
838 $a++;
840 // add clue, if any answers were found
841 if ($aa) {
842 $clue = $this->js_value($tags, $question."['clue'][0]['#']", true);
843 $str .= "I[$q][2] = '$clue';\n";
845 $q++;
847 break;
848 case 'jquiz':
849 $str .= "I=new Array();\n";
850 $tags = 'data,questions,question-record';
851 while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
853 $question_type = $this->int_value($tags, $question."['question-type'][0]['#']");
854 $weighting = $this->int_value($tags, $question."['weighting'][0]['#']");
855 $clue = $this->js_value($tags, $question."['clue'][0]['#']", true);
857 $answers = $question."['answers'][0]['#']";
859 $a = 0;
860 $aa = 0;
861 while (($answer = $answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
862 $text = $this->js_value($tags, $answer."['text'][0]['#']", true);
863 $feedback = $this->js_value($tags, $answer."['feedback'][0]['#']", true);
864 $correct = $this->int_value($tags, $answer."['correct'][0]['#']");
865 $percent = $this->int_value($tags, $answer."['percent-correct'][0]['#']");
866 $include = $this->int_value($tags, $answer."['include-in-mc-options'][0]['#']");
867 if (strlen($text)) {
868 if ($aa==0) { // first time only
869 $str .= "\n";
870 $str .= "I[$q] = new Array();\n";
871 $str .= "I[$q][0] = $weighting;\n";
872 $str .= "I[$q][1] = '$clue';\n";
873 $str .= "I[$q][2] = '".($question_type-1)."';\n";
874 $str .= "I[$q][3] = new Array();\n";
876 $str .= "I[$q][3][$aa] = new Array('$text','$feedback',$correct,$percent,$include);\n";
877 $aa++;
879 $a++;
881 $q++;
883 break;
885 return $str;
888 function v6_expand_ClozeBody() {
889 $str = '';
891 // get drop down list of words, if required
892 $dropdownlist = '';
893 if ($this->v6_use_DropDownList()) {
894 $this->v6_set_WordList();
895 foreach ($this->wordlist as $word) {
896 $dropdownlist .= '<option value="'.$word.'">'.$word.'</option>';
900 // cache clues flag and caption
901 $includeclues = $this->v6_expand_Clues();
902 $cluecaption = $this->v6_expand_ClueCaption();
904 // detect if cloze starts with gap
905 $strpos = strpos($this->parent->source, '<gap-fill><question-record>');
906 if (is_numeric($strpos)) {
907 $startwithgap = true;
908 } else {
909 $startwithgap = false;
912 // initialize loop values
913 $q = 0;
914 $tags = 'data,gap-fill';
915 $question_record = "$tags,question-record";
917 // loop through text and gaps
918 do {
919 $text = $this->parent->xml_value($tags, "[0]['#'][$q]");
920 $gap = '';
921 if (($question="[$q]['#']") && $this->parent->xml_value($question_record, $question)) {
922 $gap .= '<span class="GapSpan" id="GapSpan'.$q.'">';
923 if ($this->v6_use_DropDownList()) {
924 $gap .= '<select id="Gap'.$q.'"><option value=""></option>'.$dropdownlist.'</select>';
925 } else {
926 // minimum gap size
927 if (! $gapsize = $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',minimum-gap-size')) {
928 $gapsize = 6;
931 // increase gap size to length of longest answer for this gap
932 $a = 0;
933 while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($question_record, $answer)) {
934 $answertext = $this->parent->xml_value($question_record, $answer."['text'][0]['#']");
935 $answertext = preg_replace('|&[#a-zA-Z0-9]+;|', 'x', $answertext);
936 $gapsize = max($gapsize, strlen($answertext));
937 $a++;
940 $gap .= '<input type="text" id="Gap'.$q.'" onfocus="TrackFocus('.$q.')" onblur="LeaveGap()" class="GapBox" size="'.$gapsize.'"></input>';
942 if ($includeclues) {
943 $clue = $this->parent->xml_value($question_record, $question."['clue'][0]['#']");
944 if (strlen($clue)) {
945 $gap .= '<button style="line-height: 1.0" class="FuncButton" onfocus="FuncBtnOver(this)" onmouseover="FuncBtnOver(this)" onblur="FuncBtnOut(this)" onmouseout="FuncBtnOut(this)" onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)" onclick="ShowClue('.$q.')">'.$cluecaption.'</button>';
948 $gap .= '</span>';
950 if ($startwithgap) {
951 $str .= "$gap$text";
952 } else {
953 $str .= "$text$gap";
955 $q++;
956 } while (strlen($text) || strlen($gap));
958 return $str;
961 // JCloze quiztype
963 function v6_expand_WordList() {
964 $str = '';
965 if ($this->v6_include_WordList()) {
966 $this->v6_set_WordList();
967 $str = implode(' &#160;&#160; ', $this->wordlist);
969 return $str;
971 function v6_include_WordList() {
972 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-word-list');
974 function v6_use_DropDownList() {
975 return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',use-drop-down-list');
977 function v6_set_WordList() {
979 if (isset($this->wordlist)) {
980 // do nothing
981 } else {
982 $this->wordlist = array();
984 // is the wordlist required
985 if ($this->v6_include_WordList() || $this->v6_use_DropDownList()) {
987 $q = 0;
988 $tags = 'data,gap-fill,question-record';
989 while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
990 $a = 0;
991 $aa = 0;
992 while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
993 $text = $this->parent->xml_value($tags, $answer."['text'][0]['#']");
994 $correct = $this->int_value($tags, $answer."['correct'][0]['#']");
995 if ($text && $correct) { // $correct is always true
996 $this->wordlist[] = $text;
997 $aa++;
999 $a++;
1001 $q++;
1003 $this->wordlist = array_unique($this->wordlist);
1004 sort($this->wordlist);
1008 function v6_expand_Keypad() {
1009 $str = '';
1010 if ($this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-keypad')) {
1012 // these characters must always be in the keypad
1013 $chars = array();
1014 $this->add_keypad_chars($chars, $this->parent->xml_value('hotpot-config-file,global,keypad-characters'));
1016 // append other characters used in the answers
1017 $tags = '';
1018 switch ($this->parent->quiztype) {
1019 case 'jcloze':
1020 $tags = 'data,gap-fill,question-record';
1021 break;
1022 case 'jquiz':
1023 $tags = 'data,questions,question-record';
1024 break;
1026 if ($tags) {
1027 $q = 0;
1028 while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
1030 if ($this->parent->quiztype=='jquiz') {
1031 $answers = $question."['answers'][0]['#']";
1032 } else {
1033 $answers = $question;
1036 $a = 0;
1037 while (($answer=$answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
1038 $this->add_keypad_chars($chars, $this->parent->xml_value($tags, $answer."['text'][0]['#']"));
1039 $a++;
1041 $q++;
1045 // remove duplicate characters and sort
1046 $chars = array_unique($chars);
1047 usort($chars, "hotpot_sort_keypad_chars");
1049 // create keypad buttons for each character
1050 foreach ($chars as $char) {
1051 $str .= "<button onclick=\"TypeChars('".$this->js_safe($char, true)."'); return false;\">$char</button>";
1054 return $str;
1056 function add_keypad_chars(&$chars, $text) {
1057 if (preg_match_all('|&[^;]+;|i', $text, $more_chars)) {
1058 $chars = array_merge($chars, $more_chars[0]);
1061 function v6_expand_Correct() {
1062 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guesses-correct');
1064 function v6_expand_Incorrect() {
1065 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guesses-incorrect');
1067 function v6_expand_GiveHint() {
1068 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-letter');
1070 function v6_expand_CaseSensitive() {
1071 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',case-sensitive');
1074 // JCross quiztype
1076 function v6_expand_CluesAcrossLabel() {
1077 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',clues-across');
1079 function v6_expand_CluesDownLabel() {
1080 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',clues-down');
1082 function v6_expand_EnterCaption() {
1083 return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',enter-caption');
1085 function v6_expand_ShowHideClueList() {
1086 $value = $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-clue-list');
1087 return empty($value) ? ' style="display: none;"' : '';
1090 // JCross specials
1092 function v6_expand_CluesDown() {
1093 return $this->v6_expand_jcross_clues('D');
1095 function v6_expand_CluesAcross() {
1096 return $this->v6_expand_jcross_clues('A');
1098 function v6_expand_jcross_clues($direction) {
1099 // $direction: A(cross) or D(own)
1100 $row = NULL;
1101 $r_max = 0;
1102 $c_max = 0;
1103 $this->v6_get_jcross_grid($row, $r_max, $c_max);
1105 $i = 0; // clue index;
1106 $str = '';
1107 for($r=0; $r<=$r_max; $r++) {
1108 for($c=0; $c<=$c_max; $c++) {
1109 $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
1110 $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
1111 if ($aword || $dword) {
1112 $i++; // increment clue index
1114 // get the definition for this word
1115 $def = '';
1116 $word = ($direction=='A') ? $aword : $dword;
1117 $clues = $this->parent->xml_values('data,crossword,clues,item');
1118 foreach ($clues as $clue) {
1119 if ($clue['word'][0]['#']==$word) {
1120 $def = $clue['def'][0]['#'];
1121 $def = strtr($def, array('&#x003C;'=>'<', '&#x003E;'=>'>', "\n"=>'<br />'));
1122 break;
1126 if (!empty($def)) {
1127 $str .= '<tr><td class="ClueNum">'.$i.'. </td><td id="Clue_'.$direction.'_'.$i.'" class="Clue">'.$def.'</td></tr>';
1132 return $str;
1135 // jcross6.js_
1137 function v6_expand_LetterArray() {
1138 $row = NULL;
1139 $r_max = 0;
1140 $c_max = 0;
1141 $this->v6_get_jcross_grid($row, $r_max, $c_max);
1143 $str = '';
1144 for($r=0; $r<=$r_max; $r++) {
1145 $str .= "L[$r] = new Array(";
1146 for($c=0; $c<=$c_max; $c++) {
1147 $str .= ($c>0 ? ',' : '')."'".$this->js_safe($row[$r]['cell'][$c]['#'], true)."'";
1149 $str .= ");\n";
1151 return $str;
1153 function v6_expand_GuessArray() {
1154 $row = NULL;
1155 $r_max = 0;
1156 $c_max = 0;
1157 $this->v6_get_jcross_grid($row, $r_max, $c_max);
1159 $str = '';
1160 for($r=0; $r<=$r_max; $r++) {
1161 $str .= "G[$r] = new Array('".str_repeat("','", $c_max)."');\n";
1163 return $str;
1165 function v6_expand_ClueNumArray() {
1166 $row = NULL;
1167 $r_max = 0;
1168 $c_max = 0;
1169 $this->v6_get_jcross_grid($row, $r_max, $c_max);
1171 $i = 0; // clue index
1172 $str = '';
1173 for($r=0; $r<=$r_max; $r++) {
1174 $str .= "CL[$r] = new Array(";
1175 for($c=0; $c<=$c_max; $c++) {
1176 if ($c>0) {
1177 $str .= ',';
1179 $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
1180 $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
1181 if (empty($aword) && empty($dword)) {
1182 $str .= 0;
1183 } else {
1184 $i++; // increment the clue index
1185 $str .= $i;
1188 $str .= ");\n";
1190 return $str;
1192 function v6_expand_GridBody() {
1193 $row = NULL;
1194 $r_max = 0;
1195 $c_max = 0;
1196 $this->v6_get_jcross_grid($row, $r_max, $c_max);
1198 $i = 0; // clue index;
1199 $str = '';
1200 for($r=0; $r<=$r_max; $r++) {
1201 $str .= '<tr id="Row_'.$r.'">';
1202 for($c=0; $c<=$c_max; $c++) {
1203 if (empty($row[$r]['cell'][$c]['#'])) {
1204 $str .= '<td class="BlankCell">&nbsp;</td>';
1205 } else {
1206 $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
1207 $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
1208 if (empty($aword) && empty($dword)) {
1209 $str .= '<td class="LetterOnlyCell"><span id="L_'.$r.'_'.$c.'">&nbsp;</span></td>';
1210 } else {
1211 $i++; // increment clue index
1212 $str .= '<td class="NumLetterCell"><a href="javascript:void(0);" class="GridNum" onclick="ShowClue('.$i.','.$r.','.$c.')">'.$i.'</a><span class="NumLetterCellText" id="L_'.$r.'_'.$c.'" onclick="ShowClue('.$i.','.$r.','.$c.')">&nbsp;&nbsp;&nbsp;</span></td>';
1216 $str .= '</tr>';
1218 return $str;
1220 function v6_get_jcross_grid(&$row, &$r_max, &$c_max) {
1221 $row = $this->parent->xml_values('data,crossword,grid,row');
1222 $r_max = 0;
1223 $c_max = 0;
1224 if (isset($row) && is_array($row)) {
1225 for($r=0; $r<count($row); $r++) {
1226 if (isset($row[$r]['cell']) && is_array($row[$r]['cell'])) {
1227 for($c=0; $c<count($row[$r]['cell']); $c++) {
1228 if (!empty($row[$r]['cell'][$c]['#'])) {
1229 $r_max = max($r, $r_max);
1230 $c_max = max($c, $c_max);
1232 } // end for $c
1234 } // end for $r
1237 function get_jcross_dword(&$row, $r, $r_max, $c, $c_max) {
1238 $str = '';
1239 if (($r==0 || empty($row[$r-1]['cell'][$c]['#'])) && $r<$r_max && !empty($row[$r+1]['cell'][$c]['#'])) {
1240 $str = $this->get_jcross_word($row, $r, $r_max, $c, $c_max, true);
1242 return $str;
1244 function get_jcross_aword(&$row, $r, $r_max, $c, $c_max) {
1245 $str = '';
1246 if (($c==0 || empty($row[$r]['cell'][$c-1]['#'])) && $c<$c_max && !empty($row[$r]['cell'][$c+1]['#'])) {
1247 $str = $this->get_jcross_word($row, $r, $r_max, $c, $c_max, false);
1249 return $str;
1251 function get_jcross_word(&$row, $r, $r_max, $c, $c_max, $go_down=false) {
1252 $str = '';
1253 while ($r<=$r_max && $c<=$c_max && !empty($row[$r]['cell'][$c]['#'])) {
1254 $str .= $row[$r]['cell'][$c]['#'];
1255 if ($go_down) {
1256 $r++;
1257 } else {
1258 $c++;
1261 return $str;
1264 // specials (JQuiz)
1266 function v6_expand_QuestionOutput() {
1267 $str = '';
1268 $str .= '<ol class="QuizQuestions" id="Questions">'."\n";
1270 $q = 0;
1271 $tags = 'data,questions,question-record';
1272 while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
1274 // get question
1275 $question_text = $this->parent->xml_value($tags, $question."['question'][0]['#']");
1276 $question_type = $this->parent->xml_value($tags, $question."['question-type'][0]['#']");
1278 $first_answer_text = $this->parent->xml_value($tags, $question."['answers'][0]['#']['answer'][0]['#']['text'][0]['#']");
1280 // check we have a question (or at least one answer)
1281 if (($question_text || $first_answer_text) && $question_type) {
1283 $str .= '<li class="QuizQuestion" id="Q_'.$q.'" style="display: none;">';
1284 $str .= '<p class="QuestionText">'.$question_text.'</p>';
1286 if (
1287 $question_type==HOTPOT_JQUIZ_SHORTANSWER ||
1288 $question_type==HOTPOT_JQUIZ_HYBRID
1290 $size = 9; // default size
1291 $a = 0;
1292 $answers = $question."['answers'][0]['#']";
1293 while (($answer = $answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
1294 $text = $this->parent->xml_value($tags, $answer."['text'][0]['#']");
1295 $text = preg_replace('/&[#a-zA-Z0-9]+;/', 'x', $text);
1296 $size = max($size, strlen($text));
1297 $a++;
1300 $str .= '<div class="ShortAnswer" id="Q_'.$q.'_SA"><form method="post" action="" onsubmit="return false;"><div>';
1301 if ($size<=25) { // text box
1302 $str .= '<input type="text" id="Q_'.$q.'_Guess" onfocus="TrackFocus('."'".'Q_'.$q.'_Guess'."'".')" onblur="LeaveGap()" class="ShortAnswerBox" size="'.$size.'"></input>';
1303 } else { // textarea (29 cols wide)
1304 $str .= '<textarea id="Q_'.$q.'_Guess" onfocus="TrackFocus('."'".'Q_'.$q.'_Guess'."'".')" onblur="LeaveGap()" class="ShortAnswerBox" cols="29" rows="'.ceil($size/25).'"></textarea>';
1306 $str .= '<br /><br />';
1308 $caption = $this->v6_expand_CheckCaption();
1309 $str .= $this->v6_expand_jquiz_button($caption, "CheckShortAnswer($q)");
1311 if ($this->v6_expand_Hint()) {
1312 $caption = $this->v6_expand_HintCaption();
1313 $str .= $this->v6_expand_jquiz_button($caption, "ShowHint($q)");
1316 if ($this->v6_expand_ShowAnswer()) {
1317 $caption = $this->v6_expand_ShowAnswerCaption();
1318 $str .= $this->v6_expand_jquiz_button($caption, "ShowAnswers($q)");
1321 $str .= '</div></form></div>';
1324 if (
1325 $question_type==HOTPOT_JQUIZ_MULTICHOICE ||
1326 $question_type==HOTPOT_JQUIZ_HYBRID ||
1327 $question_type==HOTPOT_JQUIZ_MULTISELECT
1330 switch ($question_type) {
1331 case HOTPOT_JQUIZ_MULTICHOICE:
1332 $str .= '<ol class="MCAnswers">'."\n";
1333 break;
1334 case HOTPOT_JQUIZ_HYBRID:
1335 $str .= '<ol class="MCAnswers" id="Q_'.$q.'_Hybrid_MC" style="display: none;">'."\n";
1336 break;
1337 case HOTPOT_JQUIZ_MULTISELECT:
1338 $str .= '<ol class="MSelAnswers">'."\n";
1339 break;
1342 $a = 0;
1343 $aa = 0;
1344 $answers = $question."['answers'][0]['#']";
1345 while (($answer = $answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
1346 $text = $this->parent->xml_value($tags, $answer."['text'][0]['#']");
1347 if ($text) {
1348 switch ($question_type) {
1349 case HOTPOT_JQUIZ_MULTICHOICE:
1350 case HOTPOT_JQUIZ_HYBRID:
1351 $include = $this->int_value($tags, $answer."['include-in-mc-options'][0]['#']");
1352 if ($include) {
1353 $str .= '<li id="Q_'.$q.'_'.$aa.'"><button class="FuncButton" onfocus="FuncBtnOver(this)" onblur="FuncBtnOut(this)" onmouseover="FuncBtnOver(this)" onmouseout="FuncBtnOut(this)" onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)" id="Q_'.$q.'_'.$aa.'_Btn" onclick="CheckMCAnswer('.$q.','.$aa.',this)">&nbsp;&nbsp;?&nbsp;&nbsp;</button>&nbsp;&nbsp;'.$text.'</li>'."\n";
1355 break;
1356 case HOTPOT_JQUIZ_MULTISELECT:
1357 $str .= '<li id="Q_'.$q.'_'.$aa.'"><form method="post" action="" onsubmit="return false;"><div><input type="checkbox" id="Q_'.$q.'_'.$aa.'_Chk" class="MSelCheckbox" />'.$text.'</div></form></li>'."\n";
1358 break;
1360 $aa++;
1362 $a++;
1365 $str .= '</ol>';
1367 if ($question_type==HOTPOT_JQUIZ_MULTISELECT) {
1368 $caption = $this->v6_expand_CheckCaption();
1369 $str .= $this->v6_expand_jquiz_button($caption, "CheckMultiSelAnswer($q)");
1373 $str .= "</li>\n";
1375 $q++;
1377 } // end while $question
1379 $str .= "</ol>\n";
1380 return $str;
1383 function v6_expand_jquiz_button($caption, $onclick) {
1384 return '<button class="FuncButton" onfocus="FuncBtnOver(this)" onblur="FuncBtnOut(this)" onmouseover="FuncBtnOver(this)" onmouseout="FuncBtnOut(this)" onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)" onclick="'.$onclick.'">'.$caption.'</button>';
1387 // jquiz.js_
1389 function v6_expand_MultiChoice() {
1390 return $this->v6_jquiz_question_type(HOTPOT_JQUIZ_MULTICHOICE);
1392 function v6_expand_ShortAnswer() {
1393 return $this->v6_jquiz_question_type(HOTPOT_JQUIZ_SHORTANSWER);
1395 function v6_expand_MultiSelect() {
1396 return $this->v6_jquiz_question_type(HOTPOT_JQUIZ_MULTISELECT);
1398 function v6_jquiz_question_type($type) {
1399 // does this quiz have any questions of the given $type?
1400 $flag = false;
1402 $q = 0;
1403 $tags = 'data,questions,question-record';
1404 while (($question = "[$q]['#']") && $this->parent->xml_value($tags, $question)) {
1405 $question_type = $this->parent->xml_value($tags, $question."['question-type'][0]['#']");
1406 if ($question_type==$type || ($question_type==HOTPOT_JQUIZ_HYBRID && ($type==HOTPOT_JQUIZ_MULTICHOICE || $type==HOTPOT_JQUIZ_SHORTANSWER))) {
1407 $flag = true;
1408 break;
1410 $q++;
1412 return $flag;
1414 function v6_expand_CorrectFirstTime() {
1415 return $this->js_value('hotpot-config-file,global,correct-first-time');
1417 function v6_expand_ContinuousScoring() {
1418 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',continuous-scoring');
1420 function v6_expand_ShowCorrectFirstTime() {
1421 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',show-correct-first-time');
1423 function v6_expand_ShuffleAs() {
1424 return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',shuffle-answers');
1427 function v6_expand_DefaultRight() {
1428 return $this->v6_expand_GuessCorrect();
1430 function v6_expand_DefaultWrong() {
1431 return $this->v6_expand_GuessIncorrect();
1433 function v6_expand_ShowAllQuestionsCaptionJS() {
1434 return $this->v6_expand_ShowAllQuestionsCaption();
1436 function v6_expand_ShowOneByOneCaptionJS() {
1437 return $this->v6_expand_ShowOneByOneCaption();
1440 // hp6checkshortanswers.js_ (JQuiz)
1442 function v6_expand_CorrectList() {
1443 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',correct-answers');
1445 function v6_expand_HybridTries() {
1446 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',short-answer-tries-on-hybrid-q');
1448 function v6_expand_PleaseEnter() {
1449 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',enter-a-guess');
1451 function v6_expand_PartlyIncorrect() {
1452 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',partly-incorrect');
1454 function v6_expand_ShowAnswerCaption() {
1455 return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',show-answer-caption');
1457 function v6_expand_ShowAlsoCorrect() {
1458 return $this->bool_value('hotpot-config-file,global,show-also-correct');
1461 } // end class
1462 function hotpot_sort_keypad_chars($a, $b) {
1463 $a = hotpot_keypad_sort_value($a);
1464 $b = hotpot_keypad_sort_value($b);
1465 return ($a<$b) ? -1 : ($a==$b ? 0 : 1);
1467 function hotpot_keypad_sort_value($char) {
1469 // hexadecimal
1470 if (preg_match('|&#x([0-9A-F]+);|ie', $char, $matches)) {
1471 $ord = hexdec($matches[1]);
1473 // decimal
1474 } else if (preg_match('|&#(\d+);|i', $char, $matches)) {
1475 $ord = intval($matches[1]);
1477 // other html entity
1478 } else if (preg_match('|&[^;]+;|', $char, $matches)) {
1479 $char = html_entity_decode($matches[0]);
1480 $ord = empty($char) ? 0 : ord($char);
1482 // not an html entity
1483 } else {
1484 $char = trim($char);
1485 $ord = empty($char) ? 0 : ord($char);
1488 // lowercase letters (plain or accented)
1489 if (($ord>=97 && $ord<=122) || ($ord>=224 && $ord<=255)) {
1490 $sort_value = ($ord-31).'.'.sprintf('%04d', $ord);
1492 // all other characters
1493 } else {
1494 $sort_value = $ord;
1497 return $sort_value;