mumuki-gobstones-runner 2.8.3 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89e0406fba09775ee9df64aa7ef21365776864e6120967fb04118e2a30cf7bc9
4
- data.tar.gz: 436e821d7c9c868cb9605d253cfbdefe47cadcb73056b04c6d7ef68e3d5e6a6a
3
+ metadata.gz: abbf0ec6c1d486eadb06f71d4d479a520700c79376ab9d1e785a8fc658485624
4
+ data.tar.gz: 60dc12c4d5e60614a94987ae2c725892cb6e06065aefd673952cf0f73880ce06
5
5
  SHA512:
6
- metadata.gz: 5ab9ec621d9701a07c95f65fcc956a908d8b2e3e3c5d310829026beb5fc771b621ba916638538348c4c06a75fb925fd7cba998e669fbda3d535b654d6c94459b
7
- data.tar.gz: fa1e3349408ede7cbedbb983df1759c6d58595a7f1f278a50498470b3e106d80d696309f52ff73fa61e4f8dab71582a79453836a3fac90421b570f83eaab4ac1
6
+ metadata.gz: f253ffce6bfffc3aa0af02995cd3739df9a1ccb2122c6c7fbdb70883b3cb7da9ef6078372a1aa141fe62561491c929450fe2e9ba30fe7d8dad5561abc57f6c55
7
+ data.tar.gz: d7227916c7b0e94173319e8753364ceec1818a07aa10f70e155735549872c6541cf7065235be16f0d43aece40df847c3a5ee1842e20c33fa205161fafa0b23d0
@@ -18,8 +18,9 @@ class Mumukit::Server::App < Sinatra::Base
18
18
  end
19
19
 
20
20
  def self.get_media_assets(folder)
21
- Dir.glob("lib/public/#{folder}/*").each do |path|
22
- get_local_asset "#{folder}/#{File.basename path}", path
21
+ Dir.glob(File.join(__dir__,"../lib/public/#{folder}/*")).each do |path|
22
+ relative_media_asset_path = "#{folder}/#{File.basename path}"
23
+ get_local_asset relative_media_asset_path, "lib/public/#{relative_media_asset_path}"
23
24
  end
24
25
  end
25
26
 
@@ -113,6 +113,7 @@ module Gobstones
113
113
  return "no_stones" if code == 'cannot-remove-stone'
114
114
  return "out_of_board" if code == 'cannot-move-to'
115
115
  return "unassigned_variable" if code == 'undefined-variable'
116
+ return "boom_called" if code == 'boom-called'
116
117
  return "wrong_argument_type" if has_wrong_argument_type? code
117
118
  return "wrong_arguments_quantity" if code.include? 'arity-mismatch'
118
119
 
Binary file
@@ -116,7 +116,6 @@
116
116
  }
117
117
  }
118
118
 
119
-
120
119
  .mu-kids-exercise.mu-kindergarten .gbs_boom {
121
120
  display: none;
122
121
  }
@@ -124,3 +123,13 @@
124
123
  .mu-kids-exercise.mu-kindergarten .mu-scenario.active table.gbs_board {
125
124
  display: block !important;
126
125
  }
126
+
127
+ .mu-kids-exercise.mu-kindergarten .gbs_gh {
128
+ outline: 0 !important;
129
+ }
130
+
131
+ .mu-kids-exercise.mu-kindergarten .mu-kids-state .mu-kids-state-image gs-board>table {
132
+ background-color: #6D6E70!important;
133
+ border: 1px solid #6D6E70 !important;
134
+ border-radius: 4px;
135
+ }
@@ -4,6 +4,30 @@
4
4
  <link href="./gobstones-code-runner.html" rel="import"/>
5
5
  <script src="./hammer.min.js"></script>
6
6
 
7
+ <script>
8
+ Blockly.FieldImage.prototype.__setValue__ = Blockly.FieldImage.prototype.setValue;
9
+ Blockly.FieldImage.prototype.setValue = function(a) {
10
+ if (a && a.startsWith("undefined")) {
11
+ console.debug(`Ignoring unavailable image ${a}`);
12
+ } else {
13
+ this.__setValue__(a);
14
+ }
15
+ }
16
+
17
+ Blockly.FieldImage.prototype.__setTooltip__ = Blockly.FieldImage.prototype.setTooltip;
18
+ Blockly.FieldImage.prototype.setTooltip = function(a) {
19
+ try {
20
+ this.__setTooltip__(a);
21
+ } catch (_e) {
22
+ console.debug('Could not set tooltip');
23
+ }
24
+ }
25
+
26
+ function postpone(action) {
27
+ return setTimeout(action, 50);
28
+ }
29
+ </script>
30
+
7
31
  <dom-module id="mu-gobstones-custom-editor">
