@nitro-bolt/scratchblocks 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +205 -0
  3. package/browser.es.js +8 -0
  4. package/browser.js +8 -0
  5. package/build/scratchblocks.min.es.js +11 -0
  6. package/build/scratchblocks.min.es.js.map +1 -0
  7. package/build/scratchblocks.min.js +11 -0
  8. package/build/scratchblocks.min.js.map +1 -0
  9. package/build/translations-all-es.js +10 -0
  10. package/build/translations-all.js +10 -0
  11. package/build/translations-es.js +10 -0
  12. package/build/translations.js +10 -0
  13. package/index.js +163 -0
  14. package/locales/ab.json +261 -0
  15. package/locales/af.json +261 -0
  16. package/locales/all.js +161 -0
  17. package/locales/am.json +262 -0
  18. package/locales/an.json +261 -0
  19. package/locales/ar.json +261 -0
  20. package/locales/ast.json +261 -0
  21. package/locales/az.json +262 -0
  22. package/locales/be.json +261 -0
  23. package/locales/bg.json +261 -0
  24. package/locales/bn.json +261 -0
  25. package/locales/ca.json +267 -0
  26. package/locales/ckb.json +261 -0
  27. package/locales/cs.json +267 -0
  28. package/locales/cy.json +266 -0
  29. package/locales/da.json +261 -0
  30. package/locales/de.json +266 -0
  31. package/locales/el.json +268 -0
  32. package/locales/eo.json +261 -0
  33. package/locales/es-419.json +261 -0
  34. package/locales/es.json +266 -0
  35. package/locales/et.json +261 -0
  36. package/locales/eu.json +261 -0
  37. package/locales/fa.json +266 -0
  38. package/locales/fi.json +261 -0
  39. package/locales/fil.json +262 -0
  40. package/locales/forums.js +37 -0
  41. package/locales/fr.json +266 -0
  42. package/locales/fy.json +261 -0
  43. package/locales/ga.json +261 -0
  44. package/locales/gd.json +266 -0
  45. package/locales/gl.json +261 -0
  46. package/locales/ha.json +261 -0
  47. package/locales/he.json +266 -0
  48. package/locales/hi.json +266 -0
  49. package/locales/hr.json +266 -0
  50. package/locales/ht.json +261 -0
  51. package/locales/hu.json +267 -0
  52. package/locales/hy.json +261 -0
  53. package/locales/id.json +266 -0
  54. package/locales/is.json +261 -0
  55. package/locales/it.json +266 -0
  56. package/locales/ja-Hira.json +268 -0
  57. package/locales/ja.json +268 -0
  58. package/locales/ka.json +261 -0
  59. package/locales/kk.json +263 -0
  60. package/locales/km.json +261 -0
  61. package/locales/ko.json +261 -0
  62. package/locales/ku.json +263 -0
  63. package/locales/lt.json +261 -0
  64. package/locales/lv.json +261 -0
  65. package/locales/mi.json +261 -0
  66. package/locales/mn.json +262 -0
  67. package/locales/nb.json +266 -0
  68. package/locales/nl.json +266 -0
  69. package/locales/nn.json +261 -0
  70. package/locales/nso.json +261 -0
  71. package/locales/oc.json +261 -0
  72. package/locales/or.json +262 -0
  73. package/locales/pl.json +266 -0
  74. package/locales/pt-br.json +261 -0
  75. package/locales/pt.json +266 -0
  76. package/locales/qu.json +261 -0
  77. package/locales/rap.json +263 -0
  78. package/locales/ro.json +266 -0
  79. package/locales/ru.json +266 -0
  80. package/locales/sk.json +261 -0
  81. package/locales/sl.json +266 -0
  82. package/locales/sr.json +261 -0
  83. package/locales/sv.json +261 -0
  84. package/locales/sw.json +261 -0
  85. package/locales/th.json +261 -0
  86. package/locales/tn.json +261 -0
  87. package/locales/tr.json +269 -0
  88. package/locales/uk.json +261 -0
  89. package/locales/uz.json +262 -0
  90. package/locales/vi.json +262 -0
  91. package/locales/xh.json +261 -0
  92. package/locales/zh-cn.json +266 -0
  93. package/locales/zh-tw.json +266 -0
  94. package/locales/zu.json +261 -0
  95. package/package.json +77 -0
  96. package/scratch3/blocks.js +959 -0
  97. package/scratch3/draw.js +404 -0
  98. package/scratch3/index.js +12 -0
  99. package/scratch3/style.css.js +226 -0
  100. package/scratch3/style.js +807 -0
  101. package/syntax/blocks.js +467 -0
  102. package/syntax/commands.js +1692 -0
  103. package/syntax/extensions.js +33 -0
  104. package/syntax/index.js +17 -0
  105. package/syntax/model.js +427 -0
  106. package/syntax/syntax.js +941 -0
