pxt-core 7.5.1 → 7.5.4

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 (54) hide show
  1. package/built/cli.js +57 -5
  2. package/built/pxt.js +194 -11
  3. package/built/pxtblockly.js +714 -657
  4. package/built/pxtblocks.d.ts +7 -0
  5. package/built/pxtblocks.js +159 -40
  6. package/built/pxtcompiler.js +42 -4
  7. package/built/pxtlib.d.ts +2 -0
  8. package/built/pxtlib.js +22 -0
  9. package/built/pxtpy.js +73 -2
  10. package/built/server.js +4 -0
  11. package/built/target.js +1 -1
  12. package/built/web/authcode/css/main.1cf9dc37.css +2 -0
  13. package/built/web/authcode/js/main.03da4c20.js +2 -0
  14. package/built/web/main.js +1 -1
  15. package/built/web/pxtapp.js +1 -1
  16. package/built/web/pxtasseteditor.js +1 -1
  17. package/built/web/pxtblockly.js +2 -2
  18. package/built/web/pxtblocks.js +1 -1
  19. package/built/web/pxtcompiler.js +1 -1
  20. package/built/web/pxtembed.js +2 -2
  21. package/built/web/pxtlib.js +1 -1
  22. package/built/web/pxtpy.js +1 -1
  23. package/built/web/pxtworker.js +1 -1
  24. package/built/web/react-common-authcode.css +6008 -0
  25. package/built/web/react-common-skillmap.css +1 -1
  26. package/built/web/rtlreact-common-skillmap.css +1 -1
  27. package/built/web/rtlsemantic.css +1 -1
  28. package/built/web/semantic.css +1 -1
  29. package/built/web/skillmap/css/{main.4939cd1e.chunk.css → main.e0620cee.chunk.css} +1 -1
  30. package/built/web/skillmap/js/main.f6866fc6.chunk.js +1 -0
  31. package/common-docs/faq.md +1 -1
  32. package/common-docs/translate.md +2 -2
  33. package/localtypings/pxtblockly.d.ts +1 -0
  34. package/package.json +5 -3
  35. package/pxtarget.json +1 -1
  36. package/react-common/components/controls/EditorToggle.tsx +109 -0
  37. package/react-common/components/controls/Modal.tsx +1 -1
  38. package/react-common/components/profile/UserPane.tsx +8 -4
  39. package/react-common/styles/controls/Button.less +30 -14
  40. package/react-common/styles/controls/EditorToggle.less +233 -0
  41. package/react-common/styles/controls/MenuDropdown.less +1 -1
  42. package/react-common/styles/profile/profile.less +2 -2
  43. package/react-common/styles/react-common-authcode-core.less +10 -0
  44. package/react-common/styles/react-common-authcode.less +12 -0
  45. package/react-common/styles/react-common-variables.less +26 -2
  46. package/react-common/styles/react-common.less +1 -0
  47. package/theme/common.less +5 -0
  48. package/theme/image-editor/imageEditor.less +7 -116
  49. package/theme/tutorial-sidebar.less +11 -4
  50. package/webapp/public/authcode.html +1 -0
  51. package/webapp/public/blockly/blockly_compressed.js +555 -617
  52. package/webapp/public/index.html +1 -1
  53. package/webapp/public/skillmap.html +2 -2
  54. package/built/web/skillmap/js/main.2a4cb15b.chunk.js +0 -1
@@ -39,6 +39,7 @@ declare namespace pxt.blocks {
39
39
  alreadyDeclared?: BlockDeclarationType;
40
40
  firstReference?: Blockly.Block;
41
41
  isAssigned?: boolean;
42
+ isFunctionParameter?: boolean;
42
43
  }
43
44
  function compileExpression(e: Environment, b: Blockly.Block, comments: string[]): JsNode;
44
45
  interface Environment {
@@ -264,6 +265,12 @@ declare namespace pxt.blocks {
264
265
  protected createDom_(): void;
265
266
  protected highlightSearchGroup_(blocks: Blockly.BlockSvg[]): void;
266
267
  protected unhighlightSearchGroup_(blocks: Blockly.BlockSvg[]): void;
268
+ /**
269
+ * https://github.com/google/blockly-samples/blob/master/plugins/workspace-search/src/WorkspaceSearch.js#L633
270
+ *
271
+ * Modified to center offscreen blocks.
272
+ */
273
+ protected scrollToVisible_(block: Blockly.BlockSvg): void;
267
274
  open(): void;
268
275
  close(): void;
269
276
  }
@@ -179,7 +179,7 @@ var pxt;
179
179
  }
