mumuki-laboratory 8.2.0 → 8.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) 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 +0 -2
  5. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +1 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/i18n.js +73 -0
  7. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +22 -1
  8. data/app/assets/javascripts/mumuki_laboratory/application/kindergarten.js +6 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/primary.js +5 -3
  10. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +28 -1
  11. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +72 -48
  12. data/app/assets/stylesheets/mumuki_laboratory/application.scss +1 -0
  13. data/app/assets/stylesheets/mumuki_laboratory/application/_codemirror-themes.scss +1 -0
  14. data/app/assets/stylesheets/mumuki_laboratory/application/codemirror-themes/_mu-light.scss +3 -0
  15. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +31 -8
  16. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids.scss +2 -3
  17. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids_results.scss +1 -0
  18. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_terms.scss +4 -0
  19. data/app/controllers/application_controller.rb +1 -1
  20. data/app/controllers/chapters_controller.rb +9 -5
  21. data/app/controllers/exam_authorization_requests_controller.rb +26 -0
  22. data/app/controllers/exam_registrations_controller.rb +6 -0
  23. data/app/controllers/guide_container_controller.rb +2 -7
  24. data/app/helpers/application_helper.rb +4 -0
  25. data/app/helpers/contextualization_result_helper.rb +0 -8
  26. data/app/helpers/discussions_helper.rb +8 -0
  27. data/app/helpers/icons_helper.rb +3 -11
  28. data/app/helpers/menu_bar_helper.rb +6 -2
  29. data/app/helpers/overlapped_buttons_helper.rb +10 -6
  30. data/app/helpers/progress_bar_helper.rb +2 -2
  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 +7 -7
  35. data/app/views/discussions/index.html.erb +11 -4
  36. data/app/views/exam_authorization_requests/show.html.erb +17 -0
  37. data/app/views/exam_registrations/show.html.erb +37 -0
  38. data/app/views/exams/show.html.erb +1 -1
  39. data/app/views/exercises/show.html.erb +2 -2
  40. data/app/views/layouts/_discussions.html.erb +0 -4
  41. data/app/views/layouts/_guide.html.erb +4 -33
  42. data/app/views/layouts/_guide_container.html.erb +28 -0
  43. data/app/views/layouts/_guide_title_icons.html.erb +9 -0
  44. data/app/views/layouts/_progress_bar.html.erb +9 -7
  45. data/app/views/layouts/_progress_listing.html.erb +5 -5
  46. data/app/views/layouts/_social_media.html.erb +2 -2
  47. data/app/views/layouts/application.html.erb +4 -7
  48. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +1 -1
  49. data/app/views/layouts/modals/_kindergarten_results_aborted.html.erb +1 -1
  50. data/app/views/lessons/show.html.erb +1 -1
  51. data/app/views/notifications/_discussion.html.erb +1 -0
  52. data/app/views/notifications/_dropdown.html.erb +13 -0
  53. data/app/views/notifications/_exam_authorization_request.html.erb +1 -0
  54. data/app/views/notifications/_exam_registration.html.erb +1 -0
  55. data/app/views/notifications/_message.html.erb +1 -0
  56. data/app/views/users/_term.html.erb +1 -1
  57. data/config/routes.rb +3 -0
  58. data/lib/mumuki/laboratory/controllers/notifications.rb +3 -22
  59. data/lib/mumuki/laboratory/controllers/results_rendering.rb +2 -1
  60. data/lib/mumuki/laboratory/locales/en.yml +31 -22
  61. data/lib/mumuki/laboratory/locales/es-CL.yml +23 -9
  62. data/lib/mumuki/laboratory/locales/es.yml +30 -22
  63. data/lib/mumuki/laboratory/locales/pt.yml +25 -16
  64. data/lib/mumuki/laboratory/version.rb +1 -1
  65. data/spec/controllers/exam_authorization_requests_controller_spec.rb +40 -0
  66. data/spec/controllers/exam_registrations_controller_spec.rb +19 -0
  67. data/spec/controllers/exercise_solutions_controller_spec.rb +3 -2
  68. data/spec/dummy/db/schema.rb +50 -1
  69. data/spec/dummy/public/character/animations.json +1 -0
  70. data/{public → spec/dummy/public}/character/kibi/context.svg +0 -0
  71. data/{public → spec/dummy/public}/character/kibi/failure.svg +0 -0
  72. data/{public → spec/dummy/public}/character/kibi/jump.svg +0 -0
  73. data/spec/dummy/public/character/kibi/passed_with_warnings.svg +4 -0
  74. data/{public → spec/dummy/public}/character/kibi/success2_l.svg +0 -0
  75. data/{public → spec/dummy/public}/character/kibi/success_l.svg +0 -0
  76. data/{public → spec/dummy/public}/character/magnifying_glass/apparition.svg +0 -0
  77. data/{public → spec/dummy/public}/character/magnifying_glass/loop.svg +0 -0
  78. data/spec/features/chapters_flow_spec.rb +112 -0
  79. data/spec/features/login_flow_spec.rb +1 -1
  80. data/spec/features/notifications_flow_spec.rb +46 -0
  81. data/spec/features/topic_flow_spec.rb +0 -1
  82. data/spec/javascripts/bridge-spec.js +2 -2
  83. data/spec/javascripts/csrf-token-spec.js +2 -2
  84. data/spec/javascripts/editors-spec.js +7 -9
  85. data/spec/javascripts/elipsis-spec.js +4 -4
  86. data/spec/javascripts/events-spec.js +7 -7
  87. data/spec/javascripts/exercise-spec.js +7 -8
  88. data/spec/javascripts/global-spec.js +3 -3
  89. data/spec/javascripts/i18n-spec.js +82 -0
  90. data/spec/javascripts/kids-button-spec.js +34 -0
  91. data/spec/javascripts/results-renderers-spec.js +5 -5
  92. data/spec/javascripts/speech-bubble-renderer-spec.js +2 -3
  93. data/spec/javascripts/submissions-store-spec.js +14 -14
  94. data/spec/javascripts/sync-mode-spec.js +3 -3
  95. data/spec/javascripts/timeout-spec.js +2 -2
  96. data/spec/javascripts/timer-spec.js +2 -2
  97. metadata +125 -92
  98. data/spec/features/chapter_spec.rb +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef8bf0f9bb9d5a3bc7daced2376fdb01ce52872178726bffc6d86def1081a2a4
