PageRenderTime 25ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/hotpot/attempt/hp/6/jmix/renderer.php

https://github.com/cgtaylor/moodle
PHP | 298 lines | 227 code | 17 blank | 54 comment | 14 complexity | ed0f37e00eeefb4cbe4b4d5f4b032d5f MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Render an attempt at a HotPot quiz
  18. * Output format: hp_6_jmix
  19. *
  20. * @package mod-hotpot
  21. * @copyright 2010 Gordon Bateson <gordon.bateson@gmail.com>
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. // get parent class
  26. require_once($CFG->dirroot.'/mod/hotpot/attempt/hp/6/renderer.php');
  27. class mod_hotpot_attempt_hp_6_jmix_renderer extends mod_hotpot_attempt_hp_6_renderer {
  28. public $icon = 'pix/f/jmx.gif';
  29. public $js_object_type = 'JMix';
  30. public $templatefile = 'jmix6.ht_';
  31. public $templatestrings = 'PreloadImageList|SegmentArray|AnswerArray';
  32. // Glossary autolinking settings
  33. public $headcontent_strings = 'CorrectResponse|IncorrectResponse|ThisMuchCorrect|TheseAnswersToo|YourScoreIs|NextCorrect|Segments';
  34. public $headcontent_arrays = '';
  35. public $response_num_fields = array(
  36. 'score', 'hints', 'checks' // remove: weighting, clues
  37. );
  38. /**
  39. * init
  40. *
  41. * @param xxx $quiz (passed by reference)
  42. */
  43. function init(&$quiz) {
  44. parent::init($quiz);
  45. array_push($this->javascripts, 'mod/hotpot/attempt/hp/6/jmix/jmix.js');
  46. }
  47. /**
  48. * fix_headcontent
  49. *
  50. * @return xxx
  51. */
  52. function fix_headcontent() {
  53. $this->fix_headcontent_DragAndDrop();
  54. // change number of drop lines if required (there is now setting for
  55. // this in the JMix application but it can be set in a cfg file)
  56. if ($drop_total = $this->expand_DropTotal()) {
  57. $this->headcontent = preg_replace('/(?<=var DropTotal = )\d+(?=;)/', $drop_total, $this->headcontent, 1);
  58. }
  59. // we must add a false return value to segment links in order not to trigger the onbeforeunload event handler
  60. // (this is only really required on v6 output format)
  61. $search = '/(?<='.'onclick="'.')'.'AddSegment\(\[SegmentNumber\]\)'.'(?='.'"'.')/';
  62. $replace = '\\0'.'; return false;';
  63. $this->headcontent = preg_replace($search, $replace, $this->headcontent);
  64. }
  65. /**
  66. * fix_bodycontent_DragAndDrop
  67. */
  68. function fix_bodycontent_DragAndDrop() {
  69. $search = '/for \(var i=0; i<DropTotal; i\+\+\)\{.*?\}/s';
  70. $replace = ''
  71. ."var myParentNode = null;\n"
  72. ."if (navigator.appName=='Microsoft Internet Explorer' && (document.documentMode==null || document.documentMode<8)) {\n"
  73. ." // IE8+ (compatible mode) IE7, IE6, IE5 ...\n"
  74. ."} else {\n"
  75. ." // Firefox, Safari, Opera, IE8+\n"
  76. ." var obj = document.getElementsByTagName('div');\n"
  77. ." if (obj && obj.length) {\n"
  78. ." myParentNode = obj[obj.length - 1].parentNode;\n"
  79. ." }\n"
  80. ."}\n"
  81. ."for (var i=0; i<DropTotal; i++){\n"
  82. ." if (myParentNode){\n"
  83. ." var div = document.createElement('div');\n"
  84. ." div.setAttribute('id', 'Drop' + i);\n"
  85. ." div.setAttribute('class', 'DropLine');\n"
  86. ." div.setAttribute('align', 'center');\n"
  87. ." div.innerHTML = '&nbsp;<br />&nbsp;';\n"
  88. ." myParentNode.appendChild(div);\n"
  89. ." } else {\n"
  90. ." document.write('".'<div id="'."Drop' + i + '".'" class="DropLine" align="center"'.">&nbsp;<br />&nbsp;</div>');\n"
  91. ." }\n"
  92. ."}"
  93. ;
  94. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent, 1);
  95. $search = '/for \(var i=0; i<Segments\.length; i\+\+\)\{.*?\}/s';
  96. $replace = ''
  97. ."for (var i=0; i<Segments.length; i++){\n"
  98. ." if (myParentNode){\n"
  99. ." var div = document.createElement('div');\n"
  100. ." div.setAttribute('id', 'D' + i);\n"
  101. ." div.setAttribute('class', 'CardStyle');\n"
  102. ." div.setAttribute('onmousedown', 'beginDrag(event, ' + i + ')');\n"
  103. ." myParentNode.appendChild(div);\n"
  104. ." } else {\n"
  105. ." document.write('".'<div id="'."D' + i + '".'" class="CardStyle" onmousedown="'."beginDrag(event, ' + i + ')".'"'."></div>');\n"
  106. ." }\n"
  107. ."}\n"
  108. ."m = div = myParentNode = null;"
  109. ;
  110. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent, 1);
  111. }
  112. /**
  113. * get_js_functionnames
  114. *
  115. * @return xxx
  116. */
  117. function get_js_functionnames() {
  118. // start list of function names
  119. $names = parent::get_js_functionnames();
  120. $names .= ($names ? ',' : '').'CheckAnswer,TimesUp';
  121. return $names;
  122. }
  123. /**
  124. * fix_js_TimesUp
  125. *
  126. * @param xxx $str (passed by reference)
  127. * @param xxx $start
  128. * @param xxx $length
  129. */
  130. function fix_js_TimesUp(&$str, $start, $length) {
  131. $substr = substr($str, $start, $length);
  132. $search = "/\s*document\.getElementById\('Timer'\)\.innerHTML = '([^']*)';/s";
  133. if (preg_match($search, $substr, $matches, PREG_OFFSET_CAPTURE)) {
  134. $msg = $matches[1][0];
  135. $substr = substr_replace($substr, '', $matches[0][1], strlen($matches[0][0]));
  136. } else {
  137. $msg = 'Your time is over!';
  138. }
  139. if ($pos = strrpos($substr, '}')) {
  140. if ($this->hotpot->delay3==hotpot::TIME_AFTEROK) {
  141. $flag = 1; // set form values only
  142. } else {
  143. $flag = 0; // set form values and send form
  144. }
  145. $insert = ''
  146. ." Finished = true;\n"
  147. ." HP.onunload(".hotpot::STATUS_TIMEDOUT.",$flag);\n"
  148. ." ShowMessage('$msg');\n"
  149. ;
  150. $substr = substr_replace($substr, $insert, $pos, 0);
  151. }
  152. $str = substr_replace($str, $substr, $start, $length);
  153. }
  154. /**
  155. * fix_js_CheckAnswer
  156. *
  157. * @param xxx $str (passed by reference)
  158. * @param xxx $start
  159. * @param xxx $length
  160. * @return xxx
  161. */
  162. function fix_js_CheckAnswer(&$str, $start, $length) {
  163. $substr = substr($str, $start, $length);
  164. // remove the premature "return" if there is no guesses, because this function
  165. // is also called for the "Give Up" button and the onbeforeunload() event handler
  166. // and in those cases we need to continue and return the results to Moodle
  167. $search = ''
  168. .'/(if \(GuessSequence\.length < 1\)\{)' // $1
  169. .'(.*?)' // $2
  170. .'\s*return;'
  171. .'(\s*\})' // $3
  172. .'/s'
  173. ;
  174. $substr = preg_replace($search, '\\1\\2\\3', $substr, 1);
  175. // encapsulate the main body of the function code in an "if" block
  176. $search = ''
  177. .'/(?<=var AllDone = false;)' // look behind
  178. .'(.*?)'
  179. .'(\s*)'
  180. .'(?=if \(\(AllDone == true\)\|\|\(TimeOver == true\)\)\{)' // look ahead
  181. .'/s'
  182. ;
  183. if (preg_match($search, $substr, $matches, PREG_OFFSET_CAPTURE)) {
  184. $replace = "\n"
  185. .' if (GuessSequence.length){'
  186. .preg_replace('/[\\n\\r]+/', '\\0 ', $matches[1][0])."\n"
  187. ." }\n"
  188. ;
  189. $substr = substr_replace($substr, $replace, $matches[1][1], strlen($matches[1][0]));
  190. }
  191. // add other changes as per CheckAnswers in other type of HP quiz
  192. $this->fix_js_CheckAnswers($substr, 0, strlen($substr));
  193. $str = substr_replace($str, $substr, $start, $length);
  194. }
  195. /**
  196. * fix_js_StartUp_DragAndDrop
  197. *
  198. * @param xxx $substr (passed by reference)
  199. */
  200. function fix_js_StartUp_DragAndDrop(&$substr) {
  201. // fix top and left of drag area
  202. $this->fix_js_StartUp_DragAndDrop_DragArea($substr);
  203. // restrict width of drop lines
  204. $search = "L[i] = new Card('Drop' + i, 0);";
  205. if ($pos = strpos($substr, $search)) {
  206. $insert = "\n\t\t"."L[i].SetW(pg.W * 0.8);";
  207. $substr = substr_replace($substr, $insert, $pos + strlen($search), 0);
  208. }
  209. // stretch the canvas vertically down
  210. if ($pos = strrpos($substr, '}')) {
  211. $insert = ''
  212. ." var b = 0;\n"
  213. ." var objParentNode = null;\n"
  214. ." if (window.Segments) {\n"
  215. ." var obj = document.getElementById('D'+(Segments.length-1));\n"
  216. ." if (obj) {\n"
  217. ." b = Math.max(b, getOffset(obj,'Bottom'));\n"
  218. ." objParentNode = objParentNode || obj.parentNode;\n"
  219. ." }\n"
  220. ." }\n"
  221. ." if (b) {\n"
  222. ." // stretch parentNodes down vertically, if necessary\n"
  223. ." var canvas = document.getElementById('$this->themecontainer');\n"
  224. ." while (objParentNode) {\n"
  225. ." var more_height = Math.max(0, b - getOffset(objParentNode, 'Bottom'));\n"
  226. ." if (more_height) {\n"
  227. ." setOffset(objParentNode, 'Height', getOffset(objParentNode, 'Height') + more_height + 4);\n"
  228. ." }\n"
  229. ." if (canvas && objParentNode==canvas) {\n"
  230. ." objParentNode = null;\n"
  231. ." } else {\n"
  232. ." objParentNode = objParentNode.parentNode;\n"
  233. ." }\n"
  234. ." }\n"
  235. ." }\n"
  236. ;
  237. $substr = substr_replace($substr, $insert, $pos, 0);
  238. }
  239. }
  240. /**
  241. * get_stop_function_name
  242. *
  243. * @return xxx
  244. */
  245. function get_stop_function_name() {
  246. return 'CheckAnswer';
  247. }
  248. /**
  249. * get_stop_function_args
  250. *
  251. * @return xxx
  252. */
  253. function get_stop_function_args() {
  254. return '0,'.hotpot::STATUS_ABANDONED;
  255. }
  256. /**
  257. * get_stop_function_intercept
  258. *
  259. * @return xxx
  260. */
  261. function get_stop_function_intercept() {
  262. return "\n"
  263. ." if (CheckType==0) HP.onclickCheck(); // intercept Checks\n"
  264. ." if (CheckType==1) HP.onclickHint(0); // intercept Hints\n"
  265. ;
  266. }
  267. }