pxt-core 9.3.13 → 9.3.15

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 (42) hide show
  1. package/README.md +11 -0
  2. package/built/cli.js +7 -2
  3. package/built/pxt.js +57 -3
  4. package/built/pxtblockly.js +97 -57
  5. package/built/pxtblocks.d.ts +59 -21
  6. package/built/pxtblocks.js +97 -57
  7. package/built/pxtlib.d.ts +22 -0
  8. package/built/pxtlib.js +50 -1
  9. package/built/target.js +1 -1
  10. package/built/tests/blocksrunner.js +1 -1
  11. package/built/web/main.js +1 -1
  12. package/built/web/multiplayer/js/{main.78cecdcb.js → main.75ca8c58.js} +2 -2
  13. package/built/web/pxtapp.js +1 -1
  14. package/built/web/pxtasseteditor.js +1 -1
  15. package/built/web/pxtblockly.js +2 -2
  16. package/built/web/pxtblocks.js +1 -1
  17. package/built/web/pxtembed.js +2 -2
  18. package/built/web/pxtlib.js +1 -1
  19. package/built/web/pxtworker.js +1 -1
  20. package/built/web/rtlsemantic.css +1 -1
  21. package/built/web/runnerembed.js +1 -0
  22. package/built/web/semantic.css +1 -1
  23. package/built/web/skillmap/js/{main.8222bb34.js → main.236bd49e.js} +2 -2
  24. package/built/web/teachertool/css/main.e9386f28.css +1 -0
  25. package/built/web/teachertool/js/{main.3a94a341.js → main.8aa6604c.js} +2 -2
  26. package/localtypings/projectheader.d.ts +21 -0
  27. package/{built → localtypings}/pxteditor.d.ts +1095 -1090
  28. package/package.json +1 -1
  29. package/react-common/components/controls/MenuDropdown.tsx +5 -2
  30. package/react-common/components/util.tsx +1 -1
  31. package/theme/pxt.less +1 -0
  32. package/theme/themepacks.less +41 -0
  33. package/webapp/public/embed.js +1 -1
  34. package/webapp/public/multiplayer.html +1 -1
  35. package/webapp/public/skillmap.html +1 -1
  36. package/webapp/public/teachertool.html +1 -3
  37. package/built/pxteditor.js +0 -1834
  38. package/built/pxtrunner.d.ts +0 -151
  39. package/built/pxtrunner.js +0 -2626
  40. package/built/web/pxteditor.js +0 -1
  41. package/built/web/pxtrunner.js +0 -1
  42. package/built/web/teachertool/css/main.59776cd1.css +0 -1
