mumuki-laboratory 6.4.2 → 6.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +61 -1
  3. data/app/assets/javascripts/application.js +1 -0
  4. data/app/assets/javascripts/application/characters.js +26 -0
  5. data/app/assets/javascripts/application/kids.js +179 -123
  6. data/app/assets/javascripts/application/load-error-svg.js +3 -20
  7. data/app/assets/javascripts/application/multiple-scenarios.js +152 -0
  8. data/app/assets/javascripts/application/popover.js +18 -0
  9. data/app/assets/javascripts/application/submission.js +5 -10
  10. data/app/assets/stylesheets/application/_layout.scss +10 -2
  11. data/app/assets/stylesheets/application/_modules.scss +1 -0
  12. data/app/assets/stylesheets/application/modules/_kids.scss +11 -4
  13. data/app/assets/stylesheets/application/modules/popover.scss +9 -0
  14. data/app/controllers/application_controller.rb +2 -2
  15. data/app/helpers/application_helper.rb +14 -5
  16. data/app/helpers/assets_helper.rb +13 -1
  17. data/app/helpers/links_helper.rb +15 -1
  18. data/app/helpers/organization_list_helper.rb +1 -1
  19. data/app/helpers/reset_button_helper.rb +1 -1
  20. data/app/helpers/version_helper.rb +5 -0
  21. data/app/views/errors/forbidden.html.erb +1 -1
  22. data/app/views/errors/internal_server_error.html.erb +1 -1
  23. data/app/views/errors/not_found.html.erb +1 -1
  24. data/app/views/exercise_solutions/_kids_results.html.erb +2 -2
  25. data/app/views/exercises/show.html.erb +2 -1
  26. data/app/views/layouts/_guide.html.erb +21 -5
  27. data/app/views/layouts/_kids.html.erb +1 -1
  28. data/app/views/layouts/_main.html.erb +1 -0
  29. data/app/views/layouts/_organizations_listing.html.erb +1 -1
  30. data/app/views/layouts/_submission_result_error_body.html.erb +1 -1
  31. data/app/views/layouts/exercise_inputs/layouts/_input_kids.html.erb +21 -10
  32. data/app/views/layouts/modals/_kids_context.html.erb +2 -2
  33. data/app/views/layouts/modals/_kids_results.html.erb +1 -1
  34. data/app/views/layouts/modals/_kids_results_aborted.html.erb +1 -1
  35. data/lib/mumuki/laboratory/controllers/results_rendering.rb +2 -1
  36. data/lib/mumuki/laboratory/engine.rb +3 -0
  37. data/lib/mumuki/laboratory/locales/en.yml +2 -0
  38. data/lib/mumuki/laboratory/locales/es.yml +2 -0
  39. data/lib/mumuki/laboratory/locales/pt.yml +2 -0
  40. data/lib/mumuki/laboratory/version.rb +1 -1
  41. data/public/character/kibi/context.svg +1 -0
  42. data/public/character/kibi/failure.svg +1 -0
  43. data/public/character/kibi/jump.svg +1 -0
  44. data/public/character/kibi/success2_l.svg +1 -0
  45. data/public/character/kibi/success_l.svg +1 -0
  46. data/public/character/magnifying_glass/apparition.svg +1 -0
  47. data/public/character/magnifying_glass/loop.svg +1 -0
  48. data/public/error/403.svg +1 -0
  49. data/public/error/404.svg +1 -0
  50. data/public/error/500.svg +1 -0
  51. data/public/error/timeout_1.svg +1 -0
  52. data/public/error/timeout_2.svg +1 -0
  53. data/public/error/timeout_3.svg +1 -0
  54. data/spec/controllers/exercise_solutions_controller_spec.rb +2 -1
  55. data/spec/dummy/db/schema.rb +5 -2
  56. data/spec/dummy/public/character/animations.json +30 -0
  57. data/spec/features/exercise_flow_spec.rb +3 -3
  58. data/spec/features/guides_flow_spec.rb +3 -3
  59. metadata +52 -17
  60. data/public/character/kids/kibi_context.svg +0 -11
  61. data/public/error_403.svg +0 -2
  62. data/public/error_404.svg +0 -5
  63. data/public/error_500.svg +0 -2
  64. data/public/error_timeout_1.svg +0 -2
  65. data/public/error_timeout_2.svg +0 -1
  66. data/public/error_timeout_3.svg +0 -1
  67. data/public/kibi_animated.svg +0 -2
  68. data/public/kibi_failure.svg +0 -5
  69. data/public/kibi_success.svg +0 -11
  70. data/public/kibi_success_dancing.svg +0 -16
  71. data/public/magnifying_glass_apparition.svg +0 -2
  72. data/public/magnifying_glass_loop.svg +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c70e70c721988ae2a17c12556ceb48438c3eb7d45e1c43f186f61f1db8a7eb68