@@ -0,0 +1,33 @@
1
+ /*
2
+ When a new extension is added:
3
+ 1) Add it to extensions object
4
+ 2) Add its blocks to commands.js
5
+ 3) Add icon width/height to scratch3/blocks.js IconView
6
+ 4) Add icon to scratch3/style.js
7
+ */
8
+
9
+ // Moved extension aliases: key is current, value is legacy.
10
+ export const movedExtensions = {
11
+ pen: "pen",
12
+ video: "sensing",
13
+ music: "sound",
14
+ }
15
+
16
+ export const extensions = {
17
+ ...movedExtensions,
18
+ tts: "tts",
19
+ translate: "translate",
20
+ microbit: "microbit",
21
+ wedo: "wedo",
22
+ makeymakey: "makeymakey",
23
+ ev3: "ev3",
24
+ boost: "boost",
25
+ gdxfor: "gdxfor",
26
+ }
27
+
28
+ // Extension aliases accepted in addition to movedExtensions.
29
+ // Key is alias, value is real extension name
30
+ export const aliasExtensions = {
31
+ wedo2: "wedo",
32
+ text2speech: "tts",
33
+ }
@@ -0,0 +1,17 @@
1
+ export { parse } from "./syntax.js"
2
+
3
+ export {
4
+ Label,
5
+ Icon,
6
+ Input,
7
+ Newline,
8
+ Block,
9
+ Comment,
10
+ Glow,
11
+ Script,
12
+ Document,
13
+ } from "./model.js"
14
+
15
+ export { allLanguages, loadLanguages } from "./blocks.js"
16
+
17
+ export { extensions, movedExtensions, aliasExtensions } from "./extensions.js"
@@ -0,0 +1,427 @@
1
+ function assert(bool, message) {
2
+ if (!bool) {
3
+ throw new Error(`Assertion failed! ${message || ""}`)
4
+ }
5
+ }
6
+
7
+ function indent(text) {
8
+ return text
9
+ .split("\n")
10
+ .map(line => {
11
+ return ` ${line}`
12
+ })
13
+ .join("\n")
14
+ }
15
+
16
+ import {
17
+ parseSpec,
18
+ inputPat,
19
+ parseInputNumber,
20
+ iconPat,
21
+ rtlLanguages,
22
+ unicodeIcons,
23
+ } from "./blocks.js"
24
+
25
+ export class Label {
26
+ constructor(value, cls) {
27
+ this.value = value
28
+ this.cls = cls || ""
29
+ this.el = null
30
+ this.height = 12
31
+ this.metrics = null
32
+ this.x = 0
33
+ }
34
+ get isLabel() {
35
+ return true
36
+ }
37
+
38
+ stringify() {
39
+ if (this.value === "<" || this.value === ">") {
40
+ return this.value
41
+ }
42
+ return this.value.replace(/([<>[\](){}])/g, "\\$1")
43
+ }
44
+ }
45
+
46
+ export class Icon {
47
+ constructor(name) {
48
+ this.name = name
49
+ this.isArrow = name === "loopArrow"
50
+
51
+ assert(Icon.icons[name], `no info for icon ${name}`)
52
+ }
53
+ get isIcon() {
54
+ return true
55
+ }
56
+
57
+ static get icons() {
58
+ return {
59
+ greenFlag: true,
60
+ stopSign: true,
61
+ turnLeft: true,
62
+ turnRight: true,
63
+ loopArrow: true,
64
+ addInput: true,
65
+ delInput: true,
66
+ list: true,
67
+ }
68
+ }
69
+
70
+ stringify() {
71
+ return unicodeIcons[`@${this.name}`] || `@${this.name}`
72
+ }
73
+ }
74
+
75
+ export class Input {
76
+ constructor(shape, value, menu) {
77
+ this.shape = shape
78
+ this.value = value
79
+ this.menu = menu || null
80
+
81
+ this.isRound = shape === "number" || shape === "number-dropdown"
82
+ this.isBoolean = shape === "boolean"
83
+ this.isObject = shape === "object"
84
+ this.isArray = shape === "array"
85
+ this.isStack = shape === "stack"
86
+ this.isInset =
87
+ shape === "boolean" ||
88
+ shape === "object" ||
89
+ shape === "array" ||
90
+ shape === "stack" ||
91
+ shape === "reporter"
92
+ this.isColor = shape === "color"
93
+ this.hasArrow = shape === "dropdown" || shape === "number-dropdown"
94
+ this.isDarker =
95
+ shape === "boolean" || shape === "stack" || shape === "dropdown"
96
+ this.isSquare =
97
+ shape === "string" || shape === "color" || shape === "dropdown"
98
+
99
+ this.hasLabel = !(this.isColor || this.isInset)
100
+ this.label = this.hasLabel
101
+ ? new Label(value, `literal-${this.shape}`)
102
+ : null
103
+ this.x = 0
104
+ }
105
+ get isInput() {
106
+ return true
107
+ }
108
+
109
+ stringify() {
110
+ if (this.isColor) {
111
+ assert(this.value[0] === "#")
112
+ return `[${this.value}]`
113
+ }
114
+ // Order sensitive; see #439
115
+ let text = (this.value ? String(this.value) : "")
116
+ .replace(/([\]\\])/g, "\\$1")
117
+ .replace(/ v$/, " \\v")
118
+ if (this.hasArrow) {
119
+ text += " v"
120
+ }
121
+ return this.isRound
122
+ ? `(${text})`
123
+ : this.isSquare
124
+ ? `[${text}]`
125
+ : this.isBoolean
126
+ ? "<>"
127
+ : this.isObject
128
+ ? "{}"
129
+ : this.isArray
130
+ ? "[]"
131
+ : this.isStack
132
+ ? "{}"
133
+ : text
134
+ }
135
+
136
+ translate(_lang) {
137
+ if (this.hasArrow) {
138
+ const value = this.menu || this.value
139
+ this.value = value // TODO translate dropdown value
140
+ this.label = new Label(this.value, `literal-${this.shape}`)
141
+ }
142
+ }
143
+ }
144
+
145
+ export class Newline {
146
+ get isNewline() {
147
+ return true
148
+ }
149
+
150
+ stringify() {
151
+ return "\\n"
152
+ }
153
+ }
154
+
155
+ export class Block {
156
+ constructor(info, children, comment) {
157
+ assert(info)
158
+ this.info = { ...info }
159
+ this.children = children
160
+ this.comment = comment || null
161
+ this.diff = null
162
+
163
+ const shape = this.info.shape
164
+ this.isHat = shape === "hat" || shape === "cat" || shape === "define-hat"
165
+ this.hasPuzzle =
166
+ shape === "stack" ||
167
+ shape === "hat" ||
168
+ shape === "cat" ||
169
+ shape === "c-block"
170
+ this.isFinal = /cap/.test(shape)
171
+ this.isCommand = shape === "stack" || shape === "cap" || /block/.test(shape)
172
+ this.isOutline = shape === "outline"
173
+ this.isReporter =
174
+ shape === "reporter" || shape === "object" || shape === "array"
175
+ this.isObject = shape === "object"
176
+ this.isArray = shape === "array"
177
+ this.isBoolean = shape === "boolean"
178
+
179
+ this.isRing = shape === "ring"
180
+ this.hasScript = /block/.test(shape)
181
+ this.isElse = shape === "celse"
182
+ this.isEnd = shape === "cend"
183
+ }
184
+ get isBlock() {
185
+ return true
186
+ }
187
+
188
+ stringify(extras) {
189
+ let firstInput = null
190
+ let checkAlias = false
191
+ const text = this.children
192
+ .map(child => {
193
+ if (child.isIcon) {
194
+ checkAlias = true
195
+ }
196
+ if (!firstInput && !(child.isLabel || child.isIcon)) {
197
+ firstInput = child
198
+ }
199
+ return child.isScript
200
+ ? `\n${indent(child.stringify())}\n`
201
+ : child.stringify().trim() + " "
202
+ })
203
+ .join("")
204
+ .trim()
205
+
206
+ const lang = this.info.language
207
+ if (checkAlias && lang && this.info.selector) {
208
+ const aliases = lang.nativeAliases[this.info.id]
209
+ if (aliases && aliases.length) {
210
+ let alias = aliases[0]
211
+ // TODO make translate() not in-place, and use that
212
+ if (inputPat.test(alias) && firstInput) {
213
+ alias = alias.replace(inputPat, firstInput.stringify())
214
+ }
215
+ return alias
216
+ }
217
+ }
218
+
219
+ const isNitroShape =
220
+ this.info.shape === "object" || this.info.shape === "array"
221
+ let overrides = extras || ""
222
+ if (
223
+ this.info.categoryIsDefault === false ||
224
+ (this.info.category === "custom-arg" &&
225
+ (this.isReporter || this.isBoolean)) ||
226
+ (this.info.category === "custom" && this.info.shape === "stack")
227
+ ) {
228
+ if (overrides) {
229
+ overrides += " "
230
+ }
231
+ overrides += this.info.category
232
+ }
233
+ const overrideText = overrides ? ` :: ${overrides}` : ""
234
+ if (this.hasScript) {
235
+ if (!isNitroShape) {
236
+ return text + overrideText + "\nend"
237
+ }
238
+ const firstNewline = text.indexOf("\n")
239
+ if (firstNewline === -1) {
240
+ return text + overrideText + "\nend"
241
+ }
242
+ return (
243
+ text.slice(0, firstNewline).trimEnd() +
244
+ overrideText +
245
+ text.slice(firstNewline) +
246
+ "\nend"
247
+ )
248
+ }
249
+ if (isNitroShape) {
250
+ return this.isObject
251
+ ? `{${text}${overrideText}}`
252
+ : `[${text}${overrideText}]`
253
+ }
254
+ const textWithOverrides = text + overrideText
255
+ return this.info.shape === "reporter"
256
+ ? `(${textWithOverrides})`
257
+ : this.info.shape === "boolean"
258
+ ? `<${textWithOverrides}>`
259
+ : textWithOverrides
260
+ }
261
+
262
+ translate(lang, isShallow) {
263
+ if (!lang) {
264
+ throw new Error("Missing language")
265
+ }
266
+
267
+ const id = this.info.id
268
+ if (!id) {
269
+ return
270
+ }
271
+
272
+ if (id === "PROCEDURES_DEFINITION") {
273
+ // Find the first 'outline' child (there should be exactly one).
274
+ const outline = this.children.find(child => child.isOutline)
275
+
276
+ this.children = []
277
+ for (const word of lang.definePrefix) {
278
+ this.children.push(new Label(word))
279
+ }
280
+ this.children.push(outline)
281
+ for (const word of lang.defineSuffix) {
282
+ this.children.push(new Label(word))
283
+ }
284
+ return
285
+ }
286
+
287
+ const oldSpec = this.info.language.commands[id]
288
+
289
+ const nativeSpec = lang.commands[id]
290
+ if (!nativeSpec) {
291
+ return
292
+ }
293
+ const nativeInfo = parseSpec(nativeSpec)
294
+
295
+ const rawArgs = this.children.filter(
296
+ child => !child.isLabel && !child.isIcon,
297
+ )
298
+
299
+ if (!isShallow) {
300
+ rawArgs.forEach(child => child.translate(lang))
301
+ }
302
+
303
+ // Work out indexes of existing children
304
+ const oldParts = parseSpec(oldSpec).parts
305
+ const oldInputOrder = oldParts
306
+ .map(part => parseInputNumber(part))
307
+ .filter(x => x)
308
+
309
+ let highestNumber = 0
310
+ const args = oldInputOrder.map(number => {
311
+ highestNumber = Math.max(highestNumber, number)
312
+ return rawArgs[number - 1]
313
+ })
314
+ const remainingArgs = rawArgs.slice(highestNumber)
315
+
316
+ // Get new children by index
317
+ this.children = nativeInfo.parts
318
+ .map(part => {
319
+ part = part.trim()
320
+ if (!part) {
321
+ return
322
+ }
323
+ const number = parseInputNumber(part)
324
+ if (number) {
325
+ return args[number - 1]
326
+ }
327
+ return iconPat.test(part) ? new Icon(part.slice(1)) : new Label(part)
328
+ })
329
+ .filter(x => x)
330
+
331
+ // Push any remaining children, so we pick up C block bodies
332
+ remainingArgs.forEach((arg, index) => {
333
+ if (index === 1 && this.info.id === "CONTROL_IF") {
334
+ this.children.push(new Label(lang.commands.CONTROL_ELSE))
335
+ }
336
+ this.children.push(arg)
337
+ })
338
+
339
+ this.info.language = lang
340
+ this.info.isRTL = rtlLanguages.includes(lang.code)
341
+ this.info.categoryIsDefault = true
342
+ }
343
+ }
344
+
345
+ export class Comment {
346
+ constructor(value, hasBlock) {
347
+ this.label = new Label(value, "comment-label")
348
+ this.width = null
349
+ this.hasBlock = hasBlock
350
+ }
351
+ get isComment() {
352
+ return true
353
+ }
354
+
355
+ stringify() {
356
+ return `// ${this.label.value}`
357
+ }
358
+ }
359
+
360
+ export class Glow {
361
+ constructor(child) {
362
+ assert(child)
363
+ this.child = child
364
+ if (child.isBlock) {
365
+ this.shape = child.info.shape
366
+ this.info = child.info
367
+ } else {
368
+ this.shape = "stack"
369
+ }
370
+ }
371
+ get isGlow() {
372
+ return true
373
+ }
374
+
375
+ stringify() {
376
+ if (this.child.isBlock) {
377
+ return this.child.stringify("+")
378
+ }
379
+ const lines = this.child.stringify().split("\n")
380
+ return lines.map(line => `+ ${line}`).join("\n")
381
+ }
382
+
383
+ translate(lang) {
384
+ this.child.translate(lang)
385
+ }
386
+ }
387
+
388
+ export class Script {
389
+ constructor(blocks) {
390
+ this.blocks = blocks
391
+ this.isEmpty = !blocks.length
392
+ this.isFinal = !this.isEmpty && blocks[blocks.length - 1].isFinal
393
+ }
394
+ get isScript() {
395
+ return true
396
+ }
397
+
398
+ stringify() {
399
+ return this.blocks
400
+ .map(block => {
401
+ let line = block.stringify()
402
+ if (block.comment) {
403
+ line += ` ${block.comment.stringify()}`
404
+ }
405
+ return line
406
+ })
407
+ .join("\n")
408
+ }
409
+
410
+ translate(lang) {
411
+ this.blocks.forEach(block => block.translate(lang))
412
+ }
413
+ }
414
+
415
+ export class Document {
416
+ constructor(scripts) {
417
+ this.scripts = scripts
418
+ }
419
+
420
+ stringify() {
421
+ return this.scripts.map(script => script.stringify()).join("\n\n")
422
+ }
423
+
424
+ translate(lang) {
425
+ this.scripts.forEach(script => script.translate(lang))
426
+ }
427
+ }