/libraries/widgets/quiz/js/jquery.quiz.js

https://bitbucket.org/reinholdsson/slidifyexamples · JavaScript · 565 lines · 419 code · 118 blank · 28 comment · 51 complexity · a197d48873e4c4e81ceccdcdd5aaa3fb MD5 · raw file

  1. (function( $ ) {
  2. function isundef(x, y) {
  3. return (x == undefined) ? y : x;
  4. }
  5. QuizSingleHandler = function (question, idQuestion) {
  6. this.question = question;
  7. this.idQuestion = idQuestion;
  8. this.hintsShown = 0;
  9. this.attempts = 0;
  10. }
  11. QuizSingleHandler.prototype = {
  12. makeCorrection: function(self) {
  13. // when used as a callback, self will be passed as parameter,
  14. // since 'this' will be overridden by jquery
  15. var self = isundef(self, this);
  16. self.question.data('submitted', true);
  17. self.attempts += 1;
  18. console.log(self.attempts);
  19. $('.quiz-radio:checked', self.question).each(function() {
  20. var $radio = $(this);
  21. var $option = $radio.parent();
  22. if ( $option.hasClass('quiz-answer') ) {
  23. $option.addClass('quiz-correct');
  24. $option.parents('div.quiz').data('attempts', self.attempts);
  25. $option.parents('div.quiz').data('status', 'correct');
  26. self.explanationVisible(true);
  27. } else {
  28. $option.addClass('quiz-wrong');
  29. $option.parents('div.quiz').data('attempts', self.attempts);
  30. $option.parents('div.quiz').data('status', 'wrong');
  31. };
  32. });
  33. },
  34. clear: function(self) {
  35. var self = isundef(self, this);
  36. $('.quiz-radio', self.question).removeAttr('checked');
  37. self.clearCorrection();
  38. },
  39. clearCorrection: function(self) {
  40. var self = isundef(self, this);
  41. if ( self.question.data('submitted') == true ) {
  42. $('.quiz-option', self.question).removeClass('quiz-correct quiz-wrong');
  43. self.question.data('submitted', false);
  44. }
  45. self.explanationVisible(false);
  46. self.hintVisible(false);
  47. },
  48. showAnswer: function(self) {
  49. var self = isundef(self, this);
  50. self.clear();
  51. $('.quiz-answer', self.question).each(function() {
  52. $(this).addClass('quiz-correct');
  53. $(this).find('.quiz-radio').attr('checked', 'checked');
  54. });
  55. self.question.data('submitted', true);
  56. self.explanationVisible(true);
  57. },
  58. hintVisible: function(mode, self) {
  59. var self = isundef(self, this);
  60. mode = (mode) ? 'block' : 'none';
  61. $('.quiz-hint', self.question).each(function() {
  62. $(this).css('display', mode);
  63. // $(this).find('li:lt(' + self.hintsShown + 1, ')').show();
  64. // console.log(self.hintsShown);
  65. // self.hintsShown++;
  66. });
  67. },
  68. toggleHint: function(self) {
  69. var self = isundef(self, this);
  70. $('.quiz-hint', self.question).each(function() {
  71. if ( $(this).css('display') == 'none' )
  72. self.hintVisible(true);
  73. else
  74. self.hintVisible(false);
  75. });
  76. },
  77. explanationVisible: function(mode, self) {
  78. var self = isundef(self, this);
  79. mode = (mode) ? 'block' : 'none';
  80. $('.quiz-explanation', self.question).each(function() {
  81. // commented out to display answer as modal
  82. // $(this).css('display', mode);
  83. $(this).css('display', 'none');
  84. });
  85. },
  86. init: function() {
  87. var self = this;
  88. // added data-numOption to the template
  89. var template = Mustache.compile('<input type="radio" class="quiz-radio" ' +
  90. 'name="quiz-question-{{idQuestion}}-options" value="{{label}}"' +
  91. 'data-numOption="{{numOption}}"' +
  92. 'id="quiz-question-{{idQuestion}}-option-{{numOption}}"/>' +
  93. '<label for="quiz-question-{{idQuestion}}-option-{{numOption}}">{{label}}</label>'
  94. );
  95. /*
  96. var template = Mustache.compile('<label for="quiz-question-{{idQuestion}}-option-{{numOption}}" class="radio">' +
  97. '<input type="radio" class="quiz-radio" ' +
  98. 'name="quiz-question-{{idQuestion}}-options" value="{{label}}"' +
  99. 'data-numOption="{{numOption}}"' +
  100. 'id="quiz-question-{{idQuestion}}-option-{{numOption}}"/>' +
  101. '{{label}}</label>'
  102. );
  103. */
  104. // creates radio buttons for the quiz
  105. var numOption = 0;
  106. $('.quiz-answer, .quiz-option', self.question).each(function() {
  107. var $this = $(this);
  108. $this.addClass('quiz-option');
  109. $this.html(template({
  110. idQuestion: self.idQuestion,
  111. numOption: ++numOption,
  112. label: $this.text()
  113. }));
  114. });
  115. // bind clear to the quiz-clear elements, if any
  116. $('.quiz-clear', self.question).each(function() {
  117. $(this).bind('click.clear', function() { self.clear(self) });
  118. });
  119. // clear correction if there are correct/wrong classes but the options checked changed
  120. $('.quiz-radio', self.question).each(function() {
  121. $(this).bind('click.clearCorrection', function() { self.clearCorrection(self) });
  122. });
  123. // bind makeCorrection to the quiz-submit elements, if any
  124. $('.quiz-submit', self.question).each(function() {
  125. $(this).bind('click.makeCorrection', function() { self.makeCorrection(self) });
  126. });
  127. // bind makeCorrection to the quiz-submit elements, if any
  128. $('.quiz-show-answer', self.question).each(function() {
  129. $(this).bind('click.showAnswer', function() { self.showAnswer(self) });
  130. });
  131. $('.quiz-toggle-hint', self.question).each(function() {
  132. $(this).bind('click.toggleHint', function() { self.toggleHint(self) });
  133. });
  134. self.hintVisible(false);
  135. self.explanationVisible(false);
  136. }
  137. }
  138. QuizMultipleHandler = function (question, idQuestion) {
  139. this.question = question;
  140. this.idQuestion = idQuestion;
  141. }
  142. QuizMultipleHandler.prototype = {
  143. _individual: function(self) {
  144. var self = isundef(self, this);
  145. self.question.data('submitted', true);
  146. var isCorrect = true;
  147. $('.quiz-checkbox', self.question).each(function() {
  148. var $checkbox = $(this);
  149. var $option = $checkbox.parent();
  150. isCorrect = isCorrect &&
  151. ($option.hasClass('quiz-answer') == $checkbox.is(':checked'));
  152. });
  153. $('.quiz-checkbox:checked', self.question).each(function() {
  154. var $checkbox = $(this);
  155. var $option = $checkbox.parent();
  156. if ( $option.hasClass('quiz-answer') == $checkbox.is(':checked') )
  157. $option.addClass('quiz-correct');
  158. else
  159. $option.addClass('quiz-wrong');
  160. });
  161. if ( isCorrect )
  162. self.explanationVisible(true);
  163. },
  164. _whole: function(self) {
  165. var self = isundef(self, this);
  166. self.question.data('submitted', true);
  167. var isCorrect = true;
  168. $('.quiz-checkbox', self.question).each(function() {
  169. var $checkbox = $(this);
  170. var $option = $checkbox.parent();
  171. isCorrect = isCorrect &&
  172. ($option.hasClass('quiz-answer') == $checkbox.is(':checked'));
  173. });
  174. $('.quiz-checkbox:checked', self.question).each(function() {
  175. var $checkbox = $(this);
  176. var $option = $checkbox.parent();
  177. if ( isCorrect )
  178. $option.addClass('quiz-correct');
  179. else
  180. $option.addClass('quiz-wrong');
  181. });
  182. if ( isCorrect )
  183. self.explanationVisible(true);
  184. },
  185. clear: function(self) {
  186. var self = isundef(self, this);
  187. $('.quiz-checkbox', self.question).removeAttr('checked');
  188. self.clearCorrection();
  189. },
  190. clearCorrection: function(self) {
  191. var self = isundef(self, this);
  192. if ( self.question.data('submitted') == true ) {
  193. $('.quiz-option', self.question).removeClass('quiz-correct quiz-wrong');
  194. self.question.data('submitted', false);
  195. }
  196. self.explanationVisible(false);
  197. },
  198. showAnswer: function(self) {
  199. var self = isundef(self, this);
  200. self.clear();
  201. $('.quiz-answer', self.question).each(function() {
  202. var $this = $(this);
  203. $this.addClass('quiz-correct');
  204. $('.quiz-checkbox', $this).each(function() {
  205. $(this).attr('checked', 'checked');
  206. });
  207. });
  208. self.question.data('submitted', true);
  209. self.explanationVisible(true);
  210. },
  211. hintVisible: function(mode, self) {
  212. var self = isundef(self, this);
  213. mode = (mode) ? 'block' : 'none';
  214. $('.quiz-hint', self.question).each(function() {
  215. $(this).css('display', mode);
  216. });
  217. },
  218. toggleHint: function(self) {
  219. var self = isundef(self, this);
  220. $('.quiz-hint', self.question).each(function() {
  221. if ( $(this).css('display') == 'none' )
  222. self.hintVisible(true);
  223. else
  224. self.hintVisible(false);
  225. });
  226. },
  227. explanationVisible: function(mode, self) {
  228. var self = isundef(self, this);
  229. mode = (mode) ? 'block' : 'none';
  230. $('.quiz-explanation', self.question).each(function() {
  231. $(this).css('display', 'none');
  232. // $(this).css('display', mode);
  233. });
  234. },
  235. init: function() {
  236. var self = this;
  237. var template = Mustache.compile('<input type="checkbox" class="quiz-checkbox" ' +
  238. 'id="quiz-question-{{idQuestion}}-option-{{numOption}}"/>' +
  239. '<label for="quiz-question-{{idQuestion}}-option-{{numOption}}">{{label}}</label>');
  240. var numOption = 0;
  241. $('.quiz-answer, .quiz-option', self.question).each(function() {
  242. var $this = $(this);
  243. $this.addClass('quiz-option');
  244. $this.html(template({
  245. idQuestion: self.idQuestion,
  246. numOption: ++numOption,
  247. label: $this.html()
  248. }));
  249. });
  250. if ( self.question.attr('data-individual') != undefined ) {
  251. self.makeCorrection = self._individual;
  252. } else {
  253. self.makeCorrection = self._whole;
  254. }
  255. $('.quiz-clear', self.question).each(function() {
  256. $(this).bind('click.clear', function() { self.clear(self) });
  257. });
  258. $('.quiz-checkbox', self.question).each(function() {
  259. $(this).bind('click.clearCorrection', function() { self.clearCorrection(self) });
  260. });
  261. $('.quiz-submit', self.question).each(function() {
  262. $(this).bind('click.makeCorrection', function() { self.makeCorrection(self) });
  263. });
  264. $('.quiz-show-answer', self.question).each(function() {
  265. $(this).bind('click.showAnswer', function() { self.showAnswer(self) });
  266. });
  267. $('.quiz-toggle-hint', self.question).each(function() {
  268. $(this).bind('click.toggleHint', function() { self.toggleHint(self) });
  269. });
  270. self.hintVisible(false);
  271. self.explanationVisible(false);
  272. }
  273. }
  274. QuizTextHandler = function(question, idQuestion) {
  275. this.question = question;
  276. this.idQuestion = idQuestion;
  277. }
  278. QuizTextHandler.prototype = {
  279. makeCorrection: function(self){
  280. var self = isundef(self, this);
  281. self.clearCorrection();
  282. self.question.data('submitted', true);
  283. $(".quiz-answerbox", self.question).each(function(){
  284. var userAnswer = parseFloat($(this).val());
  285. self.answer = $(this).data("answer");
  286. if (Math.abs(userAnswer - self.answer)/self.answer < 0.02){
  287. self.explanationVisible(true);
  288. $(this).addClass('quiz-correct')
  289. } else {
  290. $(this).addClass('quiz-wrong')
  291. }
  292. });
  293. },
  294. makeCorrection2: function(self) {
  295. var self = isundef(self, this);
  296. self.clearCorrection();
  297. self.question.data('submitted', true);
  298. var userAnswer = '';
  299. $('.quiz-answerbox', self.question).each(function() {
  300. var $answerbox = $(this);
  301. userAnswer = $answerbox.val();
  302. self.answer = $(this).attr("data-answer");
  303. });
  304. if ( !self.caseSensitive ) {
  305. userAnswer = userAnswer.toLowerCase();
  306. if ( self.answer )
  307. self.answer = self.answer.toLowerCase();
  308. }
  309. if ( !self.notrim ) {
  310. userAnswer = $.trim(userAnswer);
  311. self.answer = $.trim(self.answer);
  312. if ( self.answer )
  313. self.answer = self.answer.toLowerCase();
  314. }
  315. if ( userAnswer == self.answer || self.validator(userAnswer) ) {
  316. self.explanationVisible(true);
  317. $('.quiz-answerbox', self.question).each(function() {
  318. $(this).addClass('quiz-correct');
  319. });
  320. } else
  321. $('.quiz-answerbox', self.question).each(function() {
  322. $(this).addClass('quiz-wrong');
  323. });
  324. },
  325. hintVisible: function(mode, self) {
  326. var self = isundef(self, this);
  327. mode = (mode) ? 'block' : 'none';
  328. $('.quiz-hint', self.question).each(function() {
  329. $(this).css('display', mode);
  330. });
  331. },
  332. toggleHint: function(self) {
  333. var self = isundef(self, this);
  334. $('.quiz-hint', self.question).each(function() {
  335. if ( $(this).css('display') == 'none' )
  336. self.hintVisible(true);
  337. else
  338. self.hintVisible(false);
  339. });
  340. },
  341. clear: function(self) {
  342. var self = isundef(self, this);
  343. $('.quiz-answerbox', self.question).val('');
  344. self.clearCorrection();
  345. },
  346. clearCorrection: function(self) {
  347. var self = isundef(self, this);
  348. if ( self.question.data('submitted') == true ) {
  349. $('.quiz-answerbox', self.question).removeClass('quiz-correct quiz-wrong');
  350. self.question.data('submitted', false);
  351. }
  352. self.explanationVisible(false);
  353. },
  354. showAnswer: function(self) {
  355. // if ( self.answer == undefined )
  356. // return;
  357. var self = isundef(self, this);
  358. self.clear();
  359. $('.quiz-answerbox', self.question).each(function() {
  360. var $this = $(this);
  361. self.answer = $this.attr("data-answer");
  362. $this.val(self.answer);
  363. $this.addClass('quiz-correct');
  364. });
  365. self.question.data('submitted', true);
  366. self.explanationVisible(true);
  367. },
  368. explanationVisible: function(mode, self) {
  369. var self = isundef(self, this);
  370. mode = (mode) ? 'block' : 'none';
  371. $('.quiz-explanation', self.question).each(function() {
  372. $(this).css('display', 'none')
  373. // $(this).css('display', mode);
  374. });
  375. },
  376. init: function() {
  377. var self = this;
  378. self.answer = self.question.attr('data-answer');
  379. if ( self.question.attr('data-validator') != undefined )
  380. self.validator = eval("(" + self.question.attr('data-validator') + ")");
  381. self.validator = isundef(self.validator, function(x) { return false; });
  382. self.caseSensitive = self.question.attr('data-sensitive') != undefined;
  383. self.notrim = self.question.attr('data-notrim') != undefined;
  384. $('.quiz-clear', self.question).each(function() {
  385. $(this).bind('click.clear', function() { self.clear(self) });
  386. });
  387. $('.quiz-submit', self.question).each(function() {
  388. $(this).bind('click.makeCorrection', function() { self.makeCorrection(self) });
  389. });
  390. $('.quiz-answerbox', self.question).each(function() {
  391. $(this).bind('change.clearCorrection', function() { self.clearCorrection(self) });
  392. });
  393. $('.quiz-show-answer', self.question).each(function() {
  394. $(this).bind('click.showAnswer', function() { self.showAnswer(self) });
  395. });
  396. $('.quiz-toggle-hint', self.question).each(function() {
  397. $(this).bind('click.toggleHint', function() { self.toggleHint(self) });
  398. });
  399. self.hintVisible(false);
  400. self.explanationVisible(false);
  401. }
  402. }
  403. $.quiz = function($context) {
  404. var self = $.quiz;
  405. // if no context is given, set it as document
  406. $context = ($context == undefined) ? $(document) : $context;
  407. var handler = self.getHandler($context);
  408. // if it doesn't have a handler, check all children for handlers
  409. if ( handler == undefined ) {
  410. for (handler in self.handlers)
  411. $('.' + handler, $context).each(function() {
  412. var $this = $(this);
  413. var newQuiz = new self.handlers[handler]($this, self.getId($this));
  414. self.quizzes.push(newQuiz);
  415. newQuiz.init();
  416. });
  417. } // if it has, make it a quiz
  418. else {
  419. var newQuiz = new self.handlers[handler]($context, self.getId($context));
  420. self.quizzes.push(newQuiz);
  421. newQuiz.init();
  422. }
  423. }
  424. $.quiz.getId = function($element) {
  425. self.counter = (self.counter == undefined) ? 0 : self.counter;
  426. if ( $element.attr('id') != undefined )
  427. return $element.attr('id');
  428. else
  429. return ++self.counter;
  430. }
  431. $.quiz.getHandler = function($context) {
  432. for (var handler in $.quiz.handlers)
  433. if ( $context.hasClass(handler) )
  434. return handler;
  435. return undefined;
  436. }
  437. $.quiz.handlers = {
  438. 'quiz-single': QuizSingleHandler,
  439. 'quiz-multiple': QuizMultipleHandler,
  440. 'quiz-text': QuizTextHandler
  441. };
  442. $.quiz.quizzes = [];
  443. $.fn.quiz = function() {
  444. $.quiz(this);
  445. return this;
  446. }
  447. })(jQuery);