mumuki-gobstones-runner 2.8.4 → 2.10.1

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: b81904b19b4ecb207d1b52ffec98b8cd617b83f47eb02ec37b4fdd33f7087db4
4
- data.tar.gz: ad185dffbfc9c311578b2f4ab5a71f0b42cf4bd2110563ede627e4050d1280d8
3
+ metadata.gz: 2438326f59bc9d3a85086e5bf5c11edc44cb2907d64fda30c23a9dcd8f952f16
4
+ data.tar.gz: 16ea0ef08aed78f7f24b67d21088b7ab3f0b6bee8fd4d42798010ef5915000b5
5
5
  SHA512:
6
- metadata.gz: a9f6ac4c47444e5ff26c1151d19a120dd797bf16d394518399157be3eda926b869064b9536ee940c0587168bea17b2e492dad48678460cb9f5558fd5ecc1909f
7
- data.tar.gz: 87e3b5464e1daf5027c0f84e9fe806076f343542e0bd4ce1ca3bdea61ee345e7f46bc066bca4ca8267a8dad8dd7c9ce8581c5e6cb3b81a35042b8c088f1fa643
6
+ metadata.gz: f762dc032fd7d2f32cadf4d88fb4d2c14659e270af017bc498bb2dfcd65e91f38c1314fb23c44d05f87ef1e2c3bbc681f45c08de444769d2e60d009ecff62e0c
7
+ data.tar.gz: 5fa4241b9d422bba3ac77803fefe331958830d0c110fa80cec8b2dd80cf544b31447cc4b73e1188b2a46dfca3585aebc13f3719a415406b101d65bed00766751
@@ -54,6 +54,8 @@ class Mumukit::Server::App < Sinatra::Base
54
54
 
55
55
  get '/assets/editor/editor.html' do
56
56
  cross_origin
57
+ content_type 'text/html'
58
+
57
59
  @game_framework_extra = Gobstones::CompilationMode::GameFramework.extra_code
58
60
  @game_framework_program = Gobstones::CompilationMode::GameFramework.program_code
59
61
  @game_framework_default = Gobstones::CompilationMode::GameFramework.default_code
@@ -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,35 @@
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 if (a) {
13
+ const absoluteUrl = a
14
+ .replace("../media/", "../local-media/")
15
+ .replace("../local-media/", "<%= @assets_url %>/local-media/");
16
+ this.__setValue__(absoluteUrl);
17
+ } else {
18
+ this.__setValue__(a);
19
+ }
20
+ }
21
+
22
+ Blockly.FieldImage.prototype.__setTooltip__ = Blockly.FieldImage.prototype.setTooltip;
23
+ Blockly.FieldImage.prototype.setTooltip = function(a) {
24
+ try {
25
+ this.__setTooltip__(a);
26
+ } catch (_e) {
27
+ console.debug('Could not set tooltip');
28
+ }
29
+ }
30
+
31
+ function postpone(action) {
32
+ return setTimeout(action, 50);
33
+ }
34
+ </script>
35
+
7
36
  <dom-module id="mu-gobstones-custom-editor">
8
37
 
9
38
  <template>
@@ -20,15 +49,6 @@
20
49
  </template>
21
50
 
22
51
  <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
52
  const compilationModes = {
33
53
  gameFramework: {
34
54
  compile: ({ main, teacher, ...args }) => {
@@ -46,7 +66,9 @@
46
66
 
47
67
  scrollToMainBlock: (blockly) => {
48
68
  const mainBlock = blockly.getBlocksOfType('procedures_defnoreturnnoparams')[0];
49
- blockly.scrollToBlock(mainBlock.id);
69
+ if (mainBlock) {
70
+ blockly.scrollToBlock(mainBlock.id);
71
+ }
50
72
  },
51
73
 
52
74
  defaultCode: `<%= @game_framework_default %>`
@@ -90,184 +112,270 @@
90
112
  }
91
113
  },
92
114
 
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
- };
115
+ // ==== Polymer events
105
116
 
