PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/MoodleWebRole/mod/hotpot/template/v6.php

#
PHP | 1515 lines | 1258 code | 167 blank | 90 comment | 207 complexity | ca07b47e446f97cb434dd2136df1c8d7 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, LGPL-2.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?PHP
  2. class hotpot_xml_quiz_template extends hotpot_xml_template_default {
  3. // left and right items for JMatch
  4. var $l_items = array();
  5. var $r_items = array();
  6. // constructor function for this class
  7. function hotpot_xml_quiz_template(&$parent) {
  8. $this->parent = &$parent;
  9. $get_js = optional_param('js', false);
  10. $get_css = optional_param('css', false);
  11. if (!empty($get_css)) {
  12. // set $this->css
  13. $this->v6_expand_StyleSheet();
  14. } else if (!empty($get_js)) {
  15. // set $this->js
  16. $this->read_template($this->parent->draganddrop.$this->parent->quiztype.'6.js_', 'js');
  17. } else {
  18. // set $this->html
  19. $this->read_template($this->parent->draganddrop.$this->parent->quiztype.'6.ht_', 'html');
  20. }
  21. // expand special strings, if any
  22. $pattern = '';
  23. switch ($this->parent->quiztype) {
  24. case 'jcloze':
  25. $pattern = '/\[(PreloadImageList)\]/';
  26. break;
  27. case 'jcross':
  28. $pattern = '/\[(PreloadImageList|ShowHideClueList)\]/';
  29. break;
  30. case 'jmatch':
  31. $pattern = '/\[(PreloadImageList|QsToShow|FixedArray|DragArray)\]/';
  32. break;
  33. case 'jmix':
  34. $pattern = '/\[(PreloadImageList|SegmentArray|AnswerArray)\]/';
  35. break;
  36. case 'jquiz':
  37. $pattern = '/\[(PreloadImageList|QsToShow)\]/';
  38. break;
  39. }
  40. if (!empty($pattern)) {
  41. $this->expand_strings('html', $pattern);
  42. }
  43. // fix doctype (convert short dtd to long dtd)
  44. $this->html = preg_replace(
  45. '/<!DOCTYPE[^>]*>/',
  46. '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
  47. $this->html, 1
  48. );
  49. }
  50. // captions and messages
  51. function v6_expand_AlsoCorrect() {
  52. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',also-correct');
  53. }
  54. function v6_expand_CapitalizeFirst() {
  55. return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',capitalize-first-letter');
  56. }
  57. function v6_expand_CheckCaption() {
  58. return $this->parent->xml_value('hotpot-config-file,global,check-caption');
  59. }
  60. function v6_expand_CorrectIndicator() {
  61. return $this->js_value('hotpot-config-file,global,correct-indicator');
  62. }
  63. function v6_expand_Back() {
  64. return $this->int_value('hotpot-config-file,global,include-back');
  65. }
  66. function v6_expand_BackCaption() {
  67. return str_replace('<=', '&lt;=', $this->parent->xml_value('hotpot-config-file,global,back-caption'));
  68. }
  69. function v6_expand_ClickToAdd() {
  70. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',click-to-add');
  71. }
  72. function v6_expand_ClueCaption() {
  73. return $this->parent->xml_value('hotpot-config-file,global,clue-caption');
  74. }
  75. function v6_expand_Clues() {
  76. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-clues');
  77. }
  78. function v6_expand_Contents() {
  79. return $this->int_value('hotpot-config-file,global,include-contents');
  80. }
  81. function v6_expand_ContentsCaption() {
  82. return $this->parent->xml_value('hotpot-config-file,global,contents-caption');
  83. }
  84. function v6_expand_GuessCorrect() {
  85. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guess-correct');
  86. }
  87. function v6_expand_GuessIncorrect() {
  88. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',guess-incorrect');
  89. }
  90. function v6_expand_Hint() {
  91. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-hint');
  92. }
  93. function v6_expand_HintCaption() {
  94. return $this->parent->xml_value('hotpot-config-file,global,hint-caption');
  95. }
  96. function v6_expand_IncorrectIndicator() {
  97. return $this->js_value('hotpot-config-file,global,incorrect-indicator');
  98. }
  99. function v6_expand_LastQCaption() {
  100. $caption = $this->parent->xml_value('hotpot-config-file,global,last-q-caption');
  101. return ($caption=='<=' ? '&lt;=' : $caption);
  102. }
  103. function v6_expand_NextCorrect() {
  104. $value = $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-part');
  105. if (empty($value)) { // jquiz
  106. $value = $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-letter');
  107. }
  108. return $value;
  109. }
  110. function v6_expand_NextEx() {
  111. return $this->int_value('hotpot-config-file,global,include-next-ex');
  112. }
  113. function v6_expand_NextExCaption() {
  114. return str_replace('=>', '=&gt;', $this->parent->xml_value('hotpot-config-file,global,next-ex-caption'));
  115. }
  116. function v6_expand_NextQCaption() {
  117. return $this->parent->xml_value('hotpot-config-file,global,next-q-caption');
  118. }
  119. function v6_expand_OKCaption() {
  120. return $this->parent->xml_value('hotpot-config-file,global,ok-caption');
  121. }
  122. function v6_expand_Restart() {
  123. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-restart');
  124. }
  125. function v6_expand_RestartCaption() {
  126. return $this->parent->xml_value('hotpot-config-file,global,restart-caption');
  127. }
  128. function v6_expand_ShowAllQuestionsCaption() {
  129. return $this->js_value('hotpot-config-file,global,show-all-questions-caption');
  130. }
  131. function v6_expand_ShowOneByOneCaption() {
  132. return $this->js_value('hotpot-config-file,global,show-one-by-one-caption');
  133. }
  134. function v6_expand_TheseAnswersToo() {
  135. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',also-correct');
  136. }
  137. function v6_expand_ThisMuch() {
  138. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',this-much-correct');
  139. }
  140. function v6_expand_Undo() {
  141. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-undo');
  142. }
  143. function v6_expand_UndoCaption() {
  144. return $this->parent->xml_value('hotpot-config-file,global,undo-caption');
  145. }
  146. function v6_expand_YourScoreIs() {
  147. return $this->js_value('hotpot-config-file,global,your-score-is');
  148. }
  149. // reading
  150. function v6_expand_Reading() {
  151. return $this->int_value('data,reading,include-reading');
  152. }
  153. function v6_expand_ReadingText() {
  154. $title = $this->v6_expand_ReadingTitle();
  155. $value = $this->parent->xml_value('data,reading,reading-text');
  156. $value = empty($value) ? '' : ('<div class="ReadingText">'.$value.'</div>');
  157. return $title.$value;
  158. }
  159. function v6_expand_ReadingTitle() {
  160. $value = $this->parent->xml_value('data,reading,reading-title');
  161. return empty($value) ? '' : ('<h3 class="ExerciseSubtitle">'.$value.'</h3>');
  162. }
  163. // timer
  164. function v6_expand_Timer() {
  165. return $this->int_value('data,timer,include-timer');
  166. }
  167. function v6_expand_JSTimer() {
  168. return $this->read_template('hp6timer.js_');
  169. }
  170. function v6_expand_Seconds() {
  171. return $this->parent->xml_value('data,timer,seconds');
  172. }
  173. // send results
  174. function v6_expand_SendResults() {
  175. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',send-email');
  176. }
  177. function v6_expand_JSSendResults() {
  178. return $this->read_template('hp6sendresults.js_');
  179. }
  180. function v6_expand_FormMailURL() {
  181. return $this->parent->xml_value('hotpot-config-file,global,formmail-url');
  182. }
  183. function v6_expand_EMail() {
  184. return $this->parent->xml_value('hotpot-config-file,global,email');
  185. }
  186. function v6_expand_NamePlease() {
  187. return $this->js_value('hotpot-config-file,global,name-please');
  188. }
  189. // preload images
  190. function v6_expand_PreloadImages() {
  191. $value = $this->v6_expand_PreloadImageList();
  192. return empty($value) ? false : true;
  193. }
  194. function v6_expand_PreloadImageList() {
  195. // check it has not been set already
  196. if (!isset($this->PreloadImageList)) {
  197. // the list of image urls
  198. $list = array();
  199. // extract <img> tags
  200. $img_tag = htmlspecialchars('|&#x003C;img.*?src="(.*?)".*?&#x003E;|is');
  201. if (preg_match_all($img_tag, $this->parent->source, $matches)) {
  202. $list = $matches[1];
  203. // remove duplicates
  204. $list = array_unique($list);
  205. }
  206. // convert to comma delimited string
  207. $this->PreloadImageList = empty($list) ? '' : "'".implode("','", $list)."'";
  208. }
  209. return $this->PreloadImageList;
  210. }
  211. // html files (all quiz types)
  212. function v6_expand_PlainTitle() {
  213. return $this->parent->xml_value('data,title');
  214. }
  215. function v6_expand_ExerciseSubtitle() {
  216. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',exercise-subtitle');
  217. }
  218. function v6_expand_Instructions() {
  219. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',instructions');
  220. }
  221. function v6_expand_DublinCoreMetadata() {
  222. $dc = '<link rel="schema.DC" href="'.$this->parent->xml_value('', "['rdf:RDF'][0]['@']['xmlns:dc']").'" />'."\n";
  223. if (is_string($this->parent->xml_value('rdf:RDF,rdf:Description'))) {
  224. // do nothing (there is no more dc info)
  225. } else {
  226. $dc .= '<meta name="DC:Creator" content="'.$this->parent->xml_value('rdf:RDF,rdf:Description,dc:creator').'" />'."\n";
  227. $dc .= '<meta name="DC:Title" content="'.strip_tags($this->parent->xml_value('rdf:RDF,rdf:Description,dc:title')).'" />'."\n";
  228. }
  229. return $dc;
  230. }
  231. function v6_expand_FullVersionInfo() {
  232. global $CFG;
  233. require_once($CFG->hotpotroot.DIRECTORY_SEPARATOR.'version.php'); // set $module
  234. return $this->parent->xml_value('version').'.x (Moodle '.$CFG->release.', hotpot-module '.$this->parent->obj_value($module, 'release').')';
  235. }
  236. function v6_expand_HeaderCode() {
  237. return $this->parent->xml_value('hotpot-config-file,global,header-code');
  238. }
  239. function v6_expand_StyleSheet() {
  240. $this->read_template('hp6.cs_', 'css');
  241. $this->css = hotpot_convert_stylesheets_urls($this->parent->get_baseurl(), $this->parent->reference, $this->css);
  242. return $this->css;
  243. }
  244. // stylesheet (hp6.cs_)
  245. function v6_expand_PageBGColor() {
  246. return $this->parent->xml_value('hotpot-config-file,global,page-bg-color');
  247. }
  248. function v6_expand_GraphicURL() {
  249. return $this->parent->xml_value('hotpot-config-file,global,graphic-url');
  250. }
  251. function v6_expand_ExBGColor() {
  252. return $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
  253. }
  254. function v6_expand_FontFace() {
  255. return $this->parent->xml_value('hotpot-config-file,global,font-face');
  256. }
  257. function v6_expand_FontSize() {
  258. $value = $this->parent->xml_value('hotpot-config-file,global,font-size');
  259. return (empty($value) ? 'small' : $value);
  260. }
  261. function v6_expand_TextColor() {
  262. return $this->parent->xml_value('hotpot-config-file,global,text-color');
  263. }
  264. function v6_expand_TitleColor() {
  265. return $this->parent->xml_value('hotpot-config-file,global,title-color');
  266. }
  267. function v6_expand_LinkColor() {
  268. return $this->parent->xml_value('hotpot-config-file,global,link-color');
  269. }
  270. function v6_expand_VLinkColor() {
  271. return $this->parent->xml_value('hotpot-config-file,global,vlink-color');
  272. }
  273. function v6_expand_NavTextColor() {
  274. return $this->parent->xml_value('hotpot-config-file,global,page-bg-color');
  275. }
  276. function v6_expand_NavBarColor() {
  277. return $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
  278. }
  279. function v6_expand_NavLightColor() {
  280. $color = $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
  281. return $this->get_halfway_color($color, '#ffffff');
  282. }
  283. function v6_expand_NavShadeColor() {
  284. $color = $this->parent->xml_value('hotpot-config-file,global,nav-bar-color');
  285. return $this->get_halfway_color($color, '#000000');
  286. }
  287. function v6_expand_FuncLightColor() { // top-left of buttons
  288. $color = $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
  289. return $this->get_halfway_color($color, '#ffffff');
  290. }
  291. function v6_expand_FuncShadeColor() { // bottom right of buttons
  292. $color = $this->parent->xml_value('hotpot-config-file,global,ex-bg-color');
  293. return $this->get_halfway_color($color, '#000000');
  294. }
  295. // navigation buttons
  296. function v6_expand_NavButtons() {
  297. $back = $this->v6_expand_Back();
  298. $next_ex = $this->v6_expand_NextEx();
  299. $contents = $this->v6_expand_Contents();
  300. return (empty($back) && empty($next_ex) && empty($contents) ? false : true);
  301. }
  302. function v6_expand_NavBarJS() {
  303. return $this->v6_expand_NavButtons();
  304. }
  305. // switch off scorm
  306. function v6_expand_Scorm12() {
  307. return false;
  308. }
  309. // js files (all quiz types)
  310. function v6_expand_JSBrowserCheck() {
  311. return $this->read_template('hp6browsercheck.js_');
  312. }
  313. function v6_expand_JSButtons() {
  314. return $this->read_template('hp6buttons.js_');
  315. }
  316. function v6_expand_JSCard() {
  317. return $this->read_template('hp6card.js_');
  318. }
  319. function v6_expand_JSCheckShortAnswer() {
  320. return $this->read_template('hp6checkshortanswer.js_');
  321. }
  322. function v6_expand_JSHotPotNet() {
  323. return $this->read_template('hp6hotpotnet.js_');
  324. }
  325. function v6_expand_JSShowMessage() {
  326. return $this->read_template('hp6showmessage.js_');
  327. }
  328. function v6_expand_JSUtilities() {
  329. return $this->read_template('hp6utilities.js_');
  330. }
  331. // js files
  332. function v6_expand_JSJCloze6() {
  333. return $this->read_template('jcloze6.js_');
  334. }
  335. function v6_expand_JSJCross6() {
  336. return $this->read_template('jcross6.js_');
  337. }
  338. function v6_expand_JSJMatch6() {
  339. return $this->read_template('jmatch6.js_');
  340. }
  341. function v6_expand_JSJMix6() {
  342. return $this->read_template('jmix6.js_');
  343. }
  344. function v6_expand_JSJQuiz6() {
  345. return $this->read_template('jquiz6.js_');
  346. }
  347. // drag and drop
  348. function v6_expand_JSDJMatch6() {
  349. return $this->read_template('djmatch6.js_');
  350. }
  351. function v6_expand_JSDJMix6() {
  352. return $this->read_template('djmix6.js_');
  353. }
  354. // what are these for?
  355. function v6_expand_JSFJMatch6() {
  356. return $this->read_template('fjmatch6.js_');
  357. }
  358. function v6_expand_JSFJMix6() {
  359. return $this->read_template('fjmix6.js_');
  360. }
  361. // jmatch6.js_
  362. function v6_expand_ShuffleQs() {
  363. return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',shuffle-questions');
  364. }
  365. function v6_expand_QsToShow() {
  366. $i = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',show-limited-questions');
  367. if ($i) {
  368. $i = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',questions-to-show');
  369. }
  370. if (empty($i)) {
  371. $i = 0;
  372. switch ($this->parent->quiztype) {
  373. case 'jmatch':
  374. $values = $this->parent->xml_values('data,matching-exercise,pair');
  375. $i = count($values);
  376. break;
  377. case 'jquiz':
  378. $tags = 'data,questions,question-record';
  379. while (($question="[$i]['#']") && $this->parent->xml_value($tags, $question)) {
  380. $i++;
  381. }
  382. break;
  383. } // end switch
  384. }
  385. return $i;
  386. }
  387. function v6_expand_MatchDivItems() {
  388. $this->set_jmatch_items();
  389. $l_keys = $this->shuffle_jmatch_items($this->l_items);
  390. $r_keys = $this->shuffle_jmatch_items($this->r_items);
  391. $options = '<option value="x">'.$this->parent->xml_value('data,matching-exercise,default-right-item').'</option>';
  392. foreach ($r_keys as $key) {
  393. if (! $this->r_items[$key]['fixed']) {
  394. $options .= '<option value="'.$key.'">'.$this->r_items[$key]['text'].'</option>'."\n";
  395. }
  396. }
  397. $str = '';
  398. foreach ($l_keys as $key) {
  399. $str .= '<tr><td class="LeftItem">'.$this->l_items[$key]['text'].'</td>';
  400. $str .= '<td class="RightItem">';
  401. if ($this->r_items[$key]['fixed']) {
  402. $str .= $this->r_items[$key]['text'];
  403. } else {
  404. $str .= '<select id="s'.$key.'_'.$key.'">'.$options.'</select>';
  405. }
  406. $str .= '</td><td></td></tr>';
  407. }
  408. return $str;
  409. }
  410. // jmix6.js_
  411. function v6_expand_Punctuation() {
  412. $tags = 'data,jumbled-order-exercise';
  413. $chars = array_merge(
  414. $this->jmix_Punctuation("$tags,main-order,segment"),
  415. $this->jmix_Punctuation("$tags,alternate")
  416. );
  417. $chars = array_unique($chars);
  418. $chars = implode('', $chars);
  419. $chars = $this->js_safe($chars, true);
  420. return $chars;
  421. }
  422. function jmix_Punctuation($tags) {
  423. $chars = array();
  424. // all punctutation except '&#;' (because they are used in html entities)
  425. $ENTITIES = $this->jmix_encode_punctuation('!"$%'."'".'()*+,-./:<=>?@[\]^_`{|}~');
  426. $pattern = "/&#x([0-9A-F]+);/i";
  427. $i = 0;
  428. // get next segment (or alternate answer)
  429. while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
  430. // convert low-ascii punctuation to entities
  431. $value = strtr($value, $ENTITIES);
  432. // extract all hex HTML entities
  433. if (preg_match_all($pattern, $value, $matches)) {
  434. // loop through hex entities
  435. $m_max = count($matches[0]);
  436. for ($m=0; $m<$m_max; $m++) {
  437. // convert to hex number
  438. eval('$hex=0x'.$matches[1][$m].';');
  439. // is this a punctuation character?
  440. if (
  441. ($hex>=0x0020 && $hex<=0x00BF) || // ascii punctuation
  442. ($hex>=0x2000 && $hex<=0x206F) || // general punctuation
  443. ($hex>=0x3000 && $hex<=0x303F) || // CJK punctuation
  444. ($hex>=0xFE30 && $hex<=0xFE4F) || // CJK compatability
  445. ($hex>=0xFE50 && $hex<=0xFE6F) || // small form variants
  446. ($hex>=0xFF00 && $hex<=0xFF40) || // halfwidth and fullwidth forms (1)
  447. ($hex>=0xFF5B && $hex<=0xFF65) || // halfwidth and fullwidth forms (2)
  448. ($hex>=0xFFE0 && $hex<=0xFFEE) // halfwidth and fullwidth forms (3)
  449. ) {
  450. // add this character
  451. $chars[] = $matches[0][$m];
  452. }
  453. }
  454. }
  455. $i++;
  456. }
  457. return $chars;
  458. }
  459. function v6_expand_OpenPunctuation() {
  460. $tags = 'data,jumbled-order-exercise';
  461. $chars = array_merge(
  462. $this->jmix_OpenPunctuation("$tags,main-order,segment"),
  463. $this->jmix_OpenPunctuation("$tags,alternate")
  464. );
  465. $chars = array_unique($chars);
  466. $chars = implode('', $chars);
  467. $chars = $this->js_safe($chars, true);
  468. return $chars;
  469. }
  470. function jmix_OpenPunctuation($tags) {
  471. $chars = array();
  472. // unicode punctuation designations (pi="initial quote", ps="open")
  473. // http://www.sql-und-xml.de/unicode-database/pi.html
  474. // http://www.sql-und-xml.de/unicode-database/ps.html
  475. $pi = '0022|0027|00AB|2018|201B|201C|201F|2039';
  476. $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';
  477. $pattern = "/(&#x($pi|$ps);)/i";
  478. $ENTITIES = $this->jmix_encode_punctuation('"'."'".'(<[{');
  479. $i = 0;
  480. while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
  481. $value = strtr($value, $ENTITIES);
  482. if (preg_match_all($pattern, $value, $matches)) {
  483. $chars = array_merge($chars, $matches[0]);
  484. }
  485. $i++;
  486. }
  487. return $chars;
  488. }
  489. function jmix_encode_punctuation($str) {
  490. $ENTITIES = array();
  491. $i_max = strlen($str);
  492. for ($i=0; $i<$i_max; $i++) {
  493. $ENTITIES[$str{$i}] = '&#x'.sprintf('%04X', ord($str{$i})).';';
  494. }
  495. return $ENTITIES;
  496. }
  497. function v6_expand_ExerciseTitle() {
  498. return $this->parent->xml_value('data,title');
  499. }
  500. // Jmix specials
  501. function v6_expand_SegmentArray() {
  502. $segments = array();
  503. $values = array();
  504. $VALUES = array();
  505. // XML tags to the start of a segment
  506. $tags = 'data,jumbled-order-exercise,main-order,segment';
  507. $i = 0;
  508. while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
  509. $VALUE = strtoupper($value);
  510. $key = array_search($VALUE, $VALUES);
  511. if (is_numeric($key)) {
  512. $segments[] = $key;
  513. } else {
  514. $segments[] = $i;
  515. $values[$i] = $value;
  516. $VALUES[$i] = $VALUE;
  517. }
  518. $i++;
  519. }
  520. $this->seed_random_number_generator();
  521. $keys = array_keys($segments);
  522. shuffle($keys);
  523. $str = '';
  524. for($i=0; $i<count($keys); $i++) {
  525. $key = $segments[$keys[$i]];
  526. $str .= "Segments[$i] = new Array();\n";
  527. $str .= "Segments[$i][0] = '".$this->js_safe($values[$key], true)."';\n";
  528. $str .= "Segments[$i][1] = ".($key+1).";\n";
  529. $str .= "Segments[$i][2] = 0;\n";
  530. }
  531. return $str;
  532. }
  533. function v6_expand_AnswerArray() {
  534. $segments = array();
  535. $values = array();
  536. $VALUES = array();
  537. $escapedvalues = array();
  538. // XML tags to the start of a segment
  539. $tags = 'data,jumbled-order-exercise,main-order,segment';
  540. $i = 0;
  541. while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
  542. $VALUE = strtoupper($value);
  543. $key = array_search($VALUE, $VALUES);
  544. if (is_numeric($key)) {
  545. $segments[] = $key+1;
  546. } else {
  547. $segments[] = $i+1;
  548. $values[$i] = $value;
  549. $VALUES[$i] = $VALUE;
  550. $escapedvalues[] = preg_quote($value, '/');
  551. }
  552. $i++;
  553. }
  554. // start the answers array
  555. $a = 0;
  556. $str = 'Answers['.($a++).'] = new Array('.implode(',', $segments).");\n";
  557. // pattern to match the next part of an alternate answer
  558. $pattern = '/^('.implode('|', $escapedvalues).')\\s*/i';
  559. // XML tags to the start of an alternate answer
  560. $tags = 'data,jumbled-order-exercise,alternate';
  561. $i = 0;
  562. while ($value = $this->parent->xml_value($tags, "[$i]['#']")) {
  563. $segments = array();
  564. while (strlen($value) && preg_match($pattern, $value, $matches)) {
  565. $key = array_search($matches[1], $values);
  566. if (is_numeric($key)) {
  567. $segments[] = $key+1;
  568. $value = substr($value, strlen($matches[0]));
  569. } else {
  570. // invalid alternate sequence
  571. $segments = array();
  572. break;
  573. }
  574. }
  575. if (count($segments)) {
  576. $str .= 'Answers['.($a++).'] = new Array('.implode(',', $segments).");\n";
  577. }
  578. $i++;
  579. }
  580. return $str;
  581. }
  582. // ===============================================================
  583. // JMix (jmix6.js_)
  584. function v6_expand_RemainingWords() {
  585. return $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',remaining-words');
  586. }
  587. function v6_expand_TimesUp() {
  588. return $this->js_safe($this->parent->xml_value('hotpot-config-file,global,times-up'));
  589. }
  590. // nav bar
  591. function v6_expand_NavBar($navbarid='') {
  592. $this->navbarid = $navbarid;
  593. $tag = 'navbar';
  594. $this->read_template('hp6navbar.ht_', $tag);
  595. unset($this->navbarid);
  596. return $this->$tag;
  597. }
  598. function v6_expand_TopNavBar() {
  599. return $this->v6_expand_NavBar('TopNavBar');
  600. }
  601. function v6_expand_BottomNavBar() {
  602. return $this->v6_expand_NavBar('BottomNavBar');
  603. }
  604. // hp6navbar.ht_
  605. function v6_expand_NavBarID() {
  606. // $this->navbarid is set in "$this->v6_expand_NavBar"
  607. return empty($this->navbarid) ? '' : $this->navbarid;
  608. }
  609. function v6_expand_ContentsURL() {
  610. $url = $this->parent->xml_value('hotpot-config-file,global,contents-url');
  611. if ($url) {
  612. $url = hotpot_convert_navbutton_url($this->parent->get_baseurl(), $this->parent->reference, $url, $this->parent->course);
  613. }
  614. return $url;
  615. }
  616. function v6_expand_NextExURL() {
  617. $url = $this->parent->xml_value('hotpot-config-file,'.$this->parent->quiztype.',next-ex-url');
  618. if ($url) {
  619. $url = hotpot_convert_navbutton_url($this->parent->get_baseurl(), $this->parent->reference, $url, $this->parent->course);
  620. }
  621. return $url;
  622. }
  623. // conditional blocks
  624. function v6_expand_ShowAnswer() {
  625. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-show-answer');
  626. }
  627. function v6_expand_Slide() {
  628. return true; // whats's this (JMatch drag and drop)
  629. }
  630. // specials (JMatch)
  631. function v6_expand_FixedArray() {
  632. $this->set_jmatch_items();
  633. $str = '';
  634. foreach ($this->l_items as $i=>$item) {
  635. for ($ii=0; $ii<$i; $ii++) {
  636. if ($this->r_items[$ii]['text']==$this->r_items[$i]['text']) {
  637. break;
  638. }
  639. }
  640. $str .= "F[$i] = new Array();\n";
  641. $str .= "F[$i][0] = '".$this->js_safe($item['text'], true)."';\n";
  642. $str .= "F[$i][1] = ".($ii+1).";\n";
  643. }
  644. return $str;
  645. }
  646. function v6_expand_DragArray() {
  647. $this->set_jmatch_items();
  648. $str = '';
  649. foreach ($this->r_items as $i=>$item) {
  650. for ($ii=0; $ii<$i; $ii++) {
  651. if ($this->r_items[$ii]['text']==$this->r_items[$i]['text']) {
  652. break;
  653. }
  654. }
  655. $str .= "D[$i] = new Array();\n";
  656. $str .= "D[$i][0] = '".$this->js_safe($item['text'], true)."';\n";
  657. $str .= "D[$i][1] = ".($ii+1).";\n";
  658. $str .= "D[$i][2] = ".$item['fixed'].";\n";
  659. }
  660. return $str;
  661. }
  662. function set_jmatch_items() {
  663. if (count($this->l_items)) {
  664. return;
  665. }
  666. $tags = 'data,matching-exercise,pair';
  667. $i = 0;
  668. while (($item = "[$i]['#']") && $this->parent->xml_value($tags, $item)) {
  669. $leftitem = $item."['left-item'][0]['#']";
  670. $lefttext = $this->parent->xml_value($tags, $leftitem."['text'][0]['#']");
  671. $rightitem = $item."['right-item'][0]['#']";
  672. $righttext = $this->parent->xml_value($tags, $rightitem."['text'][0]['#']");
  673. if (strlen($righttext)) {
  674. $addright = true;
  675. } else {
  676. $addright = false;
  677. }
  678. if (strlen($lefttext)) {
  679. $this->l_items[] = array(
  680. 'text' => $lefttext,
  681. 'fixed' => $this->int_value($tags, $leftitem."['fixed'][0]['#']")
  682. );
  683. $addright = true; // force right item to be added
  684. }
  685. if ($addright) {
  686. $this->r_items[] = array(
  687. 'text' => $righttext,
  688. 'fixed' => $this->int_value($tags, $rightitem."['fixed'][0]['#']")
  689. );
  690. }
  691. $i++;
  692. }
  693. }
  694. function shuffle_jmatch_items(&$items) {
  695. // get moveable items
  696. $moveable_keys = array();
  697. for($i=0; $i<count($items); $i++) {
  698. if(empty($items[$i]['fixed'])) {
  699. $moveable_keys[] = $i;
  700. }
  701. }
  702. // shuffle moveable items
  703. $this->seed_random_number_generator();
  704. shuffle($moveable_keys);
  705. $keys = array();
  706. for($i=0, $ii=0; $i<count($items); $i++) {
  707. if(empty($items[$i]['fixed'])) {
  708. // moveable items are inserted in a shuffled order
  709. $keys[] = $moveable_keys[$ii++];
  710. } else {
  711. // fixed items stay where they are
  712. $keys[] = $i;
  713. }
  714. }
  715. return $keys;
  716. }
  717. function seed_random_number_generator() {
  718. static $seeded_RNG = FALSE;
  719. if (!$seeded_RNG) {
  720. srand((double) microtime() * 1000000);
  721. $seeded_RNG = TRUE;
  722. }
  723. }
  724. // specials (JMix)
  725. // specials (JCloze)
  726. function v6_expand_ItemArray() {
  727. $q = 0;
  728. $str = '';
  729. switch ($this->parent->quiztype) {
  730. case 'jcloze':
  731. $tags = 'data,gap-fill,question-record';
  732. while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
  733. $a = 0;
  734. $aa = 0;
  735. while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
  736. $text = $this->js_value($tags, $answer."['text'][0]['#']", true);
  737. if (strlen($text)) {
  738. if ($aa==0) { // first time only
  739. $str .= "\n";
  740. $str .= "I[$q] = new Array();\n";
  741. $str .= "I[$q][1] = new Array();\n";
  742. }
  743. $str .= "I[$q][1][$aa] = new Array();\n";
  744. $str .= "I[$q][1][$aa][0] = '$text';\n";
  745. $aa++;
  746. }
  747. $a++;
  748. }
  749. // add clue, if any answers were found
  750. if ($aa) {
  751. $clue = $this->js_value($tags, $question."['clue'][0]['#']", true);
  752. $str .= "I[$q][2] = '$clue';\n";
  753. }
  754. $q++;
  755. }
  756. break;
  757. case 'jquiz':
  758. $str .= "I=new Array();\n";
  759. $tags = 'data,questions,question-record';
  760. while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
  761. $question_type = $this->int_value($tags, $question."['question-type'][0]['#']");
  762. $weighting = $this->int_value($tags, $question."['weighting'][0]['#']");
  763. $clue = $this->js_value($tags, $question."['clue'][0]['#']", true);
  764. $answers = $question."['answers'][0]['#']";
  765. $a = 0;
  766. $aa = 0;
  767. while (($answer = $answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
  768. $text = $this->js_value($tags, $answer."['text'][0]['#']", true);
  769. $feedback = $this->js_value($tags, $answer."['feedback'][0]['#']", true);
  770. $correct = $this->int_value($tags, $answer."['correct'][0]['#']");
  771. $percent = $this->int_value($tags, $answer."['percent-correct'][0]['#']");
  772. $include = $this->int_value($tags, $answer."['include-in-mc-options'][0]['#']");
  773. if (strlen($text)) {
  774. if ($aa==0) { // first time only
  775. $str .= "\n";
  776. $str .= "I[$q] = new Array();\n";
  777. $str .= "I[$q][0] = $weighting;\n";
  778. $str .= "I[$q][1] = '$clue';\n";
  779. $str .= "I[$q][2] = '".($question_type-1)."';\n";
  780. $str .= "I[$q][3] = new Array();\n";
  781. }
  782. $str .= "I[$q][3][$aa] = new Array('$text','$feedback',$correct,$percent,$include);\n";
  783. $aa++;
  784. }
  785. $a++;
  786. }
  787. $q++;
  788. }
  789. break;
  790. }
  791. return $str;
  792. }
  793. function v6_expand_ClozeBody() {
  794. $str = '';
  795. // get drop down list of words, if required
  796. $dropdownlist = '';
  797. if ($this->v6_use_DropDownList()) {
  798. $this->v6_set_WordList();
  799. foreach ($this->wordlist as $word) {
  800. $dropdownlist .= '<option value="'.$word.'">'.$word.'</option>';
  801. }
  802. }
  803. // cache clues flag and caption
  804. $includeclues = $this->v6_expand_Clues();
  805. $cluecaption = $this->v6_expand_ClueCaption();
  806. // detect if cloze starts with gap
  807. $strpos = strpos($this->parent->source, '<gap-fill><question-record>');
  808. if (is_numeric($strpos)) {
  809. $startwithgap = true;
  810. } else {
  811. $startwithgap = false;
  812. }
  813. // initialize loop values
  814. $q = 0;
  815. $tags = 'data,gap-fill';
  816. $question_record = "$tags,question-record";
  817. // loop through text and gaps
  818. do {
  819. $text = $this->parent->xml_value($tags, "[0]['#'][$q]");
  820. $gap = '';
  821. if (($question="[$q]['#']") && $this->parent->xml_value($question_record, $question)) {
  822. $gap .= '<span class="GapSpan" id="GapSpan'.$q.'">';
  823. if ($this->v6_use_DropDownList()) {
  824. $gap .= '<select id="Gap'.$q.'"><option value=""></option>'.$dropdownlist.'</select>';
  825. } else {
  826. // minimum gap size
  827. if (! $gapsize = $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',minimum-gap-size')) {
  828. $gapsize = 6;
  829. }
  830. // increase gap size to length of longest answer for this gap
  831. $a = 0;
  832. while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($question_record, $answer)) {
  833. $answertext = $this->parent->xml_value($question_record, $answer."['text'][0]['#']");
  834. $answertext = preg_replace('|&[#a-zA-Z0-9]+;|', 'x', $answertext);
  835. $gapsize = max($gapsize, strlen($answertext));
  836. $a++;
  837. }
  838. $gap .= '<input type="text" id="Gap'.$q.'" onfocus="TrackFocus('.$q.')" onblur="LeaveGap()" class="GapBox" size="'.$gapsize.'"></input>';
  839. }
  840. if ($includeclues) {
  841. $clue = $this->parent->xml_value($question_record, $question."['clue'][0]['#']");
  842. if (strlen($clue)) {
  843. $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>';
  844. }
  845. }
  846. $gap .= '</span>';
  847. }
  848. if ($startwithgap) {
  849. $str .= "$gap$text";
  850. } else {
  851. $str .= "$text$gap";
  852. }
  853. $q++;
  854. } while (strlen($text) || strlen($gap));
  855. return $str;
  856. }
  857. // JCloze quiztype
  858. function v6_expand_WordList() {
  859. $str = '';
  860. if ($this->v6_include_WordList()) {
  861. $this->v6_set_WordList();
  862. $str = implode(' &#160;&#160; ', $this->wordlist);
  863. }
  864. return $str;
  865. }
  866. function v6_include_WordList() {
  867. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-word-list');
  868. }
  869. function v6_use_DropDownList() {
  870. return $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',use-drop-down-list');
  871. }
  872. function v6_set_WordList() {
  873. if (isset($this->wordlist)) {
  874. // do nothing
  875. } else {
  876. $this->wordlist = array();
  877. // is the wordlist required
  878. if ($this->v6_include_WordList() || $this->v6_use_DropDownList()) {
  879. $q = 0;
  880. $tags = 'data,gap-fill,question-record';
  881. while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
  882. $a = 0;
  883. $aa = 0;
  884. while (($answer=$question."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
  885. $text = $this->parent->xml_value($tags, $answer."['text'][0]['#']");
  886. $correct = $this->int_value($tags, $answer."['correct'][0]['#']");
  887. if ($text && $correct) { // $correct is always true
  888. $this->wordlist[] = $text;
  889. $aa++;
  890. }
  891. $a++;
  892. }
  893. $q++;
  894. }
  895. $this->wordlist = array_unique($this->wordlist);
  896. sort($this->wordlist);
  897. }
  898. }
  899. }
  900. function v6_expand_Keypad() {
  901. $str = '';
  902. if ($this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-keypad')) {
  903. // these characters must always be in the keypad
  904. $chars = array();
  905. $this->add_keypad_chars($chars, $this->parent->xml_value('hotpot-config-file,global,keypad-characters'));
  906. // append other characters used in the answers
  907. $tags = '';
  908. switch ($this->parent->quiztype) {
  909. case 'jcloze':
  910. $tags = 'data,gap-fill,question-record';
  911. break;
  912. case 'jquiz':
  913. $tags = 'data,questions,question-record';
  914. break;
  915. }
  916. if ($tags) {
  917. $q = 0;
  918. while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
  919. if ($this->parent->quiztype=='jquiz') {
  920. $answers = $question."['answers'][0]['#']";
  921. } else {
  922. $answers = $question;
  923. }
  924. $a = 0;
  925. while (($answer=$answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
  926. $this->add_keypad_chars($chars, $this->parent->xml_value($tags, $answer."['text'][0]['#']"));
  927. $a++;
  928. }
  929. $q++;
  930. }
  931. }
  932. // remove duplicate characters and sort
  933. $chars = array_unique($chars);
  934. usort($chars, "hotpot_sort_keypad_chars");
  935. // create keypad buttons for each character
  936. foreach ($chars as $char) {
  937. $str .= "<button onclick=\"TypeChars('".$this->js_safe($char, true)."'); return false;\">$char</button>";
  938. }
  939. }
  940. return $str;
  941. }
  942. function add_keypad_chars(&$chars, $text) {
  943. if (preg_match_all('|&[^;]+;|i', $text, $more_chars)) {
  944. $chars = array_merge($chars, $more_chars[0]);
  945. }
  946. }
  947. function v6_expand_Correct() {
  948. if ($this->parent->quiztype=='jcloze') {
  949. $tag = 'guesses-correct';
  950. } else {
  951. $tag = 'guess-correct';
  952. }
  953. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.','.$tag);
  954. }
  955. function v6_expand_Incorrect() {
  956. if ($this->parent->quiztype=='jcloze') {
  957. $tag = 'guesses-incorrect';
  958. } else {
  959. $tag = 'guess-incorrect';
  960. }
  961. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.','.$tag);
  962. }
  963. function v6_expand_GiveHint() {
  964. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',next-correct-letter');
  965. }
  966. function v6_expand_CaseSensitive() {
  967. return $this->bool_value('hotpot-config-file,'.$this->parent->quiztype.',case-sensitive');
  968. }
  969. // JCross quiztype
  970. function v6_expand_CluesAcrossLabel() {
  971. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',clues-across');
  972. }
  973. function v6_expand_CluesDownLabel() {
  974. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',clues-down');
  975. }
  976. function v6_expand_EnterCaption() {
  977. return $this->js_value('hotpot-config-file,'.$this->parent->quiztype.',enter-caption');
  978. }
  979. function v6_expand_ShowHideClueList() {
  980. $value = $this->int_value('hotpot-config-file,'.$this->parent->quiztype.',include-clue-list');
  981. return empty($value) ? ' style="display: none;"' : '';
  982. }
  983. // JCross specials
  984. function v6_expand_CluesDown() {
  985. return $this->v6_expand_jcross_clues('D');
  986. }
  987. function v6_expand_CluesAcross() {
  988. return $this->v6_expand_jcross_clues('A');
  989. }
  990. function v6_expand_jcross_clues($direction) {
  991. // $direction: A(cross) or D(own)
  992. $row = NULL;
  993. $r_max = 0;
  994. $c_max = 0;
  995. $this->v6_get_jcross_grid($row, $r_max, $c_max);
  996. $i = 0; // clue index;
  997. $str = '';
  998. for($r=0; $r<=$r_max; $r++) {
  999. for($c=0; $c<=$c_max; $c++) {
  1000. $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
  1001. $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
  1002. if ($aword || $dword) {
  1003. $i++; // increment clue index
  1004. // get the definition for this word
  1005. $def = '';
  1006. $word = ($direction=='A') ? $aword : $dword;
  1007. $clues = $this->parent->xml_values('data,crossword,clues,item');
  1008. foreach ($clues as $clue) {
  1009. if ($clue['word'][0]['#']==$word) {
  1010. $def = $clue['def'][0]['#'];
  1011. $def = strtr($def, array('&#x003C;'=>'<', '&#x003E;'=>'>', "\n"=>'<br />'));
  1012. break;
  1013. }
  1014. }
  1015. if (!empty($def)) {
  1016. $str .= '<tr><td class="ClueNum">'.$i.'. </td><td id="Clue_'.$direction.'_'.$i.'" class="Clue">'.$def.'</td></tr>';
  1017. }
  1018. }
  1019. }
  1020. }
  1021. return $str;
  1022. }
  1023. // jcross6.js_
  1024. function v6_expand_LetterArray() {
  1025. $row = NULL;
  1026. $r_max = 0;
  1027. $c_max = 0;
  1028. $this->v6_get_jcross_grid($row, $r_max, $c_max);
  1029. $str = '';
  1030. for($r=0; $r<=$r_max; $r++) {
  1031. $str .= "L[$r] = new Array(";
  1032. for($c=0; $c<=$c_max; $c++) {
  1033. $str .= ($c>0 ? ',' : '')."'".$this->js_safe($row[$r]['cell'][$c]['#'], true)."'";
  1034. }
  1035. $str .= ");\n";
  1036. }
  1037. return $str;
  1038. }
  1039. function v6_expand_GuessArray() {
  1040. $row = NULL;
  1041. $r_max = 0;
  1042. $c_max = 0;
  1043. $this->v6_get_jcross_grid($row, $r_max, $c_max);
  1044. $str = '';
  1045. for($r=0; $r<=$r_max; $r++) {
  1046. $str .= "G[$r] = new Array('".str_repeat("','", $c_max)."');\n";
  1047. }
  1048. return $str;
  1049. }
  1050. function v6_expand_ClueNumArray() {
  1051. $row = NULL;
  1052. $r_max = 0;
  1053. $c_max = 0;
  1054. $this->v6_get_jcross_grid($row, $r_max, $c_max);
  1055. $i = 0; // clue index
  1056. $str = '';
  1057. for($r=0; $r<=$r_max; $r++) {
  1058. $str .= "CL[$r] = new Array(";
  1059. for($c=0; $c<=$c_max; $c++) {
  1060. if ($c>0) {
  1061. $str .= ',';
  1062. }
  1063. $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
  1064. $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
  1065. if (empty($aword) && empty($dword)) {
  1066. $str .= 0;
  1067. } else {
  1068. $i++; // increment the clue index
  1069. $str .= $i;
  1070. }
  1071. }
  1072. $str .= ");\n";
  1073. }
  1074. return $str;
  1075. }
  1076. function v6_expand_GridBody() {
  1077. $row = NULL;
  1078. $r_max = 0;
  1079. $c_max = 0;
  1080. $this->v6_get_jcross_grid($row, $r_max, $c_max);
  1081. $i = 0; // clue index;
  1082. $str = '';
  1083. for($r=0; $r<=$r_max; $r++) {
  1084. $str .= '<tr id="Row_'.$r.'">';
  1085. for($c=0; $c<=$c_max; $c++) {
  1086. if (empty($row[$r]['cell'][$c]['#'])) {
  1087. $str .= '<td class="BlankCell">&nbsp;</td>';
  1088. } else {
  1089. $aword = $this->get_jcross_aword($row, $r, $r_max, $c, $c_max);
  1090. $dword = $this->get_jcross_dword($row, $r, $r_max, $c, $c_max);
  1091. if (empty($aword) && empty($dword)) {
  1092. $str .= '<td class="LetterOnlyCell"><span id="L_'.$r.'_'.$c.'">&nbsp;</span></td>';
  1093. } else {
  1094. $i++; // increment clue index
  1095. $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>';
  1096. }
  1097. }
  1098. }
  1099. $str .= '</tr>';
  1100. }
  1101. return $str;
  1102. }
  1103. function v6_get_jcross_grid(&$row, &$r_max, &$c_max) {
  1104. $row = $this->parent->xml_values('data,crossword,grid,row');
  1105. $r_max = 0;
  1106. $c_max = 0;
  1107. if (isset($row) && is_array($row)) {
  1108. for($r=0; $r<count($row); $r++) {
  1109. if (isset($row[$r]['cell']) && is_array($row[$r]['cell'])) {
  1110. for($c=0; $c<count($row[$r]['cell']); $c++) {
  1111. if (!empty($row[$r]['cell'][$c]['#'])) {
  1112. $r_max = max($r, $r_max);
  1113. $c_max = max($c, $c_max);
  1114. }
  1115. } // end for $c
  1116. }
  1117. } // end for $r
  1118. }
  1119. }
  1120. function get_jcross_dword(&$row, $r, $r_max, $c, $c_max) {
  1121. $str = '';
  1122. if (($r==0 || empty($row[$r-1]['cell'][$c]['#'])) && $r<$r_max && !empty($row[$r+1]['cell'][$c]['#'])) {
  1123. $str = $this->get_jcross_word($row, $r, $r_max, $c, $c_max, true);
  1124. }
  1125. return $str;
  1126. }
  1127. function get_jcross_aword(&$row, $r, $r_max, $c, $c_max) {
  1128. $str = '';
  1129. if (($c==0 || empty($row[$r]['cell'][$c-1]['#'])) && $c<$c_max && !empty($row[$r]['cell'][$c+1]['#'])) {
  1130. $str = $this->get_jcross_word($row, $r, $r_max, $c, $c_max, false);
  1131. }
  1132. return $str;
  1133. }
  1134. function get_jcross_word(&$row, $r, $r_max, $c, $c_max, $go_down=false) {
  1135. $str = '';
  1136. while ($r<=$r_max && $c<=$c_max && !empty($row[$r]['cell'][$c]['#'])) {
  1137. $str .= $row[$r]['cell'][$c]['#'];
  1138. if ($go_down) {
  1139. $r++;
  1140. } else {
  1141. $c++;
  1142. }
  1143. }
  1144. return $str;
  1145. }
  1146. // specials (JQuiz)
  1147. function v6_expand_QuestionOutput() {
  1148. $str = '';
  1149. $str .= '<ol class="QuizQuestions" id="Questions">'."\n";
  1150. $q = 0;
  1151. $tags = 'data,questions,question-record';
  1152. while (($question="[$q]['#']") && $this->parent->xml_value($tags, $question)) {
  1153. // get question
  1154. $question_text = $this->parent->xml_value($tags, $question."['question'][0]['#']");
  1155. $question_type = $this->parent->xml_value($tags, $question."['question-type'][0]['#']");
  1156. $first_answer_text = $this->parent->xml_value($tags, $question."['answers'][0]['#']['answer'][0]['#']['text'][0]['#']");
  1157. // check we have a question (or at least one answer)
  1158. if (($question_text || $first_answer_text) && $question_type) {
  1159. $str .= '<li class="QuizQuestion" id="Q_'.$q.'" style="display: none;">';
  1160. $str .= '<p class="QuestionText">'.$question_text.'</p>';
  1161. if (
  1162. $question_type==HOTPOT_JQUIZ_SHORTANSWER ||
  1163. $question_type==HOTPOT_JQUIZ_HYBRID
  1164. ) {
  1165. $size = 9; // default size
  1166. $a = 0;
  1167. $answers = $question."['answers'][0]['#']";
  1168. while (($answer = $answers."['answer'][$a]['#']") && $this->parent->xml_value($tags, $answer)) {
  1169. $text = $this->parent->xml_value($tags, $answer."['text'][0]['#']");
  1170. $text = preg_replace('/&[#a-zA-Z0-9]+;/', 'x', $text);
  1171. $size = max($size, strlen($text));
  1172. $a++;
  1173. }
  1174. $str .= '<div cl…

Large files files are truncated, but you can click here to view the full file