mumuki-gobstones-runner 2.8.5 → 2.11.0
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/feedback_hook.rb +210 -0
- data/lib/gobstones_runner.rb +3 -2
- data/lib/locales/en.yml +12 -0
- data/lib/locales/es.yml +12 -0
- data/lib/locales/pt.yml +12 -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 +133 -36
- data/lib/version_hook.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1636b8a5a322c4a74f84a725e8df8fa0118e38362b3732a70e76deba7b87e620
|
4
|
+
data.tar.gz: 41d818b4cb24ce921ed45f0c539437ac0395ddc4152e2f5a4ebf3e2a19168b07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb2ac8c37f98b4239de66fdc9f4b73982a013769add4d71172561e7d399774038647690468193a2dee19d2c8cf886582538f4bfc13479057a3837918affec4d0
|
7
|
+
data.tar.gz: aba3b7d54384b3c44587219236ccfb4d3720560d63f5d7ac8193bc95604e4ec1ddd3de32544c8fcbaf0d3f396d19c62b3c9b6576b036e27f17d7d207d155b827
|
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
|
|
@@ -0,0 +1,210 @@
|
|
1
|
+
class GobstonesFeedbackHook < Mumukit::Hook
|
2
|
+
def run!(request, results)
|
3
|
+
content = request.content
|
4
|
+
test_results = results.test_results[0]
|
5
|
+
|
6
|
+
GobstonesExplainer.new.explain(content, test_results) if test_results.is_a? String
|
7
|
+
end
|
8
|
+
|
9
|
+
class GobstonesExplainer < Mumukit::Explainer
|
10
|
+
def explain_program_has_a_name(submission, result)
|
11
|
+
if identifier_instead_of_brace? result
|
12
|
+
(submission.match malformed_program_header_with_name).try do |it|
|
13
|
+
{ name: it[1] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def explain_program_has_no_opening_curly_brace(submission, result)
|
19
|
+
if identifier_instead_of_brace? result
|
20
|
+
/#{malformed_program_header_with_no_curly_braces}/ =~ submission
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def explain_program_before_closing_structure_when_program(submission, result)
|
25
|
+
if program_instead_of_command?(result) && missing_brace_end?(submission) && function_or_procedure?(submission)
|
26
|
+
(submission.match program).try do |it|
|
27
|
+
{ keyword: last_function_or_procedure(submission) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def explain_program_before_closing_structure_when_no_program(submission, result)
|
33
|
+
if program_instead_of_command?(result) && missing_brace_end?(submission) && function_or_procedure?(submission)
|
34
|
+
if /#{program}/ =~ submission
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
{ keyword: last_function_or_procedure(submission) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def explain_surplus_closing_brace(_, result)
|
43
|
+
if unbalanced_closing_braces? result
|
44
|
+
(error_line(result)).try do |it|
|
45
|
+
{ line: it[1] }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def explain_upper_function_typo(submission, result)
|
51
|
+
if upper_identifier_instead_of_definition? result
|
52
|
+
/#{uppercase_function}/ =~ submission
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def explain_upper_procedure_typo(submission, result)
|
57
|
+
if upper_identifier_instead_of_definition? result
|
58
|
+
/#{uppercase_procedure}/ =~ submission
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def explain_upper_program_typo(submission, result)
|
63
|
+
if upper_identifier_instead_of_definition? result
|
64
|
+
/#{uppercase_program}/ =~ submission
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def explain_missing_closing_brace_after_procedure(submission, result)
|
69
|
+
if procedure_instead_of_command?(result) && missing_brace_end?(submission)
|
70
|
+
(error_line(result)).try do |it|
|
71
|
+
{ line: it[1] }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def explain_lower_builtin_procedure_typo(submission, result)
|
77
|
+
if open_paren_instead_of_assign?(result)
|
78
|
+
(submission.match lower_builtin_procedure).try do |it|
|
79
|
+
{ lower: it[1][0...5], upper: it[1][0...5].capitalize }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def explain_upper_builtin_function_typo(submission, result)
|
85
|
+
if procedure_invocation_instead_of_expression?(result)
|
86
|
+
(submission.match upper_builtin_function).try do |it|
|
87
|
+
{ upper: it[1], lower: it[1].camelize(:lower) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def explain_color_typo(_, result)
|
93
|
+
if roja_not_defined?(result) || negra_not_defined?(result)
|
94
|
+
(result.match color_not_defined).try do |it|
|
95
|
+
{ color: it[1], rectified_color: rectified_color(it[1]) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def malformed_program_header_with_name
|
103
|
+
'.*program +([A-Za-z]\w*)'
|
104
|
+
end
|
105
|
+
|
106
|
+
def upper_identifier_instead_of_brace?(result)
|
107
|
+
identifier_instead_of_brace?(result, 'may')
|
108
|
+
end
|
109
|
+
|
110
|
+
def lower_identifier_instead_of_brace?(result)
|
111
|
+
identifier_instead_of_brace?(result, 'min')
|
112
|
+
end
|
113
|
+
|
114
|
+
def identifier_instead_of_brace?(result, capital='...')
|
115
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba una llave izquierda \("{"\).\nSe encontró: un identificador con #{capital}úsculas.<\/pre>/
|
116
|
+
end
|
117
|
+
|
118
|
+
def malformed_program_header_with_no_curly_braces
|
119
|
+
'.*program *[\r\n]\s*[^{]\w+'
|
120
|
+
end
|
121
|
+
|
122
|
+
def program_instead_of_command?(result)
|
123
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba un comando.\nSe encontró: la palabra clave "program".<\/pre>/
|
124
|
+
end
|
125
|
+
|
126
|
+
def missing_brace_end?(submission)
|
127
|
+
submission.count('{') > submission.count('}')
|
128
|
+
end
|
129
|
+
|
130
|
+
def function_or_procedure?(submission)
|
131
|
+
submission.match? function_or_procedure
|
132
|
+
end
|
133
|
+
|
134
|
+
def function_or_procedure
|
135
|
+
'(function)\s*\w+\s*\([\w\d\s,]*\)\s*{|(procedure)\s*\w+\s*\([\w\d\s,]*\)\s*{'
|
136
|
+
end
|
137
|
+
|
138
|
+
def last_function_or_procedure(submission)
|
139
|
+
submission.scan(/#{function_or_procedure}/).last.compact.first
|
140
|
+
end
|
141
|
+
|
142
|
+
def program
|
143
|
+
'program\s*{'
|
144
|
+
end
|
145
|
+
|
146
|
+
def unbalanced_closing_braces?(result)
|
147
|
+
result.match? /<pre>\[\d+:\d+\]: Se encontró un "}" pero no había una llave abierta "{".<\/pre>/
|
148
|
+
end
|
149
|
+
|
150
|
+
def error_line(result)
|
151
|
+
result.match /<pre>\[(\d+):\d+\]:/
|
152
|
+
end
|
153
|
+
|
154
|
+
def upper_identifier_instead_of_definition?(result)
|
155
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba una definición \(de programa, función, procedimiento, o tipo\).\nSe encontró: un identificador con mayúsculas.<\/pre>/
|
156
|
+
end
|
157
|
+
|
158
|
+
def uppercase_function
|
159
|
+
'Function\s'
|
160
|
+
end
|
161
|
+
|
162
|
+
def uppercase_procedure
|
163
|
+
'Procedure\s'
|
164
|
+
end
|
165
|
+
|
166
|
+
def uppercase_program
|
167
|
+
'Program[\s{]*'
|
168
|
+
end
|
169
|
+
|
170
|
+
def procedure_instead_of_command?(result)
|
171
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba un comando.\nSe encontró: la palabra clave "procedure".<\/pre>/
|
172
|
+
end
|
173
|
+
|
174
|
+
def open_paren_instead_of_assign?(result)
|
175
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba un operador de asignación \(":="\).\nSe encontró: un paréntesis izquierdo \("\("\).<\/pre>/
|
176
|
+
end
|
177
|
+
|
178
|
+
def lower_builtin_procedure
|
179
|
+
'(mover[\s(]|poner[\s(]|sacar[\s(])'
|
180
|
+
end
|
181
|
+
|
182
|
+
def procedure_invocation_instead_of_expression?(result)
|
183
|
+
result.match? /<pre>\[\d+:\d+\]: Se esperaba una expresión.\nSe encontró: una invocación a un procedimiento.<\/pre>/
|
184
|
+
end
|
185
|
+
|
186
|
+
def upper_builtin_function
|
187
|
+
'(PuedeMover|NroBolitas|HayBolitas)'
|
188
|
+
end
|
189
|
+
|
190
|
+
def roja_not_defined?(result)
|
191
|
+
color_not_defined? result, 'Roja'
|
192
|
+
end
|
193
|
+
|
194
|
+
def negra_not_defined?(result)
|
195
|
+
color_not_defined? result, 'Negra'
|
196
|
+
end
|
197
|
+
|
198
|
+
def color_not_defined?(result, color)
|
199
|
+
result.match?(color_not_defined(color))
|
200
|
+
end
|
201
|
+
|
202
|
+
def color_not_defined(color='\w+')
|
203
|
+
/<pre>\[\d+:\d+\]: El constructor "(#{color})" no está definido.<\/pre>/
|
204
|
+
end
|
205
|
+
|
206
|
+
def rectified_color(color)
|
207
|
+
color.chop + "o"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
data/lib/gobstones_runner.rb
CHANGED
@@ -21,9 +21,10 @@ require_relative './render/html_renderer'
|
|
21
21
|
|
22
22
|
require_relative './multiple_executions_runner'
|
23
23
|
|
24
|
+
require_relative './checker'
|
25
|
+
require_relative './expectations_hook'
|
26
|
+
require_relative './feedback_hook'
|
24
27
|
require_relative './metadata_hook'
|
25
28
|
require_relative './precompile_hook'
|
26
29
|
require_relative './test_hook'
|
27
30
|
require_relative './version_hook'
|
28
|
-
require_relative './expectations_hook'
|
29
|
-
require_relative './checker'
|
data/lib/locales/en.yml
CHANGED
@@ -12,7 +12,19 @@ en:
|
|
12
12
|
code_wrong_argument_type: 'wrong arguments type'
|
13
13
|
code_unassigned_variable: 'unassigned variable'
|
14
14
|
code_wrong_arguments_quantity: 'wrong arguments quantity'
|
15
|
+
color_typo: 'It seems you wrote %{color}. Maybe you meant %{rectified_color}?'
|
15
16
|
expected_board: 'Expected final board'
|
16
17
|
final_board: 'Final board'
|
17
18
|
initial_board: 'Initial board'
|
19
|
+
lower_builtin_procedure_typo: 'Looks lie you wrote <code>%{lower}</code> in lowercase. Remember procedures such as <code>%{upper}()</code> should start in uppercase!'
|
20
|
+
missing_closing_brace_after_procedure: "Looks like you're missing a <code>}</code> to close a <code>procedure</code>. Maybe it's on line <strong>%{line}</strong> or near it?"
|
18
21
|
no_program_found: 'No program definition was found'
|
22
|
+
program_before_closing_structure_when_no_program: "Looks like you're missing a <code>}</code> to close the last <code>%{keyword}</code>."
|
23
|
+
program_before_closing_structure_when_program: "Looks like you're missing a <code>}</code> to close the last <code>%{keyword}</code> before <code>program</code>."
|
24
|
+
program_has_a_name: "Looks like you named your <code>program</code> <code>%{name}</code>, but <code>program</code>s shouldn't be named."
|
25
|
+
program_has_no_opening_curly_brace: "Looks like a <code>{</code> is missing on <code>program</code>. Remember the content in <code>program</code> should be between curly braces."
|
26
|
+
surplus_closing_brace: "The amount of <code>{</code> and <code>}</code> doesn't match. Maybe there's an extra <code>}</code> on line <strong>%{line}</strong> or near it?"
|
27
|
+
upper_builtin_function_typo: 'Looks like you wrote <code>%{upper}</code> starting in uppercase. Remember functions such as <code>%{lower}</code> should start in lowercase!'
|
28
|
+
upper_function_typo: 'Looks like you wrote <code>Function</code> instead of <code>function</code>. Remember it should start in lowercase!'
|
29
|
+
upper_procedure_typo: 'Looks like you wrote <code>Procedure</code> instead of <code>procedure</code>. Remember it should start in lowercase!'
|
30
|
+
upper_program_typo: 'Looks like you wrote <code>Program</code> instead of <code>program</code>. Remember it should start in lowercase!'
|
data/lib/locales/es.yml
CHANGED
@@ -12,7 +12,19 @@ es:
|
|
12
12
|
code_wrong_argument_type: 'problema de tipos en los argumentos'
|
13
13
|
code_unassigned_variable: 'variable no asignada'
|
14
14
|
code_wrong_arguments_quantity: 'cantidad errónea de argumentos'
|
15
|
+
color_typo: 'Parece que escribiste %{color}. ¿Quisiste referirte al color %{rectified_color}?'
|
15
16
|
expected_board: 'Tablero final esperado'
|
16
17
|
final_board: 'Tablero final'
|
17
18
|
initial_board: 'Tablero inicial'
|
19
|
+
lower_builtin_procedure_typo: 'Parece que escribiste <code>%{lower}</code> en minúsculas. ¡Recordá que los procedimientos como <code>%{upper}()</code> deben comenzar en mayúsculas!'
|
20
|
+
missing_closing_brace_after_procedure: 'Parece que te falta una <code>}</code> que cierre un <code>procedure</code>. ¿Puede que sea en la línea <strong>%{line}</strong> o cerca de ella?'
|
18
21
|
no_program_found: 'No se encontró ninguna definición de programa'
|
22
|
+
program_before_closing_structure_when_no_program: 'Parece que te falta una <code>}</code> que cierre el último <code>%{keyword}</code>.'
|
23
|
+
program_before_closing_structure_when_program: 'Parece que te falta una <code>}</code> que cierre el <code>%{keyword}</code> antes de <code>program</code>.'
|
24
|
+
program_has_a_name: 'Parece que llamaste <code>%{name}</code> a <code>program</code>, pero los <code>program</code> no llevan nombre.'
|
25
|
+
program_has_no_opening_curly_brace: 'Parece que a <code>program</code> le falta <code>{</code>. Recordá que el contenido de <code>program</code> va entre llaves.'
|
26
|
+
surplus_closing_brace: 'La cantidad de <code>{</code> y <code>}</code> no coincide. ¿Puede que sobre una <code>}</code> en la línea <strong>%{line}</strong> o cerca de ella?'
|
27
|
+
upper_builtin_function_typo: 'Parece que escribiste <code>%{upper}</code> comenzando en mayúsculas. ¡Recordá que las funciones como <code>%{lower}</code> deben comenzar en minúsculas!'
|
28
|
+
upper_function_typo: 'Parece que escribiste <code>Function</code> en lugar de <code>function</code>. ¡Recordá que debe comenzar en minúsculas!'
|
29
|
+
upper_procedure_typo: 'Parece que escribiste <code>Procedure</code> en lugar de <code>procedure</code>. ¡Recordá que debe comenzar en minúsculas!'
|
30
|
+
upper_program_typo: 'Parece que escribiste <code>Program</code> en lugar de <code>program</code>. ¡Recordá que debe comenzar en minúsculas!'
|
data/lib/locales/pt.yml
CHANGED
@@ -12,7 +12,19 @@ pt:
|
|
12
12
|
code_wrong_argument_type: 'problema de tipos nos argumentos'
|
13
13
|
code_unassigned_variable: 'variável não atribuída'
|
14
14
|
code_wrong_arguments_quantity: 'quantidade errada de argumentos'
|
15
|
+
color_typo: 'Parece que você escreveu %{color}. Você quis se referir à cor %{rectified_color}?'
|
15
16
|
expected_board: 'Tabuleiro final esperado'
|
16
17
|
final_board: 'Tabuleiro final'
|
17
18
|
initial_board: 'Tabuleiro inicial'
|
19
|
+
lower_builtin_procedure_typo: 'Parece que você escreveu <code>%{lower}</code> em letras minúsculas. Lembre-se de que procedimentos como <code>%{upper}()</code> devem começar em maiúsculas!'
|
20
|
+
missing_closing_brace_after_procedure: 'Parece que está faltando um <code>}</code> que fecha um <code>procedure</code>. Pode ser na linha <strong>%{line}</strong> ou perto dela?'
|
18
21
|
no_program_found: 'Nenhuma definição de programa foi encontrada'
|
22
|
+
program_before_closing_structure_when_no_program: 'Parece que está faltando um <code>}</code> que fecha a última <code>%{keyword}</code>.'
|
23
|
+
program_before_closing_structure_when_program: 'Parece que está faltando um <code>}</code> que fecha o <code>%{keyword}</code> antes do <code>program</code>.'
|
24
|
+
program_has_a_name: 'Parece que você chamou <code>%{name}</code> para <code>program</code>, mas <code>program</code>as não têm nomes.'
|
25
|
+
program_has_no_opening_curly_brace: 'Parece que a <code>program</code> está faltando <code>{</code>. Lembre-se de que o conteúdo do <code>program</code> está entre colchetes.'
|
26
|
+
surplus_closing_brace: 'A quantidade de <code>{</code> e <code>}</code> não corresponde. Pode ser sobre um <code>}</code> na linha <strong>%{line}</strong> ou próximo a ela?'
|
27
|
+
upper_builtin_function_typo: 'Parece que você digitou <code>%{upper}</code> começando com letras maiúsculas. Lembre-se de que funções como <code>%{lower}</code> devem começar em minúsculas!'
|
28
|
+
upper_function_typo: 'Parece que você escreveu <code>Function</code> em vez de <code>function</code>. Lembre-se de começar em letras minúsculas!'
|
29
|
+
upper_procedure_typo: 'Parece que você escreveu <code>Procedure</code> em vez de <code>procedure</code>. Lembre-se de começar em letras minúsculas!'
|
30
|
+
upper_program_typo: 'Parece que você escreveu <code>Program</code> em vez de <code>program</code>. Lembre-se de começar em letras minúsculas!'
|
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
|
+
}
|
@@ -5,6 +5,29 @@
|
|
5
5
|
<script src="./hammer.min.js"></script>
|
6
6
|
|
7
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
|
+
|
8
31
|
function postpone(action) {
|
9
32
|
return setTimeout(action, 50);
|
10
33
|
}
|
@@ -115,10 +138,28 @@
|
|
115
138
|
this._scrollToMainBlock();
|
116
139
|
},
|
117
140
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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;
|
149
|
+
|
150
|
+
console.debug('Configuring custom toolbox');
|
151
|
+
|
152
|
+
$.get(toolboxUrl, (toolboxXml) => {
|
153
|
+
this._initializeCustomToolboxBlocklyWorkspace(toolboxXml);
|
154
|
+
});
|
155
|
+
},
|
156
|
+
|
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;
|
122
163
|
},
|
123
164
|
|
124
165
|
compile(code) {
|
@@ -129,10 +170,6 @@
|
|
129
170
|
return this._getBlockly().initialXml.indexOf("block type=\"InteractiveProgram\"") !== -1;
|
130
171
|
},
|
131
172
|
|
132
|
-
hasCustomToolbox() {
|
133
|
-
return $('gs-toolbox').length;
|
134
|
-
},
|
135
|
-
|
136
173
|
toggleInteractiveMode() {
|
137
174
|
this.$exerciseContainer.toggleClass('play-mode');
|
138
175
|
this._triggerResize();
|
@@ -191,12 +228,7 @@
|
|
191
228
|
_configureBlocklyBehavior() {
|
192
229
|
this._setTeacherActions();
|
193
230
|
this._setGameActions();
|
194
|
-
|
195
|
-
// Blockly's workspace is destroyed when toolbox changes, so it doesn't make sense to initialize it here.
|
196
|
-
if (!this.hasCustomToolbox()) {
|
197
|
-
this._initializeBlocklyWorkspace();
|
198
|
-
}
|
199
|
-
|
231
|
+
this._initializeNonCustomToolboxBlocklyWorkspace();
|
200
232
|
this._registerLayoutChangedEvent();
|
201
233
|
},
|
202
234
|
|
@@ -208,7 +240,34 @@
|
|
208
240
|
$(window).resize(() => this._relocateTrash());
|
209
241
|
},
|
210
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
|
+
|
211
268
|
_initializeBlocklyWorkspace() {
|
269
|
+
console.debug('Initializing Blockly Workspace');
|
270
|
+
|
212
271
|
this._setInitialXml();
|
213
272
|
this._initializeWorkspace();
|
214
273
|
this._subscribeToWorkspace(() => this._updateSolution());
|
@@ -228,12 +287,18 @@
|
|
228
287
|
},
|
229
288
|
|
230
289
|
_updateSolution() {
|
290
|
+
console.debug("Updating solution");
|
231
291
|
const blockly = this._getBlockly();
|
232
|
-
|
292
|
+
let changed;
|
233
293
|
if (this.teacherMode) {
|
234
|
-
this._setExtraCode(blockly.workspaceXml);
|
294
|
+
changed = this._setExtraCode(blockly.workspaceXml);
|
235
295
|
} else {
|
236
|
-
this._setStudentSolution(blockly.workspaceXml);
|
296
|
+
changed = this._setStudentSolution(blockly.workspaceXml);
|
297
|
+
}
|
298
|
+
|
299
|
+
if (!changed) {
|
300
|
+
console.debug("No solution update required")
|
301
|
+
return;
|
237
302
|
}
|
238
303
|
|
239
304
|
// TODO: this hack enables Angular two-way binding for Bibliotheca. Should be replaced with Mumuki events system
|
@@ -326,22 +391,44 @@
|
|
326
391
|
return;
|
327
392
|
}
|
328
393
|
|
329
|
-
|
394
|
+
console.debug("Set teacher actions");
|
330
395
|
const actions = new Parser().getActionsFromSource(teacherCode);
|
331
|
-
|
332
|
-
|
396
|
+
|
397
|
+
this._setPrimitiveProcedures(this._withDefaultIcons(actions, 'procedureDeclarations'));
|
398
|
+
this._setPrimitiveFunctions(this._withDefaultIcons(actions, 'functionDeclarations'));
|
333
399
|
},
|
334
400
|
|
335
401
|
_setGameActions() {
|
402
|
+
if (!this._isGame()) {
|
403
|
+
return;
|
404
|
+
}
|
405
|
+
|
406
|
+
console.debug("Set game actions");
|
336
407
|
const blockly = this._getBlockly();
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
408
|
+
|
409
|
+
this._setPrimitiveProcedures((blockly.primitiveProcedures || []).concat([
|
410
|
+
this._gamePrimitive('ShiftUp'),
|
411
|
+
this._gamePrimitive('ShiftDown'),
|
412
|
+
this._gamePrimitive('ShiftLeft'),
|
413
|
+
this._gamePrimitive('ShiftRight')
|
414
|
+
]));
|
415
|
+
},
|
416
|
+
|
417
|
+
_setPrimitiveProcedures(procedures) {
|
418
|
+
this._setBlocklyActions("primitiveProcedures", procedures);
|
419
|
+
},
|
420
|
+
|
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
|
+
}
|
345
432
|
}
|
346
433
|
},
|
347
434
|
|
@@ -362,8 +449,12 @@
|
|
362
449
|
* to user content in laboratory, and default content in bibliotheca
|
363
450
|
*/
|
364
451
|
_setStudentSolution(code) {
|
365
|
-
|
366
|
-
|
452
|
+
if (this._getStudentEditor().value !== code) {
|
453
|
+
console.debug(`Setting student ${code}`)
|
454
|
+
this._getStudentEditor().value = code;
|
455
|
+
return true;
|
456
|
+
}
|
457
|
+
return false;
|
367
458
|
},
|
368
459
|
|
369
460
|
_getDefaultCode() {
|
@@ -375,8 +466,12 @@
|
|
375
466
|
},
|
376
467
|
|
377
468
|
_setExtraCode(code) {
|
378
|
-
|
379
|
-
|
469
|
+
if (this._getEditorExtra().value !== code) {
|
470
|
+
console.debug(`Setting extra ${code}`)
|
471
|
+
this._getEditorExtra().value = code;
|
472
|
+
return true;
|
473
|
+
}
|
474
|
+
return false;
|
380
475
|
},
|
381
476
|
|
382
477
|
_getEditorExtra: function() {
|
@@ -945,10 +1040,12 @@
|
|
945
1040
|
},
|
946
1041
|
|
947
1042
|
_setEditorToolbox: function () {
|
948
|
-
const editor = $("mu-gobstones-custom-editor")[0];
|
949
|
-
|
950
|
-
|
951
|
-
|
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);
|
952
1049
|
}
|
953
1050
|
});
|
954
1051
|
</script>
|
data/lib/version_hook.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Alfonso
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-10
|
12
|
+
date: 2020-11-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: gobstones-board
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '2.
|
62
|
+
version: '2.37'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '2.
|
69
|
+
version: '2.37'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: nokogiri
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,6 +183,7 @@ files:
|
|
183
183
|
- lib/checker.rb
|
184
184
|
- lib/expectations_hook.rb
|
185
185
|
- lib/extensions/string.rb
|
186
|
+
- lib/feedback_hook.rb
|
186
187
|
- lib/game_framework/default.gbs.erb
|
187
188
|
- lib/game_framework/default.xml.erb
|
188
189
|
- lib/game_framework/extra.gbs.erb
|