106
- const setBlocklyColors = () => {
107
- Blockly.HSV_SATURATION = 0.64;
108
- Blockly.HSV_VALUE = 1;
117
+ attached() {
118
+ this.$exerciseContainer = $('.mu-kids-exercise');
109
119
 
110
- Blockly.MUMUKI_COLORS = {
111
- pink: "#FF5C82",
112
- blue: "#5CBEFF",
113
- yellow: "#FFC95C",
114
- green: "#5CED71"
115
- };
120
+ /*
121
+ https://polymer-library.polymer-project.org/1.0/docs/devguide/registering-elements#initialization-order
116
122
 
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
- };
123
+ There are no guarantees with regard to initialization timing between sibling elements.
124
+ This means that siblings may become ready in any order.
125
+ For accessing sibling elements when an element initializes, you can call async from inside the attached callback.
126
+ */
127
+ this.async(() => {
128
+ this._configureBlocklyAppearance();
129
+ this._configureBlocklyBehavior();
130
+ });
131
+ },
148
132
 
149
- const setBlocklyCustomSettings = () => {
150
- if (typeof Blockly === 'undefined' || !Blockly.CUSTOM_COLORS) return postpone(setBlocklyCustomSettings);
133
+ // ==== Public methods (accesible from other components)
151
134
 
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`;
135
+ reset() {
136
+ const blockly = this._getBlockly();
137
+ blockly.workspaceXml = blockly.initialXml;
138
+ this._scrollToMainBlock();
139
+ },
155
140
 
156
- Blockly.displayMode = this._isKindergarten() ? 'iconic' : 'text';
157
- setBlocklySounds();
158
- setBlocklyColors();
141
+ /**
142
+ * Configures a custom toolbox using a toolbox url
143
+ * This method does nothing if editor is read-only, but
144
+ * assumes a gs-toolbox element is present - otherwise it would
145
+ * have no sense to have a toolboxUrl.
146
+ */
147
+ configureCustomToolboxFromUrl(toolboxUrl) {
148
+ if (this.readOnly) return;
159
149
 
160
- // Removing "/" from the block id character set to avoid syntax errors
161
- Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace("/", "");
162
- };
150
+ console.debug('Configuring custom toolbox');
163
151
 
164
- const updateFields = () => {
165
- const blockly = this.getBlockly();
152
+ $.get(toolboxUrl, (toolboxXml) => {
153
+ this._initializeCustomToolboxBlocklyWorkspace(toolboxXml);
154
+ });
155
+ },
166
156
 
167
- const editorValue = this.getEditorValue();
168
- if (editorValue) {
169
- editorValue.value = blockly.workspaceXml;
170
- }
157
+ /**
158
+ * Wether a custom toolbox is required. Read only editors and gs-toolbox-less context
159
+ * don't require it
160
+ */
161
+ hasCustomToolbox() {
162
+ return !this.readOnly && $('gs-toolbox').length;
163
+ },
171
164
 
172
- if (this.teacherMode) {
173
- const editorExtra = this.getEditorExtra();
174
- if (editorExtra) {
175
- editorExtra.value = blockly.workspaceXml;
176
- }
177
- }
165
+ compile(code) {
166
+ return this._compilationMode().compile(code);
167
+ },
178
168
 
179
- if (typeof angular !== 'undefined') {
180
- angular.element(editorValue).triggerHandler("change");
181
- }
169
+ hasInteractiveProgram() {
170
+ return this._getBlockly().initialXml.indexOf("block type=\"InteractiveProgram\"") !== -1;
171
+ },
182
172
 
183
- const submit = $("kids-submit-button")[0];
184
- if (submit && submit.$.runner.isRunning) {
185
- submit.$.runner.stop();
186
- }
187
- };
173
+ toggleInteractiveMode() {
174
+ this.$exerciseContainer.toggleClass('play-mode');
175
+ this._triggerResize();
176
+ },
188
177
 
189
- this.$exerciseContainer = $('.mu-kids-exercise');
178
+ getTeacherCode() {
179
+ const extraCode = this._getExtraCode();
180
+ if (!extraCode) return;
181
+ this.$.blocklyTmp.workspaceXml = extraCode;
182
+ return this.$.blocklyTmp.generateCode();
183
+ },
190
184
 
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
- };
185
+ getStudentCode: function () {
186
+ return this
187
+ ._getBlockly()
188
+ .generateCode({withRegions: true, clearErrors: false});
189
+ },
197
190
 
198
- var setTrashTimeout;
191
+ getStudentXml: function () {
192
+ return this._getStudentSolution() || "";
193
+ },
199
194
 
200
- const localOnResize = () => {
201
- clearTimeout(setTrashTimeout);
202
- setTrashTimeout = setTimeout(() => {
203
- setTrashPosition();
204
- });
205
- };
195
+ getTestCode() {
196
+ return this._getEditorTest().value;
197
+ },
206
198
 
207
- const triggerResizeOnContextModalClose = () => {
208
- $('.mu-kids-context, .mu-kids-results').on('hidden.bs.modal shown.bs.modal', function () {
209
- localOnResize();
210
- })
211
- };
199
+ removeBlockErrors() {
200
+ return this._getBlockly().workspace.removeBlockErrors();
201
+ },
212
202
 
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
- };
203
+ showBlockError(region, error) {
204
+ this._getBlockly().scrollToBlock(region);
205
+ this._getBlockly().showBlockError(region, error.message);
206
+ },
218
207
 
219
- const initialize = () => {
220
- postpone(() => {
221
- const blockly = this.getBlockly();
208
+ highlightBlock(region) {
209
+ this._getBlockly().highlightBlock(region);
210
+ },
222
211
 
223
- if (!blockly || !blockly.workspace) return initialize();
212
+ // ==== Private methods (only for internal usage)
224
213
 
225
- blockly.showCategories = !this._isKindergarten();
214
+ _configureBlocklyAppearance() {
215
+ this._getBlockly().showCategories = !this._isKindergarten();
216
+ this._setBlocklyDisplayMode();
217
+ this._setBlocklySounds();
218
+ this._setBlocklyColors();
226
219
 
227
- if (!this.readOnly) {
228
- relocateTrash(blockly);
229
- }
220
+ if (!this.readOnly) {
221
+ this._relocateTrashOnResize();
222
+ }
230
223
 
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();
224
+ // Removing "/" from the block id character set to avoid syntax errors
225
+ Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace("/", "");
226
+ },
238
227
 
239
- this._subscribeToWorkspace(blockly, updateFields);
240
- });
228
+ _configureBlocklyBehavior() {
229
+ this._setTeacherActions();
230
+ this._setGameActions();
231
+ this._initializeNonCustomToolboxBlocklyWorkspace();
232
+ this._registerLayoutChangedEvent();
233
+ },
241
234
 
242
- const hasCustomToolbox = $('gs-toolbox').length;
243
- if(!hasCustomToolbox) this.enableContextButton();
235
+ _relocateTrashOnResize() {
236
+ $('.mu-kids-context, .mu-kids-results').on('hidden.bs.modal shown.bs.modal', () => {
237
+ this._relocateTrash();
238
+ });
244
239
 
245
- this._registerLayoutChangedEvent();
246
- });
240
+ $(window).resize(() => this._relocateTrash());
241
+ },
242
+
243
+
244
+ /**
245
+ * Initializes a workspace using a custom toolbox.
246
+ *
247
+ * Blockly's workspace is destroyed when toolbox changes, so initialization
248
+ * is performed here
249
+ */
250
+ _initializeCustomToolboxBlocklyWorkspace(toolboxXml) {
251
+ this._getBlockly().toolbox = { defaultToolbox: toolboxXml };
252
+ this._initializeBlocklyWorkspace();
253
+ },
254
+
255
+ /**
256
+ * Initializes a workspace using a non-custom toolbox.
257
+ *
258
+ * Blockly's workspace is destroyed when toolbox changes,
259
+ * so this method will initialize it only if a there is a custom toolbox
260
+ * that will be called later.
261
+ */
262
+ _initializeNonCustomToolboxBlocklyWorkspace() {
263
+ if (!this.hasCustomToolbox()) {
264
+ this._initializeBlocklyWorkspace();
265
+ }
266
+ },
267
+
268
+ _initializeBlocklyWorkspace() {
269
+ console.debug('Initializing Blockly Workspace');
270
+
271
+ this._setInitialXml();
272
+ this._initializeWorkspace();
273
+ this._subscribeToWorkspace(() => this._updateSolution());
274
+
275
+ if (this.hasInteractiveProgram()) {
276
+ this._setInteractiveLayout();
277
+ }
278
+
279
+ mumuki.assetsLoadedFor('editor');
280
+ },
281
+
282
+ _relocateTrash() {
283
+ const width = $('#blocklyDiv').width() - 68;
284
+ const height = $('#blocklyDiv').height() - 210;
285
+ $('.blocklyTrash').css("transform", `translate(${width}px, ${height}px)`);
286
+ $('.blocklyTrash').css("display", "unset");
287
+ },
288
+
289
+ _updateSolution() {
290
+ console.debug("Updating solution");
291
+ const blockly = this._getBlockly();
292
+ let changed;
293
+ if (this.teacherMode) {
294
+ changed = this._setExtraCode(blockly.workspaceXml);
295
+ } else {
296
+ changed = this._setStudentSolution(blockly.workspaceXml);
297
+ }
298
+
299
+ if (!changed) {
300
+ console.debug("No solution update required")
301
+ return;
302
+ }
303
+
304
+ // TODO: this hack enables Angular two-way binding for Bibliotheca. Should be replaced with Mumuki events system
305
+ if (typeof angular !== 'undefined') {
306
+ angular.element(this._getStudentEditor()).triggerHandler("change");
307
+ angular.element(this._getEditorExtra()).triggerHandler("change");
308
+ }
309
+
310
+ this._stopExecution();
311
+ },
312
+
313
+ _setBlocklySounds() {
314
+ Blockly.WorkspaceAudio.prototype.preload = () => {
315
+ for (const soundName in this.SOUNDS_) {
316
+ const sound = this.SOUNDS_[soundName];
317
+ sound.volume = 0.01;
318
+ const playPromise = sound.play();
319
+ playPromise && playPromise.then(sound.pause.bind(sound));
320
+ if (goog.userAgent.IPAD || goog.userAgent.IPHONE) break;
321
+ }
247
322
  };
323
+ },
248
324
 
249
- setBlocklyCustomSettings();
250
- initialize();
325
+ _setBlocklyColors() {
326
+ Blockly.HSV_SATURATION = 0.64;
327
+ Blockly.HSV_VALUE = 1;
328
+
329
+ Blockly.MUMUKI_COLORS = {
330
+ pink: "#FF5C82",
331
+ blue: "#5CBEFF",
332
+ yellow: "#FFC95C",
333
+ green: "#5CED71"
334
+ };
335
+
336
+ // reserved
337
+ Blockly.CUSTOM_COLORS.program = Blockly.MUMUKI_COLORS.pink;
338
+ Blockly.CUSTOM_COLORS.interactiveProgram = Blockly.MUMUKI_COLORS.pink;
339
+ Blockly.CUSTOM_COLORS.interactiveBinding = Blockly.MUMUKI_COLORS.pink;
340
+ Blockly.CUSTOM_COLORS.procedure = Blockly.MUMUKI_COLORS.pink;
341
+ Blockly.CUSTOM_COLORS.function = Blockly.MUMUKI_COLORS.pink;
342
+ Blockly.CUSTOM_COLORS.complete = Blockly.MUMUKI_COLORS.pink;
343
+
344
+ // control structures
345
+ Blockly.CUSTOM_COLORS.controlStructure = this._isKindergarten() ? Blockly.MUMUKI_COLORS.green : Blockly.MUMUKI_COLORS.yellow;
346
+
347
+ // commands
348
+ Blockly.CUSTOM_COLORS.primitiveCommand = Blockly.MUMUKI_COLORS.yellow;
349
+ Blockly.CUSTOM_COLORS.primitiveProcedure = Blockly.MUMUKI_COLORS.yellow;
350
+ Blockly.CUSTOM_COLORS.procedure_call = Blockly.MUMUKI_COLORS.yellow;
351
+ Blockly.CUSTOM_COLORS.assignation = Blockly.MUMUKI_COLORS.yellow;
352
+
353
+ // expressions
354
+ Blockly.Msg.MATH_HUE = Blockly.MUMUKI_COLORS.blue;
355
+ Blockly.CUSTOM_COLORS.literalExpression = Blockly.MUMUKI_COLORS.blue;
356
+ Blockly.CUSTOM_COLORS.primitiveExpression = Blockly.MUMUKI_COLORS.blue;
357
+ Blockly.CUSTOM_COLORS.operator = Blockly.MUMUKI_COLORS.blue;
358
+ Blockly.CUSTOM_COLORS.variable = Blockly.MUMUKI_COLORS.blue;
359
+ Blockly.CUSTOM_COLORS.parameter = Blockly.MUMUKI_COLORS.blue;
360
+ Blockly.CUSTOM_COLORS.primitiveFunction = Blockly.MUMUKI_COLORS.blue;
361
+ Blockly.CUSTOM_COLORS.function_call = Blockly.MUMUKI_COLORS.blue;
251
362
  },
252
363
 
253
- toggleInteractiveMode() {
254
- this.$exerciseContainer.toggleClass('play-mode');
255
- this.triggerResize();
364
+ _setBlocklyDisplayMode() {
365
+ // The display mode configuration could be monkey-patched here, like this:
366
+ Blockly.displayModes.iconic.iconSize = 64;
367
+ Blockly.displayModes.iconic.procedureDefIcon = `${this.localMediaUrl}main-procedure.png`;
368
+
369
+ Blockly.displayMode = this._isKindergarten() ? 'iconic' : 'text';
256
370
  },
257
371
 
258
- triggerResize() {
259
- let event = document.createEvent('HTMLEvents');
372
+ _triggerResize() {
373
+ const event = document.createEvent('HTMLEvents');
260
374
  event.initEvent('resize', true, false);
261
375
  document.dispatchEvent(event);
262
376
  },
263
377
 
264
- reset(code) {
265
- const blockly = this.getBlockly();
266
- blockly.workspaceXml = code || blockly.initialXml;
267
- this._scrollToMainBlock();
268
- },
269
-
270
- setInteractiveLayout() {
378
+ _setInteractiveLayout() {
271
379
  this.$exerciseContainer.addClass('mu-kids-interactive');
272
380
  $('.mu-final-state').html('<gs-keyboard/>');
273
381
  $('.mu-editor').append($('<kids-interactive-submit-button/>'));
@@ -277,82 +385,116 @@
277
385
  })
278
386
  },
279
387
 
280
- setTeacherActions(blockly) {
388
+ _setTeacherActions() {
281
389
  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
- });
390
+ if (!teacherCode) {
391
+ return;
289
392
  }
393
+
394
+ console.debug("Set teacher actions");
395
+ const actions = new Parser().getActionsFromSource(teacherCode);
396
+
397
+ this._setPrimitiveProcedures(this._withDefaultIcons(actions, 'procedureDeclarations'));
398
+ this._setPrimitiveFunctions(this._withDefaultIcons(actions, 'functionDeclarations'));
290
399
  },
291
400
 
292
- setGameActions(blockly) {
293
- if (this._isGame()) {
294
- blockly.primitiveProcedures = blockly.primitiveProcedures || [];
295
- blockly.primitiveProcedures = blockly.primitiveProcedures.concat([
296
- this.gamePrimitive('ShiftUp'),
297
- this.gamePrimitive('ShiftDown'),
298
- this.gamePrimitive('ShiftLeft'),
299
- this.gamePrimitive('ShiftRight')
300
- ]);
401
+ _setGameActions() {
402
+ if (!this._isGame()) {
403
+ return;
301
404
  }
405
+
406
+ console.debug("Set game actions");
407
+ const blockly = this._getBlockly();
408
+
409
+ this._setPrimitiveProcedures((blockly.primitiveProcedures || []).concat([
410
+ this._gamePrimitive('ShiftUp'),
411
+ this._gamePrimitive('ShiftDown'),
412
+ this._gamePrimitive('ShiftLeft'),
413
+ this._gamePrimitive('ShiftRight')
414
+ ]));
302
415
  },
303
416
 
304
- gamePrimitive(name) {
305
- return { alias: 'procedureDeclaration', name: name, attributes: {block_icon: `<%= @assets_url %>/media/${name}.png`} };
417
+ _setPrimitiveProcedures(procedures) {
418
+ this._setBlocklyActions("primitiveProcedures", procedures);
306
419
  },
307
420
 
308
- enableContextButton() {
309
- if(mumuki.kids && mumuki.assetsLoadedFor) {
310
- return mumuki.assetsLoadedFor('editor');
311
- } else {
312
- return postpone(this.enableContextButton.bind(this));
421
+ _setPrimitiveFunctions(functions) {
422
+ this._setBlocklyActions("primitiveFunctions", functions);
423
+ },
424
+
425
+ _setBlocklyActions(key, actions) {
426
+ try {
427
+ this._getBlockly()[key] = actions
428
+ } catch (e) {
429
+ if (this._getBlockly()[key] !== actions) {
430
+ throw e;
431
+ }
313
432
  }
433
+ },
314
434
 
435
+ _gamePrimitive(name) {
436
+ return { alias: 'procedureDeclaration', name, attributes: {block_icon: `<%= @assets_url %>/media/${name}.png`} };
315
437
  },
316
438
 
317
- getBlockly: function () {
439
+ _getBlockly() {
318
440
  return this.$.blocklyElement;
319
441
  },
320
442
 
321
- getStudentCode: function () {
322
- return this
323
- .getBlockly()
324
- .generateCode({withRegions: true, clearErrors: false});
443
+ _getStudentSolution() {
444
+ return this._getStudentEditor().value;
325
445
  },
326
446
 
327
- getStudentXml: function () {
328
- return this.getEditorValue().value || "";
447
+ /**
448
+ * Sets the student solution xml, which corresponds
449
+ * to user content in laboratory, and default content in bibliotheca
450
+ */
451
+ _setStudentSolution(code) {
452
+ if (this._getStudentEditor().value !== code) {
453
+ console.debug(`Setting student ${code}`)
454
+ this._getStudentEditor().value = code;
455
+ return true;
456
+ }
457
+ return false;
329
458
  },
330
459
 
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();
460
+ _getDefaultCode() {
461
+ return this._getEditorDefaultValue().value;
336
462
  },
337
463
 
338
- getEditorValue: function() {
339
- return $("#mu-custom-editor-value")[0];
464
+ _getExtraCode() {
465
+ return this._getEditorExtra().value;
340
466
  },
341
467
 
342
- getEditorDefaultValue: function() {
343
- return $("#mu-custom-editor-default-value")[0];
468
+ _setExtraCode(code) {
469
+ if (this._getEditorExtra().value !== code) {
470
+ console.debug(`Setting extra ${code}`)
471
+ this._getEditorExtra().value = code;
472
+ return true;
473
+ }
474
+ return false;
344
475
  },
345
476
 
346
- getEditorExtra: function() {
347
- return $("#mu-custom-editor-extra")[0];
477
+ _getEditorExtra: function() {
478
+ return $("#mu-custom-editor-extra")[0] || {};
348
479
  },
349
480
 
350
- getEditorTest: function() {
481
+ _getEditorTest: function() {
351
482
  return $("#mu-custom-editor-test")[0];
352
483
  },
353
484
 
354
- compile(code) {
355
- return this._compilationMode().compile(code);
485
+ _getStudentEditor: function() {
486
+ return $("#mu-custom-editor-value")[0] || {};
487
+ },
488
+
489
+ _getEditorDefaultValue: function() {
490
+ return $("#mu-custom-editor-default-value")[0] || {};
491
+ },
492
+
493
+ _stopExecution() {
494
+ const submit = $("kids-submit-button")[0];
495
+ if (submit && submit.$.runner.isRunning) {
496
+ submit.$.runner.stop();
497
+ }
356
498
  },
357
499
 
358
500
  _registerLayoutChangedEvent() {
@@ -361,12 +503,13 @@
361
503
  }
362
504
 
363
505
  mumuki.events.on('layoutChanged', () => {
364
- this.getEditorDefaultValue().value = this._compilationMode().defaultCode;
506
+ console.debug('layout changed');
507
+ this._setStudentSolution(this._compilationMode().defaultCode);
365
508
  });
366
509
  },
367
510
 
368
511
  _scrollToMainBlock() {
369
- this._compilationMode().scrollToMainBlock(this.getBlockly());
512
+ this._compilationMode().scrollToMainBlock(this._getBlockly());
370
513
  },
371
514
 
372
515
  _withDefaultIcons(actions, type) {
@@ -400,35 +543,31 @@
400
543
  return this._isGame() ? compilationModes.gameFramework : compilationModes.classic;
401
544
  },
402
545
 
403
- _setInitialXml: function (blockly) {
404
- const editorDefaultContent = this.getEditorDefaultValue();
405
- if (editorDefaultContent && editorDefaultContent.value) {
406
- blockly.initialXml = editorDefaultContent.value;
546
+ _setInitialXml: function () {
547
+ const blockly = this._getBlockly();
548
+ const defaultCode = this._getDefaultCode();
549
+ if (defaultCode) {
550
+ blockly.initialXml = defaultCode;
407
551
  } else {
408
552
  blockly.initialXml = blockly.workspaceXml;
409
553
  }
410
554
  },
411
555
 
412
- _initializeWorkspace: function(blockly, callback) {
413
- postpone(() => {
414
- const value = this.getEditorValue().value;
556
+ _initializeWorkspace() {
557
+ const blockly = this._getBlockly();
558
+ const value = this._getStudentSolution();
415
559
 
416
- blockly.workspaceXml = value || (
417
- this.teacherMode
418
- ? "<xml></xml>"
419
- : blockly.initialXml
420
- );
421
- this._scrollToMainBlock();
560
+ blockly.workspaceXml = value || (
561
+ this.teacherMode
562
+ ? "<xml></xml>"
563
+ : blockly.initialXml
564
+ );
422
565
 
423
- callback();
424
- });
566
+ this._scrollToMainBlock();
425
567
  },
426
568
 
427
- _subscribeToWorkspace: function(blockly, action) {
428
- setTimeout(() => {
429
- blockly.workspace.addChangeListener(action);
430
- action();
431
- });
569
+ _subscribeToWorkspace: function(action) {
570
+ this._getBlockly().workspace.addChangeListener(action);
432
571
  }
433
572
  });
434
573
  </script>
@@ -796,16 +935,21 @@
796
935
  finalBoard.update(table, head);
797
936
  }
798
937
 
799
- gbsBoardRemoveBorder(finalBoard);
938
+ this._removeBorder(finalBoard);
939
+ },
940
+
941
+ _removeBorder(board) {
942
+ board._setBorderOff();
943
+ board.updateStyles();
800
944
  },
801
945
 
802
946
  _highlight: function (region) {
803
- this._getBlockly().highlightBlock(region);
947
+ this.$editor.highlightBlock(region);
804
948
  },
805
949
 
806
950
  _showError: function (region, error) {
807
951
  const expectsTimeout = /expect_endless_while *: *true/.test(
808
- this.$editor.getEditorTest().value
952
+ this.$editor.getTestCode()
809
953
  );
810
954
 
811
955
  const isExpectedTimeout = (
@@ -815,8 +959,7 @@
815
959
 
816
960
  if (isExpectedTimeout) return;
817
961
 
818
- this._getBlockly().scrollToBlock(region);
819
- this._getBlockly().showBlockError(region, error.message);
962
+ this.$editor.showBlockError(region, error);
820
963
  },
821
964
 
822
965
  _cleanState: function () {
@@ -830,8 +973,8 @@
830
973
  finalBoard.boom = false;
831
974
  },
832
975
 
833
- _removeBlockErrors: function () {
834
- this._getBlockly().workspace.removeBlockErrors();
976
+ _removeBlockErrors() {
977
+ this.$editor.removeBlockErrors();
835
978
  },
836
979
 
837
980
  _getLastRegion: function (context = {}) {
@@ -839,10 +982,6 @@
839
982
  return regionStack && regionStack.filter(it => it).slice(-1)[0];
840
983
  },
841
984
 
842
- _getBlockly: function () {
843
- return this.$editor.getBlockly();
844
- },
845
-
846
985
  _getInitialBoards: function () {
847
986
  return $(".mu-initial-state gs-board");
848
987
  },
@@ -869,7 +1008,7 @@
869
1008
  },
870
1009
 
871
1010
  _onResetState: function () {
872
- this._getBlockly().workspace.removeBlockErrors();
1011
+ this._removeBlockErrors();
873
1012
  this._resetBoards();
874
1013
  this._toggleInitialState();
875
1014
  this.multipleScenarios.resetIndicators();
@@ -891,32 +1030,22 @@
891
1030
  is: 'gs-toolbox',
892
1031
  properties: {
893
1032
  toolboxUrl: Object,
894
- observer: '_toolboxChanged'
1033
+ observer: '_setEditorToolbox'
895
1034
  },
896
1035
 
897
1036
  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();
1037
+ this.async(() => {
1038
+ this._setEditorToolbox();
919
1039
  });
1040
+ },
1041
+
1042
+ _setEditorToolbox: function () {
1043
+ const $editor = $("mu-gobstones-custom-editor")[0];
1044
+
1045
+ // there is no editor to configure
1046
+ if (!$editor) return;
1047
+
1048
+ $editor.configureCustomToolboxFromUrl(this.toolboxUrl);
920
1049
  }
921
1050
  });
922
1051
  </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.4'
2
+ VERSION = '2.10.1'
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.4
4
+ version: 2.10.1
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-24 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: []