4
- data.tar.gz: 243fe0ce527986bbb37b0557cdfb1141762a183d139cebe670a9001fafc95e1d
3
+ metadata.gz: d9cea64bf4904a9498e737c16b6d4226651758c91bfcad1bde4bddfc43d9ff54
4
+ data.tar.gz: 8800bcf52002d2d2599a7a242ab8e0586f403e6daf3483992d01c833e01c54f0
5
5
  SHA512:
6
- metadata.gz: 39077b3aabe2b22aa8c0e9aa4886e1ae1a9f196badbe84e5779c48fe85130870a13b499a27ee845160669231b60c7031957271d3407e247928f333f521699a00
7
- data.tar.gz: ac16e9e2804ea667bf2cde6857b41bb7f11cdc3e759d058210bdd1e0075195f901d80b41a6cc9fb437f49d8933768fc64c03891f326942fdc52b304c650ef880
6
+ metadata.gz: d6d64cd6f38e107745341a051587ea4baa84886a9f3c631133ffb402ac88bd49e9263d077e29ec1f549f8ee13bba7e4ba80d19c2664730317cbca04df07189c0
7
+ data.tar.gz: 9bff547b106c4404cd615ce89d89d2bc5db23d97fdb6e746423dd98a90e07de38322c74f52312b7b15cff1fc0c666dcc4286c53792ad4a2d23ea264d7d693d4c
data/README.md CHANGED
@@ -135,7 +135,67 @@ rails s
135
135
  bundle exec rspec
136
136
  ```
137
137
 
138
- ## API Docs
138
+ ## JavaScript API Docs
139
+
140
+ In order to be customized by runners, Laboratory exposes the following selectors and methods
141
+ which are granted to be safe and stable.
142
+
143
+ ### Public Selectors
144
+
145
+ * `.mu-final-state`
146
+ * `.mu-initial-state-header`
147
+ * `.mu-initial-state`
148
+ * `.mu-kids-blocks`
149
+ * `.mu-kids-context`
150
+ * `.mu-kids-exercise-description`
151
+ * `.mu-kids-exercise`
152
+ * `.mu-kids-reset-button`
153
+ * `.mu-kids-results-aborted`
154
+ * `.mu-kids-results`
155
+ * `.mu-kids-state-image`
156
+ * `.mu-kids-state`
157
+ * `.mu-kids-states`
158
+ * `.mu-kids-submit-button`
159
+ * `.mu-multiple-scenarios`
160
+ * `.mu-scenarios`
161
+ * `#mu-actual-state-text`
162
+ * `#mu-custom-editor-default-value`
163
+ * `#mu-custom-editor-extra`
164
+ * `#mu-custom-editor-test`
165
+ * `#mu-custom-editor-value`
166
+ * `#mu-initial-state-text`
167
+
168
+ ### Deprecated Selectors
169
+
170
+ * `.mu-kids-gbs-board-initial`: Use `.mu-initial-state` instead
171
+ * `.mu-state-final`: Use `.mu-final-state` instead
172
+ * `.mu-state-initial`: Use `.mu-initial-state` instead
173
+ * `#kids-context`: Use `.mu-kids-context` instead
174
+ * `#kids-results-aborted`: Use `.mu-kids-results-aborted` instead
175
+ * `#kids-results`: Use `.mu-kids-results` instead
176
+
177
+ ### Methods
178
+
179
+ * `mumuki.bridge.Laboratory`
180
+ * `mumuki.kids.registerBlocksAreaScaler`
181
+ * `mumuki.kids.registerStateScaler`
182
+ * `mumuki.kids.restart`
183
+ * `mumuki.kids.scaleBlocksArea`
184
+ * `mumuki.kids.scaleState`
185
+ * `mumuki.kids.showResult`
186
+ * `mumuki.locale`
187
+ * `mumuki.MultipleScenarios`
188
+ * `mumuki.version`
189
+
190
+ ### Kids Call order
191
+
192
+ 0. Laboratory Kids API Initialization
193
+ 1. Runner Editor JS
194
+ 2. Laboratory Kids Layout Initialization
195
+ 3. Runner Editor HTML
196
+
197
+
198
+ ## REST API Docs
139
199
 