8
32
 
9
33
  <template>
@@ -20,15 +44,6 @@
20
44
  </template>
21
45
 
22
46
  <script>
23
- function postpone(action) {
24
- return setTimeout(action, 50);
25
- }
26
-
27
- function gbsBoardRemoveBorder(board) {
28
- board._setBorderOff();
29
- board.updateStyles();
30
- }
31
-
32
47
  const compilationModes = {
33
48
  gameFramework: {
34
49
  compile: ({ main, teacher, ...args }) => {
@@ -46,7 +61,9 @@
46
61
 
47
62
  scrollToMainBlock: (blockly) => {
48
63
  const mainBlock = blockly.getBlocksOfType('procedures_defnoreturnnoparams')[0];
49
- blockly.scrollToBlock(mainBlock.id);
64
+ if (mainBlock) {
65
+ blockly.scrollToBlock(mainBlock.id);
66
+ }
50
67
  },
51
68
 
52
69
  defaultCode: `<%= @game_framework_default %>`
@@ -90,184 +107,264 @@
90
107
  }
91
108
  },
92
109
 
93
- attached: function () {
94
- const setBlocklySounds = () => {
95
- Blockly.WorkspaceAudio.prototype.preload = () => {
96
- for (var soundName in this.SOUNDS_) {
97
- let sound = this.SOUNDS_[soundName];
98
- sound.volume = 0.01;
99
- let playPromise = sound.play();
100
- playPromise && playPromise.then(sound.pause.bind(sound));
101
- if (goog.userAgent.IPAD || goog.userAgent.IPHONE) break;
102
- }
103
- };
104
- };
110
+ // ==== Polymer events
105
111
 
106
- const setBlocklyColors = () => {
107
- Blockly.HSV_SATURATION = 0.64;
108
- Blockly.HSV_VALUE = 1;
112
+ attached() {
113
+ this.$exerciseContainer = $('.mu-kids-exercise');
109
114
 
110
- Blockly.MUMUKI_COLORS = {
111
- pink: "#FF5C82",
112
- blue: "#5CBEFF",
113
- yellow: "#FFC95C",
114
- green: "#5CED71"
115
- };
115
+ /*
116
+ https://polymer-library.polymer-project.org/1.0/docs/devguide/registering-elements#initialization-order
116
117
 
117
- // reserved
118
- Blockly.CUSTOM_COLORS.program = Blockly.MUMUKI_COLORS.pink;
119
- Blockly.CUSTOM_COLORS.interactiveProgram = Blockly.MUMUKI_COLORS.pink;
120
- Blockly.CUSTOM_COLORS.interactiveBinding = Blockly.MUMUKI_COLORS.pink;
121
- Blockly.CUSTOM_COLORS.procedure = Blockly.MUMUKI_COLORS.pink;
122
- Blockly.CUSTOM_COLORS.function = Blockly.MUMUKI_COLORS.pink;
123
- Blockly.CUSTOM_COLORS.complete = Blockly.MUMUKI_COLORS.pink;
124
-
125
- // control structures
126
- Blockly.CUSTOM_COLORS.controlStructure = this._isKindergarten() ? Blockly.MUMUKI_COLORS.green : Blockly.MUMUKI_COLORS.yellow;
127
-
128
- // commands
129
- Blockly.CUSTOM_COLORS.primitiveCommand = Blockly.MUMUKI_COLORS.yellow;
130
- Blockly.CUSTOM_COLORS.primitiveProcedure = Blockly.MUMUKI_COLORS.yellow;
131
- Blockly.CUSTOM_COLORS.procedure_call = Blockly.MUMUKI_COLORS.yellow;
132
- Blockly.CUSTOM_COLORS.assignation = Blockly.MUMUKI_COLORS.yellow;
133
-
134
- // expressions
135
- Blockly.Msg.MATH_HUE = Blockly.MUMUKI_COLORS.blue;
136
- Blockly.CUSTOM_COLORS.literalExpression = Blockly.MUMUKI_COLORS.blue;
137
- Blockly.CUSTOM_COLORS.primitiveExpression = Blockly.MUMUKI_COLORS.blue;
138
- Blockly.CUSTOM_COLORS.operator = Blockly.MUMUKI_COLORS.blue;
139
- Blockly.CUSTOM_COLORS.variable = Blockly.MUMUKI_COLORS.blue;
140
- Blockly.CUSTOM_COLORS.parameter = Blockly.MUMUKI_COLORS.blue;
141
- Blockly.CUSTOM_COLORS.primitiveFunction = Blockly.MUMUKI_COLORS.blue;
142
- Blockly.CUSTOM_COLORS.function_call = Blockly.MUMUKI_COLORS.blue;
143
-
144
- setTimeout(() => {
145
- this.getBlockly().testColors(Blockly.CUSTOM_COLORS);
146
- });
147
- };
118
+ There are no guarantees with regard to initialization timing between sibling elements.
119
+ This means that siblings may become ready in any order.
120
+ For accessing sibling elements when an element initializes, you can call async from inside the attached callback.
121
+ */
122
+ this.async(() => {
123
+ this._configureBlocklyAppearance();
124
+ this._configureBlocklyBehavior();
125
+ });
126
+ },
148
127
 
149
- const setBlocklyCustomSettings = () => {
150
- if (typeof Blockly === 'undefined' || !Blockly.CUSTOM_COLORS) return postpone(setBlocklyCustomSettings);
128
+ // ==== Public methods (accesible from other components)
151
129
 
152
- // The display mode configuration could be monkey-patched here, like this:
153
- Blockly.displayModes.iconic.iconSize = 64;
154
- Blockly.displayModes.iconic.procedureDefIcon = `${this.localMediaUrl}main-procedure.png`;
130
+ reset() {
131
+ const blockly = this._getBlockly();
132
+ blockly.workspaceXml = blockly.initialXml;
133
+ this._scrollToMainBlock();
134
+ },
155
135
 
156
- Blockly.displayMode = this._isKindergarten() ? 'iconic' : 'text';
157
- setBlocklySounds();
158
- setBlocklyColors();
136
+ /**
137
+ * Configures a custom toolbox using a toolbox url
138
+ * This method does nothing if editor is read-only, but
139
+ * assumes a gs-toolbox element is present - otherwise it would
140
+ * have no sense to have a toolboxUrl.
141
+ */
142
+ configureCustomToolboxFromUrl(toolboxUrl) {
143
+ if (this.readOnly) return;
159
144
 
160
- // Removing "/" from the block id character set to avoid syntax errors
161
- Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace("/", "");
162
- };
145
+ console.debug('Configuring custom toolbox');
163
146
 
164
- const updateFields = () => {
165
- const blockly = this.getBlockly();
147
+ $.get(toolboxUrl, (toolboxXml) => {
148
+ this._initializeCustomToolboxBlocklyWorkspace(toolboxXml);
149
+ });
150
+ },
166
151
 
167
- const editorValue = this.getEditorValue();
168
- if (editorValue) {
169
- editorValue.value = blockly.workspaceXml;
170
- }
152
+ /**
153
+ * Wether a custom toolbox is required. Read only editors and gs-toolbox-less context
154
+ * don't require it
155
+ */
156
+ hasCustomToolbox() {
157
+ return !this.readOnly && $('gs-toolbox').length;
158
+ },
171
159
 
172
- if (this.teacherMode) {
173
- const editorExtra = this.getEditorExtra();
174
- if (editorExtra) {
175
- editorExtra.value = blockly.workspaceXml;
176
- }
177
- }
160
+ compile(code) {
161
+ return this._compilationMode().compile(code);
162
+ },
178
163
 
179
- if (typeof angular !== 'undefined') {
180
- angular.element(editorValue).triggerHandler("change");
181
- }
164
+ hasInteractiveProgram() {
165
+ return this._getBlockly().initialXml.indexOf("block type=\"InteractiveProgram\"") !== -1;
166
+ },
182
167
 
183
- const submit = $("kids-submit-button")[0];
184
- if (submit && submit.$.runner.isRunning) {
185
- submit.$.runner.stop();
186
- }
187
- };
168
+ toggleInteractiveMode() {
169
+ this.$exerciseContainer.toggleClass('play-mode');
170
+ this._triggerResize();
171
+ },
188
172
 
189
- this.$exerciseContainer = $('.mu-kids-exercise');
173
+ getTeacherCode() {
174
+ const extraCode = this._getExtraCode();
175
+ if (!extraCode) return;
176
+ this.$.blocklyTmp.workspaceXml = extraCode;
177
+ return this.$.blocklyTmp.generateCode();
178
+ },
190
179
 
191
- const setTrashPosition = () => {
192
- var width = $('#blocklyDiv').width() - 68;
193
- var height = $('#blocklyDiv').height() - 210;
194
- $('.blocklyTrash').css("transform", `translate(${width}px, ${height}px)`);
195
- $('.blocklyTrash').css("display", "unset");
196
- };
180
+ getStudentCode: function () {
181
+ return this
182
+ ._getBlockly()
183
+ .generateCode({withRegions: true, clearErrors: false});
184
+ },
197
185
 
198
- var setTrashTimeout;
186
+ getStudentXml: function () {
187
+ return this._getStudentSolution() || "";
188
+ },
199
189
 
200
- const localOnResize = () => {
201
- clearTimeout(setTrashTimeout);
202
- setTrashTimeout = setTimeout(() => {
203
- setTrashPosition();
204
- });
205
- };
190
+ getTestCode() {
191
+ return this._getEditorTest().value;
192
+ },
206
193
 
207
- const triggerResizeOnContextModalClose = () => {
208
- $('.mu-kids-context, .mu-kids-results').on('hidden.bs.modal shown.bs.modal', function () {
209
- localOnResize();
210
- })
211
- };
194
+ removeBlockErrors() {
195
+ return this._getBlockly().workspace.removeBlockErrors();
196
+ },
212
197
 
213
- const relocateTrash = (blockly) => {
214
- blockly.workspace.trashcan.bottom_ = 150; //Setting vertical position programmatically to adjust the draggable area
215
- $(window).resize((e) => localOnResize());
216
- triggerResizeOnContextModalClose();
217
- };
198
+ showBlockError(region, error) {
199
+ this._getBlockly().scrollToBlock(region);
200
+ this._getBlockly().showBlockError(region, error.message);
201
+ },
218
202
 
219
- const initialize = () => {
220
- postpone(() => {
221
- const blockly = this.getBlockly();
203
+ highlightBlock(region) {
204
+ this._getBlockly().highlightBlock(region);
205
+ },
222
206
 
223
- if (!blockly || !blockly.workspace) return initialize();
207
+ // ==== Private methods (only for internal usage)
224
208
 
225
- blockly.showCategories = !this._isKindergarten();
209
+ _configureBlocklyAppearance() {
210
+ this._getBlockly().showCategories = !this._isKindergarten();
211
+ this._setBlocklyDisplayMode();
212
+ this._setBlocklySounds();
213
+ this._setBlocklyColors();
226
214
 
227
- if (!this.readOnly) {
228
- relocateTrash(blockly);
229
- }
215
+ if (!this.readOnly) {
216
+ this._relocateTrashOnResize();
217
+ }
230
218
 
231
- this.setTeacherActions(blockly);
232
- this._setInitialXml(blockly);
233
- this.interactiveMode = blockly.initialXml.indexOf("block type=\"InteractiveProgram\"") !== -1;
234
- if (this.interactiveMode) this.setInteractiveLayout();
235
- this._initializeWorkspace(blockly, () => {
236
- localOnResize();
237
- blockly._onresize();
219
+ // Removing "/" from the block id character set to avoid syntax errors
220
+ Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace("/", "");
221
+ },
238
222
 
239
- this._subscribeToWorkspace(blockly, updateFields);
240
- });
223
+ _configureBlocklyBehavior() {
224
+ this._setTeacherActions();
225
+ this._setGameActions();
226
+ this._initializeNonCustomToolboxBlocklyWorkspace();
227
+ this._registerLayoutChangedEvent();
228
+ },
241
229
 
242
- const hasCustomToolbox = $('gs-toolbox').length;
243
- if(!hasCustomToolbox) this.enableContextButton();
230
+ _relocateTrashOnResize() {
231
+ $('.mu-kids-context, .mu-kids-results').on('hidden.bs.modal shown.bs.modal', () => {
232
+ this._relocateTrash();
233
+ });
244
234
 
245
- this._registerLayoutChangedEvent();
246
- });
235
+ $(window).resize(() => this._relocateTrash());
236
+ },
237
+
238
+
239
+ /**
240
+ * Initializes a workspace using a custom toolbox.
241
+ *
242
+ * Blockly's workspace is destroyed when toolbox changes, so initialization
243
+ * is performed here
244
+ */
245
+ _initializeCustomToolboxBlocklyWorkspace(toolboxXml) {
246
+ this._getBlockly().toolbox = { defaultToolbox: toolboxXml };
247
+ this._initializeBlocklyWorkspace();
248
+ },
249
+
250
+ /**
251
+ * Initializes a workspace using a non-custom toolbox.
252
+ *
253
+ * Blockly's workspace is destroyed when toolbox changes,
254
+ * so this method will initialize it only if a there is a custom toolbox
255
+ * that will be called later.
256
+ */
257
+ _initializeNonCustomToolboxBlocklyWorkspace() {
258
+ if (!this.hasCustomToolbox()) {
259
+ this._initializeBlocklyWorkspace();
260
+ }
261
+ },
262
+
263
+ _initializeBlocklyWorkspace() {
264
+ console.debug('Initializing Blockly Workspace');
265
+
266
+ this._setInitialXml();
267
+ this._initializeWorkspace();
268
+ this._subscribeToWorkspace(() => this._updateSolution());
269
+
270
+ if (this.hasInteractiveProgram()) {
271
+ this._setInteractiveLayout();
272
+ }
273
+
274
+ mumuki.assetsLoadedFor('editor');
275
+ },
276
+
277
+ _relocateTrash() {
278
+ const width = $('#blocklyDiv').width() - 68;
279
+ const height = $('#blocklyDiv').height() - 210;
280
+ $('.blocklyTrash').css("transform", `translate(${width}px, ${height}px)`);
281
+ $('.blocklyTrash').css("display", "unset");
282
+ },
283
+
284
+ _updateSolution() {
285
+ const blockly = this._getBlockly();
286
+
287
+ if (this.teacherMode) {
288
+ this._setExtraCode(blockly.workspaceXml);
289
+ } else {
290
+ this._setStudentSolution(blockly.workspaceXml);
291
+ }
292
+
293
+ // TODO: this hack enables Angular two-way binding for Bibliotheca. Should be replaced with Mumuki events system
294
+ if (typeof angular !== 'undefined') {
295
+ angular.element(this._getStudentEditor()).triggerHandler("change");
296
+ angular.element(this._getEditorExtra()).triggerHandler("change");
297
+ }
298
+
299
+ this._stopExecution();
300
+ },
301
+
302
+ _setBlocklySounds() {
303
+ Blockly.WorkspaceAudio.prototype.preload = () => {
304
+ for (const soundName in this.SOUNDS_) {
305
+ const sound = this.SOUNDS_[soundName];
306
+ sound.volume = 0.01;
307
+ const playPromise = sound.play();
308
+ playPromise && playPromise.then(sound.pause.bind(sound));
309
+ if (goog.userAgent.IPAD || goog.userAgent.IPHONE) break;
310
+ }
247
311
  };
312
+ },
313
+
314
+ _setBlocklyColors() {
315
+ Blockly.HSV_SATURATION = 0.64;
316
+ Blockly.HSV_VALUE = 1;
317
+
318
+ Blockly.MUMUKI_COLORS = {
319
+ pink: "#FF5C82",
320
+ blue: "#5CBEFF",
321
+ yellow: "#FFC95C",
322
+ green: "#5CED71"
323
+ };
324
+
325
+ // reserved
326
+ Blockly.CUSTOM_COLORS.program = Blockly.MUMUKI_COLORS.pink;
327
+ Blockly.CUSTOM_COLORS.interactiveProgram = Blockly.MUMUKI_COLORS.pink;
328
+ Blockly.CUSTOM_COLORS.interactiveBinding = Blockly.MUMUKI_COLORS.pink;
329
+ Blockly.CUSTOM_COLORS.procedure = Blockly.MUMUKI_COLORS.pink;
330
+ Blockly.CUSTOM_COLORS.function = Blockly.MUMUKI_COLORS.pink;
331
+ Blockly.CUSTOM_COLORS.complete = Blockly.MUMUKI_COLORS.pink;
332
+
333
+ // control structures
334
+ Blockly.CUSTOM_COLORS.controlStructure = this._isKindergarten() ? Blockly.MUMUKI_COLORS.green : Blockly.MUMUKI_COLORS.yellow;
335
+
336
+ // commands
337
+ Blockly.CUSTOM_COLORS.primitiveCommand = Blockly.MUMUKI_COLORS.yellow;
338
+ Blockly.CUSTOM_COLORS.primitiveProcedure = Blockly.MUMUKI_COLORS.yellow;
339
+ Blockly.CUSTOM_COLORS.procedure_call = Blockly.MUMUKI_COLORS.yellow;
340
+ Blockly.CUSTOM_COLORS.assignation = Blockly.MUMUKI_COLORS.yellow;
248
341
 
249
- setBlocklyCustomSettings();
250
- initialize();
342
+ // expressions
343
+ Blockly.Msg.MATH_HUE = Blockly.MUMUKI_COLORS.blue;
344
+ Blockly.CUSTOM_COLORS.literalExpression = Blockly.MUMUKI_COLORS.blue;
345
+ Blockly.CUSTOM_COLORS.primitiveExpression = Blockly.MUMUKI_COLORS.blue;
346
+ Blockly.CUSTOM_COLORS.operator = Blockly.MUMUKI_COLORS.blue;
347
+ Blockly.CUSTOM_COLORS.variable = Blockly.MUMUKI_COLORS.blue;
348
+ Blockly.CUSTOM_COLORS.parameter = Blockly.MUMUKI_COLORS.blue;
349
+ Blockly.CUSTOM_COLORS.primitiveFunction = Blockly.MUMUKI_COLORS.blue;
350
+ Blockly.CUSTOM_COLORS.function_call = Blockly.MUMUKI_COLORS.blue;
251
351
  },
252
352
 
253
- toggleInteractiveMode() {
254
- this.$exerciseContainer.toggleClass('play-mode');
255
- this.triggerResize();
353
+ _setBlocklyDisplayMode() {
354
+ // The display mode configuration could be monkey-patched here, like this:
355
+ Blockly.displayModes.iconic.iconSize = 64;
356
+ Blockly.displayModes.iconic.procedureDefIcon = `${this.localMediaUrl}main-procedure.png`;
357
+
358
+ Blockly.displayMode = this._isKindergarten() ? 'iconic' : 'text';
256
359
  },
257
360
 
258
- triggerResize() {
259
- let event = document.createEvent('HTMLEvents');
361
+ _triggerResize() {
362
+ const event = document.createEvent('HTMLEvents');
260
363
  event.initEvent('resize', true, false);
261
364
  document.dispatchEvent(event);
262
365
  },
263
366
 
264
- reset(code) {
265
- const blockly = this.getBlockly();
266
- blockly.workspaceXml = code || blockly.initialXml;
267
- this._scrollToMainBlock();
268
- },
269
-
270
- setInteractiveLayout() {
367
+ _setInteractiveLayout() {
271
368
  this.$exerciseContainer.addClass('mu-kids-interactive');
272
369
  $('.mu-final-state').html('<gs-keyboard/>');
273
370
  $('.mu-editor').append($('<kids-interactive-submit-button/>'));
@@ -277,82 +374,86 @@
277
374
  })
278
375
  },
279
376
 
280
- setTeacherActions(blockly) {
377
+ _setTeacherActions() {
281
378
  const teacherCode = this.getTeacherCode();
282
- if (teacherCode) {
283
- setTimeout(() => {
284
- const actions = new Parser().getActionsFromSource(teacherCode);
285
-
286
- blockly.primitiveProcedures = this._withDefaultIcons(actions, 'procedureDeclarations');
287
- blockly.primitiveFunctions = this._withDefaultIcons(actions, 'functionDeclarations');
288
- });
379
+ if (!teacherCode) {
380
+ return;
289
381
  }
382
+
383
+ const blockly = this._getBlockly();
384
+ const actions = new Parser().getActionsFromSource(teacherCode);
385
+ blockly.primitiveProcedures = this._withDefaultIcons(actions, 'procedureDeclarations');
386
+ blockly.primitiveFunctions = this._withDefaultIcons(actions, 'functionDeclarations');
290
387
  },
291
388
 
292
- setGameActions(blockly) {
389
+ _setGameActions() {
390
+ const blockly = this._getBlockly();
293
391
  if (this._isGame()) {
294
392
  blockly.primitiveProcedures = blockly.primitiveProcedures || [];
295
393
  blockly.primitiveProcedures = blockly.primitiveProcedures.concat([
296
- this.gamePrimitive('ShiftUp'),
297
- this.gamePrimitive('ShiftDown'),
298
- this.gamePrimitive('ShiftLeft'),
299
- this.gamePrimitive('ShiftRight')
394
+ this._gamePrimitive('ShiftUp'),
395
+ this._gamePrimitive('ShiftDown'),
396
+ this._gamePrimitive('ShiftLeft'),
397
+ this._gamePrimitive('ShiftRight')
300
398
  ]);
301
399
  }
302
400
  },
303
401
 
304
- gamePrimitive(name) {
305
- return { alias: 'procedureDeclaration', name: name, attributes: {block_icon: `<%= @assets_url %>/media/${name}.png`} };
402
+ _gamePrimitive(name) {
403
+ return { alias: 'procedureDeclaration', name, attributes: {block_icon: `<%= @assets_url %>/media/${name}.png`} };
306
404
  },
307
405
 
308
- enableContextButton() {
309
- if(mumuki.kids && mumuki.assetsLoadedFor) {
310
- return mumuki.assetsLoadedFor('editor');
311
- } else {
312
- return postpone(this.enableContextButton.bind(this));
313
- }
406
+ _getBlockly() {
407
+ return this.$.blocklyElement;
408
+ },
314
409
 
410
+ _getStudentSolution() {
411
+ return this._getStudentEditor().value;
315
412
  },
316
413
 
317
- getBlockly: function () {
318
- return this.$.blocklyElement;
414
+ /**
415
+ * Sets the student solution xml, which corresponds
416
+ * to user content in laboratory, and default content in bibliotheca
417
+ */
418
+ _setStudentSolution(code) {
419
+ console.debug(`Setting student ${code}`)
420
+ this._getStudentEditor().value = code;
319
421
  },
320
422
 
321
- getStudentCode: function () {
322
- return this
323
- .getBlockly()
324
- .generateCode({withRegions: true, clearErrors: false});
423
+ _getDefaultCode() {
424
+ return this._getEditorDefaultValue().value;
325
425
  },
326
426
 
327
- getStudentXml: function () {
328
- return this.getEditorValue().value || "";
427
+ _getExtraCode() {
428
+ return this._getEditorExtra().value;
329
429
  },
330
430
 
331
- getTeacherCode: function () {
332
- const teacherXml = this.getEditorExtra();
333
- if (!teacherXml || !teacherXml.value) return;
334
- this.$.blocklyTmp.workspaceXml = teacherXml.value;
335
- return this.$.blocklyTmp.generateCode();
431
+ _setExtraCode(code) {
432
+ console.debug(`Setting extra ${code}`)
433
+ this._getEditorExtra().value = code;
336
434
  },
337
435
 
338
- getEditorValue: function() {
339
- return $("#mu-custom-editor-value")[0];
436
+ _getEditorExtra: function() {
437
+ return $("#mu-custom-editor-extra")[0] || {};
340
438
  },
341
439
 
342
- getEditorDefaultValue: function() {
343
- return $("#mu-custom-editor-default-value")[0];
440
+ _getEditorTest: function() {
441
+ return $("#mu-custom-editor-test")[0];
344
442
  },
345
443
 
346
- getEditorExtra: function() {
347
- return $("#mu-custom-editor-extra")[0];
444
+ _getStudentEditor: function() {
445
+ return $("#mu-custom-editor-value")[0] || {};
348
446
  },
349
447
 
350
- getEditorTest: function() {
351
- return $("#mu-custom-editor-test")[0];
448
+ _getEditorDefaultValue: function() {
449
+ return $("#mu-custom-editor-default-value")[0] || {};
352
450
  },
353
451
 
354
- compile(code) {
355
- return this._compilationMode().compile(code);
452
+ _stopExecution() {
453
+ const submit = $("kids-submit-button")[0];
454
+ if (submit && submit.$.runner.isRunning) {
455
+ submit.$.runner.stop();
456
+ }
356
457
  },
357
458
 
358
459
  _registerLayoutChangedEvent() {
@@ -361,12 +462,13 @@
361
462
  }
362
463
 
363
464
  mumuki.events.on('layoutChanged', () => {
364
- this.getEditorDefaultValue().value = this._compilationMode().defaultCode;
465
+ console.debug('layout changed');
466
+ this._setStudentSolution(this._compilationMode().defaultCode);
365
467
  });
366
468
  },
367
469
 
368
470
  _scrollToMainBlock() {
369
- this._compilationMode().scrollToMainBlock(this.getBlockly());
471
+ this._compilationMode().scrollToMainBlock(this._getBlockly());
370
472
  },
371
473
 
372
474
  _withDefaultIcons(actions, type) {
@@ -400,35 +502,31 @@
400
502
  return this._isGame() ? compilationModes.gameFramework : compilationModes.classic;
401
503
  },
402
504
 
403
- _setInitialXml: function (blockly) {
404
- const editorDefaultContent = this.getEditorDefaultValue();
405
- if (editorDefaultContent && editorDefaultContent.value) {
406
- blockly.initialXml = editorDefaultContent.value;
505
+ _setInitialXml: function () {
506
+ const blockly = this._getBlockly();
507
+ const defaultCode = this._getDefaultCode();
508
+ if (defaultCode) {
509
+ blockly.initialXml = defaultCode;
407
510
  } else {
408
511
  blockly.initialXml = blockly.workspaceXml;
409
512
  }
410
513
  },
411
514
 
412
- _initializeWorkspace: function(blockly, callback) {
413
- postpone(() => {
414
- const value = this.getEditorValue().value;
515
+ _initializeWorkspace() {
516
+ const blockly = this._getBlockly();
517
+ const value = this._getStudentSolution();
415
518
 
416
- blockly.workspaceXml = value || (
417
- this.teacherMode
418
- ? "<xml></xml>"
419
- : blockly.initialXml
420
- );
421
- this._scrollToMainBlock();
519
+ blockly.workspaceXml = value || (
520
+ this.teacherMode
521
+ ? "<xml></xml>"
522
+ : blockly.initialXml
523
+ );
422
524
 
423
- callback();
424
- });
525
+ this._scrollToMainBlock();
425
526
  },
426
527
 
427
- _subscribeToWorkspace: function(blockly, action) {
428
- setTimeout(() => {
429
- blockly.workspace.addChangeListener(action);
430
- action();
431
- });
528
+ _subscribeToWorkspace: function(action) {
529
+ this._getBlockly().workspace.addChangeListener(action);
432
530
  }
433
531
  });
434
532
  </script>
@@ -796,16 +894,21 @@
796
894
  finalBoard.update(table, head);
797
895
  }
798
896
 
799
- gbsBoardRemoveBorder(finalBoard);
897
+ this._removeBorder(finalBoard);
898
+ },
899
+
900
+ _removeBorder(board) {
901
+ board._setBorderOff();
902
+ board.updateStyles();
800
903
  },
801
904
 
802
905
  _highlight: function (region) {
803
- this._getBlockly().highlightBlock(region);
906
+ this.$editor.highlightBlock(region);
804
907
  },
805
908
 
806
909
  _showError: function (region, error) {
807
910
  const expectsTimeout = /expect_endless_while *: *true/.test(
808
- this.$editor.getEditorTest().value
911
+ this.$editor.getTestCode()
809
912
  );
810
913
 
811
914
  const isExpectedTimeout = (
@@ -815,8 +918,7 @@
815
918
 
816
919
  if (isExpectedTimeout) return;
817
920
 
818
- this._getBlockly().scrollToBlock(region);
819
- this._getBlockly().showBlockError(region, error.message);
921
+ this.$editor.showBlockError(region, error);
820
922
  },
821
923
 
822
924
  _cleanState: function () {
@@ -830,8 +932,8 @@
830
932
  finalBoard.boom = false;
831
933
  },
832
934
 
833
- _removeBlockErrors: function () {
834
- this._getBlockly().workspace.removeBlockErrors();
935
+ _removeBlockErrors() {
936
+ this.$editor.removeBlockErrors();
835
937
  },
836
938
 
837
939
  _getLastRegion: function (context = {}) {
@@ -839,10 +941,6 @@
839
941
  return regionStack && regionStack.filter(it => it).slice(-1)[0];
840
942
  },
841
943
 
842
- _getBlockly: function () {
843
- return this.$editor.getBlockly();
844
- },
845
-
846
944
  _getInitialBoards: function () {
847
945
  return $(".mu-initial-state gs-board");
848
946
  },
@@ -869,7 +967,7 @@
869
967
  },
870
968
 
871
969
  _onResetState: function () {
872
- this._getBlockly().workspace.removeBlockErrors();
970
+ this._removeBlockErrors();
873
971
  this._resetBoards();
874
972
  this._toggleInitialState();
875
973
  this.multipleScenarios.resetIndicators();
@@ -891,32 +989,22 @@
891
989
  is: 'gs-toolbox',
892
990
  properties: {
893
991
  toolboxUrl: Object,
894
- observer: '_toolboxChanged'
992
+ observer: '_setEditorToolbox'
895
993
  },
896
994
 
897
995
  attached: function () {
898
- this._setToolbox();
899
- },
900
- _toolboxChanged: function () {
901
- this._setToolbox();
902
- },
903
- _setToolbox: function () {
904
- const editor = $("mu-gobstones-custom-editor")[0];
905
- const blockly = editor.getBlockly();
906
- if(blockly.readOnly) return;
907
- const previousCode = editor.getEditorValue().value;
908
- $.get(this.toolboxUrl, function (toolboxXml) {
909
- blockly.toolbox = { defaultToolbox: toolboxXml };
910
- editor.setTeacherActions(blockly);
911
- editor.setGameActions(blockly);
912
- // @faloi - I couldn't figure out why the workspace gets replaced with a generic code,
913
- // so here I force a reset with the code previously saved. Sorry not sorry.
914
- postpone(() => {
915
- editor.reset(previousCode);
916
- });
917
- }).always(function () {
918
- editor.enableContextButton();
996
+ this.async(() => {
997
+ this._setEditorToolbox();
919
998
  });
999
+ },
1000
+
1001
+ _setEditorToolbox: function () {
1002
+ const $editor = $("mu-gobstones-custom-editor")[0];
1003
+
1004
+ // there is no editor to configure
1005
+ if (!$editor) return;
1006
+
1007
+ $editor.configureCustomToolboxFromUrl(this.toolboxUrl);
920
1008
  }
921
1009
  });
922
1010
  </script>
@@ -1,3 +1,4 @@
1
+ /* eslint-disable */
1
2
  /*! Hammer.JS - v2.0.8 - 2016-04-23
2
3
  * http://hammerjs.github.io/
3
4
  *
@@ -1,3 +1,3 @@
1
1
  module GobstonesVersionHook
2
- VERSION = '2.8.3'
2
+ VERSION = '2.10.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumuki-gobstones-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.3
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Alfonso
8
+ - Federico Aloi
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2020-09-22 00:00:00.000000000 Z
12
+ date: 2020-11-06 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: gobstones-board
@@ -173,6 +174,7 @@ dependencies:
173
174
  description:
174
175
  email:
175
176
  - rodri042@gmail.com
177
+ - federico.aloi@gmail.com
176
178
  executables: []
177
179
  extensions: []
178
180
  extra_rdoc_files: []