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 +4 -4
- data/lib/assets_server.rb +2 -0
- data/lib/checker.rb +1 -0
- data/lib/public/local-media/main-procedure.png +0 -0
- data/lib/public/local-media/program.png +0 -0
- data/lib/public/media/ShiftDown.png +0 -0
- data/lib/public/media/ShiftLeft.png +0 -0
- data/lib/public/media/ShiftRight.png +0 -0
- data/lib/public/media/ShiftUp.png +0 -0
- data/lib/render/editor/editor.css +10 -1
- data/lib/render/editor/editor.html.erb +382 -253
- data/lib/render/editor/hammer.min.js +1 -0
- data/lib/version_hook.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2438326f59bc9d3a85086e5bf5c11edc44cb2907d64fda30c23a9dcd8f952f16
|
4
|
+
data.tar.gz: 16ea0ef08aed78f7f24b67d21088b7ab3f0b6bee8fd4d42798010ef5915000b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f762dc032fd7d2f32cadf4d88fb4d2c14659e270af017bc498bb2dfcd65e91f38c1314fb23c44d05f87ef1e2c3bbc681f45c08de444769d2e60d009ecff62e0c
|
7
|
+
data.tar.gz: 5fa4241b9d422bba3ac77803fefe331958830d0c110fa80cec8b2dd80cf544b31447cc4b73e1188b2a46dfca3585aebc13f3719a415406b101d65bed00766751
|
data/lib/assets_server.rb
CHANGED
@@ -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
|
data/lib/checker.rb
CHANGED
@@ -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
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
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
|
-
|
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
|
-
|
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
|
-
|
107
|
-
|
108
|
-
Blockly.HSV_VALUE = 1;
|
117
|
+
attached() {
|
118
|
+
this.$exerciseContainer = $('.mu-kids-exercise');
|
109
119
|
|
110
|
-
|
111
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
150
|
-
if (typeof Blockly === 'undefined' || !Blockly.CUSTOM_COLORS) return postpone(setBlocklyCustomSettings);
|
133
|
+
// ==== Public methods (accesible from other components)
|
151
134
|
|
152
|
-
|
153
|
-
|
154
|
-
|
135
|
+
reset() {
|
136
|
+
const blockly = this._getBlockly();
|
137
|
+
blockly.workspaceXml = blockly.initialXml;
|
138
|
+
this._scrollToMainBlock();
|
139
|
+
},
|
155
140
|
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
161
|
-
Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace("/", "");
|
162
|
-
};
|
150
|
+
console.debug('Configuring custom toolbox');
|
163
151
|
|
164
|
-
|
165
|
-
|
152
|
+
$.get(toolboxUrl, (toolboxXml) => {
|
153
|
+
this._initializeCustomToolboxBlocklyWorkspace(toolboxXml);
|
154
|
+
});
|
155
|
+
},
|
166
156
|
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
editorExtra.value = blockly.workspaceXml;
|
176
|
-
}
|
177
|
-
}
|
165
|
+
compile(code) {
|
166
|
+
return this._compilationMode().compile(code);
|
167
|
+
},
|
178
168
|
|
179
|
-
|
180
|
-
|
181
|
-
|
169
|
+
hasInteractiveProgram() {
|
170
|
+
return this._getBlockly().initialXml.indexOf("block type=\"InteractiveProgram\"") !== -1;
|
171
|
+
},
|
182
172
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
};
|
173
|
+
toggleInteractiveMode() {
|
174
|
+
this.$exerciseContainer.toggleClass('play-mode');
|
175
|
+
this._triggerResize();
|
176
|
+
},
|
188
177
|
|
189
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
};
|
185
|
+
getStudentCode: function () {
|
186
|
+
return this
|
187
|
+
._getBlockly()
|
188
|
+
.generateCode({withRegions: true, clearErrors: false});
|
189
|
+
},
|
197
190
|
|
198
|
-
|
191
|
+
getStudentXml: function () {
|
192
|
+
return this._getStudentSolution() || "";
|
193
|
+
},
|
199
194
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
setTrashPosition();
|
204
|
-
});
|
205
|
-
};
|
195
|
+
getTestCode() {
|
196
|
+
return this._getEditorTest().value;
|
197
|
+
},
|
206
198
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
})
|
211
|
-
};
|
199
|
+
removeBlockErrors() {
|
200
|
+
return this._getBlockly().workspace.removeBlockErrors();
|
201
|
+
},
|
212
202
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
};
|
203
|
+
showBlockError(region, error) {
|
204
|
+
this._getBlockly().scrollToBlock(region);
|
205
|
+
this._getBlockly().showBlockError(region, error.message);
|
206
|
+
},
|
218
207
|
|
219
|
-
|
220
|
-
|
221
|
-
|
208
|
+
highlightBlock(region) {
|
209
|
+
this._getBlockly().highlightBlock(region);
|
210
|
+
},
|
222
211
|
|
223
|
-
|
212
|
+
// ==== Private methods (only for internal usage)
|
224
213
|
|
225
|
-
|
214
|
+
_configureBlocklyAppearance() {
|
215
|
+
this._getBlockly().showCategories = !this._isKindergarten();
|
216
|
+
this._setBlocklyDisplayMode();
|
217
|
+
this._setBlocklySounds();
|
218
|
+
this._setBlocklyColors();
|
226
219
|
|
227
|
-
|
228
|
-
|
229
|
-
|
220
|
+
if (!this.readOnly) {
|
221
|
+
this._relocateTrashOnResize();
|
222
|
+
}
|
230
223
|
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
240
|
-
|
228
|
+
_configureBlocklyBehavior() {
|
229
|
+
this._setTeacherActions();
|
230
|
+
this._setGameActions();
|
231
|
+
this._initializeNonCustomToolboxBlocklyWorkspace();
|
232
|
+
this._registerLayoutChangedEvent();
|
233
|
+
},
|
241
234
|
|
242
|
-
|
243
|
-
|
235
|
+
_relocateTrashOnResize() {
|
236
|
+
$('.mu-kids-context, .mu-kids-results').on('hidden.bs.modal shown.bs.modal', () => {
|
237
|
+
this._relocateTrash();
|
238
|
+
});
|
244
239
|
|
245
|
-
|
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
|
-
|
250
|
-
|
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
|
-
|
254
|
-
this
|
255
|
-
|
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
|
-
|
259
|
-
|
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
|
-
|
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
|
-
|
388
|
+
_setTeacherActions() {
|
281
389
|
const teacherCode = this.getTeacherCode();
|
282
|
-
if (teacherCode) {
|
283
|
-
|
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
|
-
|
293
|
-
if (this._isGame()) {
|
294
|
-
|
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
|
-
|
305
|
-
|
417
|
+
_setPrimitiveProcedures(procedures) {
|
418
|
+
this._setBlocklyActions("primitiveProcedures", procedures);
|
306
419
|
},
|
307
420
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
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
|
-
|
439
|
+
_getBlockly() {
|
318
440
|
return this.$.blocklyElement;
|
319
441
|
},
|
320
442
|
|
321
|
-
|
322
|
-
return this
|
323
|
-
.getBlockly()
|
324
|
-
.generateCode({withRegions: true, clearErrors: false});
|
443
|
+
_getStudentSolution() {
|
444
|
+
return this._getStudentEditor().value;
|
325
445
|
},
|
326
446
|
|
327
|
-
|
328
|
-
|
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
|
-
|
332
|
-
|
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
|
-
|
339
|
-
return
|
464
|
+
_getExtraCode() {
|
465
|
+
return this._getEditorExtra().value;
|
340
466
|
},
|
341
467
|
|
342
|
-
|
343
|
-
|
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
|
-
|
347
|
-
return $("#mu-custom-editor-extra")[0];
|
477
|
+
_getEditorExtra: function() {
|
478
|
+
return $("#mu-custom-editor-extra")[0] || {};
|
348
479
|
},
|
349
480
|
|
350
|
-
|
481
|
+
_getEditorTest: function() {
|
351
482
|
return $("#mu-custom-editor-test")[0];
|
352
483
|
},
|
353
484
|
|
354
|
-
|
355
|
-
return
|
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
|
-
|
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.
|
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 (
|
404
|
-
const
|
405
|
-
|
406
|
-
|
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
|
413
|
-
|
414
|
-
|
556
|
+
_initializeWorkspace() {
|
557
|
+
const blockly = this._getBlockly();
|
558
|
+
const value = this._getStudentSolution();
|
415
559
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
this._scrollToMainBlock();
|
560
|
+
blockly.workspaceXml = value || (
|
561
|
+
this.teacherMode
|
562
|
+
? "<xml></xml>"
|
563
|
+
: blockly.initialXml
|
564
|
+
);
|
422
565
|
|
423
|
-
|
424
|
-
});
|
566
|
+
this._scrollToMainBlock();
|
425
567
|
},
|
426
568
|
|
427
|
-
_subscribeToWorkspace: function(
|
428
|
-
|
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
|
-
|
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.
|
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.
|
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.
|
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
|
834
|
-
this.
|
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.
|
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: '
|
1033
|
+
observer: '_setEditorToolbox'
|
895
1034
|
},
|
896
1035
|
|
897
1036
|
attached: function () {
|
898
|
-
this.
|
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>
|
data/lib/version_hook.rb
CHANGED
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.
|
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-
|
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: []
|