mumuki-laboratory 6.4.2 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  });