180
180
  else if (b.type == "argument_reporter_array") {
181
181
  if (!tp) {
182
- tp = ground("any[]");
182
+ tp = lookup(e, b, b.getFieldValue("VALUE")).type;
183
183
  }
184
184
  }
185
185
  if (tp)
@@ -492,8 +492,14 @@ var pxt;
492
492
  // Last pass: if some variable has no type (because it was never used or
493
493
  // assigned to), just unify it with int...
494
494
  e.allVariables.forEach((v) => {
495
- if (getConcreteType(v.type).type == null)
496
- union(v.type, ground(v.type.isArrayType ? "number[]" : pNumber.type));
495
+ if (getConcreteType(v.type).type == null) {
496
+ if (!v.isFunctionParameter) {
497
+ union(v.type, ground(v.type.isArrayType ? "number[]" : pNumber.type));
498
+ }
499
+ else if (v.type.isArrayType) {
500
+ v.type.type = "any[]";
501
+ }
502
+ }
497
503
  });
498
504
  function connectionCheck(i) {
499
505
  return i.name ? i.connection && i.connection.check_ && i.connection.check_.length ? i.connection.check_[0] : "T" : undefined;
@@ -582,9 +588,31 @@ var pxt;
582
588
  function compileNumber(e, b, comments) {
583
589
  return blocks_1.H.mkNumberLiteral(extractNumber(b));
584
590
  }
591
+ function isNumericLiteral(e, b) {
592
+ if (!b)
593
+ return false;
594
+ if (b.type === "math_number" || b.type === "math_integer" || b.type === "math_number_minmax" || b.type === "math_whole_number") {
595
+ return true;
596
+ }
597
+ const blockInfo = e.stdCallTable[b.type];
598
+ if (!blockInfo)
599
+ return false;
600
+ const { comp } = blockInfo;
601
+ if (blockInfo.attrs.shim === "TD_ID" && comp.parameters.length === 1) {
602
+ const fieldValue = b.getFieldValue(comp.parameters[0].definitionName);
603
+ if (fieldValue) {
604
+ return !isNaN(parseInt(fieldValue));
605
+ }
606
+ else {
607
+ return isNumericLiteral(e, getInputTargetBlock(b, comp.parameters[0].definitionName));
608
+ }
609
+ }
610
+ return false;
611
+ }
612
+ function isLiteral(e, b) {
613
+ return isNumericLiteral(e, b) || b.type === "logic_boolean" || b.type === "text";
614
+ }
585
615
  let opToTok = {
586
- // POWER gets a special treatment because there's no operator for it in
587
- // TouchDevelop
588
616
  "ADD": "+",
589
617
  "MINUS": "-",
590
618
  "MULTIPLY": "*",
@@ -599,11 +627,21 @@ var pxt;
599
627
  "NEQ": "!=",
600
628
  "POWER": "**"
601
629
  };
630
+ function isComparisonOp(op) {
631
+ return ["LT", "LTE", "GT", "GTE", "EQ", "NEQ"].indexOf(op) !== -1;
632
+ }
602
633
  function compileArithmetic(e, b, comments) {
603
634
  let bOp = b.getFieldValue("OP");
604
635
  let left = getInputTargetBlock(b, "A");
605
636
  let right = getInputTargetBlock(b, "B");
606
637
  let args = [compileExpression(e, left, comments), compileExpression(e, right, comments)];
638
+ // Special handling for the case of comparing two literals (e.g. 0 === 5). TypeScript
639
+ // throws an error if we don't first cast to any
640
+ if (isComparisonOp(bOp) && isLiteral(e, left) && isLiteral(e, right)) {
641
+ if (blocks_1.flattenNode([args[0]]).output !== blocks_1.flattenNode([args[1]]).output) {
642
+ args = args.map(arg => blocks_1.H.mkParenthesizedExpression(blocks_1.mkGroup([arg, blocks_1.mkText(" as any")])));
643
+ }
644
+ }
607
645
  let t = returnType(e, left).type;
608
646
  if (t == pString.type) {
609
647
  if (bOp == "EQ")
@@ -711,7 +749,10 @@ var pxt;
711
749
  const stmts = getInputTargetBlock(b, "STACK");
712
750
  const argsDeclaration = b.getArguments().map(a => {
713
751
  if (a.type == "Array") {
714
- return `${escapeVarName(a.name, e)}: any[]`;
752
+ const binding = lookup(e, b, a.name);
753
+ const declaredType = getConcreteType(binding.type);
754
+ const paramType = ((declaredType === null || declaredType === void 0 ? void 0 : declaredType.type) && declaredType.type !== "Array") ? declaredType.type : "any[]";
755
+ return `${escapeVarName(a.name, e)}: ${paramType}`;
715
756
  }
716
757
  return `${escapeVarName(a.name, e)}: ${a.type}`;
717
758
  });
@@ -1859,7 +1900,7 @@ var pxt;
1859
1900
  return res;
1860
1901
  }
1861
1902
  function getEscapedCBParameters(b, stdfun, e) {
1862
- return getCBParameters(b, stdfun).map(binding => lookup(e, b, binding[0]).escapedName);
1903
+ return getCBParameters(b, stdfun).map(binding => lookup(e, b, binding.name).escapedName);
1863
1904
  }
1864
1905
  function getCBParameters(b, stdfun) {
1865
1906
  let handlerArgs = [];
@@ -1875,7 +1916,10 @@ var pxt;
1875
1916
  varName = varBlock && varBlock.getField("VAR").getText();
1876
1917
  }
1877
1918
  if (varName !== null) {
1878
- handlerArgs.push([varName, mkPoint(arg.type)]);
1919
+ handlerArgs.push({
1920
+ name: varName,
1921
+ type: mkPoint(arg.type)
1922
+ });
1879
1923
  }
1880
1924
  else {
1881
1925
  break;
@@ -1888,7 +1932,10 @@ var pxt;
1888
1932
  const varField = b.getField("HANDLER_" + arg.name);
1889
1933
  const varName = varField && varField.getText();
1890
1934
  if (varName !== null) {
1891
- handlerArgs.push([varName, mkPoint(arg.type)]);
1935
+ handlerArgs.push({
1936
+ name: varName,
1937
+ type: mkPoint(arg.type)
1938
+ });
1892
1939
  }
1893
1940
  else {
1894
1941
  break;
@@ -2091,11 +2138,7 @@ var pxt;
2091
2138
  }
2092
2139
  if (hasStatementInput(block)) {
2093
2140
  const vars = getDeclaredVariables(block, e).map(binding => {
2094
- return {
2095
- name: binding[0],
2096
- type: binding[1],
2097
- id: id++
2098
- };
2141
+ return Object.assign(Object.assign({}, binding), { id: id++ });
2099
2142
  });
2100
2143
  let parentScope = currentScope;
2101
2144
  if (vars.length) {
@@ -2179,17 +2222,37 @@ var pxt;
2179
2222
  switch (block.type) {
2180
2223
  case 'pxt_controls_for':
2181
2224
  case 'controls_simple_for':
2182
- return [[getLoopVariableField(block).getField("VAR").getText(), pNumber]];
2225
+ return [{
2226
+ name: getLoopVariableField(block).getField("VAR").getText(),
2227
+ type: pNumber
2228
+ }];
2183
2229
  case 'pxt_controls_for_of':
2184
2230
  case 'controls_for_of':
2185
- return [[getLoopVariableField(block).getField("VAR").getText(), mkPoint(null)]];
2231
+ return [{
2232
+ name: getLoopVariableField(block).getField("VAR").getText(),
2233
+ type: mkPoint(null)
2234
+ }];
2235
+ case 'function_definition':
2236
+ return block.getArguments().filter(arg => arg.type === "Array")
2237
+ .map(arg => {
2238
+ const point = mkPoint(null);
2239
+ point.isArrayType = true;
2240
+ return {
2241
+ name: arg.name,
2242
+ type: point,
2243
+ isFunctionParameter: true
2244
+ };
2245
+ });
2186
2246
  default:
2187
2247
  break;
2188
2248
  }
2189
2249
  if (isMutatingBlock(block)) {
2190
2250
  const declarations = block.mutation.getDeclaredVariables();
2191
2251
  if (declarations) {
2192
- return Object.keys(declarations).map(varName => [varName, mkPoint(declarations[varName])]);
2252
+ return Object.keys(declarations).map(varName => ({
2253
+ name: varName,
2254
+ type: mkPoint(declarations[varName])
2255
+ }));
2193
2256
  }
2194
2257
  }
2195
2258
  let stdFunc = e.stdCallTable[block.type];
@@ -4078,21 +4141,9 @@ var pxt;
4078
4141
  || (nsinfo && nsinfo.attributes.color)
4079
4142
  || pxt.toolbox.getNamespaceColor(ns)
4080
4143
  || 255;
4081
- if (fn.attributes.help) {
4082
- const helpUrl = fn.attributes.help.replace(/^\//, '');
4083
- if (/^github:/.test(helpUrl)) {
4084
- block.setHelpUrl(helpUrl);
4085
- }
4086
- else if (helpUrl !== "none") {
4087
- block.setHelpUrl("/reference/" + helpUrl);
4088
- }
4089
- }
4090
- else if (fn.pkg && !pxt.appTarget.bundledpkgs[fn.pkg]) { // added package
4091
- let anchor = fn.qName.toLowerCase().split('.');
4092
- if (anchor[0] == fn.pkg)
4093
- anchor.shift();
4094
- block.setHelpUrl(`/pkg/${fn.pkg}#${encodeURIComponent(anchor.join('-'))}`);
4095
- }
4144
+ const helpUrl = pxt.blocks.getHelpUrl(fn);
4145
+ if (helpUrl)
4146
+ block.setHelpUrl(helpUrl);
4096
4147
  block.setColour(color);
4097
4148
  let blockShape = Blockly.OUTPUT_SHAPE_ROUND;
4098
4149
  if (fn.retType == "boolean")
@@ -6324,8 +6375,71 @@ var pxt;
6324
6375
  Blockly.utils.dom.removeClass(blockPath, 'blockly-ws-search-highlight-pxt');
6325
6376
  });
6326
6377
  }
6378
+ /**
6379
+ * https://github.com/google/blockly-samples/blob/master/plugins/workspace-search/src/WorkspaceSearch.js#L633
6380
+ *
6381
+ * Modified to center offscreen blocks.
6382
+ */
6383
+ scrollToVisible_(block) {
6384
+ if (!this.workspace_.isMovable()) {
6385
+ // Cannot scroll to block in a non-movable workspace.
6386
+ return;
6387
+ }
6388
+ // XY is in workspace coordinates.
6389
+ const xy = block.getRelativeToSurfaceXY();
6390
+ const scale = this.workspace_.scale;
6391
+ // Block bounds in pixels relative to the workspace origin (0,0 is centre).
6392
+ const width = block.width * scale;
6393
+ const height = block.height * scale;
6394
+ const top = xy.y * scale;
6395
+ const bottom = (xy.y + block.height) * scale;
6396
+ // In RTL the block's position is the top right of the block, not top left.
6397
+ const left = this.workspace_.RTL ? xy.x * scale - width : xy.x * scale;
6398
+ const right = this.workspace_.RTL ? xy.x * scale : xy.x * scale + width;
6399
+ const metrics = this.workspace_.getMetrics();
6400
+ let targetLeft = metrics.viewLeft;
6401
+ const overflowLeft = left < metrics.viewLeft;
6402
+ const overflowRight = right > metrics.viewLeft + metrics.viewWidth;
6403
+ const wideBlock = width > metrics.viewWidth;
6404
+ if ((!wideBlock && overflowLeft) || (wideBlock && !this.workspace_.RTL)) {
6405
+ // Scroll to show left side of block
6406
+ targetLeft = left;
6407
+ }
6408
+ else if ((!wideBlock && overflowRight) ||
6409
+ (wideBlock && this.workspace_.RTL)) {
6410
+ // Scroll to show right side of block
6411
+ targetLeft = right - metrics.viewWidth;
6412
+ }
6413
+ let targetTop = metrics.viewTop;
6414
+ const overflowTop = top < metrics.viewTop;
6415
+ const overflowBottom = bottom > metrics.viewTop + metrics.viewHeight;
6416
+ const tallBlock = height > metrics.viewHeight;
6417
+ if (overflowTop || (tallBlock && overflowBottom)) {
6418
+ // Scroll to show top of block
6419
+ targetTop = top;
6420
+ }
6421
+ else if (overflowBottom) {
6422
+ // Scroll to show bottom of block
6423
+ targetTop = bottom - metrics.viewHeight;
6424
+ }
6425
+ if (targetLeft !== metrics.viewLeft || targetTop !== metrics.viewTop) {
6426
+ const activeEl = document.activeElement;
6427
+ if (wideBlock || tallBlock) {
6428
+ this.workspace_.scroll(-targetLeft, -targetTop);
6429
+ }
6430
+ else {
6431
+ this.workspace_.centerOnBlock(block.id);
6432
+ }
6433
+ if (activeEl) {
6434
+ // Blockly.WidgetDiv.hide called in scroll is taking away focus.
6435
+ // TODO: Review setFocused call in Blockly.WidgetDiv.hide.
6436
+ activeEl.focus();
6437
+ }
6438
+ }
6439
+ }
6327
6440
  open() {
6328
6441
  super.open();
6442
+ this.inputElement_.select();
6329
6443
  Blockly.utils.dom.addClass(this.workspace_.getInjectionDiv(), 'blockly-ws-searching');
6330
6444
  }
6331
6445
  close() {
@@ -12128,15 +12242,20 @@ var pxtblockly;
12128
12242
  let leftPadding = 0, rightPadding = 0;
12129
12243
  switch (outputShape) {
12130
12244
  case Blockly.OUTPUT_SHAPE_HEXAGONAL:
12131
- width = innerWidth;
12245
+ width = size.width / 2;
12132
12246
  halfWidth = width / 2;
12133
- let quarterWidth = halfWidth / 2;
12134
- // TODO: the left padding calculation is a hack, we should calculate left padding based on width (generic case)
12135
- leftPadding = -halfWidth + quarterWidth;
12136
- rightPadding = -quarterWidth;
12137
- const topLeftPoint = -quarterWidth;
12138
- const bottomRightPoint = halfWidth;
12139
- this.toggleThumb_.setAttribute('points', `${topLeftPoint},-14 ${topLeftPoint - 14},0 ${topLeftPoint},14 ${bottomRightPoint},14 ${bottomRightPoint + 14},0 ${bottomRightPoint},-14`);
12247
+ leftPadding = -halfWidth; // total translation when toggle is left-aligned = 0
12248
+ rightPadding = halfWidth - innerWidth; // total translation when right-aligned = width
12249
+ /**
12250
+ * Toggle defined clockwise from bottom left:
12251
+ *
12252
+ * 0, 14 ----------- width, 14
12253
+ * / \
12254
+ * -14, 0 width + 14, 0
12255
+ * \ /
12256
+ * 0, -14 ----------- width, -14
12257
+ */
12258
+ this.toggleThumb_.setAttribute('points', `${0},-14 -14,0 ${0},14 ${width},14 ${width + 14},0 ${width},-14`);
12140
12259
  break;
12141
12260
  case Blockly.OUTPUT_SHAPE_ROUND:
12142
12261
  case Blockly.OUTPUT_SHAPE_SQUARE:
@@ -4620,6 +4620,8 @@ ${output}</xml>`;
4620
4620
  return getTaggedTemplateExpression(n);
4621
4621
  case SK.CallExpression:
4622
4622
  return getStatementBlock(n, undefined, undefined, true);
4623
+ case SK.AsExpression:
4624
+ return getOutputBlock(n.expression);
4623
4625
  default:
4624
4626
  error(n, pxtc.Util.lf("Unsupported syntax kind for output expression block: {0}", SK[n.kind]));
4625
4627
  break;
@@ -5500,11 +5502,14 @@ ${output}</xml>`;
5500
5502
  r.mutationChildren = [];
5501
5503
  n.parameters.forEach(p => {
5502
5504
  const paramName = p.name.getText();
5505
+ let type = normalizeType(p.type.getText());
5506
+ if (pxt.U.endsWith(type, "[]"))
5507
+ type = "Array";
5503
5508
  r.mutationChildren.push({
5504
5509
  nodeName: "arg",
5505
5510
  attributes: {
5506
5511
  name: paramName,
5507
- type: p.type.getText(),
5512
+ type,
5508
5513
  id: env.functionParamIds[name][paramName]
5509
5514
  }
5510
5515
  });
@@ -5580,11 +5585,14 @@ ${output}</xml>`;
5580
5585
  env.declaredFunctions[name].parameters.forEach((p, i) => {
5581
5586
  const paramName = p.name.getText();
5582
5587
  const argId = env.functionParamIds[name][paramName];
5588
+ let type = normalizeType(p.type.getText());
5589
+ if (pxt.U.endsWith(type, "[]"))
5590
+ type = "Array";
5583
5591
  r.mutationChildren.push({
5584
5592
  nodeName: "arg",
5585
5593
  attributes: {
5586
5594
  name: paramName,
5587
- type: p.type.getText(),
5595
+ type: type,
5588
5596
  id: argId
5589
5597
  }
5590
5598
  });
@@ -6452,7 +6460,8 @@ ${output}</xml>`;
6452
6460
  if (!type) {
6453
6461
  return pxtc.Util.lf("Function parameters must declare a type");
6454
6462
  }
6455
- if (env.opts.allowedArgumentTypes.indexOf(normalizeType(type)) === -1) {
6463
+ const normalized = normalizeType(type);
6464
+ if (env.opts.allowedArgumentTypes.indexOf(normalized) === -1 && !pxtc.U.endsWith(normalized, "[]")) {
6456
6465
  return pxtc.Util.lf("Only types that can be added in blocks can be used for function arguments");
6457
6466
  }
6458
6467
  }
@@ -6727,6 +6736,8 @@ ${output}</xml>`;
6727
6736
  return checkStatement(n, env, true, undefined);
6728
6737
  case SK.TaggedTemplateExpression:
6729
6738
  return checkTaggedTemplateExpression(n, env);
6739
+ case SK.AsExpression:
6740
+ return checkAsExpression(n);
6730
6741
  }
6731
6742
  return pxtc.Util.lf("Unsupported syntax kind for output expression block: {0}", SK[n.kind]);
6732
6743
  function checkStringLiteral(n) {
@@ -6823,6 +6834,31 @@ ${output}</xml>`;
6823
6834
  // The compiler will have already caught any invalid tags or templates
6824
6835
  return undefined;
6825
6836
  }
6837
+ function checkAsExpression(n) {
6838
+ // The only time we allow casts to decompile is in the very special case where someone has
6839
+ // written a program comparing two string, boolean, or numeric literals in blocks and
6840
+ // converted to text. e.g. 3 == 5 or true != false
6841
+ if (n.type.getText().trim() === "any" && (ts.isStringOrNumericLiteral(n.expression) ||
6842
+ n.expression.kind === SK.TrueKeyword || n.expression.kind === SK.FalseKeyword)) {
6843
+ const [parent] = getParent(n);
6844
+ if (parent.kind === SK.BinaryExpression) {
6845
+ switch (parent.operatorToken.kind) {
6846
+ case SK.EqualsEqualsToken:
6847
+ case SK.EqualsEqualsEqualsToken:
6848
+ case SK.ExclamationEqualsToken:
6849
+ case SK.ExclamationEqualsEqualsToken:
6850
+ case SK.LessThanToken:
6851
+ case SK.LessThanEqualsToken:
6852
+ case SK.GreaterThanToken:
6853
+ case SK.GreaterThanEqualsToken:
6854
+ return undefined;
6855
+ default:
6856
+ break;
6857
+ }
6858
+ }
6859
+ }
6860
+ return pxtc.Util.lf("Casting not supported in blocks");
6861
+ }
6826
6862
  function getParent(node) {
6827
6863
  if (!node.parent) {
6828
6864
  return [undefined, node];
@@ -12615,8 +12651,10 @@ ${lbl}: .short 0xffff
12615
12651
  return needsNumberConversions() ? pxtc.ir.rtcall("pxt::toInt", [e]) : e;
12616
12652
  };
12617
12653
  // c = a[i]
12618
- if (iterVar)
12654
+ if (iterVar) {
12619
12655
  proc.emitExpr(iterVar.storeByRef(pxtc.ir.rtcall(indexer, [collectionVar.loadCore(), toInt(intVarIter.loadCore())])));
12656
+ emitBrk(node.initializer);
12657
+ }
12620
12658
  flushHoistedFunctionDefinitions();
12621
12659
  emit(node.statement);
12622
12660
  proc.emitLblDirect(l.cont);
package/built/pxtlib.d.ts CHANGED
@@ -552,6 +552,7 @@ declare namespace pxt {
552
552
  multiUrl?: string;
553
553
  asseteditorUrl?: string;
554
554
  skillmapUrl?: string;
555
+ authcodeUrl?: string;
555
556
  isStatic?: boolean;
556
557
  verprefix?: string;
557
558
  }
@@ -656,6 +657,7 @@ declare namespace pxt.blocks {
656
657
  function normalizeBlock(b: string, err?: (msg: string) => void): string;
657
658
  function compileInfo(fn: pxtc.SymbolInfo): BlockCompileInfo;
658
659
  function hasHandler(fn: pxtc.SymbolInfo): boolean;
660
+ function getHelpUrl(fn: pxtc.SymbolInfo): string;
659
661
  /**
660
662
  * Returns which Blockly block type to use for an argument reporter based
661
663
  * on the specified TypeScript type.
package/built/pxtlib.js CHANGED
@@ -3909,6 +3909,25 @@ var pxt;
3909
3909
  });
3910
3910
  }
3911
3911
  blocks.hasHandler = hasHandler;
3912
+ function getHelpUrl(fn) {
3913
+ if (fn.attributes.help) {
3914
+ const helpUrl = fn.attributes.help.replace(/^\//, '');
3915
+ if (/^github:/.test(helpUrl)) {
3916
+ return helpUrl;
3917
+ }
3918
+ else if (helpUrl !== "none") {
3919
+ return "/reference/" + helpUrl;
3920
+ }
3921
+ }
3922
+ else if (fn.pkg && !pxt.appTarget.bundledpkgs[fn.pkg]) { // added package
3923
+ let anchor = fn.qName.toLowerCase().split('.');
3924
+ if (anchor[0] == fn.pkg)
3925
+ anchor.shift();
3926
+ return `/pkg/${fn.pkg}#${encodeURIComponent(anchor.join('-'))}`;
3927
+ }
3928
+ return undefined;
3929
+ }
3930
+ blocks.getHelpUrl = getHelpUrl;
3912
3931
  /**
3913
3932
  * Returns which Blockly block type to use for an argument reporter based
3914
3933
  * on the specified TypeScript type.
@@ -3920,6 +3939,9 @@ var pxt;
3920
3939
  if (varType === "boolean" || varType === "number" || varType === "string") {
3921
3940
  reporterType = `argument_reporter_${varType}`;
3922
3941
  }
3942
+ if (/^(?:Array<(?:.+)>)|(?:(?:.+)\[\])$/.test(varType)) {
3943
+ reporterType = "argument_reporter_array";
3944
+ }
3923
3945
  return reporterType;
3924
3946
  }
3925
3947
  blocks.reporterTypeForArgType = reporterTypeForArgType;
package/built/pxtpy.js CHANGED
@@ -955,6 +955,14 @@ var pxt;
955
955
  FloorDiv: 1,
956
956
  Mult: 1, // this can be also used on strings and arrays, but let's ignore that for now
957
957
  };
958
+ const arithmeticCompareOps = {
959
+ Eq: 1,
960
+ NotEq: 1,
961
+ Lt: 1,
962
+ LtE: 1,
963
+ Gt: 1,
964
+ GtE: 1
965
+ };
958
966
  const opMapping = {
959
967
  Add: "+",
960
968
  Sub: "-",
@@ -1826,9 +1834,24 @@ var pxt;
1826
1834
  let idx = B.mkInfix(expr(n.comparators[0]), ".", B.H.mkCall("indexOf", [expr(n.left)]));
1827
1835
  return B.mkInfix(idx, n.ops[0] == "In" ? ">=" : "<", B.mkText("0"));
1828
1836
  }
1829
- let r = binop(expr(n.left), n.ops[0], expr(n.comparators[0]));
1837
+ let left = expr(n.left);
1838
+ let right = expr(n.comparators[0]);
1839
+ // Special handling for comparisons of literal types, e.g. 0 === 5
1840
+ const castIfLiteralComparison = (op, leftExpr, rightExpr) => {
1841
+ if (arithmeticCompareOps[op]) {
1842
+ if (isNumStringOrBool(leftExpr) && isNumStringOrBool(rightExpr) && B.flattenNode([left]) !== B.flattenNode([right])) {
1843
+ left = B.H.mkParenthesizedExpression(B.mkGroup([left, B.mkText(" as any")]));
1844
+ right = B.H.mkParenthesizedExpression(B.mkGroup([right, B.mkText(" as any")]));
1845
+ }
1846
+ }
1847
+ };
1848
+ castIfLiteralComparison(n.ops[0], n.left, n.comparators[0]);
1849
+ let r = binop(left, n.ops[0], right);
1830
1850
  for (let i = 1; i < n.ops.length; ++i) {
1831
- r = binop(r, "And", binop(expr(n.comparators[i - 1]), n.ops[i], expr(n.comparators[i])));
1851
+ left = expr(n.comparators[i - 1]);
1852
+ right = expr(n.comparators[i]);
1853
+ castIfLiteralComparison(n.ops[i], n.comparators[i - 1], n.comparators[i]);
1854
+ r = binop(r, "And", binop(left, n.ops[i], right));
1832
1855
  }
1833
1856
  return r;
1834
1857
  },
@@ -2616,6 +2639,16 @@ var pxt;
2616
2639
  const t = canonicalize(typeOf(expr));
2617
2640
  return t && t.primType === "@array";
2618
2641
  }
2642
+ function isNumStringOrBool(expr) {
2643
+ switch (expr.kind) {
2644
+ case "Num":
2645
+ case "Str":
2646
+ return true;
2647
+ case "NameConstant":
2648
+ return expr.value !== null;
2649
+ }
2650
+ return false;
2651
+ }
2619
2652
  function buildOverride(override, args, recv) {
2620
2653
  const result = [];
2621
2654
  for (const part of override.parts) {
@@ -5987,6 +6020,42 @@ var pxt;
5987
6020
  }
5988
6021
  return fn(s);
5989
6022
  }
6023
+ function getParent(node) {
6024
+ if (!node.parent) {
6025
+ return undefined;
6026
+ }
6027
+ else if (node.parent.kind === ts.SyntaxKind.ParenthesizedExpression) {
6028
+ return getParent(node.parent);
6029
+ }
6030
+ else {
6031
+ return node.parent;
6032
+ }
6033
+ }
6034
+ function isDecompilableAsExpression(n) {
6035
+ // The only time we allow casts to decompile is in the very special case where someone has
6036
+ // written a program comparing two string, boolean, or numeric literals in blocks and
6037
+ // converted to text. e.g. 3 == 5 or true != false
6038
+ if (n.type.getText().trim() === "any" && (ts.isNumericLiteral(n.expression) || ts.isStringLiteral(n.expression) ||
6039
+ n.expression.kind === ts.SyntaxKind.TrueKeyword || n.expression.kind === ts.SyntaxKind.FalseKeyword)) {
6040
+ const parent = getParent(n);
6041
+ if ((parent === null || parent === void 0 ? void 0 : parent.kind) === ts.SyntaxKind.BinaryExpression) {
6042
+ switch (parent.operatorToken.kind) {
6043
+ case ts.SyntaxKind.EqualsEqualsToken:
6044
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
6045
+ case ts.SyntaxKind.ExclamationEqualsToken:
6046
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
6047
+ case ts.SyntaxKind.LessThanToken:
6048
+ case ts.SyntaxKind.LessThanEqualsToken:
6049
+ case ts.SyntaxKind.GreaterThanToken:
6050
+ case ts.SyntaxKind.GreaterThanEqualsToken:
6051
+ return true;
6052
+ default:
6053
+ break;
6054
+ }
6055
+ }
6056
+ }
6057
+ return false;
6058
+ }
5990
6059
  function isConstExp(s) {
5991
6060
  let isConst = (s) => {
5992
6061
  switch (s.kind) {
@@ -6069,6 +6138,8 @@ var pxt;
6069
6138
  return asExpRes(s.getText());
6070
6139
  if (ts.isConditionalExpression(s))
6071
6140
  return emitCondExp(s);
6141
+ if (ts.isAsExpression(s) && isDecompilableAsExpression(s))
6142
+ return emitExp(s.expression);
6072
6143
  // TODO handle more expressions
6073
6144
  pxt.tickEvent("depython.todo.expression", { kind: s.kind });
6074
6145
  // return asExpRes(s.getText(), ["# unknown expression: " + s.kind]) // uncomment for easier locating
package/built/server.js CHANGED
@@ -1046,6 +1046,10 @@ function serveAsync(options) {
1046
1046
  sendFile(path.join(publicDir, 'skillmap.html'));
1047
1047
  return;
1048
1048
  }
1049
+ if (pathname == "/--authcode") {
1050
+ sendFile(path.join(publicDir, 'authcode.html'));
1051
+ return;
1052
+ }
1049
1053
  if (/\/-[-]*docs.*$/.test(pathname)) {
1050
1054
  sendFile(path.join(publicDir, 'docs.html'));
1051
1055
  return;