editor.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. (function ($) {
  2. var QUEUE = MathJax.Hub.queue; // shorthand for the queue
  3. var math = null; // the element jax for the math output
  4. var trigger_update = true;
  5. var input_textarea = $('#MathInput');
  6. input_textarea.change(function(){ trigger_update = true; })
  7. .keyup(function(){ trigger_update = true; });
  8. $('#input').click(function(){
  9. input_textarea.focus();
  10. });
  11. // Get the element jax when MathJax has produced it.
  12. QUEUE.Push(function() {
  13. // The onchange event handler that typesets the math entered
  14. // by the user. Hide the box, then typeset, then show it again
  15. // so we don't see a flash as the math is cleared and replaced.
  16. var update_math = function(tex) {
  17. var parts = tex.split('\n');
  18. var math_container = $('#math'),
  19. math_lines = math_container.find('div.box script');
  20. // Select all mathjax instances which are inside a div.box element
  21. var mathjax_instances = [];
  22. var all_instances = MathJax.Hub.getAllJax('math');
  23. for (var i = 0; i < all_instances.length; i++) {
  24. var elem = all_instances[i];
  25. if ($('#' + elem.inputID).parent().hasClass('box'))
  26. mathjax_instances.push(elem);
  27. }
  28. var real_lines = 0,
  29. updated_line = -1;
  30. for (var p = 0; p < parts.length; p++) {
  31. if (!parts[p])
  32. continue;
  33. // Check if we want to update an existing line or append the
  34. // line.
  35. if (real_lines < math_lines.length) {
  36. var elem = mathjax_instances[real_lines];
  37. // Update the line when the input is modified.
  38. if (elem.originalText != parts[p]) {
  39. updated_line = real_lines;
  40. QUEUE.Push(['Text', elem, parts[p]]);
  41. }
  42. if (updated_line > -1) {
  43. // Remove the out-of-date status information. This will
  44. // be done from now on for all remaining lines, whether
  45. // they are up-to-date or not.
  46. $(math_lines[real_lines]).parent()
  47. .removeClass('wrong').removeClass('correct');
  48. }
  49. } else {
  50. var line = '`' + parts[p] + '`',
  51. elem = $('<div class="box"/>').text(line);
  52. math_container.append(elem);
  53. QUEUE.Push(['Typeset', MathJax.Hub, elem[0]]);
  54. }
  55. real_lines++;
  56. }
  57. QUEUE.Push(function() {
  58. // Remove out-dated mathematical lines.
  59. for (var p = real_lines; p < math_lines.length; p++)
  60. $(math_lines[p].parentNode).remove();
  61. // Remove old hints, given that at least one line is updated.
  62. // Iterate over the DOM nodes until the updated line is found,
  63. // and remove all following hint nodes. Note that if there is
  64. // no line updated, all hints not directly following the last
  65. // line are removed.
  66. var elems = $('#math div');
  67. if(updated_line == -1)
  68. updated_line = real_lines;
  69. for(var i = 0, lines = 0, hints = 0; i < elems.length; i++) {
  70. var elem = $(elems[i]);
  71. if (lines > updated_line || hints >= updated_line) {
  72. if (elem.hasClass('hint'))
  73. elem.remove();
  74. } else if (elem.hasClass('hint'))
  75. hints++;
  76. else if (elem.hasClass('box'))
  77. lines++;
  78. }
  79. });
  80. }
  81. window.update_math = function() {
  82. if (trigger_update) {
  83. trigger_update = false;
  84. update_math(input_textarea.val());
  85. }
  86. };
  87. setInterval(window.update_math, 100);
  88. });
  89. var loader = $('#loader');
  90. window.show_loader = function() {
  91. loader.show().css('display', 'inline-block');
  92. };
  93. window.hide_loader = function() {
  94. loader.hide();
  95. };
  96. window.append_hint = function(hint) {
  97. $('#math div').last().filter('.hint').remove();
  98. var elem = $('<div class=hint/>');
  99. elem.text(hint);
  100. $('#math').append(elem);
  101. QUEUE.Push(['Typeset', MathJax.Hub, elem[0]]);
  102. };
  103. window.append_input = function(input) {
  104. input_textarea.val(input_textarea.val() + '\n' + input);
  105. };
  106. window.answer_input = function() {
  107. show_loader();
  108. // TODO: disable input box and enable it when this ajax request is done
  109. // (on failure and success).
  110. $.post('/answer', {data: input_textarea.val()}, function(response) {
  111. if (!response)
  112. return;
  113. if ('steps' in response) {
  114. for(i = 0; i < response.steps.length; i++) {
  115. cur = response.steps[i];
  116. if ('step' in cur)
  117. window.append_input(cur.step);
  118. if('hint' in cur)
  119. window.append_hint(cur.hint);
  120. trigger_update = true;
  121. window.update_math();
  122. }
  123. }
  124. if('hint' in response)
  125. window.append_hint(response.hint);
  126. input_textarea.focus();
  127. hide_loader();
  128. }, 'json').error(function(e) {
  129. console.log('error:', e);
  130. hide_loader();
  131. });
  132. };
  133. window.hint_input = function() {
  134. show_loader();
  135. // TODO: disable input box and enable it when this ajax request is done
  136. // (on failure and success).
  137. $.post('/hint', {data: input_textarea.val()}, function(response) {
  138. if (!response)
  139. return;
  140. window.append_hint(response.hint);
  141. input_textarea.focus();
  142. hide_loader();
  143. }, 'json').error(function(e) {
  144. console.log('error:', e);
  145. hide_loader();
  146. });
  147. };
  148. window.step_input = function() {
  149. show_loader();
  150. // TODO: disable input box and enable it when this ajax request is done
  151. // (on failure and success).
  152. $.post('/step', {data: input_textarea.val()}, function(response) {
  153. if (!response)
  154. return;
  155. if ('step' in response) {
  156. window.append_input(response.step);
  157. trigger_update = true;
  158. }
  159. if('hint' in response)
  160. window.append_hint(response.hint);
  161. input_textarea.focus();
  162. hide_loader();
  163. }, 'json').error(function(e) {
  164. console.log('error:', e);
  165. hide_loader();
  166. });
  167. };
  168. window.validate_input = function() {
  169. show_loader();
  170. // TODO: disable input box and enable it when this ajax request is done
  171. // (on failure and success).
  172. $.post('/validate', {data: input_textarea.val()}, function(response) {
  173. if (!response)
  174. return;
  175. var math_container = $('#math'),
  176. math_lines = math_container.find('div.box');
  177. var i = 0;
  178. // Mark every line as correct (and remove previous class names).
  179. for(; i < math_lines.length && i <= response.validated; i++)
  180. $(math_lines[i]).removeClass('wrong').addClass('correct');
  181. if (i < math_lines.length) {
  182. // Mark the current line as wrong.
  183. $(math_lines[i]).removeClass('correct').addClass('wrong');
  184. // Remove the status indicator from the remaining lines.
  185. for(i += 1; i < math_lines.length; i++)
  186. $(math_lines[i]).removeClass('correct')
  187. .removeClass('wrong');
  188. }
  189. hide_loader();
  190. }, 'json').error(function(e) {
  191. console.log('error:', e);
  192. hide_loader();
  193. });
  194. };
  195. window.clear_input = function() {
  196. input_textarea.val('');
  197. $('#math .box,#math .hint').remove();
  198. trigger_update = true;
  199. hide_loader();
  200. };
  201. })(jQuery);