PageRenderTime 85ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/attempt/hp/6/renderer.php

https://github.com/KieranRBriggs/moodle-mod_hotpot
PHP | 4229 lines | 2453 code | 381 blank | 1395 comment | 398 complexity | 74c22c395ce0ea25cc8f6346482f9e29 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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
  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/renderer.php');
  27. /**
  28. * mod_hotpot_attempt_hp_6_renderer
  29. *
  30. * @copyright 2010 Gordon Bateson
  31. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32. * @since Moodle 2.0
  33. */
  34. class mod_hotpot_attempt_hp_6_renderer extends mod_hotpot_attempt_hp_renderer {
  35. /**
  36. * init
  37. *
  38. * @param xxx $hotpot
  39. */
  40. function init($hotpot) {
  41. parent::init($hotpot);
  42. array_unshift($this->templatesfolders, 'mod/hotpot/attempt/hp/6/templates');
  43. }
  44. /**
  45. * fix_headcontent_DragAndDrop for JMix and JMatch
  46. */
  47. function fix_headcontent_DragAndDrop() {
  48. // replace one line functions that get and set positions of positionable elements
  49. $search = array(
  50. '/(?<=function CardGetL).*/',
  51. '/(?<=function CardGetT).*/',
  52. '/(?<=function CardGetW).*/',
  53. '/(?<=function CardGetH).*/',
  54. '/(?<=function CardGetB).*/',
  55. '/(?<=function CardGetR).*/',
  56. '/(?<=function CardSetL).*/',
  57. '/(?<=function CardSetT).*/',
  58. '/(?<=function CardSetW).*/',
  59. '/(?<=function CardSetH).*/',
  60. );
  61. $replace = array(
  62. "(){return getOffset(this.elm, 'Left')}",
  63. "(){return getOffset(this.elm, 'Top')}",
  64. "(){return getOffset(this.elm, 'Width')}",
  65. "(){return getOffset(this.elm, 'Height')}",
  66. "(){return getOffset(this.elm, 'Bottom')}",
  67. "(){return getOffset(this.elm, 'Right')}",
  68. "(NewL){setOffset(this.elm, 'Left', NewL)}",
  69. "(NewT){setOffset(this.elm, 'Top', NewT)}",
  70. "(NewW){setOffset(this.elm, 'Width', NewW)}",
  71. "(NewH){setOffset(this.elm, 'Height', NewH)}"
  72. );
  73. $this->headcontent = preg_replace($search, $replace, $this->headcontent, 1);
  74. }
  75. /**
  76. * fix_headcontent_rottmeier
  77. *
  78. * @param xxx $type (optional, default='')
  79. */
  80. function fix_headcontent_rottmeier($type='') {
  81. switch ($type) {
  82. case 'dropdown':
  83. // adding missing brackets to call to Is_ExerciseFinished() in CheckAnswers()
  84. $search = '/(Finished\s*=\s*Is_ExerciseFinished)(;)/';
  85. $this->headcontent = preg_replace($search, '$1()$2', $this->headcontent);
  86. break;
  87. case 'findit':
  88. // get position of last </style> tag and
  89. // insert CSS to make <b> and <em> tags bold
  90. // even within GapSpans added by javascript
  91. $search = '</style>';
  92. if ($pos = strrpos($this->headcontent, $search)) {
  93. $insert = "\n"
  94. .'<!--[if IE 6]><style type="text/css">'."\n"
  95. .'span.GapSpan{'."\n"
  96. .' font-size:24px;'."\n"
  97. .'}'."\n"
  98. .'</style><![endif]-->'."\n"
  99. .'<style type="text/css">'."\n"
  100. .'b span.GapSpan,'."\n"
  101. .'em span.GapSpan{'."\n"
  102. .' font-weight:inherit;'."\n"
  103. .'}'."\n"
  104. .'</style>'."\n"
  105. ;
  106. $this->headcontent = substr_replace($this->headcontent, $insert, $pos + strlen($search), 0);
  107. }
  108. break;
  109. case 'jintro':
  110. // add TimeOver variable, so we can use standard detection of quiz completion
  111. if ($pos = strpos($this->headcontent, 'var Score = 0;')) {
  112. $insert = "var TimeOver = false;\n";
  113. $this->headcontent = substr_replace($this->headcontent, $insert, $pos, 0);
  114. }
  115. break;
  116. case 'jmemori':
  117. // add TimeOver variable, so we can use standard detection of quiz completion
  118. if ($pos = strpos($this->headcontent, 'var Score = 0;')) {
  119. $insert = "var TimeOver = false;\n";
  120. $this->headcontent = substr_replace($this->headcontent, $insert, $pos, 0);
  121. }
  122. // override table border collapse from standard Moodle styles
  123. if ($pos = strrpos($this->headcontent, '</style>')) {
  124. $insert = ''
  125. .'#'.$this->themecontainer.' form table'."\n"
  126. .'{'."\n"
  127. .' border-collapse: separate;'."\n"
  128. .' border-spacing: 2px;'."\n"
  129. .'}'."\n"
  130. ;
  131. $this->headcontent = substr_replace($this->headcontent, $insert, $pos, 0);
  132. }
  133. break;
  134. }
  135. }
  136. /**
  137. * fix_bodycontent_rottmeier
  138. *
  139. * @param xxx $hideclozeform (optional, default=false)
  140. */
  141. function fix_bodycontent_rottmeier($hideclozeform=false) {
  142. // fix left aligned instructions in Rottmeier-based formats
  143. // JCloze: DropDown, FindIt(a)+(b), JGloss
  144. // JMatch: JMemori
  145. $search = '/<p id="Instructions">(.*?)<\/p>/is';
  146. $replace = '<div id="Instructions">$1</div>';
  147. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent);
  148. if ($hideclozeform) {
  149. // initially hide the Cloze text (so gaps are not revealed)
  150. $search = '/<(form id="Cloze" [^>]*)>/is';
  151. $replace = '<$1 style="display:none;">';
  152. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent);
  153. }
  154. }
  155. /**
  156. * get_js_functionnames
  157. *
  158. * @return xxx
  159. */
  160. function get_js_functionnames() {
  161. // return a comma-separated list of js functions to be "fixed".
  162. // Each function name requires an corresponding function called:
  163. // fix_js_{$name}
  164. return 'Client,ShowElements,GetViewportHeight,PageDim,TrimString,RemoveBottomNavBarForIE,StartUp,GetUserName,PreloadImages,ShowMessage,HideFeedback,SendResults,Finish,WriteToInstructions,ShowSpecialReadingForQuestion';
  165. }
  166. /**
  167. * fix_js_Client
  168. *
  169. * @param xxx $str (passed by reference)
  170. * @param xxx $start
  171. * @param xxx $length
  172. */
  173. function fix_js_Client(&$str, $start, $length) {
  174. $substr = substr($str, $start, $length);
  175. // refine detection of Chrome browser
  176. $search = 'this.geckoVer < 20020000';
  177. if ($pos = strpos($substr, $search)) {
  178. $substr = substr_replace($substr, 'this.geckoVer > 10000000 && ', $pos, 0);
  179. }
  180. // add detection of Chrome browser
  181. $search = '/(\s*)if \(this\.min == false\)\{/s';
  182. $replace = '$1'
  183. ."this.chrome = (this.ua.indexOf('Chrome') > 0);".'$1'
  184. ."if (this.chrome) {".'$1'
  185. ." this.geckoVer = 0;".'$1'
  186. ." this.safari = false;".'$1'
  187. ." this.min = true;".'$1'
  188. ."}$0"
  189. ;
  190. $substr = preg_replace($search, $replace, $substr, 1);
  191. $str = substr_replace($str, $substr, $start, $length);
  192. }
  193. /**
  194. * fix_js_ShowElements
  195. *
  196. * @param xxx $str (passed by reference)
  197. * @param xxx $start
  198. * @param xxx $length
  199. */
  200. function fix_js_ShowElements(&$str, $start, $length) {
  201. $substr = substr($str, $start, $length);
  202. // hide <embed> tags (required for Chrome browser)
  203. if ($pos = strpos($substr, 'TagName == "object"')) {
  204. $substr = substr_replace($substr, 'TagName == "embed" || ', $pos, 0);
  205. }
  206. $str = substr_replace($str, $substr, $start, $length);
  207. }
  208. /**
  209. * fix_js_PageDim
  210. *
  211. * @param xxx $str (passed by reference)
  212. * @param xxx $start
  213. * @param xxx $length
  214. * @return xxx
  215. */
  216. function fix_js_PageDim(&$str, $start, $length) {
  217. if ($this->usemoodletheme) {
  218. $obj = "document.getElementById('$this->themecontainer')"; // moodle
  219. } else {
  220. $obj = "document.getElementsByTagName('body')[0]"; // original
  221. }
  222. $replace = ''
  223. ."function getStyleValue(obj, property_name, propertyName){\n"
  224. ." var value = 0;\n"
  225. ." // Watch out for HTMLDocument which has no style property\n"
  226. ." // as this causes errors later in getComputedStyle() in FF\n"
  227. ." if (obj && obj.style){\n"
  228. ." // based on http://www.quirksmode.org/dom/getstyles.html\n"
  229. ." if (document.defaultView && document.defaultView.getComputedStyle){\n"
  230. ." // Firefox, Opera, Safari\n"
  231. ." value = document.defaultView.getComputedStyle(obj, null).getPropertyValue(property_name);\n"
  232. ." } else if (obj.currentStyle) {"
  233. ." // IE (and Opera)\n"
  234. ." value = obj.currentStyle[propertyName];\n"
  235. ." }\n"
  236. ." if (typeof(value)=='string'){\n"
  237. ." var r = new RegExp('([0-9.]*)([a-z]+)');\n"
  238. ." var m = value.match(r);\n"
  239. ." if (m){\n"
  240. ." switch (m[2]){\n"
  241. ." case 'em':\n"
  242. ." // as far as I can see, only IE needs this\n"
  243. ." // other browsers have getComputedStyle() in px\n"
  244. ." if (typeof(obj.EmInPx)=='undefined'){\n"
  245. ." var div = obj.parentNode.appendChild(document.createElement('div'));\n"
  246. ." div.style.margin = '0px';\n"
  247. ." div.style.padding = '0px';\n"
  248. ." div.style.border = 'none';\n"
  249. ." div.style.height = '1em';\n"
  250. ." obj.EmInPx = getOffset(div, 'Height');\n"
  251. ." obj.parentNode.removeChild(div);\n"
  252. ." }\n"
  253. ." value = parseFloat(m[1] * obj.EmInPx);\n"
  254. ." break;\n"
  255. ." case 'px':\n"
  256. ." value = parseFloat(m[1]);\n"
  257. ." break;\n"
  258. ." default:\n"
  259. ." value = 0;\n"
  260. ." }\n"
  261. ." } else {\n"
  262. ." value = 0 ;\n"
  263. ." }\n"
  264. ." } else {\n"
  265. ." value = 0;\n"
  266. ." }\n"
  267. ." }\n"
  268. ." return value;\n"
  269. ."}\n"
  270. ."function isStrict(){\n"
  271. ." return false;\n"
  272. ." if (typeof(window.cache_isStrict)=='undefined'){\n"
  273. ." if (document.compatMode) { // ie6+\n"
  274. ." window.cache_isStrict = (document.compatMode=='CSS1Compat');\n"
  275. ." } else if (document.doctype){\n"
  276. ." var s = document.doctype.systemId || document.doctype.name; // n6 OR ie5mac\n"
  277. ." window.cache_isStrict = (s && s.indexOf('strict.dtd') >= 0);\n"
  278. ." } else {\n"
  279. ." window.cache_isStrict = false;\n"
  280. ." }\n"
  281. ." }\n"
  282. ." return window.cache_isStrict;\n"
  283. ."}\n"
  284. ."function setOffset(obj, type, value){\n"
  285. ." if (! obj){\n"
  286. ." return false;\n"
  287. ." }\n"
  288. ."\n"
  289. ." switch (type){\n"
  290. ." case 'Right':\n"
  291. ." return setOffset(obj, 'Width', value - getOffset(obj, 'Left'));\n"
  292. ." case 'Bottom':\n"
  293. ." return setOffset(obj, 'Height', value - getOffset(obj, 'Top'));\n"
  294. ." }\n"
  295. ."\n"
  296. ." if (isStrict()){\n"
  297. ." // set arrays of p(roperties) and s(ub-properties)\n"
  298. ." var properties = new Array('margin', 'border', 'padding');\n"
  299. ." switch (type){\n"
  300. ." case 'Top':\n"
  301. ." var sides = new Array('Top');\n"
  302. ." break;\n"
  303. ." case 'Left':\n"
  304. ." var sides = new Array('Left');\n"
  305. ." break;\n"
  306. ." case 'Width':\n"
  307. ." var sides = new Array('Left', 'Right');\n"
  308. ." break;\n"
  309. ." case 'Height':\n"
  310. ." var sides = new Array('Top', 'Bottom');\n"
  311. ." break;\n"
  312. ." default:\n"
  313. ." return 0;\n"
  314. ." }\n"
  315. ." for (var p=0; p<properties.length; p++){\n"
  316. ." for (var s=0; s<sides.length; s++){\n"
  317. ." var propertyName = properties[p] + sides[s];\n"
  318. ." var property_name = properties[p] + '-' + sides[s].toLowerCase();\n"
  319. ." value -= getStyleValue(obj, property_name, propertyName);\n"
  320. ." }\n"
  321. ." }\n"
  322. ." value = Math.floor(value);\n"
  323. ." }\n"
  324. ." if (type=='Top' || type=='Left') {\n"
  325. ." value -= getOffset(obj.offsetParent, type);\n"
  326. ." }\n"
  327. ." if (obj.style) {\n"
  328. ." obj.style[type.toLowerCase()] = value + 'px';\n"
  329. ." }\n"
  330. ."}\n"
  331. ."function getOffset(obj, type){\n"
  332. ." if (! obj){\n"
  333. ." return 0;\n"
  334. ." }\n"
  335. ." switch (type){\n"
  336. ." case 'Width':\n"
  337. ." case 'Height':\n"
  338. ." return eval('(obj.offset'+type+'||0)');\n"
  339. ."\n"
  340. ." case 'Top':\n"
  341. ." case 'Left':\n"
  342. ." return eval('(obj.offset'+type+'||0) + getOffset(obj.offsetParent, type)');\n"
  343. ."\n"
  344. ." case 'Right':\n"
  345. ." return getOffset(obj, 'Left') + getOffset(obj, 'Width');\n"
  346. ."\n"
  347. ." case 'Bottom':\n"
  348. ." return getOffset(obj, 'Top') + getOffset(obj, 'Height');\n"
  349. ."\n"
  350. ." default:\n"
  351. ." return 0;\n"
  352. ." } // end switch\n"
  353. ."}\n"
  354. ."function PageDim(){\n"
  355. ." var obj = $obj;\n"
  356. ." this.W = getOffset(obj, 'Width');\n"
  357. ." this.H = getOffset(obj, 'Height');\n"
  358. ." this.Top = getOffset(obj, 'Top');\n"
  359. ." this.Left = getOffset(obj, 'Left');\n"
  360. ."}\n"
  361. ."function getClassAttribute(className, attributeName){\n"
  362. ." //based on http://www.shawnolson.net/a/503/\n"
  363. ." if (! document.styleSheets){\n"
  364. ." return null; // old browser\n"
  365. ." }\n"
  366. ." var css = document.styleSheets;\n"
  367. ." var rules = (document.all ? 'rules' : 'cssRules');\n"
  368. ." var regexp = new RegExp('\\\\.'+className+'\\\\b');\n"
  369. ." try {\n"
  370. ." var i_max = css.length;\n"
  371. ." } catch(err) {\n"
  372. ." var i_max = 0; // shouldn't happen !!\n"
  373. ." }\n"
  374. ." for (var i=0; i<i_max; i++){\n"
  375. ." try {\n"
  376. ." var ii_max = css[i][rules].length;\n"
  377. ." } catch(err) {\n"
  378. ." var ii_max = 0; // shouldn't happen !!\n"
  379. ." }\n"
  380. ." for (var ii=0; ii<ii_max; ii++){\n"
  381. ." if (! css[i][rules][ii].selectorText){\n"
  382. ." continue;\n"
  383. ." }\n"
  384. ." if (css[i][rules][ii].selectorText.match(regexp)){\n"
  385. ." if (css[i][rules][ii].style[attributeName]){\n"
  386. ." // class/attribute found\n"
  387. ." return css[i][rules][ii].style[attributeName];\n"
  388. ." }\n"
  389. ." }\n"
  390. ." }\n"
  391. ." }\n"
  392. ." // class/attribute not found\n"
  393. ." return null;\n"
  394. ."}\n"
  395. ;
  396. $str = substr_replace($str, $replace, $start, $length);
  397. }
  398. /**
  399. * fix_js_GetViewportHeight
  400. *
  401. * @param xxx $str (passed by reference)
  402. * @param xxx $start
  403. * @param xxx $length
  404. * @return xxx
  405. */
  406. function fix_js_GetViewportHeight(&$str, $start, $length) {
  407. $replace = ''
  408. ."function GetViewportSize(type){\n"
  409. ." if (eval('window.inner' + type)){\n"
  410. ." return eval('window.inner' + type);\n"
  411. ." }\n"
  412. ." if (document.documentElement){\n"
  413. ." if (eval('document.documentElement.client' + type)){\n"
  414. ." return eval('document.documentElement.client' + type);\n"
  415. ." }\n"
  416. ." }\n"
  417. ." if (document.body){\n"
  418. ." if (eval('document.body.client' + type)){\n"
  419. ." return eval('document.body.client' + type);\n"
  420. ." }\n"
  421. ." }\n"
  422. ." return 0;\n"
  423. ."}\n"
  424. ."function GetViewportHeight(){\n"
  425. ." return GetViewportSize('Height');\n"
  426. ."}\n"
  427. ."function GetViewportWidth(){\n"
  428. ." return GetViewportSize('Width');\n"
  429. ."}"
  430. ;
  431. $str = substr_replace($str, $replace, $start, $length);
  432. }
  433. /**
  434. * remove_js_function
  435. *
  436. * @param xxx $str (passed by reference)
  437. * @param xxx $start
  438. * @param xxx $length
  439. * @param xxx $function
  440. */
  441. function remove_js_function(&$str, $start, $length, $function) {
  442. // remove this function
  443. $str = substr_replace($str, '', $start, $length);
  444. // remove all direct calls to this function
  445. $search = '/\s*'.$function.'\([^)]*\);/s';
  446. $str = preg_replace($search, '', $str);
  447. }
  448. /**
  449. * fix_js_TrimString
  450. *
  451. * @param xxx $str (passed by reference)
  452. * @param xxx $start
  453. * @param xxx $length
  454. * @return xxx
  455. */
  456. function fix_js_TrimString(&$str, $start, $length) {
  457. $replace = ''
  458. ."function TrimString(InString){\n"
  459. ." if (typeof(InString)=='string'){\n"
  460. ." InString = InString.replace(new RegExp('^\\\\s+', 'g'), ''); // left\n"
  461. ." InString = InString.replace(new RegExp('\\\\s+$', 'g'), ''); // right\n"
  462. ." InString = InString.replace(new RegExp('\\\\s+', 'g'), ' '); // inner\n"
  463. ." }\n"
  464. ." return InString;\n"
  465. ."}"
  466. ;
  467. $str = substr_replace($str, $replace, $start, $length);
  468. }
  469. /**
  470. * fix_js_TypeChars
  471. *
  472. * @param xxx $str (passed by reference)
  473. * @param xxx $start
  474. * @param xxx $length
  475. */
  476. function fix_js_TypeChars(&$str, $start, $length) {
  477. if ($obj = $this->fix_js_TypeChars_obj()) {
  478. $substr = substr($str, $start, $length);
  479. if (strpos($substr, 'document.selection')===false) {
  480. $replace = ''
  481. ."function TypeChars(Chars){\n"
  482. .$this->fix_js_TypeChars_init()
  483. ." if ($obj==null || $obj.style.display=='none') {\n"
  484. ." return;\n"
  485. ." }\n"
  486. ." $obj.focus();\n"
  487. ." if (typeof($obj.selectionStart)=='number') {\n"
  488. ." // FF, Safari, Chrome, Opera\n"
  489. ." var startPos = $obj.selectionStart;\n"
  490. ." var endPos = $obj.selectionEnd;\n"
  491. ." $obj.value = $obj.value.substring(0, startPos) + Chars + $obj.value.substring(endPos);\n"
  492. ." var newPos = startPos + Chars.length;\n"
  493. ." $obj.setSelectionRange(newPos, newPos);\n"
  494. ." } else if (document.selection) {\n"
  495. ." // IE (tested on IE6, IE7, IE8)\n"
  496. ." var rng = document.selection.createRange();\n"
  497. ." rng.text = Chars;\n"
  498. ." rng = null; // prevent memory leak\n"
  499. ." } else {\n"
  500. ." // this browser can't insert text, so append instead\n"
  501. ." $obj.value += Chars;\n"
  502. ." }\n"
  503. ."}"
  504. ;
  505. $str = substr_replace($str, $replace, $start, $length);
  506. }
  507. }
  508. }
  509. /**
  510. * fix_js_TypeChars_init
  511. *
  512. * @return xxx
  513. */
  514. function fix_js_TypeChars_init() {
  515. return '';
  516. }
  517. /**
  518. * fix_js_TypeChars_obj
  519. *
  520. * @return xxx
  521. */
  522. function fix_js_TypeChars_obj() {
  523. return '';
  524. }
  525. /**
  526. * fix_js_SendResults
  527. *
  528. * @param xxx $str (passed by reference)
  529. * @param xxx $start
  530. * @param xxx $length
  531. */
  532. function fix_js_SendResults(&$str, $start, $length) {
  533. $this->remove_js_function($str, $start, $length, 'SendResults');
  534. }
  535. /**
  536. * fix_js_GetUserName
  537. *
  538. * @param xxx $str (passed by reference)
  539. * @param xxx $start
  540. * @param xxx $length
  541. */
  542. function fix_js_GetUserName(&$str, $start, $length) {
  543. $this->remove_js_function($str, $start, $length, 'GetUserName');
  544. }
  545. /**
  546. * fix_js_Finish
  547. *
  548. * @param xxx $str (passed by reference)
  549. * @param xxx $start
  550. * @param xxx $length
  551. */
  552. function fix_js_Finish(&$str, $start, $length) {
  553. $this->remove_js_function($str, $start, $length, 'Finish');
  554. }
  555. /**
  556. * fix_js_PreloadImages
  557. *
  558. * @param xxx $str (passed by reference)
  559. * @param xxx $start
  560. * @param xxx $length
  561. */
  562. function fix_js_PreloadImages(&$str, $start, $length) {
  563. $substr = substr($str, $start, $length);
  564. // fix issue in IE8 which sometimes doesn't have Image object in popups
  565. // http://moodle.org/mod/forum/discuss.php?d=134510
  566. $search = "Imgs[i] = new Image();";
  567. if ($pos = strpos($substr, $search)) {
  568. $replace = "Imgs[i] = (window.Image ? new Image() : document.createElement('img'));";
  569. $substr = substr_replace($substr, $replace, $pos, strlen($search));
  570. }
  571. $str = substr_replace($str, $substr, $start, $length);
  572. }
  573. /**
  574. * fix_js_WriteToInstructions
  575. *
  576. * @param xxx $str (passed by reference)
  577. * @param xxx $start
  578. * @param xxx $length
  579. * @return xxx
  580. */
  581. function fix_js_WriteToInstructions(&$str, $start, $length) {
  582. $substr = substr($str, $start, $length);
  583. if ($pos = strpos($substr, '{')) {
  584. $insert = "\n"
  585. ." // check required HTML element exists\n"
  586. ." if (! document.getElementById('InstructionsDiv')) return false;\n"
  587. ;
  588. $substr = substr_replace($substr, $insert, $pos+1, 0);
  589. }
  590. if ($pos = strrpos($substr, '}')) {
  591. $append = "\n"
  592. ." StretchCanvasToCoverContent(true);\n"
  593. ;
  594. $substr = substr_replace($substr, $append, $pos, 0);
  595. }
  596. $str = substr_replace($str, $substr, $start, $length);
  597. }
  598. /**
  599. * fix_js_ShowMessage
  600. *
  601. * @param xxx $str (passed by reference)
  602. * @param xxx $start
  603. * @param xxx $length
  604. * @return xxx
  605. */
  606. function fix_js_ShowMessage(&$str, $start, $length) {
  607. // the ShowMessage function is used by all HP6 quizzes
  608. $substr = substr($str, $start, $length);
  609. // only show feedback if the required HTML elements exist
  610. // this prevents JavaScript errors which block the returning of the quiz results to Moodle
  611. if ($pos = strpos($substr, '{')) {
  612. $insert = "\n"
  613. ." // check required HTML elements exist\n"
  614. ." if (! document.getElementById('FeedbackDiv')) return false;\n"
  615. ." if (! document.getElementById('FeedbackContent')) return false;\n"
  616. ." if (! document.getElementById('FeedbackOKButton')) return false;\n"
  617. ;
  618. $substr = substr_replace($substr, $insert, $pos+1, 0);
  619. }
  620. // hide <embed> elements on Chrome browser
  621. $search = "/(\s*)ShowElements\(true, 'object', 'FeedbackContent'\);/s";
  622. $replace = ''
  623. ."$0".'$1'
  624. ."if (C.chrome) {".'$1'
  625. ." ShowElements(false, 'embed');".'$1'
  626. ." ShowElements(true, 'embed', 'FeedbackContent');".'$1'
  627. ."}"
  628. ;
  629. $substr = preg_replace($search, $replace, $substr, 1);
  630. // fix "top" setting to position FeedbackDiv
  631. if ($this->usemoodletheme) {
  632. $canvas = "document.getElementById('$this->themecontainer')"; // moodle
  633. } else {
  634. $canvas = "document.getElementsByTagName('body')[0]"; // original
  635. }
  636. $search = "/FDiv.style.top = [^;]*;(\s*)(FDiv.style.display = [^;]*;)/s";
  637. $replace = ''
  638. .'$1$2'
  639. .'$1'."var t = getOffset($canvas, 'Top');"
  640. .'$1'."setOffset(FDiv, 'Top', Math.max(t, TopSettingWithScrollOffset(30)));"
  641. ;
  642. $substr = preg_replace($search, $replace, $substr, 1);
  643. // append link to student feedback form, if necessary
  644. if ($this->hotpot->studentfeedback) {
  645. $search = '/(\s*)var Output = [^;]*;/';
  646. $replace = ''
  647. ."$0".'$1'
  648. ."if (window.FEEDBACK) {".'$1'
  649. ." Output += '".'<a href="javascript:hpFeedback();">'."' + FEEDBACK[6] + '</a>';".'$1'
  650. ."}"
  651. ;
  652. $substr = preg_replace($search, $replace, $substr, 1);
  653. }
  654. $substr = preg_replace($search, $replace, $substr, 1);
  655. }
  656. /**
  657. * fix_js_RemoveBottomNavBarForIE
  658. *
  659. * @param xxx $str (passed by reference)
  660. * @param xxx $start
  661. * @param xxx $length
  662. */
  663. function fix_js_RemoveBottomNavBarForIE(&$str, $start, $length) {
  664. $replace = ''
  665. ."function RemoveBottomNavBarForIE(){\n"
  666. ." if (C.ie) {\n"
  667. ." if (document.getElementById('Reading')){\n"
  668. ." var obj = document.getElementById('BottomNavBar');\n"
  669. ." if (obj){\n"
  670. ." obj.parentNode.removeChild(obj);\n"
  671. ." }\n"
  672. ." }\n"
  673. ." }\n"
  674. ."}"
  675. ;
  676. $str = substr_replace($str, $replace, $start, $length);
  677. }
  678. /**
  679. * fix_js_StartUp_DragAndDrop
  680. *
  681. * @param xxx $substr (passed by reference)
  682. */
  683. function fix_js_StartUp_DragAndDrop(&$substr) {
  684. // fixes for Drag and Drop (JMatch and JMix)
  685. }
  686. /**
  687. * fix_js_StartUp_DragAndDrop_DragArea
  688. *
  689. * @param xxx $substr (passed by reference)
  690. */
  691. function fix_js_StartUp_DragAndDrop_DragArea(&$substr) {
  692. // fix LeftCol (=left side of drag area)
  693. $search = '/(LeftColPos = [^;]+);/';
  694. $replace = '$1 + pg.Left;';
  695. $substr = preg_replace($search, $replace, $substr, 1);
  696. // fix DragTop (=top side of Drag area)
  697. $search = '/DragTop = [^;]+;/';
  698. $replace = "DragTop = getOffset(document.getElementById('CheckButtonDiv'),'Bottom') + 10;";
  699. $substr = preg_replace($search, $replace, $substr, 1);
  700. }
  701. /**
  702. * fix_js_StartUp
  703. *
  704. * @param xxx $str (passed by reference)
  705. * @param xxx $start
  706. * @param xxx $length
  707. */
  708. function fix_js_StartUp(&$str, $start, $length) {
  709. $substr = substr($str, $start, $length);
  710. // if necessary, fix drag area for JMatch or JMix drag-and-drop
  711. $this->fix_js_StartUp_DragAndDrop($substr);
  712. if ($pos = strrpos($substr, '}')) {
  713. if ($this->hotpot->delay3==hotpot::TIME_DISABLE) {
  714. $forceajax = 1;
  715. } else {
  716. $forceajax = 0;
  717. }
  718. if ($this->can_continue()==hotpot::CONTINUE_RESUMEQUIZ) {
  719. $onunload_status = hotpot::STATUS_INPROGRESS;
  720. } else {
  721. $onunload_status = hotpot::STATUS_ABANDONED;
  722. }
  723. $append = "\n"
  724. ."// adjust size and position of Feedback DIV\n"
  725. ." if (! window.pg){\n"
  726. ." window.pg = new PageDim();\n"
  727. ." }\n"
  728. ." var FDiv = document.getElementById('FeedbackDiv');\n"
  729. ." if (FDiv){\n"
  730. ." var w = getOffset(FDiv, 'Width') || FDiv.style.width || getClassAttribute(FDiv.className, 'width');\n"
  731. ." if (w){\n"
  732. ." if (typeof(w)=='string' && w.indexOf('%')>=0){\n"
  733. ." var percent = parseInt(w);\n"
  734. ." } else {\n"
  735. ." var percent = Math.floor(100 * parseInt(w) / pg.W);\n"
  736. ." }\n"
  737. ." } else if (window.FeedbackWidth && window.DivWidth){\n"
  738. ." var percent = Math.floor(100 * FeedbackWidth / DivWidth);\n"
  739. ." } else {\n"
  740. ." var percent = 34; // default width as percentage\n"
  741. ." }\n"
  742. ." FDiv.style.display = 'block';\n"
  743. ." setOffset(FDiv, 'Left', pg.Left + Math.floor(pg.W * (50 - percent/2) / 100));\n"
  744. ." setOffset(FDiv, 'Width', Math.floor(pg.W * percent / 100));\n"
  745. ." FDiv.style.display = 'none';\n"
  746. ." }\n"
  747. ."\n"
  748. ."// create HP object (to collect and send responses)\n"
  749. ." window.HP = new ".$this->js_object_type."('".$this->can_clickreport()."','".$forceajax."');\n"
  750. ."\n"
  751. ."// call HP.onunload to send results when this page unloads\n"
  752. ." var s = '';\n"
  753. ." if (typeof(window.onunload)=='function'){\n"
  754. ." window.onunload_StartUp = onunload;\n"
  755. ." s += 'window.onunload_StartUp();'\n"
  756. ." }\n"
  757. ." window.onunload = new Function(s + 'if(window.HP){HP.status=$onunload_status;HP.onunload();object_destroy(HP);}return true;');\n"
  758. ."\n"
  759. ;
  760. $substr = substr_replace($substr, $append, $pos, 0);
  761. }
  762. // stretch the canvas vertically down, if there is a reading
  763. if ($pos = strrpos($substr, '}')) {
  764. // Reading is contained in <div class="LeftContainer">
  765. // MainDiv is contained in <div class="RightContainer">
  766. // when there is a reading. Otherwise, MainDiv is not contained.
  767. // ReadingDiv is used to show different reading for each question
  768. if ($this->usemoodletheme) {
  769. $canvas = "document.getElementById('$this->themecontainer')"; // moodle
  770. } else {
  771. $canvas = "document.getElementsByTagName('body')[0]"; // original
  772. }
  773. // None: $canvas = "document.getElementById('page-mod-hotpot-attempt')"
  774. $id = $this->embed_object_id;
  775. $onload = $this->embed_object_onload;
  776. $insert = "\n"
  777. ."// fix canvas height, if necessary\n"
  778. ." if (! window.hotpot_mediafilter_loader){\n"
  779. ." StretchCanvasToCoverContent();\n"
  780. ." }\n"
  781. ."}\n"
  782. ."function StretchCanvasToCoverContent(skipTimeout){\n"
  783. ." if (! skipTimeout){\n"
  784. ." if (navigator.userAgent.indexOf('Firefox/3')>=0){\n"
  785. ." var millisecs = 1000;\n"
  786. ." } else {\n"
  787. ." var millisecs = 500;\n"
  788. ." }\n"
  789. ." setTimeout('StretchCanvasToCoverContent(true)', millisecs);\n"
  790. ." return;\n"
  791. ." }\n"
  792. ." var canvas = $canvas;\n"
  793. ." if (canvas){\n"
  794. ." var ids = new Array('Reading','ReadingDiv','MainDiv');\n"
  795. ." var i_max = ids.length;\n"
  796. ." for (var i=i_max-1; i>=0; i--){\n"
  797. ." var obj = document.getElementById(ids[i]);\n"
  798. ." if (obj){\n"
  799. ." obj.style.height = ''; // reset height\n"
  800. ." } else {\n"
  801. ." ids.splice(i, 1); // remove this id\n"
  802. ." i_max--;\n"
  803. ." }\n"
  804. ." }\n"
  805. ." var b = 0;\n"
  806. ." for (var i=0; i<i_max; i++){\n"
  807. ." var obj = document.getElementById(ids[i]);\n"
  808. ." b = Math.max(b, getOffset(obj,'Bottom'));\n"
  809. ." }\n"
  810. ." if (window.Segments) {\n" // JMix special
  811. ." var obj = document.getElementById('D'+(Segments.length-1));\n"
  812. ." if (obj) {\n"
  813. ." b = Math.max(b, getOffset(obj,'Bottom'));\n"
  814. ." }\n"
  815. ." }\n"
  816. ." if (b){\n"
  817. ." setOffset(canvas, 'Bottom', b + 21);\n"
  818. ." for (var i=0; i<i_max; i++){\n"
  819. ." var obj = document.getElementById(ids[i]);\n"
  820. ." setOffset(obj, 'Bottom', b);\n"
  821. ." }\n"
  822. ." }\n"
  823. ." }\n"
  824. ;
  825. if ($this->hotpot->navigation==hotpot::NAVIGATION_EMBED) {
  826. // stretch container object/iframe
  827. $insert .= ''
  828. ." if (parent.$onload) {\n"
  829. ." parent.$onload(null, parent.document.getElementById('".$this->embed_object_id."'));\n"
  830. ." }\n"
  831. ;
  832. }
  833. $substr = substr_replace($substr, $insert, $pos, 0);
  834. }
  835. $str = substr_replace($str, $substr, $start, $length);
  836. }
  837. /**
  838. * fix_js_HideFeedback
  839. *
  840. * @param xxx $str (passed by reference)
  841. * @param xxx $start
  842. * @param xxx $length
  843. */
  844. function fix_js_HideFeedback(&$str, $start, $length) {
  845. global $CFG;
  846. $substr = substr($str, $start, $length);
  847. // unhide <embed> elements on Chrome browser
  848. $search = "/(\s*)ShowElements\(true, 'object'\);/s";
  849. $replace = ''
  850. .'$0$1'
  851. ."if (C.chrome) {".'$1'
  852. ." ShowElements(true, 'embed');".'$1'
  853. ."}"
  854. ;
  855. $substr = preg_replace($search, $replace, $substr, 1);
  856. $search = '/('.'\s*if \(Finished == true\){\s*)(?:.*?)(\s*})/s';
  857. if ($this->hotpot->delay3==hotpot::TIME_AFTEROK) {
  858. // -1 : send form only (do not set form values, as that has already been done)
  859. $replace = '$1'.'HP.onunload(HP.status,-1);'.'$2';
  860. } else {
  861. $replace = ''; // i.e. remove this if-block
  862. }
  863. $substr = preg_replace($search, $replace, $substr, 1);
  864. $str = substr_replace($str, $substr, $start, $length);
  865. }
  866. /**
  867. * fix_js_ShowSpecialReadingForQuestion
  868. *
  869. * @param xxx $str (passed by reference)
  870. * @param xxx $start
  871. * @param xxx $length
  872. */
  873. function fix_js_ShowSpecialReadingForQuestion(&$str, $start, $length) {
  874. $replace = ''
  875. ."function ShowSpecialReadingForQuestion(){\n"
  876. ." var ReadingDiv = document.getElementById('ReadingDiv');\n"
  877. ." if (ReadingDiv){\n"
  878. ." var ReadingText = null;\n"
  879. ." var divs = ReadingDiv.getElementsByTagName('div');\n"
  880. ." for (var i=0; i<divs.length; i++){\n"
  881. ." if (divs[i].className=='ReadingText' || divs[i].className=='TempReadingText'){\n"
  882. ." ReadingText = divs[i];\n"
  883. ." break;\n"
  884. ." }\n"
  885. ." }\n"
  886. ." if (ReadingText && HiddenReadingShown){\n"
  887. ." SwapReadingTexts(ReadingText, HiddenReadingShown);\n"
  888. ." ReadingText = HiddenReadingShown;\n"
  889. ." HiddenReadingShown = false;\n"
  890. ." }\n"
  891. ." var HiddenReading = null;\n"
  892. ." if (QArray[CurrQNum]){\n"
  893. ." var divs = QArray[CurrQNum].getElementsByTagName('div');\n"
  894. ." for (var i=0; i<divs.length; i++){\n"
  895. ." if (divs[i].className=='HiddenReading'){\n"
  896. ." HiddenReading = divs[i];\n"
  897. ." break;\n"
  898. ." }\n"
  899. ." }\n"
  900. ." }\n"
  901. ." if (HiddenReading){\n"
  902. ." if (! ReadingText){\n"
  903. ." ReadingText = document.createElement('div');\n"
  904. ." ReadingText.className = 'ReadingText';\n"
  905. ." ReadingDiv.appendChild(ReadingText);\n"
  906. ." }\n"
  907. ." SwapReadingTexts(ReadingText, HiddenReading);\n"
  908. ." HiddenReadingShown = ReadingText;\n"
  909. ." }\n"
  910. ." var btn = document.getElementById('ShowMethodButton');\n"
  911. ." if (btn){\n"
  912. ." if (HiddenReadingShown){\n"
  913. ." if (btn.style.display!='none'){\n"
  914. ." btn.style.display = 'none';\n"
  915. ." }\n"
  916. ." } else {\n"
  917. ." if (btn.style.display=='none'){\n"
  918. ." btn.style.display = '';\n"
  919. ." }\n"
  920. ." }\n"
  921. ." }\n"
  922. ." btn = null;\n"
  923. ." ReadingDiv = null;\n"
  924. ." ReadingText = null;\n"
  925. ." HiddenReading = null;\n"
  926. ." }\n"
  927. ."}\n"
  928. ."function SwapReadingTexts(ReadingText, HiddenReading) {\n"
  929. ." HiddenReadingParentNode = HiddenReading.parentNode;\n"
  930. ." HiddenReadingParentNode.removeChild(HiddenReading);\n"
  931. ."\n"
  932. ." // replaceChild(new_node, old_node)\n"
  933. ." ReadingText.parentNode.replaceChild(HiddenReading, ReadingText);\n"
  934. ."\n"
  935. ." if (HiddenReading.IsOriginalReadingText){\n"
  936. ." HiddenReading.className = 'ReadingText';\n"
  937. ." } else {\n"
  938. ." HiddenReading.className = 'TempReadingText';\n"
  939. ." }\n"
  940. ." HiddenReading.style.display = '';\n"
  941. ."\n"
  942. ." if (ReadingText.className=='ReadingText'){\n"
  943. ." ReadingText.IsOriginalReadingText = true;\n"
  944. ." } else {\n"
  945. ." ReadingText.IsOriginalReadingText = false;\n"
  946. ." }\n"
  947. ." ReadingText.style.display = 'none';\n"
  948. ." ReadingText.className = 'HiddenReading';\n"
  949. ."\n"
  950. ." HiddenReadingParentNode.appendChild(ReadingText);\n"
  951. ." HiddenReadingParentNode = null;\n"
  952. ."}\n"
  953. ;
  954. $str = substr_replace($str, $replace, $start, $length);
  955. }
  956. /**
  957. * fix_js_CheckAnswers
  958. *
  959. * @param xxx $str (passed by reference)
  960. * @param xxx $start
  961. * @param xxx $length
  962. */
  963. function fix_js_CheckAnswers(&$str, $start, $length) {
  964. // JCloze, JCross, JMatch : CheckAnswers
  965. // JMix : CheckAnswer
  966. // JQuiz : CheckFinished
  967. $substr = substr($str, $start, $length);
  968. // intercept Checks, if necessary
  969. if ($insert = $this->get_stop_function_intercept()) {
  970. if ($pos = strpos($substr, '{')) {
  971. $substr = substr_replace($substr, $insert, $pos+1, 0);
  972. }
  973. }
  974. // add extra argument to function - so it can be called from the "Give Up" button
  975. $name = $this->get_stop_function_name();
  976. $search = '/(function '.$name.'\()(.*?)(\))/s';
  977. $callback = array($this, 'fix_js_CheckAnswers_arguments');
  978. $substr = preg_replace_callback($search, $callback, $substr, 1);
  979. // add call to Finish function (including QuizStatus)
  980. $search = $this->get_stop_function_search();
  981. $replace = $this->get_stop_function_replace();
  982. $substr = preg_replace($search, $replace, $substr, 1);
  983. $str = substr_replace($str, $substr, $start, $length);
  984. }
  985. /**
  986. * fix_js_CheckAnswers_arguments
  987. *
  988. * @param xxx $match
  989. * @return xxx
  990. */
  991. function fix_js_CheckAnswers_arguments($match) {
  992. if (empty($match[2])) {
  993. return $match[1].'ForceQuizStatus'.$match[3];
  994. } else {
  995. return $match[1].$match[2].',ForceQuizStatus'.$match[3];
  996. }
  997. }
  998. /**
  999. * get_stop_onclick
  1000. *
  1001. * @return xxx
  1002. */
  1003. function get_stop_onclick() {
  1004. if ($name = $this->get_stop_function_name()) {
  1005. return 'if('.$this->get_stop_function_confirm().')'.$name.'('.$this->get_stop_function_args().')';
  1006. } else {
  1007. return 'if(window.HP)HP.onunload('.QUIZPORT_STATUS_ABANDONED.')';
  1008. }
  1009. }
  1010. /**
  1011. * get_stop_function_confirm
  1012. *
  1013. * @return xxx
  1014. */
  1015. function get_stop_function_confirm() {
  1016. // Note: "&&" in onclick must be encoded as html-entities for strict XHTML
  1017. return ''
  1018. ."confirm("
  1019. ."'".$this->hotpot->source->js_value_safe(get_string('confirmstop', 'hotpot'), true)."'"
  1020. ."+'\\n\\n'+(window.onbeforeunload &amp;&amp; onbeforeunload()?(onbeforeunload()+'\\n\\n'):'')+"
  1021. ."'".$this->hotpot->source->js_value_safe(get_string('pressoktocontinue', 'hotpot'), true)."'"
  1022. .")"
  1023. ;
  1024. }
  1025. /**
  1026. * get_stop_function_name
  1027. *
  1028. * @return xxx
  1029. */
  1030. function get_stop_function_name() {
  1031. // the name of the javascript function into which the "give up" code should be inserted
  1032. return '';
  1033. }
  1034. /**
  1035. * get_stop_function_args
  1036. *
  1037. * @return xxx
  1038. */
  1039. function get_stop_function_args() {
  1040. // the arguments required by the javascript function which the stop_function() code calls
  1041. return hotpot::STATUS_ABANDONED;
  1042. }
  1043. /**
  1044. * get_stop_function_intercept
  1045. *
  1046. * @return xxx
  1047. */
  1048. function get_stop_function_intercept() {
  1049. // JMix and JQuiz each have their own version of this function
  1050. return "\n"
  1051. ." // intercept this Check\n"
  1052. ." HP.onclickCheck();\n"
  1053. ;
  1054. }
  1055. /**
  1056. * get_stop_function_search
  1057. *
  1058. * @return xxx
  1059. */
  1060. function get_stop_function_search() {
  1061. // JCloze : AllCorrect || Finished
  1062. // JCross : AllCorrect || TimeOver
  1063. // JMatch : AllDone || TimeOver
  1064. // JMix : AllDone || TimeOver (in the CheckAnswer function)
  1065. // JQuiz : AllDone (in the CheckFinished function)
  1066. return '/\s*if \(\((\w+) == true\)\|\|\(\w+ == true\)\)({).*?}\s*/s';
  1067. }
  1068. /**
  1069. * get_stop_function_replace
  1070. *
  1071. * @return xxx
  1072. */
  1073. function get_stop_function_replace() {
  1074. // $1 : name of the "all correct/done" variable
  1075. // $2 : opening curly brace of if-block plus any following text to be kept
  1076. if ($this->hotpot->delay3==hotpot::TIME_AFTEROK) {
  1077. $flag = 1; // set form values only
  1078. } else {
  1079. $flag = 0; // set form values and send form
  1080. }
  1081. return "\n"
  1082. ." if ($1){\n"
  1083. ." var QuizStatus = 4; // completed\n"
  1084. ." } else if (ForceQuizStatus){\n"
  1085. ." var QuizStatus = ForceQuizStatus; // 3=abandoned\n"
  1086. ." } else if (TimeOver){\n"
  1087. ." var QuizStatus = 2; // timed out\n"
  1088. ." } else {\n"
  1089. ." var QuizStatus = 1; // in progress\n"
  1090. ." }\n"
  1091. ." if (QuizStatus > 1) $2\n"
  1092. ." if (window.Interval) {\n"
  1093. ." clearInterval(window.Interval);\n"
  1094. ." }\n"
  1095. ." TimeOver = true;\n"
  1096. ." Locked = true;\n"
  1097. ." Finished = true;\n"
  1098. ." }\n"
  1099. ." if (Finished || HP.sendallclicks){\n"
  1100. ." if (ForceQuizStatus || QuizStatus==1){\n"
  1101. ." // send results immediately\n"
  1102. ." HP.onunload(QuizStatus);\n"
  1103. ." } else {\n"
  1104. ." // send results after delay\n"
  1105. ." setTimeout('HP.onunload('+QuizStatus+',$flag)', SubmissionTimeout);\n"
  1106. ." }\n"
  1107. ." }\n"
  1108. ;
  1109. }
  1110. /**
  1111. * postprocessing
  1112. *
  1113. * after headcontent and bodycontent have been setup and
  1114. * before content is sent to browser, we add title edit icon,
  1115. * insert submission form, adjust navigation butons (if any)
  1116. * and add external javascripts (to the top of the page)
  1117. */
  1118. function postprocessing() {
  1119. $this->fix_title_icons();
  1120. $this->fix_submissionform();
  1121. $this->fix_navigation_buttons();
  1122. foreach ($this->javascripts as $script) {
  1123. $this->page->requires->js('/'.$script, true);
  1124. }
  1125. }
  1126. /**
  1127. * fix_navigation_buttons
  1128. *
  1129. * @return xxx
  1130. */
  1131. function fix_navigation_buttons() {
  1132. if ($this->hotpot->navigation==hotpot::NAVIGATION_ORIGINAL) {
  1133. // replace relative URLs in <button class="NavButton" ... onclick="location='...'">
  1134. $search = '/'.'(?<='.'onclick="'."location='".')'."([^']*)".'(?='."'; return false;".'")'.'/is';
  1135. $callback = array($this, 'convert_url_navbutton');
  1136. $this->bodycontent = preg_replace_callback($search, $callback, $this->bodycontent);
  1137. // replace history.back() in <button class="NavButton" ... onclick="history.back(); ...">
  1138. // with a link to the course page
  1139. $params = array('id'=>$this->hotpot->course->id);
  1140. $search = '/'.'(?<='.'onclick=")'.'history\.back\(\)'.'(?=; return false;")'.'/';
  1141. $replace = "location='".new moodle_url('/course/view.php', $params)."'";
  1142. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent);
  1143. }
  1144. }
  1145. /**
  1146. * fix_TimeLimit
  1147. */
  1148. function fix_TimeLimit() {
  1149. if ($this->hotpot->timelimit > 0) {
  1150. $search = '/(?<=var Seconds = )\d+(?=;)/';
  1151. $this->headcontent = preg_replace($search, $this->hotpot->timelimit, $this->headcontent, 1);
  1152. }
  1153. }
  1154. /**
  1155. * fix_SubmissionTimeout
  1156. */
  1157. function fix_SubmissionTimeout() {
  1158. if ($this->hotpot->delay3==hotpot::TIME_TEMPLATE) {
  1159. // use default from source/template file (=30000 ms =30 seconds)
  1160. if ($this->hasSubmissionTimeout) {
  1161. $timeout = null;
  1162. } else {
  1163. $timeout = 30000; // = 30 secs is HP default
  1164. }
  1165. } else {
  1166. if ($this->hotpot->delay3 >= 0) {
  1167. $timeout = $this->hotpot->delay3 * 1000; // milliseconds
  1168. } else {
  1169. $timeout = 0; // i.e. immediately
  1170. }
  1171. }
  1172. if (is_null($timeout)) {
  1173. return; // nothing to do
  1174. }
  1175. if ($this->hasSubmissionTimeout) {
  1176. // remove HPNStartTime
  1177. $search = '/var HPNStartTime\b[^;]*?;\s*/';
  1178. $this->headcontent = preg_replace($search, '', $this->headcontent, 1);
  1179. // reset the value of SubmissionTimeout
  1180. $search = '/(?<=var SubmissionTimeout = )\d+(?=;)/';
  1181. $this->headcontent = preg_replace($search, $timeout, $this->headcontent, 1);
  1182. } else {
  1183. // Rhubarb, Sequitur and Quandary
  1184. $search = '/var FinalScore = 0;/';
  1185. $replace = '$0'."\n".'var SubmissionTimeout = '.$timeout.';';
  1186. $this->headcontent = preg_replace($search, $replace, $this->headcontent, 1);
  1187. }
  1188. }
  1189. /**
  1190. * fix_navigation
  1191. */
  1192. function fix_navigation() {
  1193. if ($this->hotpot->navigation==hotpot::NAVIGATION_ORIGINAL) {
  1194. // do nothing - leave navigation as it is
  1195. return;
  1196. }
  1197. // insert the stop button, if required
  1198. if ($this->hotpot->stopbutton) {
  1199. // replace top nav buttons with a single stop button
  1200. if ($this->hotpot->stopbutton==hotpot::STOPBUTTON_LANGPACK) {
  1201. if ($pos = strpos($this->hotpot->stoptext, '_')) {
  1202. $mod = substr($this->hotpot->stoptext, 0, $pos);
  1203. $str = substr($this->hotpot->stoptext, $pos + 1);
  1204. $stoptext = get_string($str, $mod);
  1205. } else if ($this->hotpot->stoptext) {
  1206. $stoptext = get_string($this->hotpot->stoptext);
  1207. } else {
  1208. $stoptext = '';
  1209. }
  1210. } else {
  1211. $stoptext = $this->hotpot->stoptext;
  1212. }
  1213. if (trim($stoptext)=='') {
  1214. $stoptext = get_string('giveup', 'hotpot');
  1215. }
  1216. $confirm = get_string('confirmstop', 'hotpot');
  1217. //$search = '/<!-- BeginTopNavButtons -->'.'.*?'.'<!-- EndTopNavButtons -->/s';
  1218. $search = '/<(div class="Titles")>/s';
  1219. $replace = '<$1 style="position: relative">'."\n\t"
  1220. .'<div class="hotpotstopbutton">'
  1221. .'<button class="FuncButton" '
  1222. .'onclick="'.$this->get_stop_onclick().'" '
  1223. .'onfocus="FuncBtnOver(this)" onblur="FuncBtnOut(this)" '
  1224. .'onmouseover="FuncBtnOver(this)" onmouseout="FuncBtnOut(this)" '
  1225. .'onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)">'
  1226. .hotpot_textlib('utf8_to_entities', $stoptext)
  1227. .'</button>'
  1228. .'</div>'
  1229. ;
  1230. $this->bodycontent = preg_replace($search, $replace, $this->bodycontent, 1);
  1231. }
  1232. // remove (remaining) navigation buttons
  1233. $search = '/<!-- Begin(Top|Bottom)NavButtons -->'.'.*?'.'<!-- End'.'\\1'.'NavButtons -->/…

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