140
200
  Before using the API, you must create an `ApiClient` using `rails c`, which will generate a private JWT. Use it to authenticate API calls in any Platform application within a `Authorizaion: Bearer <TOKEN>`.
141
201
 
@@ -27,6 +27,7 @@
27
27
  //= require_tree ../../../vendor/assets/javascripts/codemirror-modes
28
28
  //= require analytics
29
29
  //= require hotjar
30
+ //= require muvment
30
31
 
31
32
  //= require_tree ./application
32
33
 
@@ -0,0 +1,26 @@
1
+ mumuki.load(() => {
2
+ let characters = mumuki.characters || {};
3
+
4
+ muvment.loadCharacters(characters, '/character/animations.json').then((characterFinishedLoadingPromises) => {
5
+ Promise.all(characterFinishedLoadingPromises).then((characterIds) => {
6
+ mumuki.presenterCharacter = characters[atRandom(characterIds)];
7
+ placeKidsAnimations();
8
+ });
9
+ });
10
+
11
+ function placeKidsAnimations() {
12
+ placeAnimation('.mu-kids-character-animation', 'jump');
13
+ placeAnimation('.mu-kids-character-context', 'context');
14
+ }
15
+
16
+ function placeAnimation(selector, clip) {
17
+ let canvas = $(selector);
18
+ mumuki.presenterCharacter.playAnimation(clip, canvas);
19
+ }
20
+
21
+ function atRandom(array) {
22
+ return array[Math.floor(Math.random() * array.length)];
23
+ }
24
+
25
+ mumuki.characters = characters;
26
+ });
@@ -26,46 +26,15 @@ mumuki.load(function () {
26
26
  showParagraph(paragraphIndex || newParagraphIndex);
27
27
  }
28
28
 
29
- availableTabs.forEach(function (tabSelector) {
30
- tabParagraphs(tabSelector).contents().unwrap().wrapAll('<p>');
31
- });
32
-
33
29
  function tabParagraphs(selector) {
34
30
  return $('.mu-kids-character-speech-bubble > .mu-kids-character-speech-bubble-normal > div' + selector + ' > p');
35
31
  }
36
32
 
37
- updateSpeechParagraphs();
38
33
  function updateSpeechParagraphs() {
39
34
  $speechParagraphs = tabParagraphs('.' + getSelectedTabName());
40
35
  resizeSpeechParagraphs(0);
41
36
  }
42
37
 
43
- resizeSpeechParagraphs();
44
-
45
- $speechTabs.each(function (i) {
46
- var $tab = $($speechTabs[i]);
47
- $tab.click(function () {
48
- $speechTabs.removeClass('active');
49
- $tab.addClass('active');
50
- $texts.hide();
51
- $bubble.children('.' + $tab.data('target')).show();
52
- updateSpeechParagraphs();
53
- })
54
- });
55
-
56
- if(paragraphCount > 1) {
57
- nextSpeechBlinking = setInterval(() => $nextSpeech.fadeTo('slow', 0.1).fadeTo('slow', 1.0), 1000);
58
- }
59
-
60
- $nextSpeech.click(function () {
61
- showParagraph(currentParagraphIndex + 1);
62
- clearInterval(nextSpeechBlinking);
63
- });
64
-
65
- $prevSpeech.click(function () {
66
- showParagraph(currentParagraphIndex - 1);
67
- });
68
-
69
38
  function getSelectedTabName() {
70
39
  return $speechTabs.filter(".active").data('target') || $defaultSpeechTabName;
71
40
  }
@@ -85,95 +54,114 @@ mumuki.load(function () {
85
54
  isVisible ? element.show() : element.hide();
86
55
  }
87
56
 
88
- mumuki.resize(function () {
89
- var margin = 15;
90
- var fullMargin = margin * 2;
91
-
92
- var gbsBoard = $('.mu-kids-state');
93
-
94
- var dimension = gbsBoard.height() * 1.25 - fullMargin;
95
- gbsBoard.width(dimension);
96
-
97
- var $muKidsExercise = $('.mu-kids-exercise');
98
- var $muKidsExerciseDescription = $('.mu-kids-exercise-description');
57
+ mumuki.kids = {
99
58
 
100
- $muKidsExerciseDescription.width($muKidsExercise.width() - gbsBoard.width() - margin);
59
+ // ==========
60
+ // Public API
61
+ // ==========
62
+
63
+ // Sets a function that will be called each
64
+ // time the states need to be resized. The function takes:
65
+ //
66
+ // * $state: a single state area
67
+ // * fullMargin
68
+ // * preferredWidth
69
+ // * preferredHeight
70
+ //
71
+ // Runners must call this method on within the runner's editor.js extension
72
+ registerStateScaler: function(scaler) {
73
+ this._stateScaler = scaler;
74
+ },
101
75
 
102
- gbsBoard.each(function (i) {
103
- gsBoardScale($(gbsBoard[i]));
104
- });
76
+ // Sets a function that will be called each
77
+ // time the blocks area needs to be resized. The function takes:
78
+ //
79
+ // * $blocks: the blocks area
80
+ //
81
+ // Runners must call this method on within the runner's editor.js extension
82
+ registerBlocksAreaScaler: function(scaler) {
83
+ this._blocksAreaScaler = scaler;
84
+ },
105
85
 
106
- var $muKidsBlocks = $('.mu-kids-blocks');
107
- var $blockArea = $muKidsBlocks.find('#blocklyDiv');
108
- var $blockSvg = $muKidsBlocks.find('.blocklySvg');
86
+ // Scales a single state.
87
+ //
88
+ // This method is called by the kids code, but the runner's editor.js extension may need
89
+ // to perform additional calls to it.
90
+ scaleState: function ($state, fullMargin) {
91
+ const preferredWidth = $state.width() - fullMargin * 2;
92
+ const preferredHeight = $state.height() - fullMargin * 2;
93
+ this._stateScaler($state, fullMargin, preferredWidth, preferredHeight);
94
+ },
109
95
 
110
- $blockArea.width($muKidsBlocks.width());
111
- $blockArea.height($muKidsBlocks.height());
96
+ // Scales the blocks area.
97
+ //
98
+ // This method is called by the kids code, but the runner's editor.js extension may need
99
+ // to perform additional calls to it.
100
+ scaleBlocksArea: function($blocks) {
101
+ this._blocksAreaScaler($blocks);
102
+ },
112
103
 
113
- $blockSvg.width($muKidsBlocks.width());
114
- $blockSvg.height($muKidsBlocks.height());
104
+ // Displays the kids results, updating the progress bar
105
+ // firing the modal and running appropriate animations.
106
+ //
107
+ // This method needs to be called by the runner's editor.html extension
108
+ // in order to finish an exercise
109
+ showResult: function (data) {
110
+ mumuki.updateProgressBarAndShowModal(data);
111
+ if (data.guide_finished_by_solution) return;
112
+ mumuki.kids.resultAction[data.status](data);
113
+ },
115
114
 
116
- function gsBoardScale($element) {
117
- var $table = $element.find('gs-board > table');
118
- $table.css('transform', 'scale(1)');
119
- var scaleX = ($element.width() - fullMargin * 2) / $table.width();
120
- var scaleY = ($element.height() - fullMargin * 2) / $table.height();
121
- $table.css('transform', 'scale(' + Math.min(scaleX, scaleY) + ')');
122
- }
115
+ // Restarts the kids exercise.
116
+ //
117
+ // This method may need to be called by the runner's editor.html extension
118
+ // in order to recover from a failed submission
119
+ restart: function () {
120
+ mumuki.kids._hideMessageOnCharacterBubble();
121
+ var $bubble = mumuki.kids._getCharacterBubble();
122
+ Object.keys(mumuki.kids.resultAction).forEach($bubble.removeClass.bind($bubble));
123
+ mumuki.presenterCharacter.playAnimation('jump', mumuki.kids._getCharaterImage());
124
+ },
123
125
 
124
- resizeSpeechParagraphs();
125
- });
126
+ // ===========
127
+ // Private API
128
+ // ===========
126
129
 
127
- mumuki.kids = {
130
+ _updateSubmissionResult: function (html) {
131
+ return $('.submission-results').html(html);
132
+ },
128
133
 
129
- getResultsModal: function () {
134
+ _getResultsModal: function () {
130
135
  return $('#kids-results');
131
136
  },
132
137
 
133
- getResultsAbortedModal: function () {
138
+ _getResultsAbortedModal: function () {
134
139
  return $('#kids-results-aborted');
135
140
  },
136
141
 
137
- getCharaterImage: function () {
142
+ _getCharaterImage: function () {
138
143
  return $('.mu-kids-character > img');
139
144
  },
140
145
 
141
- getCharacterBubble: function () {
146
+ _getCharacterBubble: function () {
142
147
  return $('.mu-kids-character-speech-bubble');
143
148
  },
144
149
 
145
- getSubmissionResult: function () {
146
- return $('.submission-results');
147
- },
148
-
149
- getOverlay: function () {
150
+ _getOverlay: function () {
150
151
  return $('.mu-kids-overlay');
151
152
  },
152
153
 
153
- showResult: function (data) { // This function is called by the custom runner
154
- mumuki.updateProgressBarAndShowModal(data);
155
- if (data.guide_finished_by_solution) return;
156
- mumuki.kids.resultAction[data.status](data);
157
- },
158
-
159
- restart: function () { // This function is called by the custom runner
160
- mumuki.kids._hideMessageOnCharacterBubble();
161
- var $bubble = mumuki.kids.getCharacterBubble();
162
- Object.keys(mumuki.kids.resultAction).forEach($bubble.removeClass.bind($bubble));
163
- mumuki.kids.getCharaterImage().attr('src', '/kibi_animated.svg');
164
- },
165
-
166
154
  _hideMessageOnCharacterBubble: function () {
167
- var $bubble = mumuki.kids.getCharacterBubble();
155
+ var $bubble = mumuki.kids._getCharacterBubble();
168
156
  $bubble.find('.mu-kids-character-speech-bubble-tabs').show();
169
157
  $bubble.find('.mu-kids-character-speech-bubble-normal').show();
170
158
  $bubble.find('.mu-kids-character-speech-bubble-failed').hide();
171
159
  Object.keys(mumuki.kids.resultAction).forEach($bubble.removeClass.bind($bubble));
172
- mumuki.kids.getOverlay().hide();
160
+ mumuki.kids._getOverlay().hide();
173
161
  },
174
162
 
175
163
  _showMessageOnCharacterBubble: function (data) {
176
- var $bubble = mumuki.kids.getCharacterBubble();
164
+ var $bubble = mumuki.kids._getCharacterBubble();
177
165
  $bubble.find('.mu-kids-character-speech-bubble-tabs').hide();
178
166
  $bubble.find('.mu-kids-character-speech-bubble-normal').hide();
179
167
  $bubble.find('.mu-kids-character-speech-bubble-failed').show().html(data.title_html);
@@ -181,73 +169,89 @@ mumuki.load(function () {
181
169
  if (data.status === 'passed_with_warnings') {
182
170
  $bubble.find('.mu-kids-character-speech-bubble-failed').append(data.expectations_html);
183
171
  }
184
- mumuki.kids.getOverlay().show();
172
+ mumuki.kids._getOverlay().show();
185
173
  },
186
174
 
187
175
  _showOnSuccessPopup: function (data) {
188
- mumuki.kids.getSubmissionResult().html(data.html);
189
- mumuki.kids.getCharaterImage().attr('src', '/kibi_success.svg');
176
+ mumuki.kids._updateSubmissionResult(data.html);
177
+ mumuki.presenterCharacter.playAnimation('success_l', mumuki.kids._getCharaterImage());
190
178
  mumuki.kids._showMessageOnCharacterBubble(data);
179
+ mumuki.presenterCharacter.playAnimation('success2_l', $('.mu-kids-character-success'));
191
180
  setTimeout(function () {
192
- var results_kids_modal = mumuki.kids.getResultsModal();
193
- if (results_kids_modal) {
194
- results_kids_modal.modal({
181
+ var $resultsKidsModal = mumuki.kids._getResultsModal();
182
+ if ($resultsKidsModal) {
183
+ $resultsKidsModal.modal({
195
184
  backdrop: 'static',
196
185
  keyboard: false
197
186
  });
198
- results_kids_modal.find('.modal-header').first().html(data.title_html);
199
- results_kids_modal.find('.modal-footer').first().html(data.button_html);
187
+ $resultsKidsModal.find('.modal-header').first().html(data.title_html);
188
+ $resultsKidsModal.find('.modal-footer').first().html(data.button_html);
200
189
  mumuki.kids._showCorollaryCharacter();
201
- $('.mu-close-modal').click(() => $('#kids-results').modal('hide'));
190
+ $('.mu-close-modal').click(() => mumuki.kids._getResultsModal().modal('hide'));
202
191
  }
203
192
  }, 1000 * 4);
204
193
  },
205
194
 
206
195
  _showOnFailurePopup: function () {
207
196
  mumuki.kids.submitButton.disable();
208
- mumuki.kids.getResultsAbortedModal().modal();
197
+ mumuki.kids._getResultsAbortedModal().modal();
209
198
  mumuki.submission.animateTimeoutError(mumuki.kids.submitButton);
210
199
  },
211
200
 
212
201
  _showOnCharacterBubble: function (data) {
213
- mumuki.kids.getCharaterImage().attr('src', '/kibi_failure.svg');
202
+ mumuki.presenterCharacter.playAnimation('failure', mumuki.kids._getCharaterImage());
214
203
  mumuki.kids._showMessageOnCharacterBubble(data);
215
204
  },
216
205
 
217
206
  _showCorollaryCharacter: function () {
218
- var image = $('#mu-kids-corollary-animation')[0];
219
- image && setTimeout(function () {
220
- image.src = mumuki.characters.magnifying_glass_apparition.url;
221
- setTimeout(function () {
222
- image.src = mumuki.characters.magnifying_glass_loop.url;
223
- }, mumuki.characters.magnifying_glass_apparition.duration);
224
- }, 500);
207
+ mumuki.characters.magnifying_glass.playAnimation('show', $('.mu-kids-corollary-animation'));
208
+ },
209
+
210
+ _stateScaler: function ($state, fullMargin, preferredWidth, preferredHeight) {
211
+ var $table = $state.find('gs-board > table');
212
+ if(!$table.length) return setTimeout(() => this.scaleState($state, fullMargin));
213
+
214
+ console.warn("You are using the default states scaler, which is gobstones-specific. Please register your own scaler in the future");
215
+
216
+ $table.css('transform', 'scale(1)');
217
+ var scaleX = preferredWidth / $table.width();
218
+ var scaleY = preferredHeight / $table.height();
219
+ $table.css('transform', 'scale(' + Math.min(scaleX, scaleY) + ')');
220
+ },
221
+
222
+ _blocksAreaScaler: function($blocks) {
223
+ console.warn("You are using the default blocks scaler, which is blockly-specific. Please register your own scaler in the future");
224
+
225
+ var $blockArea = $blocks.find('#blocklyDiv');
226
+ var $blockSvg = $blocks.find('.blocklySvg');
227
+
228
+ $blockArea.width($blocks.width());
229
+ $blockArea.height($blocks.height());
230
+
231
+ $blockSvg.width($blocks.width());
232
+ $blockSvg.height($blocks.height());
225
233
  },
226
234
 
227
235
  resultAction: {}
228
236
 
229
237
  };
230
238
 
231
- _createSubmitButton = function () {
232
- var submitButton = $('#kids-btn-retry');
233
- var submissionControl = $('.submission_control');
234
- return new mumuki.submission.SubmitButton(submitButton, submissionControl);
235
- };
239
+ mumuki.kids.submitButton = new mumuki.submission.SubmitButton($('#kids-btn-retry'), $('.submission_control'));
236
240
 
237
- mumuki.kids.submitButton = _createSubmitButton();
241
+ function showPrevParagraph() {
242
+ animateSpeech();
243
+ showParagraph(currentParagraphIndex - 1);
244
+ }
238
245
 
239
- mumuki.showKidsResult = function (data) {
240
- mumuki.updateProgressBarAndShowModal(data);
241
- if (data.guide_finished_by_solution) return;
242
- mumuki.kids.getSubmissionResult().html(data.html);
246
+ function showNextParagraph() {
247
+ animateSpeech();
248
+ showParagraph(currentParagraphIndex + 1);
249
+ clearInterval(nextSpeechBlinking);
250
+ }
243
251
 
244
- var results_kids_modal = mumuki.kids.getResultsModal();
245
- if (results_kids_modal) {
246
- results_kids_modal.modal();
247
- results_kids_modal.find('.modal-header').first().html(data.title_html);
248
- results_kids_modal.find('.modal-footer').first().html(data.button_html);
249
- }
250
- };
252
+ function animateSpeech() {
253
+ mumuki.presenterCharacter.playAnimation('talk', mumuki.kids._getCharaterImage());
254
+ }
251
255
 
252
256
  mumuki.kids.resultAction.passed = mumuki.kids._showOnSuccessPopup;
253
257
  mumuki.kids.resultAction.passed_with_warnings = mumuki.kids._showOnCharacterBubble;
@@ -258,4 +262,56 @@ mumuki.load(function () {
258
262
  mumuki.kids.resultAction.errored = mumuki.kids._showOnCharacterBubble;
259
263
  mumuki.kids.resultAction.pending = mumuki.kids._showOnCharacterBubble;
260
264
 
265
+ $(document).ready(() => {
266
+ // Speech initialization
267
+
268
+ availableTabs.forEach(function (tabSelector) {
269
+ tabParagraphs(tabSelector).contents().unwrap().wrapAll('<p>');
270
+ });
271
+
272
+ updateSpeechParagraphs();
273
+
274
+ resizeSpeechParagraphs();
275
+
276
+ $speechTabs.each(function (i) {
277
+ var $tab = $($speechTabs[i]);
278
+ $tab.click(function () {
279
+ $speechTabs.removeClass('active');
280
+ $tab.addClass('active');
281
+ $texts.hide();
282
+ $bubble.children('.' + $tab.data('target')).show();
283
+ updateSpeechParagraphs();
284
+ })
285
+ });
286
+
287
+ if(paragraphCount > 1) {
288
+ nextSpeechBlinking = setInterval(() => $nextSpeech.fadeTo('slow', 0.1).fadeTo('slow', 1.0), 1000);
289
+ }
290
+
291
+ $nextSpeech.click(showNextParagraph);
292
+ $prevSpeech.click(showPrevParagraph);
293
+
294
+ // States initial resizing
295
+
296
+ mumuki.resize(function () {
297
+ var margin = 15;
298
+ var fullMargin = margin * 2;
299
+
300
+ var $muKidsStatesContainer = $('.mu-kids-states');
301
+ var $muKidsStates = $('.mu-kids-state');
302
+
303
+ var dimension = $muKidsStatesContainer.height() / 2 * 1.25 - fullMargin;
304
+ $muKidsStatesContainer.width(dimension);
305
+
306
+ var $muKidsExercise = $('.mu-kids-exercise');
307
+ var $muKidsExerciseDescription = $('.mu-kids-exercise-description');
308
+
309
+ $muKidsExerciseDescription.width($muKidsExercise.width() - $muKidsStatesContainer.width() - margin);
310
+
311
+ $muKidsStates.each((index, state) => mumuki.kids.scaleState($(state), fullMargin));
312
+ mumuki.kids.scaleBlocksArea($('.mu-kids-blocks'));
313
+
314
+ resizeSpeechParagraphs();
315
+ });
316
+ })
261
317
  });