mumuki-laboratory 8.1.3 → 8.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -8
  3. data/app/assets/javascripts/mumuki_laboratory/application/bridge.js +2 -1
  4. data/app/assets/javascripts/mumuki_laboratory/application/button.js +2 -4
  5. data/app/assets/javascripts/mumuki_laboratory/application/codemirror-builder.js +1 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +2 -2
  7. data/app/assets/javascripts/mumuki_laboratory/application/i18n.js +73 -0
  8. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +22 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/kindergarten.js +6 -1
  10. data/app/assets/javascripts/mumuki_laboratory/application/primary.js +5 -3
  11. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +28 -1
  12. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +72 -48
  13. data/app/assets/stylesheets/mumuki_laboratory/application.scss +1 -0
  14. data/app/assets/stylesheets/mumuki_laboratory/application/_codemirror-themes.scss +1 -0
  15. data/app/assets/stylesheets/mumuki_laboratory/application/codemirror-themes/_mu-light.scss +3 -0
  16. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids.scss +4 -0
  17. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids_results.scss +1 -0
  18. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_overlap.scss +0 -4
  19. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_terms.scss +4 -0
  20. data/app/controllers/chapters_controller.rb +9 -5
  21. data/app/controllers/guide_container_controller.rb +2 -7
  22. data/app/helpers/assignment_result_helper.rb +1 -1
  23. data/app/helpers/contextualization_result_helper.rb +0 -8
  24. data/app/helpers/discussions_helper.rb +12 -4
  25. data/app/helpers/editor_tabs_helper.rb +1 -1
  26. data/app/helpers/icons_helper.rb +1 -1
  27. data/app/helpers/links_helper.rb +1 -1
  28. data/app/helpers/menu_bar_helper.rb +9 -1
  29. data/app/helpers/overlapped_buttons_helper.rb +13 -5
  30. data/app/views/book/show.html.erb +1 -1
  31. data/app/views/book_discussions/index.html.erb +3 -1
  32. data/app/views/chapters/show.html.erb +21 -9
  33. data/app/views/complements/show.html.erb +1 -1
  34. data/app/views/discussions/_message.html.erb +2 -2
  35. data/app/views/discussions/index.html.erb +11 -4
  36. data/app/views/exams/show.html.erb +1 -1
  37. data/app/views/exercise_solutions/_kids_level_up.html.erb +1 -1
  38. data/app/views/exercises/_exercise_assignment.html.erb +1 -1
  39. data/app/views/exercises/_read_only.html.erb +1 -1
  40. data/app/views/exercises/show.html.erb +2 -2
  41. data/app/views/layouts/_discussions.html.erb +0 -4
  42. data/app/views/layouts/_guide.html.erb +9 -36
  43. data/app/views/layouts/_guide_container.html.erb +28 -0
  44. data/app/views/layouts/_guide_title_icons.html.erb +9 -0
  45. data/app/views/layouts/_kids.html.erb +4 -4
  46. data/app/views/layouts/_kindergarten.html.erb +5 -5
  47. data/app/views/layouts/_social_media.html.erb +4 -4
  48. data/app/views/layouts/_timer.html.erb +1 -1
  49. data/app/views/layouts/application.html.erb +7 -5
  50. data/app/views/layouts/exercise_inputs/editors/_code.html.erb +1 -6
  51. data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +4 -9
  52. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +1 -1
  53. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +1 -1
  54. data/app/views/layouts/modals/_guide_corollary.html.erb +13 -3
  55. data/app/views/layouts/modals/_kids_results.html.erb +2 -2
  56. data/app/views/layouts/modals/_kindergarten_context.html.erb +3 -3
  57. data/app/views/layouts/modals/_kindergarten_results.html.erb +3 -3
  58. data/app/views/layouts/modals/_kindergarten_results_aborted.html.erb +2 -2
  59. data/app/views/layouts/modals/_level_up.html.erb +1 -1
  60. data/app/views/lessons/show.html.erb +1 -1
  61. data/app/views/users/_edit_user_form.html.erb +1 -1
  62. data/app/views/users/_term.html.erb +1 -1
  63. data/app/views/users/_user_form.html.erb +1 -1
  64. data/lib/mumuki/laboratory/controllers/results_rendering.rb +2 -1
  65. data/lib/mumuki/laboratory/engine.rb +1 -1
  66. data/lib/mumuki/laboratory/locales/en.yml +6 -8
  67. data/lib/mumuki/laboratory/locales/es-CL.yml +6 -3
  68. data/lib/mumuki/laboratory/locales/es.yml +9 -10
  69. data/lib/mumuki/laboratory/locales/pt.yml +6 -8
  70. data/lib/mumuki/laboratory/version.rb +1 -1
  71. data/spec/controllers/exercise_solutions_controller_spec.rb +3 -2
  72. data/spec/dummy/db/schema.rb +50 -1
  73. data/spec/dummy/public/character/animations.json +1 -0
  74. data/{public → spec/dummy/public}/character/kibi/context.svg +0 -0
  75. data/{public → spec/dummy/public}/character/kibi/failure.svg +0 -0
  76. data/{public → spec/dummy/public}/character/kibi/jump.svg +0 -0
  77. data/spec/dummy/public/character/kibi/passed_with_warnings.svg +4 -0
  78. data/{public → spec/dummy/public}/character/kibi/success2_l.svg +0 -0
  79. data/{public → spec/dummy/public}/character/kibi/success_l.svg +0 -0
  80. data/{public → spec/dummy/public}/character/magnifying_glass/apparition.svg +0 -0
  81. data/{public → spec/dummy/public}/character/magnifying_glass/loop.svg +0 -0
  82. data/spec/features/chapters_flow_spec.rb +112 -0
  83. data/spec/features/login_flow_spec.rb +1 -1
  84. data/spec/features/terms_flow_spec.rb +2 -0
  85. data/spec/features/topic_flow_spec.rb +0 -1
  86. data/spec/helpers/icons_helper_spec.rb +3 -3
  87. data/spec/helpers/test_results_rendering_spec.rb +8 -8
  88. data/spec/helpers/with_navigation_spec.rb +14 -14
  89. data/spec/javascripts/bridge-spec.js +2 -2
  90. data/spec/javascripts/csrf-token-spec.js +2 -2
  91. data/spec/javascripts/editors-spec.js +7 -9
  92. data/spec/javascripts/elipsis-spec.js +4 -4
  93. data/spec/javascripts/events-spec.js +7 -7
  94. data/spec/javascripts/exercise-spec.js +7 -8
  95. data/spec/javascripts/gamification-spec.js +2 -2
  96. data/spec/javascripts/global-spec.js +3 -3
  97. data/spec/javascripts/i18n-spec.js +82 -0
  98. data/spec/javascripts/kids-button-spec.js +34 -0
  99. data/spec/javascripts/results-renderers-spec.js +5 -5
  100. data/spec/javascripts/speech-bubble-renderer-spec.js +2 -3
  101. data/spec/javascripts/submissions-store-spec.js +14 -14
  102. data/spec/javascripts/sync-mode-spec.js +3 -3
  103. data/spec/javascripts/timeout-spec.js +2 -2
  104. data/spec/javascripts/timer-spec.js +2 -2
  105. metadata +140 -117
  106. data/spec/dummy/config/database.travis.yml +0 -4
  107. data/spec/features/chapter_spec.rb +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bcc70727fa140c5ad7c313f8b9e9a5e1f9a9e3b383b1add72556f64d4fed4e3