4
- data.tar.gz: e2e907a4cf38b75a7ccb18160b84bd1beab765d3ac5cc5b986341701a7b4845e
3
+ metadata.gz: 92a4fb878a7767d1fe4b760252213ae327d8e5ae82bab805065eddb85b3baa36
4
+ data.tar.gz: e45d454449b183f1f4c200152a25f2df58ef385c1c07949e3fda7be94e6de57c
5
5
  SHA512:
6
- metadata.gz: 57196182676bed18199d12a5b44901baebe03f0763088f217117fbc85c2c5e3ab2cc009de7fa32aa7f6b1c039b939cd072e58cff755179228b6f328aa3eee2ab
7
- data.tar.gz: 268749a38da64dc28363ff6862c3ef566fe65aad62dc208f9629664d5f939414510e8005059fff43ebc6aa0817178362485996481206a442e9331a9295b22be7
6
+ metadata.gz: 9113399868c37c29fc38521c88b1f4acd2477b6094469c97be54983eb46753245e79ea7d4a56cb641ade8746b05323ecdeef276321aeb70e19ceca123d232566
7
+ data.tar.gz: 1f101adea0a26968fe86fc9f7d5084e4cd821ab9b17ca528762bb55573a0be5eeb98e2eba0e5affa83da30168f046c6ea1b7e7cd6654beed0eeb599ea0aaaa73
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);
@@ -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
  })();