@@ -1,2626 +0,0 @@
1
- var pxt;
2
- (function (pxt) {
3
- var runner;
4
- (function (runner) {
5
- /**
6
- * Starts the simulator and injects it into the provided container.
7
- * the simulator will attempt to establish a websocket connection
8
- * to the debugger's user interface on port 3234.
9
- *
10
- * @param container The container to inject the simulator into
11
- */
12
- function startDebuggerAsync(container) {
13
- const debugRunner = new DebugRunner(container);
14
- debugRunner.start();
15
- }
16
- runner.startDebuggerAsync = startDebuggerAsync;
17
- /**
18
- * Runner for the debugger that handles communication with the user
19
- * interface. Also talks to the server for anything to do with
20
- * the filesystem (like reading code)
21
- */
22
- class DebugRunner {
23
- constructor(container) {
24
- this.container = container;
25
- this.pkgLoaded = false;
26
- this.intervalRunning = false;
27
- }
28
- start() {
29
- this.initializeWebsocket();
30
- if (!this.intervalRunning) {
31
- this.intervalRunning = true;
32
- this.intervalId = setInterval(() => {
33
- if (!this.ws) {
34
- try {
35
- this.initializeWebsocket();
36
- }
37
- catch (e) {
38
- console.warn(`Connection to server failed, retrying in ${DebugRunner.RETRY_MS} ms`);
39
- }
40
- }
41
- }, DebugRunner.RETRY_MS);
42
- }
43
- this.session = new pxsim.SimDebugSession(this.container);
44
- this.session.start(this);
45
- }
46
- initializeWebsocket() {
47
- if (!pxt.BrowserUtils.isLocalHost() || !pxt.Cloud.localToken)
48
- return;
49
- pxt.debug('initializing debug pipe');
50
- this.ws = new WebSocket('ws://localhost:3234/' + pxt.Cloud.localToken + '/simdebug');
51
- this.ws.onopen = ev => {
52
- pxt.debug('debug: socket opened');
53
- };
54
- this.ws.onclose = ev => {
55
- pxt.debug('debug: socket closed');
56
- if (this.closeListener) {
57
- this.closeListener();
58
- }
59
- this.session.stopSimulator();
60
- this.ws = undefined;
61
- };
62
- this.ws.onerror = ev => {
63
- pxt.debug('debug: socket closed due to error');
64
- if (this.errorListener) {
65
- this.errorListener(ev.type);
66
- }
67
- this.session.stopSimulator();
68
- this.ws = undefined;
69
- };
70
- this.ws.onmessage = ev => {
71
- let message;
72
- try {
73
- message = JSON.parse(ev.data);
74
- }
75
- catch (e) {
76
- pxt.debug('debug: could not parse message');
77
- }
78
- if (message) {
79
- // FIXME: ideally, we should just open two websockets instead of adding to the
80
- // debug protocol. One for the debugger, one for meta-information and file
81
- // system requests
82
- if (message.type === 'runner') {
83
- this.handleRunnerMessage(message);
84
- }
85
- else {
86
- // Intercept the launch configuration and notify the server-side debug runner
87
- if (message.type === "request" && message.command === "launch") {
88
- this.sendRunnerMessage("configure", {
89
- projectDir: message.arguments.projectDir
90
- });
91
- }
92
- this.dataListener(message);
93
- }
94
- }
95
- };
96
- }
97
- send(msg) {
98
- this.ws.send(msg);
99
- }
100
- onData(cb) {
101
- this.dataListener = cb;
102
- }
103
- onError(cb) {
104
- this.errorListener = cb;
105
- }
106
- onClose(cb) {
107
- this.closeListener = cb;
108
- }
109
- close() {
110
- if (this.session) {
111
- this.session.stopSimulator(true);
112
- }
113
- if (this.intervalRunning) {
114
- clearInterval(this.intervalId);
115
- this.intervalId = undefined;
116
- }
117
- if (this.ws) {
118
- this.ws.close();
119
- }
120
- }
121
- handleRunnerMessage(msg) {
122
- switch (msg.subtype) {
123
- case "ready":
124
- this.sendRunnerMessage("ready");
125
- break;
126
- case "runcode":
127
- this.runCode(msg);
128
- break;
129
- }
130
- }
131
- runCode(msg) {
132
- const breakpoints = [];
133
- // The breakpoints are in the format returned by the compiler
134
- // and need to be converted to the format used by the DebugProtocol
135
- msg.breakpoints.forEach(bp => {
136
- breakpoints.push([bp.id, {
137
- verified: true,
138
- line: bp.line,
139
- column: bp.column,
140
- endLine: bp.endLine,
141
- endColumn: bp.endColumn,
142
- source: {
143
- path: bp.fileName
144
- }
145
- }]);
146
- });
147
- this.session.runCode(msg.code, msg.usedParts, msg.usedArguments, new pxsim.BreakpointMap(breakpoints), pxt.appTarget.simulator.boardDefinition);
148
- }
149
- sendRunnerMessage(subtype, msg = {}) {
150
- msg["subtype"] = subtype;
151
- msg["type"] = "runner";
152
- this.send(JSON.stringify(msg));
153
- }
154
- }
155
- DebugRunner.RETRY_MS = 2500;
156
- runner.DebugRunner = DebugRunner;
157
- })(runner = pxt.runner || (pxt.runner = {}));
158
- })(pxt || (pxt = {}));
159
- var pxt;
160
- (function (pxt) {
161
- var runner;
162
- (function (runner) {
163
- const JS_ICON = "icon xicon js";
164
- const PY_ICON = "icon xicon python";
165
- const BLOCKS_ICON = "icon xicon blocks";
166
- function defaultClientRenderOptions() {
167
- const renderOptions = {
168
- blocksAspectRatio: window.innerHeight < window.innerWidth ? 1.62 : 1 / 1.62,
169
- snippetClass: 'lang-blocks',
170
- signatureClass: 'lang-sig',
171
- blocksClass: 'lang-block',
172
- blocksXmlClass: 'lang-blocksxml',
173
- diffBlocksXmlClass: 'lang-diffblocksxml',
174
- diffClass: 'lang-diff',
175
- diffStaticPythonClass: 'lang-diffspy',
176
- diffBlocksClass: 'lang-diffblocks',
177
- staticPythonClass: 'lang-spy',
178
- simulatorClass: 'lang-sim',
179
- linksClass: 'lang-cards',
180
- namespacesClass: 'lang-namespaces',
181
- apisClass: 'lang-apis',
182
- codeCardClass: 'lang-codecard',
183
- packageClass: 'lang-package',
184
- jresClass: 'lang-jres',
185
- assetJSONClass: 'lang-assetsjson',
186
- projectClass: 'lang-project',
187
- snippetReplaceParent: true,
188
- simulator: true,
189
- showEdit: true,
190
- hex: true,
191
- tutorial: false,
192
- showJavaScript: false,
193
- hexName: pxt.appTarget.id
194
- };
195
- return renderOptions;
196
- }
197
- runner.defaultClientRenderOptions = defaultClientRenderOptions;
198
- function highlight($js) {
199
- if (typeof hljs !== "undefined") {
200
- if ($js.hasClass("highlight")) {
201
- hljs.highlightBlock($js[0]);
202
- }
203
- else {
204
- $js.find('code.highlight').each(function (i, block) {
205
- hljs.highlightBlock(block);
206
- });
207
- }
208
- highlightLine($js);
209
- }
210
- }
211
- function highlightLine($js) {
212
- // apply line highlighting
213
- $js.find("span.hljs-comment:contains(@highlight)")
214
- .each((i, el) => {
215
- try {
216
- highlightLineElement(el);
217
- }
218
- catch (e) {
219
- pxt.reportException(e);
220
- }
221
- });
222
- }
223
- function highlightLineElement(el) {
224
- const $el = $(el);
225
- const span = document.createElement("span");
226
- span.className = "highlight-line";
227
- // find new line and split text node
228
- let next = el.nextSibling;
229
- if (!next || next.nodeType != Node.TEXT_NODE)
230
- return; // end of snippet?
231
- let text = next.textContent;
232
- let inewline = text.indexOf('\n');
233
- if (inewline < 0)
234
- return; // there should have been a new line here
235
- // split the next node
236
- next.textContent = text.substring(0, inewline + 1);
237
- $(document.createTextNode(text.substring(inewline + 1).replace(/^\s+/, ''))).insertAfter($(next));
238
- // process and highlight new line
239
- next = next.nextSibling;
240
- while (next) {
241
- let nextnext = next.nextSibling; // before we hoist it from the tree
242
- if (next.nodeType == Node.TEXT_NODE) {
243
- text = next.textContent;
244
- const inewline = text.indexOf('\n');
245
- if (inewline < 0) {
246
- span.appendChild(next);
247
- next = nextnext;
248
- }
249
- else {
250
- // we've hit the end of the line... split node in two
251
- span.appendChild(document.createTextNode(text.substring(0, inewline)));
252
- next.textContent = text.substring(inewline + 1);
253
- break;
254
- }
255
- }
256
- else {
257
- span.appendChild(next);
258
- next = nextnext;
259
- }
260
- }
261
- // insert back
262
- $(span).insertAfter($el);
263
- // remove line entry
264
- $el.remove();
265
- }
266
- function appendBlocks($parent, $svg) {
267
- $parent.append($(`<div class="ui content blocks"/>`).append($svg));
268
- }
269
- function appendJs($parent, $js, woptions) {
270
- $parent.append($(`<div class="ui content js"><div class="subheading"><i class="ui icon xicon js"></i>JavaScript</div></div>`).append($js));
271
- highlight($js);
272
- }
273
- function appendPy($parent, $py, woptions) {
274
- $parent.append($(`<div class="ui content py"><div class="subheading"><i class="ui icon xicon python"></i>Python</div></div>`).append($py));
275
- highlight($py);
276
- }
277
- function snippetBtn(label, icon) {
278
- const $btn = $(`<a class="item" role="button" tabindex="0"><i role="presentation" aria-hidden="true"></i><span class="ui desktop only"></span></a>`);
279
- $btn.attr("aria-label", label);
280
- $btn.attr("title", label);
281
- $btn.find('i').attr("class", icon);
282
- $btn.find('span').text(label);
283
- addFireClickOnEnter($btn);
284
- return $btn;
285
- }
286
- function addFireClickOnEnter(el) {
287
- el.keypress(e => {
288
- const charCode = (typeof e.which == "number") ? e.which : e.keyCode;
289
- if (charCode === 13 /* enter */ || charCode === 32 /* space */) {
290
- e.preventDefault();
291
- e.currentTarget.click();
292
- }
293
- });
294
- }
295
- function fillWithWidget(options, $container, $js, $py, $svg, decompileResult, woptions = {}) {
296
- let $h = $('<div class="ui bottom attached tabular icon small compact menu hideprint">'
297
- + ' <div class="right icon menu"></div></div>');
298
- let $c = $('<div class="ui top attached segment codewidget"></div>');
299
- let $menu = $h.find('.right.menu');
300
- const theme = pxt.appTarget.appTheme || {};
301
- if (woptions.showEdit && !theme.hideDocsEdit && decompileResult) { // edit button
302
- const $editBtn = snippetBtn(lf("Edit"), "edit icon");
303
- const { package: pkg, compileBlocks, compilePython } = decompileResult;
304
- const host = pkg.host();
305
- if ($svg && compileBlocks) {
306
- pkg.setPreferredEditor(pxt.BLOCKS_PROJECT_NAME);
307
- host.writeFile(pkg, pxt.MAIN_BLOCKS, compileBlocks.outfiles[pxt.MAIN_BLOCKS]);
308
- }
309
- else if ($py && compilePython) {
310
- pkg.setPreferredEditor(pxt.PYTHON_PROJECT_NAME);
311
- host.writeFile(pkg, pxt.MAIN_PY, compileBlocks.outfiles[pxt.MAIN_PY]);
312
- }
313
- else {
314
- pkg.setPreferredEditor(pxt.JAVASCRIPT_PROJECT_NAME);
315
- }
316
- if (options.assetJSON) {
317
- for (const key of Object.keys(options.assetJSON)) {
318
- if (pkg.config.files.indexOf(key) < 0) {
319
- pkg.config.files.push(key);
320
- }
321
- host.writeFile(pkg, key, options.assetJSON[key]);
322
- }
323
- }
324
- const compressed = pkg.compressToFileAsync();
325
- $editBtn.click(() => {
326
- pxt.tickEvent("docs.btn", { button: "edit" });
327
- compressed.then(buf => {
328
- window.open(`${getEditUrl(options)}/#project:${ts.pxtc.encodeBase64(pxt.Util.uint8ArrayToString(buf))}`, 'pxt');
329
- });
330
- });
331
- $menu.append($editBtn);
332
- }
333
- if (options.showJavaScript || (!$svg && !$py)) {
334
- // js
335
- $c.append($js);
336
- appendBlocksButton();
337
- appendPyButton();
338
- }
339
- else if ($svg) {
340
- // blocks
341
- $c.append($svg);
342
- appendJsButton();
343
- appendPyButton();
344
- }
345
- else if ($py) {
346
- $c.append($py);
347
- appendBlocksButton();
348
- appendJsButton();
349
- }
350
- // runner menu
351
- if (woptions.run && !theme.hideDocsSimulator) {
352
- let $runBtn = snippetBtn(lf("Run"), "play icon").click(() => {
353
- pxt.tickEvent("docs.btn", { button: "sim" });
354
- if ($c.find('.sim')[0]) {
355
- $c.find('.sim').remove(); // remove previous simulators
356
- scrollJQueryIntoView($c);
357
- }
358
- else {
359
- let padding = '81.97%';
360
- if (pxt.appTarget.simulator)
361
- padding = (100 / pxt.appTarget.simulator.aspectRatio) + '%';
362
- const deps = options.package ? "&deps=" + encodeURIComponent(options.package) : "";
363
- const url = getRunUrl(options) + "#nofooter=1" + deps;
364
- const assets = options.assetJSON ? `data-assets="${encodeURIComponent(JSON.stringify(options.assetJSON))}"` : "";
365
- const data = encodeURIComponent($js.text());
366
- let $embed = $(`<div class="ui card sim"><div class="ui content"><div style="position:relative;height:0;padding-bottom:${padding};overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="${url}" data-code="${data}" ${assets} allowfullscreen="allowfullscreen" sandbox="allow-popups allow-forms allow-scripts allow-same-origin" frameborder="0"></iframe></div></div></div>`);
367
- $c.append($embed);
368
- scrollJQueryIntoView($embed);
369
- }
370
- });
371
- $menu.append($runBtn);
372
- }
373
- if (woptions.hexname && woptions.hex) {
374
- let $hexBtn = snippetBtn(lf("Download"), "download icon").click(() => {
375
- pxt.tickEvent("docs.btn", { button: "hex" });
376
- pxt.BrowserUtils.browserDownloadBinText(woptions.hex, woptions.hexname, { contentType: pxt.appTarget.compile.hexMimeType });
377
- });
378
- $menu.append($hexBtn);
379
- }
380
- let r = $(`<div class=codesnippet></div>`);
381
- // don't add menu if empty
382
- if ($menu.children().length)
383
- r.append($h);
384
- r.append($c);
385
- // inject container
386
- $container.replaceWith(r);
387
- function appendBlocksButton() {
388
- if (!$svg)
389
- return;
390
- const $svgBtn = snippetBtn(lf("Blocks"), BLOCKS_ICON).click(() => {
391
- pxt.tickEvent("docs.btn", { button: "blocks" });
392
- if ($c.find('.blocks')[0]) {
393
- $c.find('.blocks').remove();
394
- scrollJQueryIntoView($c);
395
- }
396
- else {
397
- if ($js)
398
- appendBlocks($js.parent(), $svg);
399
- else
400
- appendBlocks($c, $svg);
401
- scrollJQueryIntoView($svg);
402
- }
403
- });
404
- $menu.append($svgBtn);
405
- }
406
- function appendJsButton() {
407
- if (!$js)
408
- return;
409
- if (woptions.showJs)
410
- appendJs($c, $js, woptions);
411
- else {
412
- const $jsBtn = snippetBtn("JavaScript", JS_ICON).click(() => {
413
- pxt.tickEvent("docs.btn", { button: "js" });
414
- if ($c.find('.js')[0]) {
415
- $c.find('.js').remove();
416
- scrollJQueryIntoView($c);
417
- }
418
- else {
419
- if ($svg)
420
- appendJs($svg.parent(), $js, woptions);
421
- else
422
- appendJs($c, $js, woptions);
423
- scrollJQueryIntoView($js);
424
- }
425
- });
426
- $menu.append($jsBtn);
427
- }
428
- }
429
- function appendPyButton() {
430
- if (!$py)
431
- return;
432
- if (woptions.showPy) {
433
- appendPy($c, $py, woptions);
434
- }
435
- else {
436
- const $pyBtn = snippetBtn("Python", PY_ICON).click(() => {
437
- pxt.tickEvent("docs.btn", { button: "py" });
438
- if ($c.find('.py')[0]) {
439
- $c.find('.py').remove();
440
- scrollJQueryIntoView($c);
441
- }
442
- else {
443
- if ($svg)
444
- appendPy($svg.parent(), $py, woptions);
445
- else
446
- appendPy($c, $py, woptions);
447
- scrollJQueryIntoView($py);
448
- }
449
- });
450
- $menu.append($pyBtn);
451
- }
452
- }
453
- function scrollJQueryIntoView($toScrollTo) {
454
- var _a;
455
- (_a = $toScrollTo[0]) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
456
- behavior: "smooth",
457
- block: "center"
458
- });
459
- }
460
- }
461
- let renderQueue = [];
462
- function consumeRenderQueueAsync() {
463
- const existingFilters = {};
464
- return consumeNext()
465
- .then(() => {
466
- Blockly.Workspace.getAll().forEach(el => el.dispose());
467
- pxt.blocks.cleanRenderingWorkspace();
468
- });
469
- function consumeNext() {
470
- const job = renderQueue.shift();
471
- if (!job)
472
- return Promise.resolve(); // done
473
- const { el, options, render } = job;
474
- return pxt.runner.decompileSnippetAsync(el.text(), options)
475
- .then(r => {
476
- const errors = r.compileJS && r.compileJS.diagnostics && r.compileJS.diagnostics.filter(d => d.category == pxtc.DiagnosticCategory.Error);
477
- if (errors && errors.length) {
478
- errors.forEach(diag => pxt.reportError("docs.decompile", "" + diag.messageText, { "code": diag.code + "" }));
479
- }
480
- // filter out any blockly definitions from the svg that would be duplicates on the page
481
- r.blocksSvg.querySelectorAll("defs *").forEach(el => {
482
- if (existingFilters[el.id]) {
483
- el.remove();
484
- }
485
- else {
486
- existingFilters[el.id] = true;
487
- }
488
- });
489
- render(el, r);
490
- }, e => {
491
- pxt.reportException(e);
492
- el.append($('<div/>').addClass("ui segment warning").text(e.message));
493
- }).finally(() => {
494
- el.removeClass("lang-shadow");
495
- return consumeNext();
496
- });
497
- }
498
- }
499
- function renderNextSnippetAsync(cls, render, options) {
500
- if (!cls)
501
- return Promise.resolve();
502
- let $el = $("." + cls).first();
503
- if (!$el[0])
504
- return Promise.resolve();
505
- if (!options.emPixels)
506
- options.emPixels = 18;
507
- if (!options.layout)
508
- options.layout = pxt.blocks.BlockLayout.Align;
509
- options.splitSvg = true;
510
- renderQueue.push({ el: $el, source: $el.text(), options, render });
511
- $el.addClass("lang-shadow");
512
- $el.removeClass(cls);
513
- return renderNextSnippetAsync(cls, render, options);
514
- }
515
- function renderSnippetsAsync(options) {
516
- if (options.tutorial) {
517
- // don't render chrome for tutorials
518
- return renderNextSnippetAsync(options.snippetClass, (c, r) => {
519
- const s = r.blocksSvg;
520
- if (options.snippetReplaceParent)
521
- c = c.parent();
522
- const segment = $('<div class="ui segment codewidget"/>').append(s);
523
- c.replaceWith(segment);
524
- }, { package: options.package, snippetMode: false, aspectRatio: options.blocksAspectRatio, assets: options.assetJSON });
525
- }
526
- let snippetCount = 0;
527
- return renderNextSnippetAsync(options.snippetClass, (c, r) => {
528
- const s = r.compileBlocks && r.compileBlocks.success ? $(r.blocksSvg) : undefined;
529
- const p = r.compilePython && r.compilePython.success && r.compilePython.outfiles[pxt.MAIN_PY];
530
- const js = $('<code class="lang-typescript highlight"/>').text(c.text().trim());
531
- const py = p ? $('<code class="lang-python highlight"/>').text(p.trim()) : undefined;
532
- if (options.snippetReplaceParent)
533
- c = c.parent();
534
- const compiled = r.compileJS && r.compileJS.success;
535
- // TODO should this use pxt.outputName() and not pxtc.BINARY_HEX
536
- const hex = options.hex && compiled && r.compileJS.outfiles[pxtc.BINARY_HEX]
537
- ? r.compileJS.outfiles[pxtc.BINARY_HEX] : undefined;
538
- const hexname = `${pxt.appTarget.nickname || pxt.appTarget.id}-${options.hexName || ''}-${snippetCount++}.hex`;
539
- fillWithWidget(options, c, js, py, s, r, {
540
- showEdit: options.showEdit,
541
- run: options.simulator,
542
- hexname: hexname,
543
- hex: hex,
544
- });
545
- }, { package: options.package, aspectRatio: options.blocksAspectRatio, assets: options.assetJSON });
546
- }
547
- function decompileCallInfo(stmt) {
548
- if (!stmt || stmt.kind != ts.SyntaxKind.ExpressionStatement)
549
- return null;
550
- let estmt = stmt;
551
- if (!estmt.expression || estmt.expression.kind != ts.SyntaxKind.CallExpression)
552
- return null;
553
- let call = estmt.expression;
554
- let info = pxtc.pxtInfo(call).callInfo;
555
- return info;
556
- }
557
- function renderSignaturesAsync(options) {
558
- return renderNextSnippetAsync(options.signatureClass, (c, r) => {
559
- var _a, _b, _c, _d;
560
- let cjs = r.compileProgram;
561
- if (!cjs)
562
- return;
563
- let file = cjs.getSourceFile(pxt.MAIN_TS);
564
- let info = decompileCallInfo(file.statements[0]);
565
- if (!info || !r.apiInfo)
566
- return;
567
- const symbolInfo = r.apiInfo.byQName[info.qName];
568
- if (!symbolInfo)
569
- return;
570
- let block = Blockly.Blocks[symbolInfo.attributes.blockId];
571
- let xml = ((_a = block === null || block === void 0 ? void 0 : block.codeCard) === null || _a === void 0 ? void 0 : _a.blocksXml) || undefined;
572
- const blocksHtml = xml ? pxt.blocks.render(xml) : ((_b = r.compileBlocks) === null || _b === void 0 ? void 0 : _b.success) ? r.blocksSvg : undefined;
573
- const s = blocksHtml ? $(blocksHtml) : undefined;
574
- let jsSig = ts.pxtc.service.displayStringForSymbol(symbolInfo, /** python **/ false, r.apiInfo)
575
- .split("\n")[1] + ";";
576
- const js = $('<code class="lang-typescript highlight"/>').text(jsSig);
577
- const pySig = ((_d = (_c = pxt.appTarget) === null || _c === void 0 ? void 0 : _c.appTheme) === null || _d === void 0 ? void 0 : _d.python) && ts.pxtc.service.displayStringForSymbol(symbolInfo, /** python **/ true, r.apiInfo).split("\n")[1];
578
- const py = pySig && $('<code class="lang-python highlight"/>').text(pySig);
579
- if (options.snippetReplaceParent)
580
- c = c.parent();
581
- // add an html widge that allows to translate the block
582
- if (pxt.Util.isTranslationMode()) {
583
- const trs = $('<div class="ui segment" />');
584
- trs.append($(`<div class="ui header"><i class="ui xicon globe"></i></div>`));
585
- if (symbolInfo.attributes.translationId)
586
- trs.append($('<div class="ui message">').text(symbolInfo.attributes.translationId));
587
- if (symbolInfo.attributes.jsDoc)
588
- trs.append($('<div class="ui message">').text(symbolInfo.attributes.jsDoc));
589
- trs.insertAfter(c);
590
- }
591
- fillWithWidget(options, c, js, py, s, r, { showJs: true, showPy: true, hideGutter: true });
592
- }, { package: options.package, snippetMode: true, aspectRatio: options.blocksAspectRatio, assets: options.assetJSON });
593
- }
594
- function renderBlocksAsync(options) {
595
- return renderNextSnippetAsync(options.blocksClass, (c, r) => {
596
- const s = r.blocksSvg;
597
- if (options.snippetReplaceParent)
598
- c = c.parent();
599
- const segment = $('<div class="ui segment codewidget"/>').append(s);
600
- c.replaceWith(segment);
601
- }, { package: options.package, snippetMode: true, aspectRatio: options.blocksAspectRatio, assets: options.assetJSON });
602
- }
603
- function renderStaticPythonAsync(options) {
604
- // Highlight python snippets if the snippet has compile python
605
- const woptions = {
606
- showEdit: !!options.showEdit,
607
- run: !!options.simulator
608
- };
609
- return renderNextSnippetAsync(options.staticPythonClass, (c, r) => {
610
- const s = r.compilePython;
611
- if (s && s.success) {
612
- const $js = c.clone().removeClass('lang-shadow').addClass('highlight');
613
- const $py = $js.clone().addClass('lang-python').text(s.outfiles[pxt.MAIN_PY]);
614
- $js.addClass('lang-typescript');
615
- highlight($py);
616
- fillWithWidget(options, c.parent(), /* js */ $js, /* py */ $py, /* svg */ undefined, r, woptions);
617
- }
618
- }, { package: options.package, snippetMode: true, assets: options.assetJSON });
619
- }
620
- function renderBlocksXmlAsync(opts) {
621
- if (!opts.blocksXmlClass)
622
- return Promise.resolve();
623
- const cls = opts.blocksXmlClass;
624
- function renderNextXmlAsync(cls, render, options) {
625
- let $el = $("." + cls).first();
626
- if (!$el[0])
627
- return Promise.resolve();
628
- if (!options.emPixels)
629
- options.emPixels = 18;
630
- options.splitSvg = true;
631
- return pxt.runner.compileBlocksAsync($el.text(), options)
632
- .then((r) => {
633
- try {
634
- render($el, r);
635
- }
636
- catch (e) {
637
- pxt.reportException(e);
638
- $el.append($('<div/>').addClass("ui segment warning").text(e.message));
639
- }
640
- $el.removeClass(cls);
641
- return pxt.U.delay(1, renderNextXmlAsync(cls, render, options));
642
- });
643
- }
644
- return renderNextXmlAsync(cls, (c, r) => {
645
- const s = r.blocksSvg;
646
- if (opts.snippetReplaceParent)
647
- c = c.parent();
648
- const segment = $('<div class="ui segment codewidget"/>').append(s);
649
- c.replaceWith(segment);
650
- }, { package: opts.package, snippetMode: true, aspectRatio: opts.blocksAspectRatio, assets: opts.assetJSON });
651
- }
652
- function renderDiffBlocksXmlAsync(opts) {
653
- if (!opts.diffBlocksXmlClass)
654
- return Promise.resolve();
655
- const cls = opts.diffBlocksXmlClass;
656
- function renderNextXmlAsync(cls, render, options) {
657
- let $el = $("." + cls).first();
658
- if (!$el[0])
659
- return Promise.resolve();
660
- if (!options.emPixels)
661
- options.emPixels = 18;
662
- options.splitSvg = true;
663
- const xml = $el.text().split(/-{10,}/);
664
- const oldXml = xml[0];
665
- const newXml = xml[1];
666
- return pxt.runner.compileBlocksAsync("", options) // force loading blocks
667
- .then(r => {
668
- $el.removeClass(cls);
669
- try {
670
- const diff = pxt.blocks.diffXml(oldXml, newXml);
671
- if (!diff)
672
- $el.text("no changes");
673
- else {
674
- r.blocksSvg = diff.svg;
675
- render($el, r);
676
- }
677
- }
678
- catch (e) {
679
- pxt.reportException(e);
680
- $el.append($('<div/>').addClass("ui segment warning").text(e.message));
681
- }
682
- return pxt.U.delay(1, renderNextXmlAsync(cls, render, options));
683
- });
684
- }
685
- return renderNextXmlAsync(cls, (c, r) => {
686
- const s = r.blocksSvg;
687
- if (opts.snippetReplaceParent)
688
- c = c.parent();
689
- const segment = $('<div class="ui segment codewidget"/>').append(s);
690
- c.replaceWith(segment);
691
- }, { package: opts.package, snippetMode: true, aspectRatio: opts.blocksAspectRatio, assets: opts.assetJSON });
692
- }
693
- function renderDiffAsync(opts) {
694
- if (!opts.diffClass)
695
- return Promise.resolve();
696
- const cls = opts.diffClass;
697
- function renderNextDiffAsync(cls) {
698
- let $el = $("." + cls).first();
699
- if (!$el[0])
700
- return Promise.resolve();
701
- const { fileA: oldSrc, fileB: newSrc } = pxt.diff.split($el.text());
702
- try {
703
- const diffEl = pxt.diff.render(oldSrc, newSrc, {
704
- hideLineNumbers: true,
705
- hideMarkerLine: true,
706
- hideMarker: true,
707
- hideRemoved: true,
708
- update: true,
709
- ignoreWhitespace: true,
710
- });
711
- if (opts.snippetReplaceParent)
712
- $el = $el.parent();
713
- const segment = $('<div class="ui segment codewidget"/>').append(diffEl);
714
- $el.removeClass(cls);
715
- $el.replaceWith(segment);
716
- }
717
- catch (e) {
718
- pxt.reportException(e);
719
- $el.append($('<div/>').addClass("ui segment warning").text(e.message));
720
- }
721
- return pxt.U.delay(1, renderNextDiffAsync(cls));
722
- }
723
- return renderNextDiffAsync(cls);
724
- }
725
- function renderDiffBlocksAsync(opts) {
726
- if (!opts.diffBlocksClass)
727
- return Promise.resolve();
728
- const cls = opts.diffBlocksClass;
729
- function renderNextDiffAsync(cls) {
730
- let $el = $("." + cls).first();
731
- if (!$el[0])
732
- return Promise.resolve();
733
- const { fileA: oldSrc, fileB: newSrc } = pxt.diff.split($el.text(), {
734
- removeTrailingSemiColumns: true
735
- });
736
- return pxt.U.promiseMapAllSeries([oldSrc, newSrc], src => pxt.runner.decompileSnippetAsync(src, {
737
- generateSourceMap: true
738
- }))
739
- .then(resps => {
740
- try {
741
- const diffBlocks = pxt.blocks.decompiledDiffAsync(oldSrc, resps[0].compileBlocks, newSrc, resps[1].compileBlocks, {
742
- hideDeletedTopBlocks: true,
743
- hideDeletedBlocks: true
744
- });
745
- const diffJs = pxt.diff.render(oldSrc, newSrc, {
746
- hideLineNumbers: true,
747
- hideMarkerLine: true,
748
- hideMarker: true,
749
- hideRemoved: true,
750
- update: true,
751
- ignoreWhitespace: true
752
- });
753
- let diffPy;
754
- const [oldPy, newPy] = resps.map(resp => resp.compilePython
755
- && resp.compilePython.outfiles
756
- && resp.compilePython.outfiles[pxt.MAIN_PY]);
757
- if (oldPy && newPy) {
758
- diffPy = pxt.diff.render(oldPy, newPy, {
759
- hideLineNumbers: true,
760
- hideMarkerLine: true,
761
- hideMarker: true,
762
- hideRemoved: true,
763
- update: true,
764
- ignoreWhitespace: true
765
- });
766
- }
767
- fillWithWidget(opts, $el.parent(), $(diffJs), diffPy && $(diffPy), $(diffBlocks.svg), undefined, {
768
- showEdit: false,
769
- run: false,
770
- hexname: undefined,
771
- hex: undefined
772
- });
773
- }
774
- catch (e) {
775
- pxt.reportException(e);
776
- $el.append($('<div/>').addClass("ui segment warning").text(e.message));
777
- }
778
- return pxt.U.delay(1, renderNextDiffAsync(cls));
779
- });
780
- }
781
- return renderNextDiffAsync(cls);
782
- }
783
- let decompileApiPromise;
784
- function decompileApiAsync(options) {
785
- if (!decompileApiPromise)
786
- decompileApiPromise = pxt.runner.decompileSnippetAsync('', options);
787
- return decompileApiPromise;
788
- }
789
- function renderNamespaces(options) {
790
- if (pxt.appTarget.id == "core")
791
- return Promise.resolve();
792
- return decompileApiAsync(options)
793
- .then((r) => {
794
- let res = {};
795
- const info = r.compileBlocks.blocksInfo;
796
- info.blocks.forEach(fn => {
797
- const ns = (fn.attributes.blockNamespace || fn.namespace).split('.')[0];
798
- if (!res[ns]) {
799
- const nsn = info.apis.byQName[ns];
800
- if (nsn && nsn.attributes.color)
801
- res[ns] = nsn.attributes.color;
802
- }
803
- });
804
- let nsStyleBuffer = '';
805
- Object.keys(res).forEach(ns => {
806
- const color = res[ns] || '#dddddd';
807
- nsStyleBuffer += `
808
- span.docs.${ns.toLowerCase()} {
809
- background-color: ${color} !important;
810
- border-color: ${pxt.toolbox.fadeColor(color, 0.1, false)} !important;
811
- }
812
- `;
813
- });
814
- return nsStyleBuffer;
815
- })
816
- .then((nsStyleBuffer) => {
817
- Object.keys(pxt.toolbox.blockColors).forEach((ns) => {
818
- const color = pxt.toolbox.getNamespaceColor(ns);
819
- nsStyleBuffer += `
820
- span.docs.${ns.toLowerCase()} {
821
- background-color: ${color} !important;
822
- border-color: ${pxt.toolbox.fadeColor(color, 0.1, false)} !important;
823
- }
824
- `;
825
- });
826
- return nsStyleBuffer;
827
- })
828
- .then((nsStyleBuffer) => {
829
- // Inject css
830
- let nsStyle = document.createElement('style');
831
- nsStyle.id = "namespaceColors";
832
- nsStyle.type = 'text/css';
833
- let head = document.head || document.getElementsByTagName('head')[0];
834
- head.appendChild(nsStyle);
835
- nsStyle.appendChild(document.createTextNode(nsStyleBuffer));
836
- });
837
- }
838
- function renderInlineBlocksAsync(options) {
839
- options = pxt.Util.clone(options);
840
- options.emPixels = 18;
841
- options.snippetMode = true;
842
- const $els = $(`:not(pre) > code`);
843
- let i = 0;
844
- function renderNextAsync() {
845
- if (i >= $els.length)
846
- return Promise.resolve();
847
- const $el = $($els[i++]);
848
- const text = $el.text();
849
- const mbtn = /^(\|+)([^\|]+)\|+$/.exec(text);
850
- if (mbtn) {
851
- const mtxt = /^(([^\:\.]*?)[\:\.])?(.*)$/.exec(mbtn[2]);
852
- const ns = mtxt[2] ? mtxt[2].trim().toLowerCase() : '';
853
- const lev = mbtn[1].length == 1 ? `docs inlinebutton ${ns}` : `docs inlineblock ${ns}`;
854
- const txt = mtxt[3].trim();
855
- $el.replaceWith($(`<span class="${lev}"/>`).text(pxt.U.rlf(txt)));
856
- return renderNextAsync();
857
- }
858
- const m = /^\[(.+)\]$/.exec(text);
859
- if (!m)
860
- return renderNextAsync();
861
- const code = m[1];
862
- return pxt.runner.decompileSnippetAsync(code, options)
863
- .then(r => {
864
- if (r.blocksSvg) {
865
- let $newel = $('<span class="block"/>').append(r.blocksSvg);
866
- const file = r.compileProgram.getSourceFile(pxt.MAIN_TS);
867
- const stmt = file.statements[0];
868
- const info = decompileCallInfo(stmt);
869
- if (info && r.apiInfo) {
870
- const symbolInfo = r.apiInfo.byQName[info.qName];
871
- if (symbolInfo && symbolInfo.attributes.help) {
872
- $newel = $(`<a class="ui link"/>`).attr("href", `/reference/${symbolInfo.attributes.help}`).append($newel);
873
- }
874
- }
875
- $el.replaceWith($newel);
876
- }
877
- return pxt.U.delay(1, renderNextAsync());
878
- });
879
- }
880
- return renderNextAsync();
881
- }
882
- function renderProjectAsync(options) {
883
- if (!options.projectClass)
884
- return Promise.resolve();
885
- function render() {
886
- let $el = $("." + options.projectClass).first();
887
- let e = $el[0];
888
- if (!e)
889
- return Promise.resolve();
890
- $el.removeClass(options.projectClass);
891
- let id = pxt.Cloud.parseScriptId(e.innerText);
892
- if (id) {
893
- if (options.snippetReplaceParent) {
894
- e = e.parentElement;
895
- // create a new div to host the rendered code
896
- let d = document.createElement("div");
897
- e.parentElement.insertBefore(d, e);
898
- e.parentElement.removeChild(e);
899
- e = d;
900
- }
901
- return pxt.runner.renderProjectAsync(e, id)
902
- .then(() => render());
903
- }
904
- else
905
- return render();
906
- }
907
- return render();
908
- }
909
- function renderApisAsync(options, replaceParent) {
910
- const cls = options.apisClass;
911
- if (!cls)
912
- return Promise.resolve();
913
- const apisEl = $('.' + cls);
914
- if (!apisEl.length)
915
- return Promise.resolve();
916
- return decompileApiAsync(options)
917
- .then((r) => {
918
- const info = r.compileBlocks.blocksInfo;
919
- const symbols = pxt.Util.values(info.apis.byQName)
920
- .filter(symbol => !symbol.attributes.hidden
921
- && !symbol.attributes.deprecated
922
- && !symbol.attributes.blockAliasFor
923
- && !!symbol.attributes.jsDoc
924
- && !!symbol.attributes.block
925
- && !/^__/.test(symbol.name));
926
- apisEl.each((i, e) => {
927
- let c = $(e);
928
- const namespaces = pxt.Util.toDictionary(c.text().split('\n'), n => n); // list of namespace to list apis for.
929
- const csymbols = symbols.filter(symbol => !!namespaces[symbol.attributes.blockNamespace || symbol.namespace]);
930
- if (!csymbols.length)
931
- return;
932
- csymbols.sort((l, r) => {
933
- // render cards first
934
- const lcard = !l.attributes.blockHidden && Blockly.Blocks[l.attributes.blockId];
935
- const rcard = !r.attributes.blockHidden && Blockly.Blocks[r.attributes.blockId];
936
- if (!!lcard != !!rcard)
937
- return -(lcard ? 1 : 0) + (rcard ? 1 : 0);
938
- // sort alphabetically
939
- return l.name.localeCompare(r.name);
940
- });
941
- const ul = $('<div />').addClass('ui divided items');
942
- ul.attr("role", "listbox");
943
- csymbols.forEach(symbol => addSymbolCardItem(ul, symbol, "item"));
944
- if (replaceParent)
945
- c = c.parent();
946
- c.replaceWith(ul);
947
- });
948
- });
949
- }
950
- function addCardItem(ul, card) {
951
- if (!card)
952
- return;
953
- const mC = /^\/(v\d+)/.exec(card.url);
954
- const mP = /^\/(v\d+)/.exec(window.location.pathname);
955
- const inEditor = /#doc/i.test(window.location.href);
956
- if (card.url && !mC && mP && !inEditor)
957
- card.url = `/${mP[1]}/${card.url}`;
958
- ul.append(pxt.docs.codeCard.render(card, { hideHeader: true, shortName: true }));
959
- }
960
- function addSymbolCardItem(ul, symbol, cardStyle) {
961
- const attributes = symbol.attributes;
962
- const block = !attributes.blockHidden && Blockly.Blocks[attributes.blockId];
963
- const card = block === null || block === void 0 ? void 0 : block.codeCard;
964
- if (card) {
965
- const ccard = pxt.U.clone(block.codeCard);
966
- if (cardStyle)
967
- ccard.style = cardStyle;
968
- addCardItem(ul, ccard);
969
- }
970
- else {
971
- // default to text
972
- // no block available here
973
- addCardItem(ul, {
974
- name: symbol.qName,
975
- description: attributes.jsDoc,
976
- url: attributes.help || undefined,
977
- style: cardStyle
978
- });
979
- }
980
- }
981
- function renderLinksAsync(options, cls, replaceParent, ns) {
982
- return renderNextSnippetAsync(cls, (c, r) => {
983
- const cjs = r.compileProgram;
984
- if (!cjs)
985
- return;
986
- const file = cjs.getSourceFile(pxt.MAIN_TS);
987
- const stmts = file.statements.slice(0);
988
- const ul = $('<div />').addClass('ui cards');
989
- ul.attr("role", "listbox");
990
- stmts.forEach(stmt => {
991
- const kind = stmt.kind;
992
- const info = decompileCallInfo(stmt);
993
- if (info && r.apiInfo && r.apiInfo.byQName[info.qName]) {
994
- const symbol = r.apiInfo.byQName[info.qName];
995
- const attributes = symbol.attributes;
996
- const block = Blockly.Blocks[attributes.blockId];
997
- if (ns) {
998
- const ii = symbol;
999
- const nsi = r.compileBlocks.blocksInfo.apis.byQName[ii.namespace];
1000
- addCardItem(ul, {
1001
- name: nsi.attributes.blockNamespace || nsi.name,
1002
- url: nsi.attributes.help || ("reference/" + (nsi.attributes.blockNamespace || nsi.name).toLowerCase()),
1003
- description: nsi.attributes.jsDoc,
1004
- blocksXml: block && block.codeCard
1005
- ? block.codeCard.blocksXml
1006
- : attributes.blockId
1007
- ? `<xml xmlns="http://www.w3.org/1999/xhtml"><block type="${attributes.blockId}"></block></xml>`
1008
- : undefined
1009
- });
1010
- }
1011
- else {
1012
- addSymbolCardItem(ul, symbol);
1013
- }
1014
- }
1015
- else
1016
- switch (kind) {
1017
- case ts.SyntaxKind.ExpressionStatement: {
1018
- const es = stmt;
1019
- switch (es.expression.kind) {
1020
- case ts.SyntaxKind.TrueKeyword:
1021
- case ts.SyntaxKind.FalseKeyword:
1022
- addCardItem(ul, {
1023
- name: "Boolean",
1024
- url: "blocks/logic/boolean",
1025
- description: lf("True or false values"),
1026
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="logic_boolean"><field name="BOOL">TRUE</field></block></xml>'
1027
- });
1028
- break;
1029
- default:
1030
- pxt.debug(`card expr kind: ${es.expression.kind}`);
1031
- break;
1032
- }
1033
- break;
1034
- }
1035
- case ts.SyntaxKind.IfStatement:
1036
- addCardItem(ul, {
1037
- name: ns ? "Logic" : "if",
1038
- url: "blocks/logic" + (ns ? "" : "/if"),
1039
- description: ns ? lf("Logic operators and constants") : lf("Conditional statement"),
1040
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_if"></block></xml>'
1041
- });
1042
- break;
1043
- case ts.SyntaxKind.WhileStatement:
1044
- addCardItem(ul, {
1045
- name: ns ? "Loops" : "while",
1046
- url: "blocks/loops" + (ns ? "" : "/while"),
1047
- description: ns ? lf("Loops and repetition") : lf("Repeat code while a condition is true."),
1048
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="device_while"></block></xml>'
1049
- });
1050
- break;
1051
- case ts.SyntaxKind.ForOfStatement:
1052
- addCardItem(ul, {
1053
- name: ns ? "Loops" : "for of",
1054
- url: "blocks/loops" + (ns ? "" : "/for-of"),
1055
- description: ns ? lf("Loops and repetition") : lf("Repeat code for each item in a list."),
1056
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_for_of"></block></xml>'
1057
- });
1058
- break;
1059
- case ts.SyntaxKind.BreakStatement:
1060
- addCardItem(ul, {
1061
- name: ns ? "Loops" : "break",
1062
- url: "blocks/loops" + (ns ? "" : "/break"),
1063
- description: ns ? lf("Loops and repetition") : lf("Break out of the current loop."),
1064
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="break_keyword"></block></xml>'
1065
- });
1066
- break;
1067
- case ts.SyntaxKind.ContinueStatement:
1068
- addCardItem(ul, {
1069
- name: ns ? "Loops" : "continue",
1070
- url: "blocks/loops" + (ns ? "" : "/continue"),
1071
- description: ns ? lf("Loops and repetition") : lf("Skip iteration and continue the current loop."),
1072
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="continue_keyboard"></block></xml>'
1073
- });
1074
- break;
1075
- case ts.SyntaxKind.ForStatement: {
1076
- let fs = stmt;
1077
- // look for the 'repeat' loop style signature in the condition expression, explicitly: (let i = 0; i < X; i++)
1078
- // for loops will have the '<=' conditional.
1079
- let forloop = true;
1080
- if (fs.condition.getChildCount() == 3) {
1081
- forloop = !(fs.condition.getChildAt(0).getText() == "0" ||
1082
- fs.condition.getChildAt(1).kind == ts.SyntaxKind.LessThanToken);
1083
- }
1084
- if (forloop) {
1085
- addCardItem(ul, {
1086
- name: ns ? "Loops" : "for",
1087
- url: "blocks/loops" + (ns ? "" : "/for"),
1088
- description: ns ? lf("Loops and repetition") : lf("Repeat code for a given number of times using an index."),
1089
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_simple_for"></block></xml>'
1090
- });
1091
- }
1092
- else {
1093
- addCardItem(ul, {
1094
- name: ns ? "Loops" : "repeat",
1095
- url: "blocks/loops" + (ns ? "" : "/repeat"),
1096
- description: ns ? lf("Loops and repetition") : lf("Repeat code for a given number of times."),
1097
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_repeat_ext"></block></xml>'
1098
- });
1099
- }
1100
- break;
1101
- }
1102
- case ts.SyntaxKind.VariableStatement:
1103
- addCardItem(ul, {
1104
- name: ns ? "Variables" : "variable declaration",
1105
- url: "blocks/variables" + (ns ? "" : "/assign"),
1106
- description: ns ? lf("Variables") : lf("Assign a value to a named variable."),
1107
- blocksXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="variables_set"></block></xml>'
1108
- });
1109
- break;
1110
- default:
1111
- pxt.debug(`card kind: ${kind}`);
1112
- }
1113
- });
1114
- if (replaceParent)
1115
- c = c.parent();
1116
- c.replaceWith(ul);
1117
- }, { package: options.package, aspectRatio: options.blocksAspectRatio, assets: options.assetJSON });
1118
- }
1119
- function fillCodeCardAsync(c, cards, options) {
1120
- if (!cards || cards.length == 0)
1121
- return Promise.resolve();
1122
- if (cards.length == 0) {
1123
- let cc = pxt.docs.codeCard.render(cards[0], options);
1124
- c.replaceWith(cc);
1125
- }
1126
- else {
1127
- let cd = document.createElement("div");
1128
- cd.className = "ui cards";
1129
- cd.setAttribute("role", "listbox");
1130
- cards.forEach(card => {
1131
- // patch card url with version if necessary, we don't do this in the editor because that goes through the backend and passes the targetVersion then
1132
- const mC = /^\/(v\d+)/.exec(card.url);
1133
- const mP = /^\/(v\d+)/.exec(window.location.pathname);
1134
- const inEditor = /#doc/i.test(window.location.href);
1135
- if (card.url && !mC && mP && !inEditor)
1136
- card.url = `/${mP[1]}${card.url}`;
1137
- const cardEl = pxt.docs.codeCard.render(card, options);
1138
- cd.appendChild(cardEl);
1139
- // automitcally display package icon for approved packages
1140
- if (card.cardType == "package") {
1141
- const repoId = pxt.github.parseRepoId((card.url || "").replace(/^\/pkg\//, ''));
1142
- if (repoId) {
1143
- pxt.packagesConfigAsync()
1144
- .then(pkgConfig => {
1145
- const status = pxt.github.repoStatus(repoId, pkgConfig);
1146
- switch (status) {
1147
- case pxt.github.GitRepoStatus.Banned:
1148
- cardEl.remove();
1149
- break;
1150
- case pxt.github.GitRepoStatus.Approved:
1151
- // update card info
1152
- card.imageUrl = pxt.github.mkRepoIconUrl(repoId);
1153
- // inject
1154
- cd.insertBefore(pxt.docs.codeCard.render(card, options), cardEl);
1155
- cardEl.remove();
1156
- break;
1157
- }
1158
- })
1159
- .catch(e => {
1160
- // swallow
1161
- pxt.reportException(e);
1162
- pxt.debug(`failed to load repo ${card.url}`);
1163
- });
1164
- }
1165
- }
1166
- });
1167
- c.replaceWith(cd);
1168
- }
1169
- return Promise.resolve();
1170
- }
1171
- function renderNextCodeCardAsync(cls, options) {
1172
- if (!cls)
1173
- return Promise.resolve();
1174
- let $el = $("." + cls).first();
1175
- if (!$el[0])
1176
- return Promise.resolve();
1177
- $el.removeClass(cls);
1178
- // try parsing the card as json
1179
- const cards = pxt.gallery.parseCodeCardsHtml($el[0]);
1180
- if (!cards) {
1181
- $el.append($('<div/>').addClass("ui segment warning").text("invalid codecard format"));
1182
- }
1183
- if (options.snippetReplaceParent)
1184
- $el = $el.parent();
1185
- return fillCodeCardAsync($el, cards, { hideHeader: true })
1186
- .then(() => pxt.U.delay(1, renderNextCodeCardAsync(cls, options)));
1187
- }
1188
- function getRunUrl(options) {
1189
- return options.pxtUrl ? options.pxtUrl + '/--run' : pxt.webConfig && pxt.webConfig.runUrl ? pxt.webConfig.runUrl : '/--run';
1190
- }
1191
- function getEditUrl(options) {
1192
- const url = options.pxtUrl || pxt.appTarget.appTheme.homeUrl;
1193
- return (url || "").replace(/\/$/, '');
1194
- }
1195
- function mergeConfig(options) {
1196
- // additional config options
1197
- if (!options.packageClass)
1198
- return;
1199
- $('.' + options.packageClass).each((i, c) => {
1200
- let $c = $(c);
1201
- let name = $c.text().split('\n').map(s => s.replace(/\s*/g, '')).filter(s => !!s).join(',');
1202
- options.package = options.package ? `${options.package},${name}` : name;
1203
- if (options.snippetReplaceParent)
1204
- $c = $c.parent();
1205
- $c.remove();
1206
- });
1207
- $('.lang-config').each((i, c) => {
1208
- let $c = $(c);
1209
- if (options.snippetReplaceParent)
1210
- $c = $c.parent();
1211
- $c.remove();
1212
- });
1213
- }
1214
- function readAssetJson(options) {
1215
- let assetJson;
1216
- let tilemapJres;
1217
- if (options.jresClass) {
1218
- $(`.${options.jresClass}`).each((i, c) => {
1219
- const $c = $(c);
1220
- tilemapJres = $c.text();
1221
- c.parentElement.remove();
1222
- });
1223
- }
1224
- if (options.assetJSONClass) {
1225
- $(`.${options.assetJSONClass}`).each((i, c) => {
1226
- const $c = $(c);
1227
- assetJson = $c.text();
1228
- c.parentElement.remove();
1229
- });
1230
- }
1231
- options.assetJSON = mergeAssetJson(assetJson, tilemapJres);
1232
- function mergeAssetJson(assetJSON, tilemapJres) {
1233
- if (!assetJSON && !tilemapJres)
1234
- return undefined;
1235
- const mergedJson = pxt.tutorial.parseAssetJson(assetJSON) || {};
1236
- if (tilemapJres) {
1237
- const parsedTmapJres = JSON.parse(tilemapJres);
1238
- mergedJson[pxt.TILEMAP_JRES] = JSON.stringify(parsedTmapJres);
1239
- mergedJson[pxt.TILEMAP_CODE] = pxt.emitTilemapsFromJRes(parsedTmapJres);
1240
- }
1241
- return mergedJson;
1242
- }
1243
- }
1244
- function renderDirectPython(options) {
1245
- // Highlight python snippets written with the ```python
1246
- // language tag (as opposed to the ```spy tag, see renderStaticPythonAsync for that)
1247
- const woptions = {
1248
- showEdit: !!options.showEdit,
1249
- run: !!options.simulator
1250
- };
1251
- function render(e, ignored) {
1252
- if (typeof hljs !== "undefined") {
1253
- $(e).text($(e).text().replace(/^\s*\r?\n/, ''));
1254
- hljs.highlightBlock(e);
1255
- highlightLine($(e));
1256
- }
1257
- const opts = pxt.U.clone(woptions);
1258
- if (ignored) {
1259
- opts.run = false;
1260
- opts.showEdit = false;
1261
- }
1262
- fillWithWidget(options, $(e).parent(), $(e), /* py */ undefined, /* JQuery */ undefined, /* decompileResult */ undefined, opts);
1263
- }
1264
- $('code.lang-python').each((i, e) => {
1265
- render(e, false);
1266
- $(e).removeClass('lang-python');
1267
- });
1268
- }
1269
- function renderTypeScript(options) {
1270
- const woptions = {
1271
- showEdit: !!options.showEdit,
1272
- run: !!options.simulator
1273
- };
1274
- function render(e, ignored) {
1275
- if (typeof hljs !== "undefined") {
1276
- $(e).text($(e).text().replace(/^\s*\r?\n/, ''));
1277
- hljs.highlightBlock(e);
1278
- highlightLine($(e));
1279
- }
1280
- const opts = pxt.U.clone(woptions);
1281
- if (ignored) {
1282
- opts.run = false;
1283
- opts.showEdit = false;
1284
- }
1285
- fillWithWidget(options, $(e).parent(), $(e), /* py */ undefined, /* JQuery */ undefined, /* decompileResult */ undefined, opts);
1286
- }
1287
- $('code.lang-typescript').each((i, e) => {
1288
- render(e, false);
1289
- $(e).removeClass('lang-typescript');
1290
- });
1291
- $('code.lang-typescript-ignore').each((i, e) => {
1292
- $(e).removeClass('lang-typescript-ignore');
1293
- $(e).addClass('lang-typescript');
1294
- render(e, true);
1295
- $(e).removeClass('lang-typescript');
1296
- });
1297
- $('code.lang-typescript-invalid').each((i, e) => {
1298
- $(e).removeClass('lang-typescript-invalid');
1299
- $(e).addClass('lang-typescript');
1300
- render(e, true);
1301
- $(e).removeClass('lang-typescript');
1302
- $(e).parent('div').addClass('invalid');
1303
- $(e).parent('div').prepend($("<i>", { "class": "icon ban" }));
1304
- $(e).addClass('invalid');
1305
- });
1306
- $('code.lang-typescript-valid').each((i, e) => {
1307
- $(e).removeClass('lang-typescript-valid');
1308
- $(e).addClass('lang-typescript');
1309
- render(e, true);
1310
- $(e).removeClass('lang-typescript');
1311
- $(e).parent('div').addClass('valid');
1312
- $(e).parent('div').prepend($("<i>", { "class": "icon check" }));
1313
- $(e).addClass('valid');
1314
- });
1315
- }
1316
- function renderGhost(options) {
1317
- let c = $('code.lang-ghost');
1318
- if (options.snippetReplaceParent)
1319
- c = c.parent();
1320
- c.remove();
1321
- }
1322
- function renderBlockConfig(options) {
1323
- function render(scope) {
1324
- $(`code.lang-blockconfig.${scope}`).each((i, c) => {
1325
- let $c = $(c);
1326
- if (options.snippetReplaceParent)
1327
- $c = $c.parent();
1328
- $c.remove();
1329
- });
1330
- }
1331
- render("local");
1332
- render("global");
1333
- }
1334
- function renderSims(options) {
1335
- if (!options.simulatorClass)
1336
- return;
1337
- // simulators
1338
- $('.' + options.simulatorClass).each((i, c) => {
1339
- let $c = $(c);
1340
- let padding = '81.97%';
1341
- if (pxt.appTarget.simulator)
1342
- padding = (100 / pxt.appTarget.simulator.aspectRatio) + '%';
1343
- let $sim = $(`<div class="ui card"><div class="ui content">
1344
- <div style="position:relative;height:0;padding-bottom:${padding};overflow:hidden;">
1345
- <iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" allowfullscreen="allowfullscreen" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe>
1346
- </div>
1347
- </div></div>`);
1348
- const deps = options.package ? "&deps=" + encodeURIComponent(options.package) : "";
1349
- const url = getRunUrl(options) + "#nofooter=1" + deps;
1350
- const data = encodeURIComponent($c.text().trim());
1351
- const $simIFrame = $sim.find("iframe");
1352
- $simIFrame.attr("src", url);
1353
- $simIFrame.attr("data-code", data);
1354
- if (options.assetJSON) {
1355
- $simIFrame.attr("data-assets", JSON.stringify(options.assetJSON));
1356
- }
1357
- if (options.snippetReplaceParent)
1358
- $c = $c.parent();
1359
- $c.replaceWith($sim);
1360
- });
1361
- }
1362
- function renderAsync(options) {
1363
- pxt.analytics.enable(pxt.Util.userLanguage());
1364
- if (!options)
1365
- options = defaultClientRenderOptions();
1366
- if (options.pxtUrl)
1367
- options.pxtUrl = options.pxtUrl.replace(/\/$/, '');
1368
- if (options.showEdit)
1369
- options.showEdit = !pxt.BrowserUtils.isIFrame();
1370
- mergeConfig(options);
1371
- readAssetJson(options);
1372
- renderQueue = [];
1373
- renderGhost(options);
1374
- renderBlockConfig(options);
1375
- renderSims(options);
1376
- renderTypeScript(options);
1377
- renderDirectPython(options);
1378
- return Promise.resolve()
1379
- .then(() => renderNextCodeCardAsync(options.codeCardClass, options))
1380
- .then(() => renderNamespaces(options))
1381
- .then(() => renderInlineBlocksAsync(options))
1382
- .then(() => renderLinksAsync(options, options.linksClass, options.snippetReplaceParent, false))
1383
- .then(() => renderLinksAsync(options, options.namespacesClass, options.snippetReplaceParent, true))
1384
- .then(() => renderApisAsync(options, options.snippetReplaceParent))
1385
- .then(() => renderSignaturesAsync(options))
1386
- .then(() => renderSnippetsAsync(options))
1387
- .then(() => renderBlocksAsync(options))
1388
- .then(() => renderBlocksXmlAsync(options))
1389
- .then(() => renderDiffBlocksXmlAsync(options))
1390
- .then(() => renderDiffBlocksAsync(options))
1391
- .then(() => renderDiffAsync(options))
1392
- .then(() => renderStaticPythonAsync(options))
1393
- .then(() => renderProjectAsync(options))
1394
- .then(() => consumeRenderQueueAsync());
1395
- }
1396
- runner.renderAsync = renderAsync;
1397
- })(runner = pxt.runner || (pxt.runner = {}));
1398
- })(pxt || (pxt = {}));
1399
- /* TODO(tslint): get rid of jquery html() calls */
1400
- /// <reference path="../built/pxtlib.d.ts" />
1401
- /// <reference path="../built/pxteditor.d.ts" />
1402
- /// <reference path="../built/pxtcompiler.d.ts" />
1403
- /// <reference path="../built/pxtblocks.d.ts" />
1404
- /// <reference path="../built/pxtsim.d.ts" />
1405
- var pxt;
1406
- (function (pxt) {
1407
- var runner;
1408
- (function (runner) {
1409
- class EditorPackage {
1410
- constructor(ksPkg, topPkg) {
1411
- this.ksPkg = ksPkg;
1412
- this.topPkg = topPkg;
1413
- this.files = {};
1414
- }
1415
- getKsPkg() {
1416
- return this.ksPkg;
1417
- }
1418
- getPkgId() {
1419
- return this.ksPkg ? this.ksPkg.id : this.id;
1420
- }
1421
- isTopLevel() {
1422
- return this.ksPkg && this.ksPkg.level == 0;
1423
- }
1424
- setFiles(files) {
1425
- this.files = files;
1426
- }
1427
- getAllFiles() {
1428
- return pxt.Util.mapMap(this.files, (k, f) => f);
1429
- }
1430
- }
1431
- class Host {
1432
- constructor() {
1433
- this.githubPackageCache = {};
1434
- }
1435
- readFile(module, filename) {
1436
- let epkg = getEditorPkg(module);
1437
- return pxt.U.lookup(epkg.files, filename);
1438
- }
1439
- writeFile(module, filename, contents) {
1440
- const epkg = getEditorPkg(module);
1441
- epkg.files[filename] = contents;
1442
- }
1443
- getHexInfoAsync(extInfo) {
1444
- return pxt.hexloader.getHexInfoAsync(this, extInfo);
1445
- }
1446
- cacheStoreAsync(id, val) {
1447
- return Promise.resolve();
1448
- }
1449
- cacheGetAsync(id) {
1450
- return Promise.resolve(null);
1451
- }
1452
- patchDependencies(cfg, name, repoId) {
1453
- if (!repoId)
1454
- return false;
1455
- // check that the same package hasn't been added yet
1456
- const repo = pxt.github.parseRepoId(repoId);
1457
- if (!repo)
1458
- return false;
1459
- for (const k of Object.keys(cfg.dependencies)) {
1460
- const v = cfg.dependencies[k];
1461
- const kv = pxt.github.parseRepoId(v);
1462
- if (kv && repo.fullName == kv.fullName) {
1463
- if (pxt.semver.strcmp(repo.tag, kv.tag) < 0) {
1464
- // we have a later tag, use this one
1465
- cfg.dependencies[k] = repoId;
1466
- }
1467
- return true;
1468
- }
1469
- }
1470
- return false;
1471
- }
1472
- downloadPackageAsync(pkg, dependencies) {
1473
- let proto = pkg.verProtocol();
1474
- let cached = undefined;
1475
- // cache resolve github packages
1476
- if (proto == "github")
1477
- cached = this.githubPackageCache[pkg._verspec];
1478
- let epkg = getEditorPkg(pkg);
1479
- return (cached ? Promise.resolve(cached) : pkg.commonDownloadAsync())
1480
- .then(resp => {
1481
- if (resp) {
1482
- if (proto == "github" && !cached)
1483
- this.githubPackageCache[pkg._verspec] = pxt.Util.clone(resp);
1484
- epkg.setFiles(resp);
1485
- return Promise.resolve();
1486
- }
1487
- if (proto == "empty") {
1488
- if (Object.keys(epkg.files).length == 0) {
1489
- epkg.setFiles(emptyPrjFiles());
1490
- }
1491
- if (dependencies && dependencies.length) {
1492
- const files = getEditorPkg(pkg).files;
1493
- const cfg = JSON.parse(files[pxt.CONFIG_NAME]);
1494
- dependencies.forEach((d) => {
1495
- addPackageToConfig(cfg, d);
1496
- });
1497
- files[pxt.CONFIG_NAME] = pxt.Package.stringifyConfig(cfg);
1498
- }
1499
- return Promise.resolve();
1500
- }
1501
- else if (proto == "docs") {
1502
- let files = emptyPrjFiles();
1503
- let cfg = JSON.parse(files[pxt.CONFIG_NAME]);
1504
- // load all dependencies
1505
- pkg.verArgument().split(',').forEach(d => {
1506
- if (!addPackageToConfig(cfg, d)) {
1507
- return;
1508
- }
1509
- });
1510
- if (!cfg.yotta)
1511
- cfg.yotta = {};
1512
- cfg.yotta.ignoreConflicts = true;
1513
- files[pxt.CONFIG_NAME] = pxt.Package.stringifyConfig(cfg);
1514
- epkg.setFiles(files);
1515
- return Promise.resolve();
1516
- }
1517
- else if (proto == "invalid") {
1518
- pxt.log(`skipping invalid pkg ${pkg.id}`);
1519
- return Promise.resolve();
1520
- }
1521
- else {
1522
- return Promise.reject(`Cannot download ${pkg.version()}; unknown protocol`);
1523
- }
1524
- });
1525
- }
1526
- }
1527
- let tilemapProject;
1528
- if (!pxt.react.getTilemapProject) {
1529
- pxt.react.getTilemapProject = () => {
1530
- if (!tilemapProject) {
1531
- tilemapProject = new pxt.TilemapProject();
1532
- tilemapProject.loadPackage(runner.mainPkg);
1533
- }
1534
- return tilemapProject;
1535
- };
1536
- }
1537
- function addPackageToConfig(cfg, dep) {
1538
- let m = /^([a-zA-Z0-9_-]+)(=(.+))?$/.exec(dep);
1539
- if (m) {
1540
- // TODO this line seems bad, patchdependencies is on host not this?
1541
- // looks like this should be a method in host
1542
- if (m[3] && this && this.patchDependencies(cfg, m[1], m[3]))
1543
- return false;
1544
- cfg.dependencies[m[1]] = m[3] || "*";
1545
- }
1546
- else
1547
- console.warn(`unknown package syntax ${dep}`);
1548
- return true;
1549
- }
1550
- function getEditorPkg(p) {
1551
- let r = p._editorPkg;
1552
- if (r)
1553
- return r;
1554
- let top = null;
1555
- if (p != runner.mainPkg)
1556
- top = getEditorPkg(runner.mainPkg);
1557
- let newOne = new EditorPackage(p, top);
1558
- if (p == runner.mainPkg)
1559
- newOne.topPkg = newOne;
1560
- p._editorPkg = newOne;
1561
- return newOne;
1562
- }
1563
- function emptyPrjFiles() {
1564
- let p = pxt.appTarget.tsprj;
1565
- let files = pxt.U.clone(p.files);
1566
- files[pxt.CONFIG_NAME] = pxt.Package.stringifyConfig(p.config);
1567
- files[pxt.MAIN_BLOCKS] = "";
1568
- return files;
1569
- }
1570
- function patchSemantic() {
1571
- if ($ && $.fn && $.fn.embed && $.fn.embed.settings && $.fn.embed.settings.sources && $.fn.embed.settings.sources.youtube) {
1572
- $.fn.embed.settings.sources.youtube.url = '//www.youtube.com/embed/{id}?rel=0';
1573
- }
1574
- }
1575
- function initInnerAsync() {
1576
- pxt.setAppTarget(window.pxtTargetBundle);
1577
- pxt.analytics.enable(pxt.Util.userLanguage());
1578
- pxt.Util.assert(!!pxt.appTarget);
1579
- const href = window.location.href;
1580
- let force = false;
1581
- let lang = undefined;
1582
- if (/[&?]translate=1/.test(href) && !pxt.BrowserUtils.isIE()) {
1583
- lang = ts.pxtc.Util.TRANSLATION_LOCALE;
1584
- force = true;
1585
- pxt.Util.enableLiveLocalizationUpdates();
1586
- }
1587
- else {
1588
- const cookieValue = /PXT_LANG=(.*?)(?:;|$)/.exec(document.cookie);
1589
- const mlang = /(live)?(force)?lang=([a-z]{2,}(-[A-Z]+)?)/i.exec(href);
1590
- lang = mlang ? mlang[3] : (cookieValue && cookieValue[1] || pxt.appTarget.appTheme.defaultLocale || navigator.userLanguage || navigator.language);
1591
- const defLocale = pxt.appTarget.appTheme.defaultLocale;
1592
- const langLowerCase = lang === null || lang === void 0 ? void 0 : lang.toLocaleLowerCase();
1593
- const localDevServe = pxt.BrowserUtils.isLocalHostDev()
1594
- && (!langLowerCase || (defLocale
1595
- ? defLocale.toLocaleLowerCase() === langLowerCase
1596
- : "en" === langLowerCase || "en-us" === langLowerCase));
1597
- const serveLocal = pxt.BrowserUtils.isPxtElectron() || localDevServe;
1598
- const liveTranslationsDisabled = serveLocal || pxt.appTarget.appTheme.disableLiveTranslations;
1599
- if (!liveTranslationsDisabled || !!(mlang === null || mlang === void 0 ? void 0 : mlang[1])) {
1600
- pxt.Util.enableLiveLocalizationUpdates();
1601
- }
1602
- force = !!mlang && !!mlang[2];
1603
- }
1604
- const versions = pxt.appTarget.versions;
1605
- patchSemantic();
1606
- const cfg = pxt.webConfig;
1607
- return pxt.Util.updateLocalizationAsync({
1608
- targetId: pxt.appTarget.id,
1609
- baseUrl: cfg.commitCdnUrl,
1610
- code: lang,
1611
- pxtBranch: versions ? versions.pxtCrowdinBranch : "",
1612
- targetBranch: versions ? versions.targetCrowdinBranch : "",
1613
- force: force,
1614
- })
1615
- .then(() => initHost());
1616
- }
1617
- function initHost() {
1618
- runner.mainPkg = new pxt.MainPackage(new Host());
1619
- }
1620
- runner.initHost = initHost;
1621
- function initFooter(footer, shareId) {
1622
- if (!footer)
1623
- return;
1624
- let theme = pxt.appTarget.appTheme;
1625
- let body = $('body');
1626
- let $footer = $(footer);
1627
- let footera = $('<a/>').attr('href', theme.homeUrl)
1628
- .attr('target', '_blank');
1629
- $footer.append(footera);
1630
- if (theme.organizationLogo)
1631
- footera.append($('<img/>').attr('src', pxt.Util.toDataUri(theme.organizationLogo)));
1632
- else
1633
- footera.append(lf("powered by {0}", theme.title));
1634
- body.mouseenter(ev => $footer.fadeOut());
1635
- body.mouseleave(ev => $footer.fadeIn());
1636
- }
1637
- runner.initFooter = initFooter;
1638
- function showError(msg) {
1639
- console.error(msg);
1640
- }
1641
- runner.showError = showError;
1642
- let previousMainPackage = undefined;
1643
- function loadPackageAsync(id, code, dependencies) {
1644
- const verspec = id ? /\w+:\w+/.test(id) ? id : "pub:" + id : "empty:tsprj";
1645
- let host;
1646
- let downloadPackagePromise;
1647
- let installPromise;
1648
- if (previousMainPackage && previousMainPackage._verspec == verspec) {
1649
- runner.mainPkg = previousMainPackage;
1650
- host = runner.mainPkg.host();
1651
- downloadPackagePromise = Promise.resolve();
1652
- installPromise = Promise.resolve();
1653
- }
1654
- else {
1655
- host = runner.mainPkg.host();
1656
- runner.mainPkg = new pxt.MainPackage(host);
1657
- runner.mainPkg._verspec = id ? /\w+:\w+/.test(id) ? id : "pub:" + id : "empty:tsprj";
1658
- downloadPackagePromise = host.downloadPackageAsync(runner.mainPkg, dependencies);
1659
- installPromise = runner.mainPkg.installAllAsync();
1660
- // cache previous package
1661
- previousMainPackage = runner.mainPkg;
1662
- }
1663
- return downloadPackagePromise
1664
- .then(() => host.readFile(runner.mainPkg, pxt.CONFIG_NAME))
1665
- .then(str => {
1666
- if (!str)
1667
- return Promise.resolve();
1668
- return installPromise.then(() => {
1669
- if (code) {
1670
- //Set the custom code if provided for docs.
1671
- let epkg = getEditorPkg(runner.mainPkg);
1672
- epkg.files[pxt.MAIN_TS] = code;
1673
- //set the custom doc name from the URL.
1674
- let cfg = JSON.parse(epkg.files[pxt.CONFIG_NAME]);
1675
- cfg.name = window.location.href.split('/').pop().split(/[?#]/)[0];
1676
- ;
1677
- epkg.files[pxt.CONFIG_NAME] = pxt.Package.stringifyConfig(cfg);
1678
- //Propgate the change to main package
1679
- runner.mainPkg.config.name = cfg.name;
1680
- if (runner.mainPkg.config.files.indexOf(pxt.MAIN_BLOCKS) == -1) {
1681
- runner.mainPkg.config.files.push(pxt.MAIN_BLOCKS);
1682
- }
1683
- }
1684
- }).catch(e => {
1685
- showError(lf("Cannot load extension: {0}", e.message));
1686
- });
1687
- });
1688
- }
1689
- function getCompileOptionsAsync(hex) {
1690
- let trg = runner.mainPkg.getTargetOptions();
1691
- trg.isNative = !!hex;
1692
- trg.hasHex = !!hex;
1693
- return runner.mainPkg.getCompileOptionsAsync(trg);
1694
- }
1695
- function compileAsync(hex, updateOptions) {
1696
- return getCompileOptionsAsync(hex)
1697
- .then(opts => {
1698
- if (updateOptions)
1699
- updateOptions(opts);
1700
- let resp = pxtc.compile(opts);
1701
- if (resp.diagnostics && resp.diagnostics.length > 0) {
1702
- resp.diagnostics.forEach(diag => {
1703
- console.error(diag.messageText);
1704
- });
1705
- }
1706
- return resp;
1707
- });
1708
- }
1709
- function generateHexFileAsync(options) {
1710
- return loadPackageAsync(options.id)
1711
- .then(() => compileAsync(true, opts => {
1712
- if (options.code)
1713
- opts.fileSystem[pxt.MAIN_TS] = options.code;
1714
- }))
1715
- .then(resp => {
1716
- if (resp.diagnostics && resp.diagnostics.length > 0) {
1717
- console.error("Diagnostics", resp.diagnostics);
1718
- }
1719
- return resp.outfiles[pxtc.BINARY_HEX];
1720
- });
1721
- }
1722
- runner.generateHexFileAsync = generateHexFileAsync;
1723
- function generateVMFileAsync(options) {
1724
- pxt.setHwVariant("vm");
1725
- return loadPackageAsync(options.id)
1726
- .then(() => compileAsync(true, opts => {
1727
- if (options.code)
1728
- opts.fileSystem[pxt.MAIN_TS] = options.code;
1729
- }))
1730
- .then(resp => {
1731
- console.log(resp);
1732
- return resp;
1733
- });
1734
- }
1735
- runner.generateVMFileAsync = generateVMFileAsync;
1736
- async function simulateAsync(container, simOptions) {
1737
- var _a, _b;
1738
- const builtSimJS = simOptions.builtJsInfo || await fetchSimJsInfo(simOptions) || await buildSimJsInfo(simOptions);
1739
- const { js } = builtSimJS;
1740
- if (!js) {
1741
- console.error("Program failed to compile");
1742
- return undefined;
1743
- }
1744
- const runOptions = initDriverAndOptions(container, simOptions, builtSimJS);
1745
- simDriver.options.messageSimulators = (_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.simulator) === null || _b === void 0 ? void 0 : _b.messageSimulators;
1746
- simDriver.options.onSimulatorCommand = msg => {
1747
- if (msg.command === "restart") {
1748
- runOptions.storedState = getStoredState(simOptions.id);
1749
- simDriver.run(js, runOptions);
1750
- }
1751
- if (msg.command == "setstate") {
1752
- if (msg.stateKey) {
1753
- setStoredState(simOptions.id, msg.stateKey, msg.stateValue);
1754
- }
1755
- }
1756
- };
1757
- if (builtSimJS.breakpoints && simOptions.debug) {
1758
- simDriver.setBreakpoints(builtSimJS.breakpoints);
1759
- }
1760
- simDriver.run(js, runOptions);
1761
- return builtSimJS;
1762
- }
1763
- runner.simulateAsync = simulateAsync;
1764
- let simDriver;
1765
- // iff matches and truthy, reuse existing simdriver
1766
- let currDriverId;
1767
- function initDriverAndOptions(container, simOptions, compileInfo) {
1768
- var _a;
1769
- if (!simDriver || !simOptions.embedId || currDriverId !== simOptions.embedId) {
1770
- simDriver = new pxsim.SimulatorDriver(container);
1771
- currDriverId = simOptions.embedId;
1772
- }
1773
- else {
1774
- simDriver.container = container;
1775
- }
1776
- const { fnArgs, parts, usedBuiltinParts, } = compileInfo || {};
1777
- let board = pxt.appTarget.simulator.boardDefinition;
1778
- let storedState = getStoredState(simOptions.id);
1779
- let runOptions = {
1780
- debug: simOptions.debug,
1781
- mute: simOptions.mute,
1782
- boardDefinition: board,
1783
- parts: parts,
1784
- builtinParts: usedBuiltinParts,
1785
- fnArgs: fnArgs,
1786
- cdnUrl: pxt.webConfig.commitCdnUrl,
1787
- localizedStrings: pxt.Util.getLocalizedStrings(),
1788
- highContrast: simOptions.highContrast,
1789
- storedState: storedState,
1790
- light: simOptions.light,
1791
- single: simOptions.single,
1792
- hideSimButtons: simOptions.hideSimButtons,
1793
- autofocus: simOptions.autofocus,
1794
- queryParameters: simOptions.additionalQueryParameters,
1795
- mpRole: simOptions.mpRole,
1796
- theme: (_a = runner.mainPkg.config) === null || _a === void 0 ? void 0 : _a.theme,
1797
- };
1798
- if (pxt.appTarget.simulator && !simOptions.fullScreen)
1799
- runOptions.aspectRatio = parts.length && pxt.appTarget.simulator.partsAspectRatio
1800
- ? pxt.appTarget.simulator.partsAspectRatio
1801
- : pxt.appTarget.simulator.aspectRatio;
1802
- simDriver.setRunOptions(runOptions);
1803
- return runOptions;
1804
- }
1805
- function preloadSim(container, simOpts) {
1806
- var _a, _b;
1807
- initDriverAndOptions(container, simOpts);
1808
- simDriver.preload(((_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.simulator) === null || _b === void 0 ? void 0 : _b.aspectRatio) || 1, true /** no auto run **/);
1809
- }
1810
- runner.preloadSim = preloadSim;
1811
- function currentDriver() {
1812
- return simDriver;
1813
- }
1814
- runner.currentDriver = currentDriver;
1815
- function postSimMessage(msg) {
1816
- simDriver === null || simDriver === void 0 ? void 0 : simDriver.postMessage(msg);
1817
- }
1818
- runner.postSimMessage = postSimMessage;
1819
- async function fetchSimJsInfo(simOptions) {
1820
- try {
1821
- const start = Date.now();
1822
- const result = await pxt.Cloud.downloadBuiltSimJsInfoAsync(simOptions.id);
1823
- pxt.tickEvent("perfMeasurement", {
1824
- durationMs: Date.now() - start,
1825
- operation: "fetchSimJsInfo",
1826
- });
1827
- return result;
1828
- }
1829
- catch (e) {
1830
- // This exception will happen in the majority of cases, so we don't want to log it unless for debugging.
1831
- pxt.debug(e.toString());
1832
- return undefined;
1833
- }
1834
- }
1835
- runner.fetchSimJsInfo = fetchSimJsInfo;
1836
- async function buildSimJsInfo(simOptions) {
1837
- var _a;
1838
- const start = Date.now();
1839
- await loadPackageAsync(simOptions.id, simOptions.code, simOptions.dependencies);
1840
- let didUpgrade = false;
1841
- const currentTargetVersion = pxt.appTarget.versions.target;
1842
- let compileResult = await compileAsync(false, opts => {
1843
- var _a;
1844
- opts.computeUsedParts = true;
1845
- if (simOptions.debug)
1846
- opts.breakpoints = true;
1847
- if (simOptions.assets) {
1848
- const parsedAssets = JSON.parse(simOptions.assets);
1849
- for (const key of Object.keys(parsedAssets)) {
1850
- const el = parsedAssets[key];
1851
- opts.fileSystem[key] = el;
1852
- if (opts.sourceFiles.indexOf(key) < 0) {
1853
- opts.sourceFiles.push(key);
1854
- }
1855
- if (/\.jres$/.test(key)) {
1856
- const parsedJres = JSON.parse(el);
1857
- opts.jres = pxt.inflateJRes(parsedJres, opts.jres);
1858
- }
1859
- }
1860
- }
1861
- if (simOptions.code)
1862
- opts.fileSystem[pxt.MAIN_TS] = simOptions.code;
1863
- // Api info needed for py2ts conversion, if project is shared in Python
1864
- if (opts.target.preferredEditor === pxt.PYTHON_PROJECT_NAME) {
1865
- opts.target.preferredEditor = pxt.JAVASCRIPT_PROJECT_NAME;
1866
- opts.ast = true;
1867
- const resp = pxtc.compile(opts);
1868
- const apis = getApiInfo(resp.ast, opts);
1869
- opts.apisInfo = apis;
1870
- opts.target.preferredEditor = pxt.PYTHON_PROJECT_NAME;
1871
- }
1872
- // Apply upgrade rules if necessary
1873
- const sharedTargetVersion = (_a = runner.mainPkg.config.targetVersions) === null || _a === void 0 ? void 0 : _a.target;
1874
- if (sharedTargetVersion && currentTargetVersion &&
1875
- pxt.semver.cmp(pxt.semver.parse(sharedTargetVersion), pxt.semver.parse(currentTargetVersion)) < 0) {
1876
- for (const fileName of Object.keys(opts.fileSystem)) {
1877
- if (!pxt.Util.startsWith(fileName, "pxt_modules") && pxt.Util.endsWith(fileName, ".ts")) {
1878
- didUpgrade = true;
1879
- opts.fileSystem[fileName] = pxt.patching.patchJavaScript(sharedTargetVersion, opts.fileSystem[fileName]);
1880
- }
1881
- }
1882
- }
1883
- });
1884
- if (((_a = compileResult.diagnostics) === null || _a === void 0 ? void 0 : _a.length) > 0 && didUpgrade) {
1885
- pxt.log("Compile with upgrade rules failed, trying again with original code");
1886
- compileResult = await compileAsync(false, opts => {
1887
- if (simOptions.code)
1888
- opts.fileSystem[pxt.MAIN_TS] = simOptions.code;
1889
- });
1890
- }
1891
- if (compileResult.diagnostics && compileResult.diagnostics.length > 0) {
1892
- console.error("Diagnostics", compileResult.diagnostics);
1893
- }
1894
- const res = pxtc.buildSimJsInfo(compileResult);
1895
- res.parts = compileResult.usedParts;
1896
- pxt.tickEvent("perfMeasurement", {
1897
- durationMs: Date.now() - start,
1898
- operation: "buildSimJsInfo",
1899
- });
1900
- return res;
1901
- }
1902
- runner.buildSimJsInfo = buildSimJsInfo;
1903
- function getStoredState(id) {
1904
- let storedState = {};
1905
- try {
1906
- let projectStorage = window.localStorage.getItem(id);
1907
- if (projectStorage) {
1908
- storedState = JSON.parse(projectStorage);
1909
- }
1910
- }
1911
- catch (e) { }
1912
- return storedState;
1913
- }
1914
- function setStoredState(id, key, value) {
1915
- let storedState = getStoredState(id);
1916
- if (!id) {
1917
- return;
1918
- }
1919
- if (value != null)
1920
- storedState[key] = value;
1921
- else
1922
- delete storedState[key];
1923
- try {
1924
- window.localStorage.setItem(id, JSON.stringify(storedState));
1925
- }
1926
- catch (e) { }
1927
- }
1928
- let LanguageMode;
1929
- (function (LanguageMode) {
1930
- LanguageMode[LanguageMode["Blocks"] = 0] = "Blocks";
1931
- LanguageMode[LanguageMode["TypeScript"] = 1] = "TypeScript";
1932
- LanguageMode[LanguageMode["Python"] = 2] = "Python";
1933
- })(LanguageMode = runner.LanguageMode || (runner.LanguageMode = {}));
1934
- runner.editorLanguageMode = LanguageMode.Blocks;
1935
- function setEditorContextAsync(mode, localeInfo) {
1936
- runner.editorLanguageMode = mode;
1937
- if (localeInfo != pxt.Util.localeInfo()) {
1938
- const localeLiveRx = /^live-/;
1939
- const fetchLive = localeLiveRx.test(localeInfo);
1940
- if (fetchLive) {
1941
- pxt.Util.enableLiveLocalizationUpdates();
1942
- }
1943
- return pxt.Util.updateLocalizationAsync({
1944
- targetId: pxt.appTarget.id,
1945
- baseUrl: pxt.webConfig.commitCdnUrl,
1946
- code: localeInfo.replace(localeLiveRx, ''),
1947
- pxtBranch: pxt.appTarget.versions.pxtCrowdinBranch,
1948
- targetBranch: pxt.appTarget.versions.targetCrowdinBranch,
1949
- });
1950
- }
1951
- return Promise.resolve();
1952
- }
1953
- runner.setEditorContextAsync = setEditorContextAsync;
1954
- function receiveDocMessage(e) {
1955
- let m = e.data;
1956
- if (!m)
1957
- return;
1958
- switch (m.type) {
1959
- case "fileloaded":
1960
- let fm = m;
1961
- let name = fm.name;
1962
- let mode = LanguageMode.Blocks;
1963
- if (/\.ts$/i.test(name)) {
1964
- mode = LanguageMode.TypeScript;
1965
- }
1966
- else if (/\.py$/i.test(name)) {
1967
- mode = LanguageMode.Python;
1968
- }
1969
- setEditorContextAsync(mode, fm.locale);
1970
- break;
1971
- case "popout":
1972
- let mp = /((\/v[0-9+])\/)?[^\/]*#(doc|md):([^&?:]+)/i.exec(window.location.href);
1973
- if (mp) {
1974
- const docsUrl = pxt.webConfig.docsUrl || '/--docs';
1975
- let verPrefix = mp[2] || '';
1976
- let url = mp[3] == "doc" ? (pxt.webConfig.isStatic ? `/docs${mp[4]}.html` : `${mp[4]}`) : `${docsUrl}?md=${mp[4]}`;
1977
- // notify parent iframe that we have completed the popout
1978
- if (window.parent)
1979
- window.parent.postMessage({
1980
- type: "opendoc",
1981
- url: pxt.BrowserUtils.urlJoin(verPrefix, url)
1982
- }, "*");
1983
- }
1984
- break;
1985
- case "localtoken":
1986
- let dm = m;
1987
- if (dm && dm.localToken) {
1988
- pxt.Cloud.localToken = dm.localToken;
1989
- pendingLocalToken.forEach(p => p());
1990
- pendingLocalToken = [];
1991
- }
1992
- break;
1993
- }
1994
- }
1995
- function startRenderServer() {
1996
- pxt.tickEvent("renderer.ready");
1997
- const jobQueue = [];
1998
- let jobPromise = undefined;
1999
- function consumeQueue() {
2000
- if (jobPromise)
2001
- return; // other worker already in action
2002
- const msg = jobQueue.shift();
2003
- if (!msg)
2004
- return; // no more work
2005
- const options = (msg.options || {});
2006
- options.splitSvg = false; // don't split when requesting rendered images
2007
- pxt.tickEvent("renderer.job");
2008
- const isXml = /^\s*<xml/.test(msg.code);
2009
- const doWork = async () => {
2010
- await pxt.BrowserUtils.loadBlocklyAsync();
2011
- const result = isXml
2012
- ? await pxt.runner.compileBlocksAsync(msg.code, options)
2013
- : await runner.decompileSnippetAsync(msg.code, msg.options);
2014
- const blocksSvg = result.blocksSvg;
2015
- const width = blocksSvg.viewBox.baseVal.width;
2016
- const height = blocksSvg.viewBox.baseVal.height;
2017
- const res = blocksSvg
2018
- ? await pxt.blocks.layout.blocklyToSvgAsync(blocksSvg, 0, 0, width, height)
2019
- : undefined;
2020
- // try to render to png
2021
- let png;
2022
- try {
2023
- png = res
2024
- ? await pxt.BrowserUtils.encodeToPngAsync(res.xml, { width, height })
2025
- : undefined;
2026
- }
2027
- catch (e) {
2028
- console.warn(e);
2029
- }
2030
- window.parent.postMessage({
2031
- source: "makecode",
2032
- type: "renderblocks",
2033
- id: msg.id,
2034
- width: res === null || res === void 0 ? void 0 : res.width,
2035
- height: res === null || res === void 0 ? void 0 : res.height,
2036
- svg: res === null || res === void 0 ? void 0 : res.svg,
2037
- uri: png || (res === null || res === void 0 ? void 0 : res.xml),
2038
- css: res === null || res === void 0 ? void 0 : res.css
2039
- }, "*");
2040
- };
2041
- jobPromise = doWork()
2042
- .catch(e => {
2043
- window.parent.postMessage({
2044
- source: "makecode",
2045
- type: "renderblocks",
2046
- id: msg.id,
2047
- error: e.message
2048
- }, "*");
2049
- })
2050
- .finally(() => {
2051
- jobPromise = undefined;
2052
- consumeQueue();
2053
- });
2054
- }
2055
- pxt.editor.initEditorExtensionsAsync()
2056
- .then(() => {
2057
- // notify parent that render engine is loaded
2058
- window.addEventListener("message", function (ev) {
2059
- const msg = ev.data;
2060
- if (msg.type == "renderblocks") {
2061
- jobQueue.push(msg);
2062
- consumeQueue();
2063
- }
2064
- }, false);
2065
- window.parent.postMessage({
2066
- source: "makecode",
2067
- type: "renderready",
2068
- versions: pxt.appTarget.versions
2069
- }, "*");
2070
- });
2071
- }
2072
- runner.startRenderServer = startRenderServer;
2073
- function startDocsServer(loading, content, backButton) {
2074
- pxt.tickEvent("docrenderer.ready");
2075
- const history = [];
2076
- if (backButton) {
2077
- backButton.addEventListener("click", () => {
2078
- goBack();
2079
- });
2080
- setElementDisabled(backButton, true);
2081
- }
2082
- function render(doctype, src) {
2083
- pxt.debug(`rendering ${doctype}`);
2084
- if (backButton)
2085
- $(backButton).hide();
2086
- $(content).hide();
2087
- $(loading).show();
2088
- pxt.U.delay(100) // allow UI to update
2089
- .then(() => {
2090
- switch (doctype) {
2091
- case "print":
2092
- const data = window.localStorage["printjob"];
2093
- delete window.localStorage["printjob"];
2094
- return renderProjectFilesAsync(content, JSON.parse(data), undefined, true)
2095
- .then(() => pxsim.print(1000));
2096
- case "project":
2097
- return renderProjectFilesAsync(content, JSON.parse(src))
2098
- .then(() => pxsim.print(1000));
2099
- case "projectid":
2100
- return renderProjectAsync(content, JSON.parse(src))
2101
- .then(() => pxsim.print(1000));
2102
- case "doc":
2103
- return renderDocAsync(content, src);
2104
- case "book":
2105
- return renderBookAsync(content, src);
2106
- default:
2107
- return renderMarkdownAsync(content, src);
2108
- }
2109
- })
2110
- .catch(e => {
2111
- $(content).html(`
2112
- <img style="height:4em;" src="${pxt.appTarget.appTheme.docsLogo}" />
2113
- <h1>${lf("Oops")}</h1>
2114
- <h3>${lf("We could not load the documentation, please check your internet connection.")}</h3>
2115
- <button class="ui button primary" id="tryagain">${lf("Try Again")}</button>`);
2116
- $(content).find('#tryagain').click(() => {
2117
- render(doctype, src);
2118
- });
2119
- // notify parent iframe that docs weren't loaded
2120
- if (window.parent)
2121
- window.parent.postMessage({
2122
- type: "docfailed",
2123
- docType: doctype,
2124
- src: src
2125
- }, "*");
2126
- }).finally(() => {
2127
- $(loading).hide();
2128
- if (backButton)
2129
- $(backButton).show();
2130
- $(content).show();
2131
- })
2132
- .then(() => { });
2133
- }
2134
- function pushHistory() {
2135
- if (!backButton)
2136
- return;
2137
- history.push(window.location.hash);
2138
- if (history.length > 10) {
2139
- history.shift();
2140
- }
2141
- if (history.length > 1) {
2142
- setElementDisabled(backButton, false);
2143
- }
2144
- }
2145
- function goBack() {
2146
- if (!backButton)
2147
- return;
2148
- if (history.length > 1) {
2149
- // Top is current page
2150
- history.pop();
2151
- window.location.hash = history.pop();
2152
- }
2153
- if (history.length <= 1) {
2154
- setElementDisabled(backButton, true);
2155
- }
2156
- }
2157
- function setElementDisabled(el, disabled) {
2158
- if (disabled) {
2159
- pxsim.U.addClass(el, "disabled");
2160
- el.setAttribute("aria-disabled", "true");
2161
- }
2162
- else {
2163
- pxsim.U.removeClass(el, "disabled");
2164
- el.setAttribute("aria-disabled", "false");
2165
- }
2166
- }
2167
- async function renderHashAsync() {
2168
- let m = /^#(doc|md|tutorial|book|project|projectid|print):([^&?:]+)(:([^&?:]+):([^&?:]+))?/i.exec(window.location.hash);
2169
- if (m) {
2170
- pushHistory();
2171
- if (m[4]) {
2172
- let mode = LanguageMode.TypeScript;
2173
- if (/^blocks$/i.test(m[4])) {
2174
- mode = LanguageMode.Blocks;
2175
- }
2176
- else if (/^python$/i.test(m[4])) {
2177
- mode = LanguageMode.Python;
2178
- }
2179
- await setEditorContextAsync(mode, m[5]);
2180
- }
2181
- // navigation occured
2182
- render(m[1], decodeURIComponent(m[2]));
2183
- }
2184
- }
2185
- let promise = pxt.editor.initEditorExtensionsAsync();
2186
- promise.then(() => {
2187
- window.addEventListener("message", receiveDocMessage, false);
2188
- window.addEventListener("hashchange", () => {
2189
- renderHashAsync();
2190
- }, false);
2191
- parent.postMessage({ type: "sidedocready" }, "*");
2192
- // delay load doc page to allow simulator to load first
2193
- setTimeout(() => renderHashAsync(), 1);
2194
- });
2195
- }
2196
- runner.startDocsServer = startDocsServer;
2197
- function renderProjectAsync(content, projectid) {
2198
- return pxt.Cloud.privateGetTextAsync(projectid + "/text")
2199
- .then(txt => JSON.parse(txt))
2200
- .then(files => renderProjectFilesAsync(content, files, projectid));
2201
- }
2202
- runner.renderProjectAsync = renderProjectAsync;
2203
- function renderProjectFilesAsync(content, files, projectid = null, escapeLinks = false) {
2204
- const cfg = (JSON.parse(files[pxt.CONFIG_NAME]) || {});
2205
- let md = `# ${cfg.name} ${cfg.version ? cfg.version : ''}
2206
-
2207
- `;
2208
- const readme = "README.md";
2209
- if (files[readme])
2210
- md += files[readme].replace(/^#+/, "$0#") + '\n'; // bump all headers down 1
2211
- cfg.files.filter(f => f != pxt.CONFIG_NAME && f != readme)
2212
- .filter(f => matchesLanguageMode(f, runner.editorLanguageMode))
2213
- .forEach(f => {
2214
- if (!/^main\.(ts|blocks)$/.test(f))
2215
- md += `
2216
- ## ${f}
2217
- `;
2218
- if (/\.ts$/.test(f)) {
2219
- md += `\`\`\`typescript
2220
- ${files[f]}
2221
- \`\`\`
2222
- `;
2223
- }
2224
- else if (/\.blocks?$/.test(f)) {
2225
- md += `\`\`\`blocksxml
2226
- ${files[f]}
2227
- \`\`\`
2228
- `;
2229
- }
2230
- else {
2231
- md += `\`\`\`${f.substr(f.indexOf('.'))}
2232
- ${files[f]}
2233
- \`\`\`
2234
- `;
2235
- }
2236
- });
2237
- const deps = cfg && cfg.dependencies && Object.keys(cfg.dependencies).filter(k => k != pxt.appTarget.corepkg);
2238
- if (deps && deps.length) {
2239
- md += `
2240
- ## ${lf("Extensions")} #extensions
2241
-
2242
- ${deps.map(k => `* ${k}, ${cfg.dependencies[k]}`).join('\n')}
2243
-
2244
- \`\`\`package
2245
- ${deps.map(k => `${k}=${cfg.dependencies[k]}`).join('\n')}
2246
- \`\`\`
2247
- `;
2248
- }
2249
- if (projectid) {
2250
- let linkString = (pxt.appTarget.appTheme.shareUrl || "https://makecode.com/") + projectid;
2251
- if (escapeLinks) {
2252
- // If printing the link will show up twice if it's an actual link
2253
- linkString = "`" + linkString + "`";
2254
- }
2255
- md += `
2256
- ${linkString}
2257
-
2258
- `;
2259
- }
2260
- console.debug(`print md: ${md}`);
2261
- const options = {
2262
- print: true
2263
- };
2264
- return renderMarkdownAsync(content, md, options);
2265
- }
2266
- runner.renderProjectFilesAsync = renderProjectFilesAsync;
2267
- function matchesLanguageMode(filename, mode) {
2268
- switch (mode) {
2269
- case LanguageMode.Blocks:
2270
- return /\.blocks?$/.test(filename);
2271
- case LanguageMode.TypeScript:
2272
- return /\.ts?$/.test(filename);
2273
- case LanguageMode.Python:
2274
- return /\.py?$/.test(filename);
2275
- }
2276
- }
2277
- async function renderDocAsync(content, docid) {
2278
- docid = docid.replace(/^\//, "");
2279
- // if it fails on requesting, propagate failed promise
2280
- const md = await pxt.Cloud.markdownAsync(docid, undefined, true /** don't suppress exception **/);
2281
- try {
2282
- // just log exceptions that occur during rendering,
2283
- // similar to how normal docs handle them.
2284
- await renderMarkdownAsync(content, md, { path: docid });
2285
- }
2286
- catch (e) {
2287
- console.warn(e);
2288
- }
2289
- }
2290
- function renderBookAsync(content, summaryid) {
2291
- summaryid = summaryid.replace(/^\//, "");
2292
- pxt.tickEvent('book', { id: summaryid });
2293
- pxt.log(`rendering book from ${summaryid}`);
2294
- // display loader
2295
- const $loader = $("#loading").find(".loader");
2296
- $loader.addClass("text").text(lf("Compiling your book (this may take a minute)"));
2297
- // start the work
2298
- let toc;
2299
- return pxt.U.delay(100)
2300
- .then(() => pxt.Cloud.markdownAsync(summaryid, undefined, true))
2301
- .then(summary => {
2302
- toc = pxt.docs.buildTOC(summary);
2303
- pxt.log(`TOC: ${JSON.stringify(toc, null, 2)}`);
2304
- const tocsp = [];
2305
- pxt.docs.visitTOC(toc, entry => {
2306
- if (/^\//.test(entry.path) && !/^\/pkg\//.test(entry.path))
2307
- tocsp.push(entry);
2308
- });
2309
- return pxt.U.promisePoolAsync(4, tocsp, async (entry) => {
2310
- try {
2311
- const md = await pxt.Cloud.markdownAsync(entry.path, undefined, true);
2312
- entry.markdown = md;
2313
- }
2314
- catch (e) {
2315
- entry.markdown = `_${entry.path} failed to load._`;
2316
- }
2317
- });
2318
- })
2319
- .then(pages => {
2320
- let md = toc[0].name;
2321
- pxt.docs.visitTOC(toc, entry => {
2322
- if (entry.markdown)
2323
- md += '\n\n' + entry.markdown;
2324
- });
2325
- return renderMarkdownAsync(content, md);
2326
- });
2327
- }
2328
- const template = `
2329
- <aside id=button class=box>
2330
- <a class="ui primary button" href="@ARGS@">@BODY@</a>
2331
- </aside>
2332
-
2333
- <aside id=vimeo>
2334
- <div class="ui two column stackable grid container">
2335
- <div class="column">
2336
- <div class="ui embed mdvid" data-source="vimeo" data-id="@ARGS@" data-placeholder="/thumbnail/1024/vimeo/@ARGS@" data-icon="video play">
2337
- </div>
2338
- </div></div>
2339
- </aside>
2340
-
2341
- <aside id=youtube>
2342
- <div class="ui two column stackable grid container">
2343
- <div class="column">
2344
- <div class="ui embed mdvid" data-source="youtube" data-id="@ARGS@" data-placeholder="https://img.youtube.com/vi/@ARGS@/0.jpg">
2345
- </div>
2346
- </div></div>
2347
- </aside>
2348
-
2349
- <aside id=section>
2350
- <!-- section @ARGS@ -->
2351
- </aside>
2352
-
2353
- <aside id=hide class=box>
2354
- <div style='display:none'>
2355
- @BODY@
2356
- </div>
2357
- </aside>
2358
-
2359
- <aside id=avatar class=box>
2360
- <div class='avatar @ARGS@'>
2361
- <div class='avatar-image'></div>
2362
- <div class='ui compact message'>
2363
- @BODY@
2364
- </div>
2365
- </div>
2366
- </aside>
2367
-
2368
- <aside id=hint class=box>
2369
- <div class="ui info message">
2370
- <div class="content">
2371
- @BODY@
2372
- </div>
2373
- </div>
2374
- </aside>
2375
-
2376
- <aside id=codecard class=box>
2377
- <pre><code class="lang-codecard">@BODY@</code></pre>
2378
- </aside>
2379
-
2380
- <aside id=tutorialhint class=box>
2381
- <div class="ui hint message">
2382
- <div class="content">
2383
- @BODY@
2384
- </div>
2385
- </div>
2386
- </aside>
2387
-
2388
- <aside id=reminder class=box>
2389
- <div class="ui warning message">
2390
- <div class="content">
2391
- @BODY@
2392
- </div>
2393
- </div>
2394
- </aside>
2395
-
2396
- <aside id=alert class=box>
2397
- <div class="ui negative message">
2398
- <div class="content">
2399
- @BODY@
2400
- </div>
2401
- </div>
2402
- </aside>
2403
-
2404
- <aside id=tip class=box>
2405
- <div class="ui positive message">
2406
- <div class="content">
2407
- @BODY@
2408
- </div>
2409
- </div>
2410
- </aside>
2411
-
2412
- <!-- wrapped around ordinary content -->
2413
- <aside id=main-container class=box>
2414
- <div class="ui text">
2415
- @BODY@
2416
- </div>
2417
- </aside>
2418
-
2419
- <!-- used for 'column' box - they are collected and wrapped in 'column-container' -->
2420
- <aside id=column class=aside>
2421
- <div class='column'>
2422
- @BODY@
2423
- </div>
2424
- </aside>
2425
- <aside id=column-container class=box>
2426
- <div class="ui three column stackable grid text">
2427
- @BODY@
2428
- </div>
2429
- </aside>
2430
- @breadcrumb@
2431
- @body@`;
2432
- function renderMarkdownAsync(content, md, options = {}) {
2433
- const html = pxt.docs.renderMarkdown({
2434
- template: template,
2435
- markdown: md,
2436
- theme: pxt.appTarget.appTheme
2437
- });
2438
- let blocksAspectRatio = options.blocksAspectRatio
2439
- || window.innerHeight < window.innerWidth ? 1.62 : 1 / 1.62;
2440
- $(content).html(html);
2441
- $(content).find('a').attr('target', '_blank');
2442
- const renderOptions = pxt.runner.defaultClientRenderOptions();
2443
- renderOptions.tutorial = !!options.tutorial;
2444
- renderOptions.blocksAspectRatio = blocksAspectRatio || renderOptions.blocksAspectRatio;
2445
- renderOptions.showJavaScript = runner.editorLanguageMode == LanguageMode.TypeScript;
2446
- if (options.print) {
2447
- renderOptions.showEdit = false;
2448
- renderOptions.simulator = false;
2449
- }
2450
- return pxt.runner.renderAsync(renderOptions).then(() => {
2451
- // patch a elements
2452
- $(content).find('a[href^="/"]').removeAttr('target').each((i, a) => {
2453
- $(a).attr('href', '#doc:' + $(a).attr('href').replace(/^\//, ''));
2454
- });
2455
- // enable embeds
2456
- $(content).find('.ui.embed').embed();
2457
- });
2458
- }
2459
- runner.renderMarkdownAsync = renderMarkdownAsync;
2460
- let programCache;
2461
- let apiCache;
2462
- function decompileSnippetAsync(code, options) {
2463
- const { assets, forceCompilation, snippetMode, generateSourceMap } = options || {};
2464
- // code may be undefined or empty!!!
2465
- const packageid = options && options.packageId ? "pub:" + options.packageId :
2466
- options && options.package ? "docs:" + options.package
2467
- : null;
2468
- return loadPackageAsync(packageid, code)
2469
- .then(() => getCompileOptionsAsync(pxt.appTarget.compile ? pxt.appTarget.compile.hasHex : false))
2470
- .then(opts => {
2471
- // compile
2472
- if (code)
2473
- opts.fileSystem[pxt.MAIN_TS] = code;
2474
- opts.ast = true;
2475
- if (assets) {
2476
- for (const key of Object.keys(assets)) {
2477
- if (opts.sourceFiles.indexOf(key) < 0) {
2478
- opts.sourceFiles.push(key);
2479
- }
2480
- opts.fileSystem[key] = assets[key];
2481
- }
2482
- }
2483
- let compileJS = undefined;
2484
- let program;
2485
- if (forceCompilation) {
2486
- compileJS = pxtc.compile(opts);
2487
- program = compileJS && compileJS.ast;
2488
- }
2489
- else {
2490
- program = pxtc.getTSProgram(opts, programCache);
2491
- }
2492
- programCache = program;
2493
- // decompile to python
2494
- let compilePython = undefined;
2495
- if (pxt.appTarget.appTheme.python) {
2496
- compilePython = ts.pxtc.transpile.tsToPy(program, pxt.MAIN_TS);
2497
- }
2498
- // decompile to blocks
2499
- let apis = getApiInfo(program, opts);
2500
- return ts.pxtc.localizeApisAsync(apis, runner.mainPkg)
2501
- .then(() => {
2502
- let blocksInfo = pxtc.getBlocksInfo(apis);
2503
- pxt.blocks.initializeAndInject(blocksInfo);
2504
- const tilemapJres = assets === null || assets === void 0 ? void 0 : assets[pxt.TILEMAP_JRES];
2505
- const assetsJres = assets === null || assets === void 0 ? void 0 : assets[pxt.IMAGES_JRES];
2506
- if (tilemapJres || assetsJres) {
2507
- tilemapProject = new pxt.TilemapProject();
2508
- tilemapProject.loadPackage(runner.mainPkg);
2509
- if (tilemapJres)
2510
- tilemapProject.loadTilemapJRes(JSON.parse(tilemapJres), true);
2511
- if (assetsJres)
2512
- tilemapProject.loadAssetsJRes(JSON.parse(assetsJres));
2513
- }
2514
- let bresp = pxtc.decompiler.decompileToBlocks(blocksInfo, program.getSourceFile(pxt.MAIN_TS), {
2515
- snippetMode,
2516
- generateSourceMap
2517
- });
2518
- if (bresp.diagnostics && bresp.diagnostics.length > 0)
2519
- bresp.diagnostics.forEach(diag => console.error(diag.messageText));
2520
- if (!bresp.success)
2521
- return {
2522
- package: runner.mainPkg,
2523
- compileProgram: program,
2524
- compileJS,
2525
- compileBlocks: bresp,
2526
- apiInfo: apis
2527
- };
2528
- pxt.debug(bresp.outfiles[pxt.MAIN_BLOCKS]);
2529
- const blocksSvg = pxt.blocks.render(bresp.outfiles[pxt.MAIN_BLOCKS], options);
2530
- if (tilemapJres || assetsJres) {
2531
- tilemapProject = null;
2532
- }
2533
- return {
2534
- package: runner.mainPkg,
2535
- compileProgram: program,
2536
- compileJS,
2537
- compileBlocks: bresp,
2538
- compilePython,
2539
- apiInfo: apis,
2540
- blocksSvg
2541
- };
2542
- });
2543
- });
2544
- }
2545
- runner.decompileSnippetAsync = decompileSnippetAsync;
2546
- function getApiInfo(program, opts) {
2547
- if (!apiCache)
2548
- apiCache = {};
2549
- const key = Object.keys(opts.fileSystem).sort().join(";");
2550
- if (!apiCache[key])
2551
- apiCache[key] = pxtc.getApiInfo(program, opts.jres);
2552
- return apiCache[key];
2553
- }
2554
- function compileBlocksAsync(code, options) {
2555
- const { assets } = options || {};
2556
- const packageid = options && options.packageId ? "pub:" + options.packageId :
2557
- options && options.package ? "docs:" + options.package
2558
- : null;
2559
- return loadPackageAsync(packageid, "")
2560
- .then(() => getCompileOptionsAsync(pxt.appTarget.compile ? pxt.appTarget.compile.hasHex : false))
2561
- .then(opts => {
2562
- opts.ast = true;
2563
- if (assets) {
2564
- for (const key of Object.keys(assets)) {
2565
- if (opts.sourceFiles.indexOf(key) < 0) {
2566
- opts.sourceFiles.push(key);
2567
- }
2568
- opts.fileSystem[key] = assets[key];
2569
- }
2570
- }
2571
- const resp = pxtc.compile(opts);
2572
- const apis = getApiInfo(resp.ast, opts);
2573
- return ts.pxtc.localizeApisAsync(apis, runner.mainPkg)
2574
- .then(() => {
2575
- const blocksInfo = pxtc.getBlocksInfo(apis);
2576
- pxt.blocks.initializeAndInject(blocksInfo);
2577
- const tilemapJres = assets === null || assets === void 0 ? void 0 : assets[pxt.TILEMAP_JRES];
2578
- const assetsJres = assets === null || assets === void 0 ? void 0 : assets[pxt.IMAGES_JRES];
2579
- if (tilemapJres || assetsJres) {
2580
- tilemapProject = new pxt.TilemapProject();
2581
- tilemapProject.loadPackage(runner.mainPkg);
2582
- if (tilemapJres)
2583
- tilemapProject.loadTilemapJRes(JSON.parse(tilemapJres), true);
2584
- if (assetsJres)
2585
- tilemapProject.loadAssetsJRes(JSON.parse(assetsJres));
2586
- }
2587
- const blockSvg = pxt.blocks.render(code, options);
2588
- if (tilemapJres || assetsJres) {
2589
- tilemapProject = null;
2590
- }
2591
- return {
2592
- package: runner.mainPkg,
2593
- blocksSvg: blockSvg,
2594
- apiInfo: apis
2595
- };
2596
- });
2597
- });
2598
- }
2599
- runner.compileBlocksAsync = compileBlocksAsync;
2600
- let pendingLocalToken = [];
2601
- function waitForLocalTokenAsync() {
2602
- if (pxt.Cloud.localToken) {
2603
- return Promise.resolve();
2604
- }
2605
- return new Promise((resolve, reject) => {
2606
- pendingLocalToken.push(resolve);
2607
- });
2608
- }
2609
- runner.initCallbacks = [];
2610
- function init() {
2611
- initInnerAsync()
2612
- .then(() => {
2613
- for (let i = 0; i < runner.initCallbacks.length; ++i) {
2614
- runner.initCallbacks[i]();
2615
- }
2616
- });
2617
- }
2618
- runner.init = init;
2619
- function windowLoad() {
2620
- let f = window.ksRunnerWhenLoaded;
2621
- if (f)
2622
- f();
2623
- }
2624
- windowLoad();
2625
- })(runner = pxt.runner || (pxt.runner = {}));
2626
- })(pxt || (pxt = {}));