4
- data.tar.gz: 874a6b5d2f8a64249560dfb11f9f743d6f11267e8c73602e0d53abacb8e9c651
3
+ metadata.gz: a7c7290ee8af41b2af9f9d34977a9df9095fd3fb38a74e167bb1678b754cc001
4
+ data.tar.gz: 364699a1f019910da6ccc23d6970d20053a2ea36de3351fd32368c31395df89a
5
5
  SHA512:
6
- metadata.gz: 6c9b7889090c806dd880c5cd9d2730117d2d4927ef18765582b0d79010463f4596bdcf8f65f297b95e0bcec238ec7e36311bffe889d29460191cd79e929afdfb
7
- data.tar.gz: 2304d78fabb8a5268fb33f79737480d94550fdc22c9a94f9cd65f70ca02ed1e2dbb7d78089690503a85b99ec88dd845a70678970997a3bba4a3c644b2e90276e
6
+ metadata.gz: b1f8f15e334cfc4599eab6b8872c829553fe2c92559bc5a22f796a23df711caf233317982ca0c057eaf8e8c0c9ddbc4209e1f3454ac9cfa379f79ce07a154265
7
+ data.tar.gz: f38ef6d9aef9f9d29ff14d9a091c60906438789b124334031bd102e605296fb8d844b5d2a2f97dd094e4586c4dd06a662c149d3f9d1ad6ad70cf5b5f4edf21d5
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
- [![Build Status](https://travis-ci.com/mumuki/mumuki-laboratory.svg?branch=master)](https://travis-ci.com/mumuki/mumuki-laboratory)
1
+ ![Build status](https://github.com/mumuki/mumuki-laboratory/workflows/Test%20and%20deploy/badge.svg?branch=master)
2
2
  [![Code Climate](https://codeclimate.com/github/mumuki/mumuki-laboratory/badges/gpa.svg)](https://codeclimate.com/github/mumuki/mumuki-laboratory)
3
- [![Issue Count](https://codeclimate.com/github/mumuki/mumuki-laboratory/badges/issue_count.svg)](https://codeclimate.com/github/mumuki/mumuki-laboratory)
4
3
 
5
4
  <img width="60%" src="https://raw.githubusercontent.com/mumuki/mumuki-laboratory/master/laboratory-screenshot.png"></img>
6
5
 
@@ -286,6 +285,7 @@ which are granted to be safe and stable.
286
285
  * `updateButtonsVisibility`
287
286
  * `mumuki.submission`
288
287
  * `processSolution`
288
+ * `sendSolution`
289
289
  * `registerContentSyncer`
290
290
  * `mumuki.version`
291
291
 
@@ -482,16 +482,27 @@ mumuki.editors.addCustomSource({
482
482
  });
483
483
  ```
484
484
 
485
- #### 2.5 Optional: Sending your solution to the server programmatically
485
+ #### 2.5 Optional: Triggering submission processing programmatically
486
486
 
487
- Your solution will be automatically sent to the client when the submit button is pressed. However,
488
- if you need to trigger submission process programmatically, call `mumuki.submission.processSolution`:
487
+ Your solution will be automatically sent to the client and processed when the submit button is pressed.
488
+ However, if you need to trigger the whole submission process programmatically,
489
+ call `mumuki.submission.processSolution`:
489
490
 
490
491
  ```javascript
491
492
  mumuki.submission.processSolution({solution: {content: /* ... */}});
492
493
  ```
493
494
 
494
- #### 2.6 Optional: customizing your submit button
495
+ #### 2.6 Optional: Sending your solution to the server programmatically
496
+
497
+ Your solution will be automatically sent to the client when the submit button is pressed, as part of the
498
+ solution processing. However, if you just need to send your submission to the server programmatically,
499
+ call `mumuki.submission.sendSolution`:
500
+
501
+ ```javascript
502
+ mumuki.submission.sendSolution({solution: {content: /* ... */}});
503
+ ```
504
+
505
+ #### 2.7 Optional: customizing your submit button
495
506
 
496
507
  You can alternatively override the default submit button UI and behaviour, by replacing it with a custom component. In order to
497
508
  do that, override the `.mu-submit-button` or the kids-specific `.mu-kids-submit-button`:
@@ -506,7 +517,7 @@ However, doing this is tricky, since you will need to manually update the UI and
506
517
  * `mumuki.bridge.Laboratory.runTests`
507
518
  * `mumuki.updateProgressBarAndShowModal`
508
519
 
509
- #### 2.7 Register kids scalers
520
+ #### 2.8 Register kids scalers
510
521
 
511
522
  Kids layouts have some special areas:
512
523
 
@@ -525,7 +536,7 @@ mumuki.kids.registerBlocksAreaScaler(($blocks) => {
525
536
  });
526
537
  ```
527
538
 
528
- #### 2.8 Notify when your assets have been loaded
539
+ #### 2.9 Notify when your assets have been loaded
529
540
 
530
541
  In order to remove loading spinners, you will need to call `mumuki.assetsLoadedFor` when your code is ready.
531
542
 
@@ -113,7 +113,8 @@ mumuki.bridge = (() => {
113
113
  * @returns {SubmissionResult}
114
114
  */
115
115
  _preRenderResult(result) {
116
- result.class_for_progress_list_item = mumuki.renderers.progressListItemClassForStatus(result.status, true);
116
+ result.class_for_progress_list_item = mumuki.renderers.results.progressListItemClassForStatus(result.status, true);
117
+ result.title_html = mumuki.renderers.results.translatedTitleHtml(result.status, result.in_gamified_context);
117
118
  return result;
118
119
  }
119
120
  }
@@ -49,7 +49,6 @@ mumuki.Button = class {
49
49
  */
50
50
  wait() {
51
51
  this.$button.off('click');
52
-
53
52
  this.setWaiting();
54
53
  }
55
54
 
@@ -77,7 +76,6 @@ mumuki.Button = class {
77
76
  */
78
77
  continue() {
79
78
  this.$button.off('click');
80
-
81
79
  this.enable();
82
80
 
83
81
  this.$button.on('click', this.main);
@@ -106,11 +104,11 @@ mumuki.Button = class {
106
104
  }
107
105
 
108
106
  setWaitingText () {
109
- this.$button.html('<i class="fa fa-refresh fa-spin"></i> ' + this.$button.attr('data-waiting'));
107
+ this.$button.html('<i class="fas fa-sync-alt fa-spin"></i> ' + this.$button.attr('data-waiting'));
110
108
  }
111
109
 
112
110
  setRetryText() {
113
- this.$button.html('<i class="fa fa-undo"></i>');
111
+ this.$button.html('<i class="fas fa-undo"></i>');
114
112
  }
115
113
 
116
114
  setOriginalContent () {
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  function submit() {
3
3
  $('body').removeClass('fullscreen');
4
- $('.editor-resize .fa-stack-1x').removeClass('fa-compress').addClass('fa-expand');
4
+ $('.editor-resize .fas').toggleClass('fa-expand fa-compress');
5
5
  $('.btn-submit').click();
6
6
  }
7
7
 
@@ -36,7 +36,7 @@ mumuki.page.editors = [];
36
36
 
37
37
  function toggleFullscreen() {
38
38
  $('body').toggleClass('fullscreen');
39
- $('.editor-resize .fa-stack-1x').toggleClass('fa-expand').toggleClass('fa-compress');
39
+ $('.editor-resize .fas').toggleClass('fa-expand fa-compress');
40
40
  }
41
41
 
42
42
  function indentWithSpaces(cm) {
@@ -62,7 +62,7 @@ mumuki.page.editors = [];
62
62
 
63
63
  function setEditorLanguage(editor, language) {
64
64
  editor.setOption("mode", language);
65
- editor.setOption('theme', 'default ' + language);
65
+ editor.setOption('theme', 'mu-light ' + language);
66
66
  }
67
67
 
68
68
  function syncContent(){
@@ -0,0 +1,73 @@
1
+ mumuki.I18n = (() => {
2
+
3
+ const translations = {
4
+ 'es': {
5
+ aborted: () => "Ups, no pudimos evaluar tu solución",
6
+ errored: () => "¡Ups! Tu solución no se puede ejecutar",
7
+ failed: () => "Tu solución no pasó las pruebas",
8
+ passed: () => "¡Muy bien! Tu solución pasó todas las pruebas",
9
+ passed_with_warnings: () => "Tu solución funcionó, pero hay cosas que mejorar",
10
+ pending: () => "Pendiente",
11
+ skipped: () => "Venís aprendiendo muy bien, por lo que aprobaste este ejercicio",
12
+ },
13
+ 'es-CL': {
14
+ aborted: () => "Ups, no pudimos evaluar tu solución",
15
+ errored: () => "¡Ups! Tu solución no se puede ejecutar",
16
+ failed: () => "Tu solución no pasó las pruebas",
17
+ passed: () => "¡Muy bien! Tu solución pasó todas las pruebas",
18
+ passed_with_warnings: () => "Tu solución funcionó, pero hay cosas que mejorar",
19
+ pending: () => "Pendiente",
20
+ skipped: () => "Vienes aprendiendo muy bien, por lo que aprobaste este ejercicio",
21
+ },
22
+ 'en': {
23
+ aborted: () => "Oops, we couldn't evaluate your solution",
24
+ errored: () => "Oops, your solution didn't work",
25
+ failed: () => "Oops, something went wrong",
26
+ passed: () => "Everything is in order! Your solution passed all our tests!",
27
+ passed_with_warnings: () => "It worked, but you can do better",
28
+ pending: () => "Pending",
29
+ skipped: () => "You are doing very well, so you've passed this exercise",
30
+ },
31
+ 'pt': {
32
+ aborted: () => "Opa, não pudemos avaliar sua solução",
33
+ errored: () => "Opa! Sua solução não pode ser executada",
34
+ failed: () => "Sua solução não passou as provas",
35
+ passed: () => "Muito bem! Sua solução passou todos os testes",
36
+ passed_with_warnings: () => "Sua solução funcionou, mas há coisas para melhorar",
37
+ pending: () => "Pendente",
38
+ skipped: () => "Você está aprendendo muito bem e passou neste exercício",
39
+ }
40
+ }
41
+
42
+ return new class {
43
+
44
+ translate(key, data = {}) {
45
+ const translationValue = this._translationValue(key);
46
+ switch (typeof(translationValue)) {
47
+ case 'string': return translationValue;
48
+ case 'function': return translationValue(data);
49
+ default: return `Translation missing: ${mumuki.locale}, \`${key}\``;
50
+ }
51
+ }
52
+
53
+ t(key, data = {}) {
54
+ return mumuki.I18n.translate(key, data);
55
+ }
56
+
57
+ register(translationsToOverride) {
58
+ const locales = Object.keys(translations);
59
+ locales.forEach((it) => translations[it] = Object.assign(translations[it], translationsToOverride[it]));
60
+ }
61
+
62
+ _prefixTranslationKey(key) {
63
+ this._prefix = $('[data-i18n-prefix]');
64
+ return this._prefix.get(0) ? `${this._prefix.data('i18n-prefix')}_${key}` : key;
65
+ }
66
+
67
+ _translationValue(key) {
68
+ let translationLocale = translations[mumuki.locale];
69
+ return translationLocale && (translationLocale[this._prefixTranslationKey(key)] || translationLocale[key]);
70
+ }
71
+
72
+ }
73
+ })();
@@ -1,3 +1,7 @@
1
+ mumuki.load(() => {
2
+ mumuki.isKidsExercise = () => $('.mu-kids-exercise').length > 0;
3
+ })
4
+
1
5
  mumuki.Kids = class {
2
6
 
3
7
  constructor() {
@@ -11,8 +15,9 @@ mumuki.Kids = class {
11
15
  // ================
12
16
 
13
17
  initialize() {
14
- this.submitButton = new mumuki.submission.SubmitButton($('#kids-btn-retry'), $('.submission_control'));
18
+ this.submitButton = new mumuki.submission.KidsSubmitButton($('#kids-btn-retry'), $('.submission_control'));
15
19
  this.resultActions = {};
20
+ this.$overlay = $('.mu-kids-overlay');
16
21
  this.$states = $('.mu-kids-states');
17
22
  this.$state = $('.mu-kids-state');
18
23
  this.$blocks = $('.mu-kids-blocks');
@@ -27,6 +32,14 @@ mumuki.Kids = class {
27
32
  this.$submissionResult = $('.submission-results');
28
33
  mumuki.gamification.currentLevelProgression.registerLevelUpAction(this.levelUpAction);
29
34
  mumuki.gamification.currentLevelProgression.registerGainedExperienceAction(this.gainedExperienceAction);
35
+ this.$resultsModal.on('hidden.bs.modal', this.resetExerciseIfSubmitless);
36
+ this.$resultsAbortedModal.on('hidden.bs.modal', this.resetExerciseIfSubmitless);
37
+ }
38
+
39
+ resetExerciseIfSubmitless() {
40
+ if ($('.mu-submitless-exercise').get(0)) {
41
+ mumuki.kids.submitButton.continue();
42
+ }
30
43
  }
31
44
 
32
45
  gainedExperienceAction() {
@@ -56,6 +69,14 @@ mumuki.Kids = class {
56
69
  this.$resultsAbortedModal.modal();
57
70
  }
58
71
 
72
+ showOverlay() {
73
+ this.$overlay.show();
74
+ }
75
+
76
+ hideOverlay() {
77
+ this.$overlay.hide();
78
+ }
79
+
59
80
  // ==================
60
81
  // == Hook Methods ==
61
82
  // ==================
@@ -13,7 +13,7 @@ mumuki.load(() => {
13
13
  this.$contextModalButton = new mumuki.Button($('#mu-kids-context .mu-kids-modal-button.mu-close'));
14
14
 
15
15
  this.resultActions.passed = this._showSuccessPopup.bind(this);
16
- this.resultActions.passed_with_warnings = this._showSuccessPopup.bind(this);
16
+ this.resultActions.passed_with_warnings = this._showPassedWithWarnings.bind(this);
17
17
  this.resultActions.failed = this._showFailurePopup.bind(this);
18
18
  this.resultActions.errored = this._showFailurePopup.bind(this);
19
19
  this.resultActions.pending = this._showFailurePopup.bind(this);
@@ -50,6 +50,10 @@ mumuki.load(() => {
50
50
  this.showNonAbortedPopup(data, 'success2_l');
51
51
  }
52
52
 
53
+ _showPassedWithWarnings(data) {
54
+ this.showNonAbortedPopup(data, 'passed_with_warnings');
55
+ }
56
+
53
57
  _showFailurePopup(data) {
54
58
  this.showNonAbortedPopup(data, 'failure');
55
59
  }
@@ -78,6 +82,7 @@ mumuki.load(() => {
78
82
 
79
83
  restart() {
80
84
  mumuki.presenterCharacter.playAnimation('talk', this.$bubbleCharacterAnimation);
85
+ this.hideOverlay();
81
86
  }
82
87
 
83
88
  // =======================
@@ -16,7 +16,6 @@ mumuki.load(() => {
16
16
  super.initialize();
17
17
  this.$characterSpeechBubble = $('.mu-kids-character-speech-bubble');
18
18
  this.$characterSpeechBubbleNormal = this.$characterSpeechBubble.children('.mu-kids-character-speech-bubble-normal');
19
- this.$overlay = $('.mu-kids-overlay');
20
19
  this.$contextModalButton = new mumuki.Button($('.mu-kids-context .modal-footer button'));
21
20
 
22
21
  this._paragraphHeight = undefined;
@@ -58,6 +57,10 @@ mumuki.load(() => {
58
57
  this.showNonAbortedPopup(data, 'success2_l', 4000);
59
58
  }
60
59
 
60
+ _showPassedWithWarnings(data) {
61
+ this.showNonAbortedPopup(data, 'passed_with_warnings', 4000);
62
+ }
63
+
61
64
  _showFailurePopup(data) {
62
65
  this._showOnCharacterBubble(data);
63
66
  }
@@ -137,6 +140,7 @@ mumuki.load(() => {
137
140
  const $bubble = this.$characterSpeechBubble;
138
141
  Object.keys(this.resultActions).forEach($bubble.removeClass.bind($bubble));
139
142
  mumuki.presenterCharacter.playAnimation('talk', this.$bubbleCharacterAnimation);
143
+ this.hideOverlay();
140
144
  }
141
145
 
142
146
  // =======================
@@ -150,7 +154,6 @@ mumuki.load(() => {
150
154
  $bubble.find('.mu-kids-character-speech-bubble-failed').hide();
151
155
  $bubble.find('.mu-kids-discussion-link').remove();
152
156
  Object.keys(this.resultActions).forEach($bubble.removeClass.bind($bubble));
153
- this.$overlay.hide();
154
157
  }
155
158
 
156
159
  _showMessageOnCharacterBubble(data) {
@@ -158,7 +161,6 @@ mumuki.load(() => {
158
161
  renderer.setDiscussionsLinkHtml($('#mu-kids-discussion-link-html').html());
159
162
  renderer.setResponseData(data);
160
163
  renderer.render();
161
- this.$overlay.show();
162
164
  }
163
165
 
164
166
  _showOnCharacterBubble(data) {
@@ -35,6 +35,32 @@ mumuki.renderers.results = (() => {
35
35
  }
36
36
  }
37
37
 
38
+ /**
39
+ * @param {SubmissionStatus} status
40
+ * @param {Boolean} isGamifiedContext
41
+ * @returns {string}
42
+ */
43
+ function translatedTitleHtml(status, isGamifiedContext) {
44
+ return `
45
+ <h4 class="text-${classForStatus(status)} %>">
46
+ <strong><i class="fa-fw fas ${iconForStatus(status)}"></i> ${mumuki.I18n.t(status)}</strong>
47
+ ${gamifiedContextHtml(isGamifiedContext)}
48
+ </h4>
49
+ `
50
+ }
51
+
52
+ /**
53
+ * @param {Boolean} isGamifiedContext
54
+ * @returns {string}
55
+ */
56
+ function gamifiedContextHtml(isGamifiedContext) {
57
+ return (!isGamifiedContext) ? '' : `
58
+ <strong><small class="text-success">
59
+ <span class="mu-experience"></span>
60
+ </small></strong>
61
+ `
62
+ }
63
+
38
64
 
39
65
  /**
40
66
  * @param {SubmissionStatus} status
@@ -48,7 +74,8 @@ mumuki.renderers.results = (() => {
48
74
  return {
49
75
  classForStatus,
50
76
  iconForStatus,
51
- progressListItemClassForStatus
77
+ progressListItemClassForStatus,
78
+ translatedTitleHtml
52
79
  };
53
80
  })();
54
81
 
@@ -55,8 +55,50 @@ mumuki.submission = (() => {
55
55
  this.preventClick();
56
56
  }
57
57
  }
58
+
59
+ solutionSender(bridge, solution) {
60
+ return bridge._submitSolution(solution);
61
+ }
62
+
63
+ solutionProcessor(bridge, $submissionsResults, solution) {
64
+ const resultsBox = new ResultsBox($submissionsResults);
65
+ this.disable();
66
+ this.setWaitingText();
67
+ resultsBox.waiting();
68
+ return this.solutionSender(bridge, solution)
69
+ .done((data) => resultsBox.success(data, this))
70
+ .fail(() => resultsBox.error(this))
71
+ .always((data) => {
72
+ $(document).renderMuComponents();
73
+ resultsBox.done(data, this);
74
+ });
75
+ }
58
76
  }
59
77
 
78
+ class KidsSubmitButton extends SubmitButton {
79
+
80
+ wait() {
81
+ mumuki.kids.showOverlay();
82
+ super.wait();
83
+ }
84
+
85
+ continue() {
86
+ mumuki.kids.hideOverlay();
87
+ super.continue();
88
+ }
89
+
90
+ solutionProcessor(bridge, $submissionsResults, solution) {
91
+ this.wait();
92
+ return this.solutionSender(bridge, solution)
93
+ .then((data) => mumuki.kids.showResult(data))
94
+ .always((data) => {
95
+ this.ready(() => {
96
+ mumuki.kids.restart();
97
+ this.continue();
98
+ });
99
+ });
100
+ }
101
+ }
60
102
 
61
103
  // ==========
62
104
  // Processing
@@ -67,14 +109,28 @@ mumuki.submission = (() => {
67
109
  * restoring buttons state.
68
110
  *
69
111
  * The actual implementation of this method depends on contextual {@link _solutionProcessor}, which can
70
- * be configured using {@link _registerSolutionProcessor}. Currently there are only two available processors -
71
- * {@link _kidsSolutionProcessor} and {@link _classicSolutionProcessor} - which are automatically choosen depending
72
- * on the exercise DOM.
112
+ * be configured using {@link _registerSolutionProcessor}. Currently there are only two available processors
113
+ * which are automatically choosen depending on the exercise DOM.
73
114
  *
74
115
  * @param {Submission} solution
75
116
  */
76
117
  function processSolution(solution) {
77
- mumuki.submission._solutionProcessor(solution);
118
+ return mumuki.submission._solutionProcessor(solution);
119
+ }
120
+
121
+ /**
122
+ * Just sends solution to the server without any further processing afterwards.
123
+ *
124
+ * Consider using {@link processSolution} instead if you want the whole functionality (making buttons wait, sending to server, rendering results,
125
+ * restoring buttons state).
126
+ * The implementation of this method isn't contextual, it always sends the solution using bridge module.
127
+ *
128
+ * @see mumuki.bridge
129
+ *
130
+ * @param {Submission} solution
131
+ */
132
+ function sendSolution(solution) {
133
+ return mumuki.submission._solutionSender(solution);
78
134
  }
79
135
 
80
136
  /**
@@ -84,53 +140,19 @@ mumuki.submission = (() => {
84
140
  * and should normally not be called by runners editor, but is exposed
85
141
  * for further non-standard customizations.
86
142
  *
87
- * @param {({solution: object}) => void} processor
143
+ * @param {SubmitButton} submitButton
144
+ * @param {$ElementType} $submissionsResults
145
+ * @param {mumuki.bridge} bridge
88
146
  */
89
- function _registerSolutionProcessor(processor) {
90
- mumuki.submission._solutionProcessor = processor;
91
- }
92
-
93
- /** Processor for kids layouts */
94
- function _kidsSolutionProcessor(bridge, submitButton) {
95
- return (solution) => {
96
- submitButton.wait();
97
- bridge._submitSolution(solution).always(function (data) {
98
- submitButton.ready(() => {
99
- mumuki.kids.restart();
100
- submitButton.continue();
101
- });
102
- mumuki.kids.showResult(data);
103
- });
104
- };
105
- }
106
-
107
- /** Processor for non-kids layouts */
108
- function _classicSolutionProcessor(bridge, submitButton, resultsBox) {
109
- return (solution) => {
110
- submitButton.disable();
111
- submitButton.setWaitingText();
112
- resultsBox.waiting();
113
- bridge._submitSolution(solution).done(function (data) {
114
- resultsBox.success(data, submitButton);
115
- }).fail(function () {
116
- resultsBox.error(submitButton);
117
- }).always(function (data) {
118
- $(document).renderMuComponents();
119
- resultsBox.done(data, submitButton);
120
- });
121
- };
147
+ function _registerSolutionProcessor(submitButton, $submissionsResults, bridge) {
148
+ mumuki.submission._solutionSender = submitButton.solutionSender.bind(submitButton, bridge);
149
+ mumuki.submission._solutionProcessor = submitButton.solutionProcessor.bind(submitButton, bridge, $submissionsResults);
122
150
  }
123
151
 
124
152
  /** Selects the most appropriate solution processor */
125
153
  function _selectSolutionProcessor(submitButton, $submissionsResults) {
126
154
  const bridge = new mumuki.bridge.Laboratory();
127
- let processor;
128
- if ($('.mu-kids-exercise').length) {
129
- processor = _kidsSolutionProcessor(bridge, submitButton);
130
- } else {
131
- processor = _classicSolutionProcessor(bridge, submitButton, new ResultsBox($submissionsResults));
132
- }
133
- mumuki.submission._registerSolutionProcessor(processor);
155
+ mumuki.submission._registerSolutionProcessor(submitButton, $submissionsResults, bridge);
134
156
  }
135
157
 
136
158
 
@@ -143,8 +165,8 @@ mumuki.submission = (() => {
143
165
  if (!$submissionsResults) return;
144
166
 
145
167
  const $btnSubmit = $('.btn-submit');
146
- const submitButton = new SubmitButton($btnSubmit, $('.submission_control'));
147
-
168
+ const buttonClass = mumuki.isKidsExercise() ? KidsSubmitButton : SubmitButton;
169
+ const submitButton = new buttonClass($btnSubmit, $('.submission_control'));
148
170
  mumuki.submission._selectSolutionProcessor(submitButton, $submissionsResults);
149
171
 
150
172
  submitButton.start(() => {
@@ -154,7 +176,6 @@ mumuki.submission = (() => {
154
176
  submitButton.checkAttemptsLeft();
155
177
  });
156
178
 
157
-
158
179
  /**
159
180
  * This module contains methods for submitting solution in at high level, dealing with network communication,
160
181
  * and layout-sensitive UI updates. It is intended to be both used internally by standard editors and by runners
@@ -171,10 +192,13 @@ mumuki.submission = (() => {
171
192
  */
172
193
  return {
173
194
  processSolution,
195
+ sendSolution,
196
+
174
197
  _registerSolutionProcessor,
175
198
  _selectSolutionProcessor,
176
199
 
177
200
  animateTimeoutError,
178
201
  SubmitButton,
202
+ KidsSubmitButton,
179
203
  };
180
204
  })();