pxt-core 8.2.6 → 8.2.9

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 (59) hide show
  1. package/built/cli.js +1 -0
  2. package/built/pxt-common.json +1 -1
  3. package/built/pxt.js +69 -26
  4. package/built/pxtblockly.js +37 -9
  5. package/built/pxtblocks.d.ts +1 -0
  6. package/built/pxtblocks.js +37 -9
  7. package/built/pxtcompiler.js +3 -3
  8. package/built/pxteditor.js +5 -0
  9. package/built/pxtlib.js +26 -7
  10. package/built/pxtpy.d.ts +1 -0
  11. package/built/pxtpy.js +39 -16
  12. package/built/pxtrunner.js +13 -0
  13. package/built/target.js +1 -1
  14. package/built/web/blockly.css +1 -1
  15. package/built/web/main.js +1 -1
  16. package/built/web/pxtapp.js +1 -1
  17. package/built/web/pxtblockly.js +1 -1
  18. package/built/web/pxtblocks.js +1 -1
  19. package/built/web/pxtcompiler.js +1 -1
  20. package/built/web/pxteditor.js +1 -1
  21. package/built/web/pxtembed.js +2 -2
  22. package/built/web/pxtlib.js +1 -1
  23. package/built/web/pxtpy.js +1 -1
  24. package/built/web/pxtrunner.js +1 -1
  25. package/built/web/pxtworker.js +1 -1
  26. package/built/web/react-common-authcode.css +98 -0
  27. package/built/web/react-common-skillmap.css +1 -1
  28. package/built/web/rtlblockly.css +1 -1
  29. package/built/web/rtlreact-common-skillmap.css +1 -1
  30. package/built/web/rtlsemantic.css +1 -1
  31. package/built/web/semantic.css +1 -1
  32. package/built/web/skillmap/css/main.3684f34d.chunk.css +1 -0
  33. package/built/web/skillmap/js/2.26325281.chunk.js +2 -0
  34. package/built/web/skillmap/js/main.d94a2bd9.chunk.js +1 -0
  35. package/localtypings/pxtarget.d.ts +16 -0
  36. package/localtypings/react.d.ts +5 -0
  37. package/package.json +1 -1
  38. package/react-common/components/controls/Checkbox.tsx +1 -1
  39. package/react-common/components/palette/ColorPickerField.tsx +65 -0
  40. package/react-common/components/palette/PaletteEditor.tsx +66 -0
  41. package/react-common/components/palette/PalettePicker.tsx +52 -0
  42. package/react-common/components/palette/PaletteSwatch.tsx +27 -0
  43. package/react-common/components/palette/Palettes.ts +289 -0
  44. package/react-common/components/profile/SignInModal.tsx +100 -0
  45. package/react-common/components/profile/UserPane.tsx +17 -9
  46. package/react-common/styles/palette/ColorPickerField.less +21 -0
  47. package/react-common/styles/palette/PalettePicker.less +10 -0
  48. package/react-common/styles/palette/PaletteSwatch.less +27 -0
  49. package/react-common/styles/palette/palette.less +3 -0
  50. package/react-common/styles/profile/profile.less +64 -1
  51. package/react-common/styles/react-common.less +1 -0
  52. package/theme/blockly-core.less +1 -1
  53. package/theme/common.less +13 -2
  54. package/theme/image-editor/bottomBar.less +1 -1
  55. package/theme/tutorial-sidebar.less +2 -2
  56. package/webapp/public/skillmap.html +2 -2
  57. package/built/web/skillmap/css/main.c5811548.chunk.css +0 -1
  58. package/built/web/skillmap/js/2.26b9a6f6.chunk.js +0 -2
  59. package/built/web/skillmap/js/main.98eed582.chunk.js +0 -1
package/built/cli.js CHANGED
@@ -2029,6 +2029,7 @@ async function buildTargetCoreAsync(options = {}) {
2029
2029
  updateTOC(cfg);
2030
2030
  cfg.bundledpkgs = {};
2031
2031
  pxt.setAppTarget(cfg);
2032
+ pxt.reloadAppTargetVariant();
2032
2033
  dirsToWatch = cfg.bundleddirs.slice();
2033
2034
  if (pxt.appTarget.id != "core") {
2034
2035
  if (fs.existsSync("theme")) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "pxt-core.d.ts": "/// <reference no-default-lib=\"true\"/>\n\ninterface Array<T> {\n /**\n * Get or set the length of an array. This number is one more than the index of the last element the array.\n */\n //% shim=Array_::length weight=84\n //% blockId=\"lists_length\" block=\"length of %VALUE\" blockBuiltin=true blockNamespace=\"arrays\"\n length: number;\n\n /**\n * Append a new element to an array.\n * @param items New elements of the Array.\n */\n //% help=arrays/push\n //% shim=Array_::push weight=50\n //% blockId=\"array_push\" block=\"%list| add value %value| to end\" blockNamespace=\"arrays\"\n //% group=\"Modify\"\n push(item: T): void;\n\n /**\n * Concatenates the values with another array.\n * @param arr The other array that is being concatenated with\n */\n //% helper=arrayConcat weight=40\n concat(arr: T[]): T[];\n\n /**\n * Remove the last element from an array and return it.\n */\n //% help=arrays/pop\n //% shim=Array_::pop weight=45\n //% blockId=\"array_pop\" block=\"get and remove last value from %list\" blockNamespace=\"arrays\"\n //% group=\"Read\"\n pop(): T;\n\n /**\n * Reverse the elements in an array. The first array element becomes the last, and the last array element becomes the first.\n */\n //% help=arrays/reverse\n //% helper=arrayReverse weight=10\n //% blockId=\"array_reverse\" block=\"reverse %list\" blockNamespace=\"arrays\"\n //% group=\"Operations\"\n reverse(): void;\n\n /**\n * Remove the first element from an array and return it. This method changes the length of the array.\n */\n //% help=arrays/shift\n //% helper=arrayShift weight=30\n //% blockId=\"array_shift\" block=\"get and remove first value from %list\" blockNamespace=\"arrays\"\n //% group=\"Read\"\n shift(): T;\n\n /**\n * Add one element to the beginning of an array and return the new length of the array.\n * @param element to insert at the start of the Array.\n */\n //% help=arrays/unshift\n //% helper=arrayUnshift weight=25\n //% blockId=\"array_unshift\" block=\"%list| insert %value| at beginning\" blockNamespace=\"arrays\"\n //% group=\"Modify\"\n //unshift(...values:T[]): number; //rest is not supported in our compiler yet.\n unshift(value: T): number;\n\n /**\n * Return a section of an array.\n * @param start The beginning of the specified portion of the array. eg: 0\n * @param end The end of the specified portion of the array. eg: 0\n */\n //% help=arrays/slice\n //% helper=arraySlice weight=41 blockNamespace=\"arrays\"\n slice(start?: number, end?: number): T[];\n\n /**\n * Remove elements from an array.\n * @param start The zero-based location in the array from which to start removing elements. eg: 0\n * @param deleteCount The number of elements to remove. eg: 0\n */\n //% helper=arraySplice weight=40\n splice(start: number, deleteCount: number): void;\n\n /**\n * joins all elements of an array into a string and returns this string.\n * @param sep the string separator\n */\n //% helper=arrayJoin weight=40\n join(sep?: string): string;\n\n /**\n * Tests whether at least one element in the array passes the test implemented by the provided function.\n * @param callbackfn A function that accepts up to two arguments. The some method calls the callbackfn function one time for each element in the array.\n */\n //% helper=arraySome weight=40\n some(callbackfn: (value: T, index: number) => boolean): boolean;\n\n /**\n * Tests whether all elements in the array pass the test implemented by the provided function.\n * @param callbackfn A function that accepts up to two arguments. The every method calls the callbackfn function one time for each element in the array.\n */\n //% helper=arrayEvery weight=40\n every(callbackfn: (value: T, index: number) => boolean): boolean;\n\n /**\n * Sort the elements of an array in place and returns the array. The sort is not necessarily stable.\n * @param specifies a function that defines the sort order. If omitted, the array is sorted according to the prmitive type\n */\n //% helper=arraySort weight=40\n sort(callbackfn?: (value1: T, value2: T) => number): T[];\n\n /**\n * Call a defined callback function on each element of an array, and return an array containing the results.\n * @param callbackfn A function that accepts up to two arguments. The map method calls the callbackfn function one time for each element in the array.\n */\n //% helper=arrayMap weight=40\n map<U>(callbackfn: (value: T, index: number) => U): U[];\n\n /**\n * Call a defined callback function on each element of an array.\n * @param callbackfn A function that accepts up to two arguments. The forEach method calls the callbackfn function one time for each element in the array.\n */\n //% helper=arrayForEach weight=40\n forEach(callbackfn: (value: T, index: number) => void): void;\n\n /**\n * Return the elements of an array that meet the condition specified in a callback function.\n * @param callbackfn A function that accepts up to two arguments. The filter method calls the callbackfn function one time for each element in the array.\n */\n //% helper=arrayFilter weight=40\n filter(callbackfn: (value: T, index: number) => boolean): T[];\n\n /**\n * Fills all the elements of an array from a start index to an end index with a static value. The end index is not included.\n */\n //% helper=arrayFill weight=39\n fill(value: T, start?: number, end?: number): T[];\n\n /**\n * Returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.\n * @param callbackfn\n */\n //% helper=arrayFind weight=40\n find(callbackfn: (value: T, index: number) => boolean): T;\n\n /**\n * Call the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.\n * @param callbackfn A function that accepts up to three arguments. The reduce method calls the callbackfn function one time for each element in the array.\n * @param initialValue Initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.\n */\n //% helper=arrayReduce weight=40\n reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U;\n\n\n /** Remove the first occurence of an object. Returns true if removed. */\n //% shim=Array_::removeElement weight=48\n removeElement(element: T): boolean;\n\n /** Remove the element at a certain index. */\n //% help=arrays/remove-at\n //% shim=Array_::removeAt weight=47\n //% blockId=\"array_removeat\" block=\"%list| get and remove value at %index\" blockNamespace=\"arrays\"\n //% group=\"Read\"\n removeAt(index: number): T;\n\n /**\n * Insert the value at a particular index, increases length by 1\n * @param index the zero-based position in the list to insert the value, eg: 0\n * @param the value to insert, eg: 0\n */\n //% help=arrays/insert-at\n //% shim=Array_::insertAt weight=20\n //% blockId=\"array_insertAt\" block=\"%list| insert at %index| value %value\" blockNamespace=\"arrays\"\n //% group=\"Modify\"\n insertAt(index: number, value: T): void;\n\n /**\n * Return the index of the first occurrence of a value in an array.\n * @param item The value to locate in the array.\n * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.\n */\n //% help=arrays/index-of\n //% shim=Array_::indexOf weight=40\n //% blockId=\"array_indexof\" block=\"%list| find index of %value\" blockNamespace=\"arrays\"\n //% group=\"Operations\"\n indexOf(item: T, fromIndex?: number): number;\n\n /**\n * Get the value at a particular index\n * @param index the zero-based position in the list of the item, eg: 0\n */\n //% help=arrays/get\n //% shim=Array_::getAt weight=85\n get(index: number): T;\n\n /**\n * Store a value at a particular index\n * @param index the zero-based position in the list to store the value, eg: 0\n * @param value the value to insert, eg: 0\n */\n //% help=arrays/set\n //% shim=Array_::setAt weight=84\n set(index: number, value: T): void;\n\n /**\n * Return a random value from the array\n */\n //% help=arrays/pick-random\n //% helper=arrayPickRandom weight=25\n //% blockId=\"array_pickRandom\" block=\"get random value from %list\"\n //% blockNamespace=\"arrays\"\n //% group=\"Read\"\n _pickRandom(): T;\n\n [n: number]: T;\n\n /**\n * Add one element to the beginning of an array and return the new length of the array.\n * @param element to insert at the start of the Array.\n */\n //% help=arrays/unshift\n //% helper=arrayUnshift weight=24\n //% blockId=\"array_unshift_statement\" block=\"%list| insert %value| at beginning\" blockNamespace=\"arrays\"\n //% blockAliasFor=\"Array.unshift\"\n //% group=\"Modify\"\n _unshiftStatement(value: T): void;\n\n /**\n * Remove the last element from an array and return it.\n */\n //% help=arrays/pop\n //% shim=Array_::pop weight=44\n //% blockId=\"array_pop_statement\" block=\"remove last value from %list\" blockNamespace=\"arrays\"\n //% blockAliasFor=\"Array.pop\"\n //% group=\"Modify\"\n _popStatement(): void;\n\n /**\n * Remove the first element from an array and return it. This method changes the length of the array.\n */\n //% help=arrays/shift\n //% helper=arrayShift weight=29\n //% blockId=\"array_shift_statement\" block=\"remove first value from %list\" blockNamespace=\"arrays\"\n //% blockAliasFor=\"Array.shift\"\n //% group=\"Modify\"\n _shiftStatement(): void;\n\n /** Remove the element at a certain index. */\n //% help=arrays/remove-at\n //% shim=Array_::removeAt weight=14\n //% blockId=\"array_removeat_statement\" block=\"%list| remove value at %index\" blockNamespace=\"arrays\"\n //% blockAliasFor=\"Array.removeAt\"\n //% group=\"Modify\"\n _removeAtStatement(index: number): void;\n}\n\ndeclare interface String {\n // This block is currently disabled in favor of the built-in Blockly \"Create text with\" block, which compiles to \"\" + \"\"\n // Add % sign back to the block annotation to re-enable\n /**\n * Returns a string that contains the concatenation of two or more strings.\n * @param other The string to append to the end of the string.\n */\n //% shim=String_::concat weight=49\n //% blockId=\"string_concat\" blockNamespace=\"text\"\n // block=\"join %list=text|%other\"\n concat(other: string): string;\n\n /**\n * Return the character at the specified index.\n * @param index The zero-based index of the desired character.\n */\n //% shim=String_::charAt weight=48\n //% help=text/char-at\n //% blockId=\"string_get\" block=\"char from %this=text|at %pos\" blockNamespace=\"text\"\n charAt(index: number): string;\n\n /** Returns the length of a String object. */\n //% property shim=String_::length weight=47\n //% blockId=\"text_length\" block=\"length of %VALUE\" blockBuiltin=true blockNamespace=\"text\"\n length: number;\n\n /**\n * Return the Unicode value of the character at the specified location.\n * @param index The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.\n */\n //% shim=String_::charCodeAt\n charCodeAt(index: number): number;\n\n /**\n * See how the order of characters in two strings is different (in ASCII encoding).\n * @param that String to compare to target string\n */\n //% shim=String_::compare\n //% help=text/compare\n //% blockId=\"string_compare\" block=\"compare %this=text| to %that\" blockNamespace=\"text\"\n compare(that: string): number;\n\n /**\n * Return a substring of the current string.\n * @param start first character index; can be negative from counting from the end, eg:0\n * @param length number of characters to extract, eg: 10\n */\n //% helper=stringSubstr\n //% help=text/substr\n //% blockId=\"string_substr\" block=\"substring of %this=text|from %start|of length %length\" blockNamespace=\"text\"\n substr(start: number, length?: number): string;\n\n /**\n * Return the current string with the first occurence of toReplace\n * replaced with the replacer\n * @param toReplace the substring to replace in the current string\n * @param replacer either the string that replaces toReplace in the current string,\n * or a function that accepts the substring and returns the replacement string.\n */\n //% helper=stringReplace\n replace(toReplace: string, replacer: string | ((sub: string) => string)): string;\n\n /**\n * Return the current string with each occurence of toReplace\n * replaced with the replacer\n * @param toReplace the substring to replace in the current string\n * @param replacer either the string that replaces toReplace in the current string,\n * or a function that accepts the substring and returns the replacement string.\n */\n //% helper=stringReplaceAll\n replaceAll(toReplace: string, replacer: string | ((sub: string) => string)): string;\n\n /**\n * Return a substring of the current string.\n * @param start first character index; can be negative from counting from the end, eg:0\n * @param end one-past-last character index\n */\n //% helper=stringSlice\n slice(start: number, end?: number): string;\n\n /** Returns a value indicating if the string is empty */\n //% helper=stringEmpty\n //% help=text/is-empty\n //% blockId=\"string_isempty\" blockNamespace=\"text\"\n //% block=\"%this=text| is empty\"\n isEmpty(): boolean;\n\n /**\n * Returns the position of the first occurrence of a specified value in a string.\n * @param searchValue the text to find\n * @param start optional start index for the search\n */\n //% shim=String_::indexOf\n //% help=text/index-of\n //% blockId=\"string_indexof\" blockNamespace=\"text\"\n //% block=\"%this=text|find index of %searchValue\"\n indexOf(searchValue: string, start?: number): number;\n\n /**\n * Determines whether a string contains the characters of a specified string.\n * @param searchValue the text to find\n * @param start optional start index for the search\n */\n //% shim=String_::includes\n //% help=text/includes\n //% blockId=\"string_includes\" blockNamespace=\"text\"\n //% block=\"%this=text|includes %searchValue\"\n includes(searchValue: string, start?: number): boolean;\n\n /**\n * Splits the string according to the separators\n * @param separator\n * @param limit\n */\n //% helper=stringSplit\n //% help=text/split\n //% blockId=\"string_split\" blockNamespace=\"text\"\n //% block=\"split %this=text|at %separator\"\n split(separator?: string, limit?: number): string[];\n\n /**\n * Return a substring of the current string with whitespace removed from both ends\n */\n //% helper=stringTrim\n trim(): string;\n\n /**\n * Converts the string to upper case characters.\n */\n //% helper=stringToUpperCase\n //% help=text/to-upper-case\n toUpperCase(): string;\n\n /**\n * Converts the string to lower case characters.\n */\n //% helper=stringToLowerCase\n //% help=text/to-lower-case\n toLowerCase(): string;\n\n [index: number]: string;\n}\n\n/**\n * Convert a string to a number.\n * @param s A string to convert into a number. eg: 123\n */\n//% shim=String_::toNumber\n//% help=text/parse-float\n//% blockId=\"string_parsefloat\" block=\"parse to number %text\" blockNamespace=\"text\"\n//% text.defl=\"123\"\ndeclare function parseFloat(text: string): number;\n\n/**\n * Returns a pseudorandom number between min and max included.\n * If both numbers are integral, the result is integral.\n * @param min the lower inclusive bound, eg: 0\n * @param max the upper inclusive bound, eg: 10\n */\n//% blockId=\"device_random\" block=\"pick random %min|to %limit\"\n//% blockNamespace=\"Math\"\n//% help=math/randint\n//% shim=Math_::randomRange\ndeclare function randint(min: number, max: number): number;\n\ninterface Object { }\ninterface Function {\n __assignableToFunction: Function;\n}\ninterface IArguments {\n __assignableToIArguments: IArguments;\n}\ninterface RegExp {\n __assignableToRegExp: RegExp;\n}\ntype TemplateStringsArray = Array<string>;\n\ntype uint8 = number;\ntype uint16 = number;\ntype uint32 = number;\ntype int8 = number;\ntype int16 = number;\ntype int32 = number;\n\n\ndeclare interface Boolean {\n /**\n * Returns a string representation of an object.\n */\n //% shim=numops::toString\n toString(): string;\n}\n\n/**\n * Combine, split, and search text strings.\n*/\n//% blockNamespace=\"text\"\ndeclare namespace String {\n\n /**\n * Make a string from the given ASCII character code.\n */\n //% help=math/from-char-code\n //% shim=String_::fromCharCode weight=1\n //% blockNamespace=\"text\" blockId=\"stringFromCharCode\" block=\"text from char code %code\"\n function fromCharCode(code: number): string;\n}\n\ndeclare interface Number {\n /**\n * Returns a string representation of a number.\n */\n //% shim=numops::toString\n toString(): string;\n}\n\n/**\n * Add, remove, and replace items in lists.\n*/\n//% blockNamespace=\"Arrays\"\ndeclare namespace Array {\n /**\n * Check if a given object is an array.\n */\n //% shim=Array_::isArray\n function isArray(obj: any): boolean;\n}\n\ndeclare namespace Object {\n /**\n * Return the field names in an object.\n */\n //% shim=pxtrt::keysOf\n function keys(obj: any): string[];\n}\n\n/**\n * More complex operations with numbers.\n*/\ndeclare namespace Math {\n /**\n * Returns the value of a base expression taken to a specified power.\n * @param x The base value of the expression.\n * @param y The exponent value of the expression.\n */\n //% shim=Math_::pow\n function pow(x: number, y: number): number;\n\n /**\n * Returns a pseudorandom number between 0 and 1.\n */\n //% shim=Math_::random\n //% help=math/random\n function random(): number;\n\n /**\n * Returns a pseudorandom number between min and max included.\n * If both numbers are integral, the result is integral.\n * @param min the lower inclusive bound, eg: 0\n * @param max the upper inclusive bound, eg: 10\n */\n //% blockId=\"device_random_deprecated\" block=\"pick random %min|to %limit\"\n //% help=math/random-range deprecated\n //% shim=Math_::randomRange\n function randomRange(min: number, max: number): number;\n\n /**\n * Returns the natural logarithm (base e) of a number.\n * @param x A number\n */\n //% shim=Math_::log\n //% help=math\n function log(x: number): number;\n\n /**\n * Returns returns ``e^x``.\n * @param x A number\n */\n //% shim=Math_::exp\n //% help=math\n function exp(x: number): number;\n\n /**\n * Returns the sine of a number.\n * @param x An angle in radians\n */\n //% shim=Math_::sin\n //% help=math/trigonometry\n function sin(x: number): number;\n\n /**\n * Returns the cosine of a number.\n * @param x An angle in radians\n */\n //% shim=Math_::cos\n //% help=math/trigonometry\n function cos(x: number): number;\n\n /**\n * Returns the tangent of a number.\n * @param x An angle in radians\n */\n //% shim=Math_::tan\n //% help=math/trigonometry\n function tan(x: number): number;\n\n /**\n * Returns the arcsine (in radians) of a number\n * @param x A number\n */\n //% shim=Math_::asin\n //% help=math/trigonometry\n function asin(x: number): number;\n\n /**\n * Returns the arccosine (in radians) of a number\n * @param x A number\n */\n //% shim=Math_::acos\n //% help=math/trigonometry\n function acos(x: number): number;\n\n /**\n * Returns the arctangent (in radians) of a number\n * @param x A number\n */\n //% shim=Math_::atan\n //% help=math/trigonometry\n function atan(x: number): number;\n\n /**\n * Returns the arctangent of the quotient of its arguments.\n * @param y A number\n * @param x A number\n */\n //% shim=Math_::atan2\n //% help=math/trigonometry\n function atan2(y: number, x: number): number;\n\n /**\n * Returns the square root of a number.\n * @param x A numeric expression.\n */\n //% shim=Math_::sqrt\n //% help=math\n function sqrt(x: number): number;\n\n /**\n * Returns the smallest number greater than or equal to its numeric argument.\n * @param x A numeric expression.\n */\n //% shim=Math_::ceil\n //% help=math\n function ceil(x: number): number;\n\n /**\n * Returns the greatest number less than or equal to its numeric argument.\n * @param x A numeric expression.\n */\n //% shim=Math_::floor\n //% help=math\n function floor(x: number): number;\n\n /**\n * Returns the number with the decimal part truncated.\n * @param x A numeric expression.\n */\n //% shim=Math_::trunc\n //% help=math\n function trunc(x: number): number;\n\n /**\n * Returns a supplied numeric expression rounded to the nearest number.\n * @param x The value to be rounded to the nearest number.\n */\n //% shim=Math_::round\n //% help=math\n function round(x: number): number;\n\n /**\n * Returns the value of integer signed 32 bit multiplication of two numbers.\n * @param x The first number\n * @param y The second number\n */\n //% shim=Math_::imul\n //% help=math\n function imul(x: number, y: number): number;\n\n /**\n * Returns the value of integer signed 32 bit division of two numbers.\n * @param x The first number\n * @param y The second number\n */\n //% shim=Math_::idiv\n //% help=math\n function idiv(x: number, y: number): number;\n}\n\ndeclare namespace control {\n //% shim=_control::_onCodeStart\n export function _onCodeStart(arg: any): void;\n\n //% shim=_control::_onCodeStop\n export function _onCodeStop(arg: any): void;\n}",
3
- "pxt-helpers.ts": "type Action = () => void;\n\n/**\n * Constant representing Not-A-Number.\n */\nconst NaN = 0 / 0\n\n/**\n * Constant representing positive infinity.\n */\nconst Infinity = 1 / 0\n\nfunction isNaN(x: number) {\n x = +x // convert to number\n return x !== x\n}\n\nnamespace Number {\n /**\n * Check if a given value is of type Number and it is a NaN.\n */\n export function isNaN(x: any): boolean {\n return typeof x == \"number\" && x !== x\n }\n}\n\n/**\n * A dictionary from string key to string values\n */\ninterface StringMap {\n [index: string]: string;\n}\n\n/**\n * Convert a string to an integer.\n * @param text A string to convert into an integral number. eg: \"123\"\n * @param radix optional A value between 2 and 36 that specifies the base of the number in text.\n * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.\n * All other strings are considered decimal.\n */\n//% help=text/parse-int\n//% blockId=\"string_parseint\" block=\"parse to integer %text\" blockNamespace=\"text\"\n//% text.defl=\"123\"\n//% blockHidden=1\nfunction parseInt(text: string, radix?: number): number {\n // roughly based on https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2\n // with some consideration for avoiding unnecessary slices where easy\n if (!text || (radix != null && (radix < 2 || radix > 36)))\n return NaN;\n\n let start = 0;\n while (start < text.length && helpers.isWhitespace(text.charCodeAt(start)))\n ++start;\n\n if (start === text.length)\n return NaN;\n\n const numberOffset = 48; // 0\n const numCount = 10;\n const letterOffset = 97; // a\n const letterCount = 26;\n const lowerCaseMask = 0x20;\n\n let sign = 1;\n switch (text.charAt(start)) {\n case \"-\":\n sign = -1;\n // fallthrough\n case \"+\":\n ++start;\n }\n\n if ((!radix || radix == 16)\n && \"0\" === text[start]\n && (\"x\" === text[start + 1] || \"X\" === text[start + 1])) {\n radix = 16;\n start += 2;\n } else if (!radix) {\n radix = 10;\n }\n\n let output = 0;\n let hasDigit = false;\n for (let i = start; i < text.length; ++i) {\n const code = text.charCodeAt(i) | lowerCaseMask;\n let val: number = undefined;\n\n if (code >= numberOffset && code < numberOffset + numCount)\n val = code - numberOffset;\n else if (code >= letterOffset && code < letterOffset + letterCount)\n val = numCount + code - letterOffset;\n\n if (val == undefined || val >= radix) {\n if (!hasDigit) {\n return NaN;\n }\n break;\n }\n hasDigit = true;\n output = output * radix + val;\n }\n\n return sign * output;\n}\n\nnamespace helpers {\n export function arrayFill<T>(O: T[], value: T, start?: number, end?: number) {\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill\n // Steps 3-5.\n const len = O.length >>> 0;\n\n // Steps 6-7.\n const relativeStart = start === undefined ? 0 : start >> 0;\n\n // Step 8.\n let k = relativeStart < 0 ?\n Math.max(len + relativeStart, 0) :\n Math.min(relativeStart, len);\n\n // Steps 9-10.\n const relativeEnd = end === undefined ? len : end >> 0;\n\n // Step 11.\n const final = relativeEnd < 0 ?\n Math.max(len + relativeEnd, 0) :\n Math.min(relativeEnd, len);\n\n // Step 12.\n while (k < final) {\n O[k] = value;\n k++;\n }\n\n // Step 13.\n return O;\n }\n\n export function arraySplice<T>(arr: T[], start: number, len: number) {\n if (start < 0) {\n return;\n }\n for (let i = 0; i < len; ++i) {\n arr.removeAt(start)\n }\n }\n\n export function arrayReverse<T>(arr: T[]): void {\n let len = arr.length;\n for (let i = 0; i < len / 2; i++) {\n swap(arr, i, len - i - 1);\n }\n }\n\n export function arrayShift<T>(arr: T[]): T {\n return arr.removeAt(0);\n }\n\n export function arrayJoin<T>(arr: T[], sep?: string): string {\n if (sep === undefined || sep === null) {\n sep = \",\";\n }\n\n let r = \"\";\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n if (i > 0 && sep)\n r += sep;\n r += (arr[i] === undefined || arr[i] === null) ? \"\" : arr[i];\n }\n return r;\n }\n\n /*TODO: Enable this multiple value unshift, after rest is enabled in our compiler.\n export function arrayUnshift<T>(arr: T[], ...values: T[]) : number {\n for(let i = values.length; i > 0; --i) {\n arr.insertAt(0, values[i - 1]);\n }\n return arr.length;\n }\n */\n export function arrayUnshift<T>(arr: T[], value: T): number {\n arr.insertAt(0, value);\n return arr.length;\n }\n\n function swap<T>(arr: T[], i: number, j: number): void {\n let temp: T = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n }\n\n function sortHelper<T>(arr: T[], callbackfn?: (value1: T, value2: T) => number): T[] {\n if (arr.length <= 0 || !callbackfn) {\n return arr;\n }\n let len = arr.length;\n // simple selection sort.\n for (let i = 0; i < len - 1; ++i) {\n for (let j = i + 1; j < len; ++j) {\n if (callbackfn(arr[i], arr[j]) > 0) {\n swap(arr, i, j);\n }\n }\n }\n return arr;\n }\n\n export function arraySort<T>(arr: T[], callbackfn?: (value1: T, value2: T) => number): T[] {\n if (!callbackfn && arr.length > 1) {\n callbackfn = (a, b) => {\n // default is sort as if the element were a string, with null < undefined\n const aIsUndef = a === undefined;\n const bIsUndef = b === undefined;\n if (aIsUndef && bIsUndef) return 0;\n else if (aIsUndef) return 1;\n else if (bIsUndef) return -1;\n\n const aIsNull = a === null;\n const bIsNull = b === null;\n if (aIsNull && bIsNull) return 0;\n else if (aIsNull) return 1;\n else if (bIsNull) return -1;\n\n return (a + \"\").compare(b + \"\");\n }\n }\n return sortHelper(arr, callbackfn);\n }\n\n export function arrayMap<T, U>(arr: T[], callbackfn: (value: T, index: number) => U): U[] {\n let res: U[] = []\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n res.push(callbackfn(arr[i], i))\n }\n return res\n }\n\n export function arraySome<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): boolean {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i)\n if (callbackfn(arr[i], i))\n return true;\n return false;\n }\n\n export function arrayEvery<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): boolean {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i)\n if (!callbackfn(arr[i], i))\n return false;\n return true;\n }\n\n export function arrayForEach<T>(arr: T[], callbackfn: (value: T, index: number) => void): void {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n callbackfn(arr[i], i);\n }\n }\n\n export function arrayFilter<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): T[] {\n let res: T[] = []\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n let v = arr[i] // need to cache\n if (callbackfn(v, i)) res.push(v)\n }\n return res\n }\n\n export function arrayFind<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): T {\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n let v = arr[i] // need to cache\n if (callbackfn(v, i)) return v;\n }\n return undefined;\n }\n\n export function arrayReduce<T, U>(arr: T[], callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U {\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n initialValue = callbackfn(initialValue, arr[i], i)\n }\n return initialValue\n }\n\n export function arrayConcat<T>(arr: T[], otherArr: T[]): T[] {\n let out: T[] = [];\n for (let value of arr) {\n out.push(value);\n }\n for (let value of otherArr) {\n out.push(value);\n }\n return out;\n }\n\n export function arrayPickRandom<T>(arr: T[]): T {\n return arr[Math.randomRange(0, arr.length - 1)];\n }\n\n export function arraySlice<T>(arr: T[], start?: number, end?: number): T[] {\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice\n const res: T[] = [];\n const len = arr.length;\n\n if (start === undefined)\n start = 0;\n else if (start < 0)\n start = Math.max(len + start, 0);\n\n if (start > len)\n return res;\n\n if (end === undefined)\n end = len;\n else if (end < 0)\n end = len + end;\n\n if (end > len)\n end = len;\n\n for (let i = start; i < end; ++i) {\n res.push(arr[i]);\n }\n return res;\n }\n\n export function stringReplace(s: string, toReplace: string, replacer: string | ((sub: string) => string)) {\n toReplace = toReplace + \"\";\n const ind = s.indexOf(toReplace);\n if (ind == -1)\n return s;\n\n const begin = s.slice(0, ind);\n const end = s.slice(ind + toReplace.length);\n\n if (typeof replacer == \"string\" || !replacer) {\n return begin + replacer + end;\n } else {\n return begin + replacer(toReplace) + end;\n }\n }\n\n export function stringReplaceAll(s: string, toReplace: string, replacer: string | ((sub: string) => string)) {\n toReplace = toReplace + \"\";\n const split = s.split(toReplace);\n const empty = toReplace.isEmpty();\n\n let output = (empty ? applyReplace(toReplace, replacer) : \"\");\n\n if (split.length) {\n output += split[0];\n }\n\n for (let i = 1; i < split.length; ++i) {\n output += applyReplace(toReplace, replacer) + split[i];\n }\n\n if (!s.isEmpty() && empty) {\n output += applyReplace(toReplace, replacer);\n }\n\n return output;\n\n function applyReplace(r: string, replacer: string | ((sub: string) => string)): string {\n if (typeof replacer == \"string\" || !replacer) {\n return replacer as string;\n } else {\n return replacer(r);\n }\n }\n }\n\n //% shim=String_::substr\n declare function stringSubstrHelper(s: string, start: number, length?: number): string;\n\n export function stringSubstr(s: string, start: number, length?: number): string {\n length = length === undefined ? s.length : length || 0;\n return stringSubstrHelper(s, start, length);\n }\n\n export function stringSlice(s: string, start: number, end?: number): string {\n const len = s.length;\n\n if (start < 0) {\n start = Math.max(len + start, 0);\n }\n\n if (end === undefined) {\n end = len;\n } else if (end === null) {\n end = 0;\n }\n\n if (end < 0) {\n end = len + end;\n }\n\n return stringSubstrHelper(s, start, end - start);\n }\n\n // also note this doesn't handle unicode, but neither does JS (there's toLocaleUpperCase())\n export function stringToUpperCase(s: string): string {\n let r = \"\"\n let prev = 0\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n if (97 <= c && c <= 122) {\n r += s.slice(prev, i) + String.fromCharCode(c - 32)\n prev = i + 1\n }\n }\n r += s.slice(prev)\n return r\n }\n\n // also note this doesn't handle unicode, but neither does JS (there's toLocaleLowerCase())\n export function stringToLowerCase(s: string): string {\n let r = \"\"\n let prev = 0\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n if (65 <= c && c <= 90) {\n r += s.slice(prev, i) + String.fromCharCode(c + 32)\n prev = i + 1\n }\n }\n r += s.slice(prev)\n return r\n }\n\n export function stringSplit(S: string, separator?: string, limit?: number): string[] {\n // https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.split\n const A: string[] = [];\n let lim = 0;\n if (limit === undefined)\n lim = (1 << 29) - 1; // spec says 1 << 53, leaving it at 29 for constant folding\n else if (limit < 0)\n lim = 0;\n else\n lim = limit | 0;\n const s = S.length;\n let p = 0;\n const R = separator;\n if (lim == 0)\n return A;\n if (separator === undefined) {\n A[0] = S;\n return A;\n }\n if (s == 0) {\n let z = splitMatch(S, 0, R);\n if (z > -1) return A;\n A[0] = S;\n return A;\n }\n let T: string;\n let q = p;\n while (q != s) {\n let e = splitMatch(S, q, R);\n if (e < 0) q++;\n else {\n if (e == p) q++;\n else {\n T = stringSlice(S, p, q);\n A.push(T);\n if (A.length == lim) return A;\n p = e;\n q = p;\n }\n }\n }\n T = stringSlice(S, p, q);\n A.push(T);\n return A;\n }\n\n function splitMatch(S: string, q: number, R: string): number {\n const r = R.length;\n const s = S.length;\n if (q + r > s) return -1;\n for (let i = 0; i < r; ++i) {\n if (S[q + i] != R[i])\n return -1;\n }\n return q + r;\n }\n\n export function stringTrim(s: string): string {\n let start = 0;\n let end = s.length - 1;\n\n while (start <= end && isWhitespace(s.charCodeAt(start)))\n ++start;\n\n while (end > start && isWhitespace(s.charCodeAt(end)))\n --end;\n return s.slice(start, end + 1);\n }\n\n export function isWhitespace(c: number): boolean {\n // https://www.ecma-international.org/ecma-262/6.0/#sec-white-space\n switch (c) {\n case 0x0009: // character tab\n case 0x000B: // line tab\n case 0x000C: // form feed\n case 0x0020: // space\n case 0x00A0: // no-break space\n case 0xFEFF: // zero width no break space\n case 0x000A: // line feed\n case 0x000D: // carriage return\n case 0x2028: // line separator\n case 0x2029: // paragraph separator\n return true;\n default:\n return false;\n }\n }\n\n export function stringEmpty(S: string): boolean {\n return !S;\n }\n}\n\nnamespace Math {\n export function clamp(min: number, max: number, value: number): number {\n return Math.min(max, Math.max(min, value));\n }\n\n /**\n * Returns the absolute value of a number (the value without regard to whether it is positive or negative).\n * For example, the absolute value of -5 is the same as the absolute value of 5.\n * @param x A numeric expression for which the absolute value is needed.\n */\n //% help=math/abs\n export function abs(x: number): number {\n return x < 0 ? -x : x;\n }\n\n /**\n * Returns the sign of the x, indicating whether x is positive, negative or zero.\n * @param x The numeric expression to test\n */\n export function sign(x: number): number {\n if (x == 0) return 0;\n if (x > 0) return 1;\n return -1;\n }\n\n /**\n * Returns the larger of two supplied numeric expressions.\n */\n //% help=math/max\n export function max(a: number, b: number): number {\n if (a >= b) return a;\n return b;\n }\n\n /**\n * Returns the smaller of two supplied numeric expressions.\n */\n //% help=math/min\n export function min(a: number, b: number): number {\n if (a <= b) return a;\n return b;\n }\n\n /**\n * Rounds ``x`` to a number with the given number of ``digits``\n * @param x the number to round\n * @param digits the number of resulting digits\n */\n //%\n export function roundWithPrecision(x: number, digits: number): number {\n digits = digits | 0;\n // invalid digits input\n if (digits <= 0) return Math.round(x);\n if (x == 0) return 0;\n let r = 0;\n do {\n const d = Math.pow(10, digits);\n r = Math.round(x * d) / d;\n digits++;\n } while (r == 0 && digits < 21);\n return r;\n }\n}\n\n\n//% blockHidden=1\nnamespace __internal {\n /**\n * A shim to render a boolean as a down/up toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleDownUp block=\"%down\"\n //% down.fieldEditor=toggledownup\n //% down.fieldOptions.decompileLiterals=true\n export function __downUp(down: boolean): boolean {\n return down;\n }\n\n /**\n * A shim to render a boolean as a up/down toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleUpDown block=\"%up\"\n //% up.fieldEditor=toggleupdown\n //% up.fieldOptions.decompileLiterals=true\n export function __upDown(up: boolean): boolean {\n return up;\n }\n\n /**\n * A shim to render a boolean as a high/low toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleHighLow block=\"%high\"\n //% high.fieldEditor=togglehighlow\n //% high.fieldOptions.decompileLiterals=true\n export function __highLow(high: boolean): boolean {\n return high;\n }\n\n /**\n * A shim to render a boolean as a on/off toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleOnOff block=\"%on\"\n //% on.fieldEditor=toggleonoff\n //% on.fieldOptions.decompileLiterals=true\n export function __onOff(on: boolean): boolean {\n return on;\n }\n\n /**\n * A shim to render a boolean as a yes/no toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleYesNo block=\"%yes\"\n //% yes.fieldEditor=toggleyesno\n //% yes.fieldOptions.decompileLiterals=true\n export function __yesNo(yes: boolean): boolean {\n return yes;\n }\n\n /**\n * A shim to render a boolean as a win/lose toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleWinLose block=\"%win\"\n //% win.fieldEditor=togglewinlose\n //% win.fieldOptions.decompileLiterals=true\n export function __winLose(win: boolean): boolean {\n return win;\n }\n\n /**\n * Get the color wheel field editor\n * @param color color\n */\n //% blockId=colorNumberPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colornumber\" value.fieldOptions.decompileLiterals=true\n //% value.defl='0xff0000'\n //% value.fieldOptions.colours='[\"#ff0000\",\"#ff8000\",\"#ffff00\",\"#ff9da5\",\"#00ff00\",\"#b09eff\",\"#00ffff\",\"#007fff\",\"#65471f\",\"#0000ff\",\"#7f00ff\",\"#ff0080\",\"#ff00ff\",\"#ffffff\",\"#999999\",\"#000000\"]'\n //% value.fieldOptions.columns=4 value.fieldOptions.className='rgbColorPicker'\n export function __colorNumberPicker(value: number) {\n return value;\n }\n\n /**\n * Get the color wheel field editor\n * @param value value between 0 to 255 to get a color value, eg: 10\n */\n //% blockId=colorWheelPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colorwheel\" value.fieldOptions.decompileLiterals=true\n //% value.fieldOptions.sliderWidth='200'\n //% value.fieldOptions.min=0 value.fieldOptions.max=255\n export function __colorWheelPicker(value: number) {\n return value;\n }\n\n /**\n * Get the color wheel field editor using HSV values\n * @param value value between 0 to 255 to get a color value, eg: 10\n */\n //% blockId=colorWheelHsvPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colorwheel\" value.fieldOptions.decompileLiterals=true\n //% value.fieldOptions.sliderWidth='200'\n //% value.fieldOptions.min=0 value.fieldOptions.max=255\n //% value.fieldOptions.channel=hsvfast\n export function __colorWheelHsvPicker(value: number) {\n return value;\n }\n\n /**\n * A speed picker\n * @param speed the speed, eg: 50\n */\n //% blockId=speedPicker block=\"%speed\" shim=TD_ID\n //% speed.fieldEditor=\"speed\" colorSecondary=\"#FFFFFF\"\n //% weight=0 blockHidden=1 speed.fieldOptions.decompileLiterals=1\n export function __speedPicker(speed: number): number {\n return speed;\n }\n\n /**\n * A turn ratio picker\n * @param turnratio the turn ratio, eg: 0\n */\n //% blockId=turnRatioPicker block=\"%turnratio\" shim=TD_ID\n //% turnratio.fieldEditor=\"turnratio\" colorSecondary=\"#FFFFFF\"\n //% weight=0 blockHidden=1 turnRatio.fieldOptions.decompileLiterals=1\n export function __turnRatioPicker(turnratio: number): number {\n return turnratio;\n }\n\n /**\n * A field editor that displays a protractor\n */\n //% blockId=protractorPicker block=\"%angle\"\n //% shim=TD_ID\n //% angle.fieldEditor=protractor\n //% angle.fieldOptions.decompileLiterals=1\n //% colorSecondary=\"#FFFFFF\"\n //% blockHidden=1\n export function __protractor(angle: number) {\n return angle;\n }\n\n /**\n * Get the time field editor\n * @param ms time duration in milliseconds, eg: 500, 1000\n */\n //% blockId=timePicker block=\"%ms\"\n //% blockHidden=true shim=TD_ID\n //% colorSecondary=\"#FFFFFF\"\n //% ms.fieldEditor=\"numberdropdown\" ms.fieldOptions.decompileLiterals=true\n //% ms.fieldOptions.data='[[\"100 ms\", 100], [\"200 ms\", 200], [\"500 ms\", 500], [\"1 second\", 1000], [\"2 seconds\", 2000], [\"5 seconds\", 5000]]'\n export function __timePicker(ms: number): number {\n return ms;\n }\n}\n",
3
+ "pxt-helpers.ts": "type Action = () => void;\n\n/**\n * Constant representing Not-A-Number.\n */\nconst NaN = 0 / 0\n\n/**\n * Constant representing positive infinity.\n */\nconst Infinity = 1 / 0\n\nfunction isNaN(x: number) {\n x = +x // convert to number\n return x !== x\n}\n\nnamespace Number {\n /**\n * Check if a given value is of type Number and it is a NaN.\n */\n export function isNaN(x: any): boolean {\n return typeof x == \"number\" && x !== x\n }\n}\n\n/**\n * A dictionary from string key to string values\n */\ninterface StringMap {\n [index: string]: string;\n}\n\n/**\n * Convert a string to an integer.\n * @param text A string to convert into an integral number. eg: \"123\"\n * @param radix optional A value between 2 and 36 that specifies the base of the number in text.\n * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.\n * All other strings are considered decimal.\n */\n//% help=text/parse-int\n//% blockId=\"string_parseint\" block=\"parse to integer %text\" blockNamespace=\"text\"\n//% text.defl=\"123\"\n//% blockHidden=1\nfunction parseInt(text: string, radix?: number): number {\n // roughly based on https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2\n // with some consideration for avoiding unnecessary slices where easy\n if (!text || (radix != null && (radix < 2 || radix > 36)))\n return NaN;\n\n let start = 0;\n while (start < text.length && helpers.isWhitespace(text.charCodeAt(start)))\n ++start;\n\n if (start === text.length)\n return NaN;\n\n const numberOffset = 48; // 0\n const numCount = 10;\n const letterOffset = 97; // a\n const letterCount = 26;\n const lowerCaseMask = 0x20;\n\n let sign = 1;\n switch (text.charAt(start)) {\n case \"-\":\n sign = -1;\n // fallthrough\n case \"+\":\n ++start;\n }\n\n if ((!radix || radix == 16)\n && \"0\" === text[start]\n && (\"x\" === text[start + 1] || \"X\" === text[start + 1])) {\n radix = 16;\n start += 2;\n } else if (!radix) {\n radix = 10;\n }\n\n let output = 0;\n let hasDigit = false;\n for (let i = start; i < text.length; ++i) {\n const code = text.charCodeAt(i) | lowerCaseMask;\n let val: number = undefined;\n\n if (code >= numberOffset && code < numberOffset + numCount)\n val = code - numberOffset;\n else if (code >= letterOffset && code < letterOffset + letterCount)\n val = numCount + code - letterOffset;\n\n if (val == undefined || val >= radix) {\n if (!hasDigit) {\n return NaN;\n }\n break;\n }\n hasDigit = true;\n output = output * radix + val;\n }\n\n return sign * output;\n}\n\nnamespace helpers {\n export function arrayFill<T>(O: T[], value: T, start?: number, end?: number) {\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill\n // Steps 3-5.\n const len = O.length >>> 0;\n\n // Steps 6-7.\n const relativeStart = start === undefined ? 0 : start >> 0;\n\n // Step 8.\n let k = relativeStart < 0 ?\n Math.max(len + relativeStart, 0) :\n Math.min(relativeStart, len);\n\n // Steps 9-10.\n const relativeEnd = end === undefined ? len : end >> 0;\n\n // Step 11.\n const final = relativeEnd < 0 ?\n Math.max(len + relativeEnd, 0) :\n Math.min(relativeEnd, len);\n\n // Step 12.\n while (k < final) {\n O[k] = value;\n k++;\n }\n\n // Step 13.\n return O;\n }\n\n export function arraySplice<T>(arr: T[], start: number, len: number) {\n if (start < 0) {\n return;\n }\n for (let i = 0; i < len; ++i) {\n arr.removeAt(start)\n }\n }\n\n export function arrayReverse<T>(arr: T[]): void {\n let len = arr.length;\n for (let i = 0; i < len / 2; i++) {\n swap(arr, i, len - i - 1);\n }\n }\n\n export function arrayShift<T>(arr: T[]): T {\n return arr.removeAt(0);\n }\n\n export function arrayJoin<T>(arr: T[], sep?: string): string {\n if (sep === undefined || sep === null) {\n sep = \",\";\n }\n\n let r = \"\";\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n if (i > 0 && sep)\n r += sep;\n r += (arr[i] === undefined || arr[i] === null) ? \"\" : arr[i];\n }\n return r;\n }\n\n /*TODO: Enable this multiple value unshift, after rest is enabled in our compiler.\n export function arrayUnshift<T>(arr: T[], ...values: T[]) : number {\n for(let i = values.length; i > 0; --i) {\n arr.insertAt(0, values[i - 1]);\n }\n return arr.length;\n }\n */\n export function arrayUnshift<T>(arr: T[], value: T): number {\n arr.insertAt(0, value);\n return arr.length;\n }\n\n function swap<T>(arr: T[], i: number, j: number): void {\n let temp: T = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n }\n\n function sortHelper<T>(arr: T[], callbackfn?: (value1: T, value2: T) => number): T[] {\n if (arr.length <= 0 || !callbackfn) {\n return arr;\n }\n let len = arr.length;\n // simple selection sort.\n for (let i = 0; i < len - 1; ++i) {\n for (let j = i + 1; j < len; ++j) {\n if (callbackfn(arr[i], arr[j]) > 0) {\n swap(arr, i, j);\n }\n }\n }\n return arr;\n }\n\n export function arraySort<T>(arr: T[], callbackfn?: (value1: T, value2: T) => number): T[] {\n if (!callbackfn && arr.length > 1) {\n callbackfn = (a, b) => {\n // default is sort as if the element were a string, with null < undefined\n const aIsUndef = a === undefined;\n const bIsUndef = b === undefined;\n if (aIsUndef && bIsUndef) return 0;\n else if (aIsUndef) return 1;\n else if (bIsUndef) return -1;\n\n const aIsNull = a === null;\n const bIsNull = b === null;\n if (aIsNull && bIsNull) return 0;\n else if (aIsNull) return 1;\n else if (bIsNull) return -1;\n\n return (a + \"\").compare(b + \"\");\n }\n }\n return sortHelper(arr, callbackfn);\n }\n\n export function arrayMap<T, U>(arr: T[], callbackfn: (value: T, index: number) => U): U[] {\n let res: U[] = []\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n res.push(callbackfn(arr[i], i))\n }\n return res\n }\n\n export function arraySome<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): boolean {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i)\n if (callbackfn(arr[i], i))\n return true;\n return false;\n }\n\n export function arrayEvery<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): boolean {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i)\n if (!callbackfn(arr[i], i))\n return false;\n return true;\n }\n\n export function arrayForEach<T>(arr: T[], callbackfn: (value: T, index: number) => void): void {\n let len = arr.length // caching this seems to match V8\n for (let i = 0; i < len; ++i) {\n callbackfn(arr[i], i);\n }\n }\n\n export function arrayFilter<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): T[] {\n let res: T[] = []\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n let v = arr[i] // need to cache\n if (callbackfn(v, i)) res.push(v)\n }\n return res\n }\n\n export function arrayFind<T>(arr: T[], callbackfn: (value: T, index: number) => boolean): T {\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n let v = arr[i] // need to cache\n if (callbackfn(v, i)) return v;\n }\n return undefined;\n }\n\n export function arrayReduce<T, U>(arr: T[], callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U {\n let len = arr.length\n for (let i = 0; i < len; ++i) {\n initialValue = callbackfn(initialValue, arr[i], i)\n }\n return initialValue\n }\n\n export function arrayConcat<T>(arr: T[], otherArr: T[]): T[] {\n let out: T[] = [];\n for (let value of arr) {\n out.push(value);\n }\n for (let value of otherArr) {\n out.push(value);\n }\n return out;\n }\n\n export function arrayPickRandom<T>(arr: T[]): T {\n return arr[Math.randomRange(0, arr.length - 1)];\n }\n\n export function arraySlice<T>(arr: T[], start?: number, end?: number): T[] {\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice\n const res: T[] = [];\n const len = arr.length;\n\n if (start === undefined)\n start = 0;\n else if (start < 0)\n start = Math.max(len + start, 0);\n\n if (start > len)\n return res;\n\n if (end === undefined)\n end = len;\n else if (end < 0)\n end = len + end;\n\n if (end > len)\n end = len;\n\n for (let i = start; i < end; ++i) {\n res.push(arr[i]);\n }\n return res;\n }\n\n export function stringReplace(s: string, toReplace: string, replacer: string | ((sub: string) => string)) {\n toReplace = toReplace + \"\";\n const ind = s.indexOf(toReplace);\n if (ind == -1)\n return s;\n\n const begin = s.slice(0, ind);\n const end = s.slice(ind + toReplace.length);\n\n if (typeof replacer == \"string\" || !replacer) {\n return begin + replacer + end;\n } else {\n return begin + replacer(toReplace) + end;\n }\n }\n\n export function stringReplaceAll(s: string, toReplace: string, replacer: string | ((sub: string) => string)) {\n toReplace = toReplace + \"\";\n const split = s.split(toReplace);\n const empty = toReplace.isEmpty();\n\n let output = (empty ? applyReplace(toReplace, replacer) : \"\");\n\n if (split.length) {\n output += split[0];\n }\n\n for (let i = 1; i < split.length; ++i) {\n output += applyReplace(toReplace, replacer) + split[i];\n }\n\n if (!s.isEmpty() && empty) {\n output += applyReplace(toReplace, replacer);\n }\n\n return output;\n\n function applyReplace(r: string, replacer: string | ((sub: string) => string)): string {\n if (typeof replacer == \"string\" || !replacer) {\n return replacer as string;\n } else {\n return replacer(r);\n }\n }\n }\n\n //% shim=String_::substr\n declare function stringSubstrHelper(s: string, start: number, length?: number): string;\n\n export function stringSubstr(s: string, start: number, length?: number): string {\n length = length === undefined ? s.length : length || 0;\n return stringSubstrHelper(s, start, length);\n }\n\n export function stringSlice(s: string, start: number, end?: number): string {\n const len = s.length;\n\n if (start < 0) {\n start = Math.max(len + start, 0);\n }\n\n if (end === undefined) {\n end = len;\n } else if (end === null) {\n end = 0;\n }\n\n if (end < 0) {\n end = len + end;\n }\n\n return stringSubstrHelper(s, start, end - start);\n }\n\n // also note this doesn't handle unicode, but neither does JS (there's toLocaleUpperCase())\n export function stringToUpperCase(s: string): string {\n let r = \"\"\n let prev = 0\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n if (97 <= c && c <= 122) {\n r += s.slice(prev, i) + String.fromCharCode(c - 32)\n prev = i + 1\n }\n }\n r += s.slice(prev)\n return r\n }\n\n // also note this doesn't handle unicode, but neither does JS (there's toLocaleLowerCase())\n export function stringToLowerCase(s: string): string {\n let r = \"\"\n let prev = 0\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n if (65 <= c && c <= 90) {\n r += s.slice(prev, i) + String.fromCharCode(c + 32)\n prev = i + 1\n }\n }\n r += s.slice(prev)\n return r\n }\n\n export function stringSplit(S: string, separator?: string, limit?: number): string[] {\n // https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.split\n const A: string[] = [];\n let lim = 0;\n if (limit === undefined)\n lim = (1 << 29) - 1; // spec says 1 << 53, leaving it at 29 for constant folding\n else if (limit < 0)\n lim = 0;\n else\n lim = limit | 0;\n const s = S.length;\n let p = 0;\n const R = separator;\n if (lim == 0)\n return A;\n if (separator === undefined) {\n A[0] = S;\n return A;\n }\n if (s == 0) {\n let z = splitMatch(S, 0, R);\n if (z > -1) return A;\n A[0] = S;\n return A;\n }\n let T: string;\n let q = p;\n while (q != s) {\n let e = splitMatch(S, q, R);\n if (e < 0) q++;\n else {\n if (e == p) q++;\n else {\n T = stringSlice(S, p, q);\n A.push(T);\n if (A.length == lim) return A;\n p = e;\n q = p;\n }\n }\n }\n T = stringSlice(S, p, q);\n A.push(T);\n return A;\n }\n\n function splitMatch(S: string, q: number, R: string): number {\n const r = R.length;\n const s = S.length;\n if (q + r > s) return -1;\n for (let i = 0; i < r; ++i) {\n if (S[q + i] != R[i])\n return -1;\n }\n return q + r;\n }\n\n export function stringTrim(s: string): string {\n let start = 0;\n let end = s.length - 1;\n\n while (start <= end && isWhitespace(s.charCodeAt(start)))\n ++start;\n\n while (end > start && isWhitespace(s.charCodeAt(end)))\n --end;\n return s.slice(start, end + 1);\n }\n\n export function isWhitespace(c: number): boolean {\n // https://www.ecma-international.org/ecma-262/6.0/#sec-white-space\n switch (c) {\n case 0x0009: // character tab\n case 0x000B: // line tab\n case 0x000C: // form feed\n case 0x0020: // space\n case 0x00A0: // no-break space\n case 0xFEFF: // zero width no break space\n case 0x000A: // line feed\n case 0x000D: // carriage return\n case 0x2028: // line separator\n case 0x2029: // paragraph separator\n return true;\n default:\n return false;\n }\n }\n\n export function stringEmpty(S: string): boolean {\n return !S;\n }\n}\n\nnamespace Math {\n export function clamp(min: number, max: number, value: number): number {\n return Math.min(max, Math.max(min, value));\n }\n\n /**\n * Returns the absolute value of a number (the value without regard to whether it is positive or negative).\n * For example, the absolute value of -5 is the same as the absolute value of 5.\n * @param x A numeric expression for which the absolute value is needed.\n */\n //% blockId=math_op3\n //% help=math/abs\n export function abs(x: number): number {\n return x < 0 ? -x : x;\n }\n\n /**\n * Returns the sign of the x, indicating whether x is positive, negative or zero.\n * @param x The numeric expression to test\n */\n export function sign(x: number): number {\n if (x == 0) return 0;\n if (x > 0) return 1;\n return -1;\n }\n\n /**\n * Returns the larger of two supplied numeric expressions.\n */\n //% blockId=math_op2\n //% help=math/max\n export function max(a: number, b: number): number {\n if (a >= b) return a;\n return b;\n }\n\n /**\n * Returns the smaller of two supplied numeric expressions.\n */\n //% blockId=math_op2\n //% help=math/min\n export function min(a: number, b: number): number {\n if (a <= b) return a;\n return b;\n }\n\n /**\n * Rounds ``x`` to a number with the given number of ``digits``\n * @param x the number to round\n * @param digits the number of resulting digits\n */\n //%\n export function roundWithPrecision(x: number, digits: number): number {\n digits = digits | 0;\n // invalid digits input\n if (digits <= 0) return Math.round(x);\n if (x == 0) return 0;\n let r = 0;\n do {\n const d = Math.pow(10, digits);\n r = Math.round(x * d) / d;\n digits++;\n } while (r == 0 && digits < 21);\n return r;\n }\n}\n\n\n//% blockHidden=1\nnamespace __internal {\n /**\n * A shim to render a boolean as a down/up toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleDownUp block=\"%down\"\n //% down.fieldEditor=toggledownup\n //% down.fieldOptions.decompileLiterals=true\n export function __downUp(down: boolean): boolean {\n return down;\n }\n\n /**\n * A shim to render a boolean as a up/down toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleUpDown block=\"%up\"\n //% up.fieldEditor=toggleupdown\n //% up.fieldOptions.decompileLiterals=true\n export function __upDown(up: boolean): boolean {\n return up;\n }\n\n /**\n * A shim to render a boolean as a high/low toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleHighLow block=\"%high\"\n //% high.fieldEditor=togglehighlow\n //% high.fieldOptions.decompileLiterals=true\n export function __highLow(high: boolean): boolean {\n return high;\n }\n\n /**\n * A shim to render a boolean as a on/off toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleOnOff block=\"%on\"\n //% on.fieldEditor=toggleonoff\n //% on.fieldOptions.decompileLiterals=true\n export function __onOff(on: boolean): boolean {\n return on;\n }\n\n /**\n * A shim to render a boolean as a yes/no toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleYesNo block=\"%yes\"\n //% yes.fieldEditor=toggleyesno\n //% yes.fieldOptions.decompileLiterals=true\n export function __yesNo(yes: boolean): boolean {\n return yes;\n }\n\n /**\n * A shim to render a boolean as a win/lose toggle\n */\n //% shim=TD_ID blockHidden=1\n //% blockId=toggleWinLose block=\"%win\"\n //% win.fieldEditor=togglewinlose\n //% win.fieldOptions.decompileLiterals=true\n export function __winLose(win: boolean): boolean {\n return win;\n }\n\n /**\n * Get the color wheel field editor\n * @param color color\n */\n //% blockId=colorNumberPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colornumber\" value.fieldOptions.decompileLiterals=true\n //% value.defl='0xff0000'\n //% value.fieldOptions.colours='[\"#ff0000\",\"#ff8000\",\"#ffff00\",\"#ff9da5\",\"#00ff00\",\"#b09eff\",\"#00ffff\",\"#007fff\",\"#65471f\",\"#0000ff\",\"#7f00ff\",\"#ff0080\",\"#ff00ff\",\"#ffffff\",\"#999999\",\"#000000\"]'\n //% value.fieldOptions.columns=4 value.fieldOptions.className='rgbColorPicker'\n export function __colorNumberPicker(value: number) {\n return value;\n }\n\n /**\n * Get the color wheel field editor\n * @param value value between 0 to 255 to get a color value, eg: 10\n */\n //% blockId=colorWheelPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colorwheel\" value.fieldOptions.decompileLiterals=true\n //% value.fieldOptions.sliderWidth='200'\n //% value.fieldOptions.min=0 value.fieldOptions.max=255\n export function __colorWheelPicker(value: number) {\n return value;\n }\n\n /**\n * Get the color wheel field editor using HSV values\n * @param value value between 0 to 255 to get a color value, eg: 10\n */\n //% blockId=colorWheelHsvPicker block=\"%value\"\n //% blockHidden=true\n //% shim=TD_ID colorSecondary=\"#FFFFFF\"\n //% value.fieldEditor=\"colorwheel\" value.fieldOptions.decompileLiterals=true\n //% value.fieldOptions.sliderWidth='200'\n //% value.fieldOptions.min=0 value.fieldOptions.max=255\n //% value.fieldOptions.channel=hsvfast\n export function __colorWheelHsvPicker(value: number) {\n return value;\n }\n\n /**\n * A speed picker\n * @param speed the speed, eg: 50\n */\n //% blockId=speedPicker block=\"%speed\" shim=TD_ID\n //% speed.fieldEditor=\"speed\" colorSecondary=\"#FFFFFF\"\n //% weight=0 blockHidden=1 speed.fieldOptions.decompileLiterals=1\n export function __speedPicker(speed: number): number {\n return speed;\n }\n\n /**\n * A turn ratio picker\n * @param turnratio the turn ratio, eg: 0\n */\n //% blockId=turnRatioPicker block=\"%turnratio\" shim=TD_ID\n //% turnratio.fieldEditor=\"turnratio\" colorSecondary=\"#FFFFFF\"\n //% weight=0 blockHidden=1 turnRatio.fieldOptions.decompileLiterals=1\n export function __turnRatioPicker(turnratio: number): number {\n return turnratio;\n }\n\n /**\n * A field editor that displays a protractor\n */\n //% blockId=protractorPicker block=\"%angle\"\n //% shim=TD_ID\n //% angle.fieldEditor=protractor\n //% angle.fieldOptions.decompileLiterals=1\n //% colorSecondary=\"#FFFFFF\"\n //% blockHidden=1\n export function __protractor(angle: number) {\n return angle;\n }\n\n /**\n * Get the time field editor\n * @param ms time duration in milliseconds, eg: 500, 1000\n */\n //% blockId=timePicker block=\"%ms\"\n //% blockHidden=true shim=TD_ID\n //% colorSecondary=\"#FFFFFF\"\n //% ms.fieldEditor=\"numberdropdown\" ms.fieldOptions.decompileLiterals=true\n //% ms.fieldOptions.data='[[\"100 ms\", 100], [\"200 ms\", 200], [\"500 ms\", 500], [\"1 second\", 1000], [\"2 seconds\", 2000], [\"5 seconds\", 5000]]'\n export function __timePicker(ms: number): number {\n return ms;\n }\n}\n",
4
4
  "pxt-python-helpers.ts": "namespace _py {\n export const ATTRIBUTE_ERROR: string = \"AttributeError\";\n export const INDEX_ERROR: string = \"IndexError\";\n export const VALUE_ERROR: string = \"ValueError\";\n export const TYPE_ERROR: string = \"TypeError\";\n\n export function py_string_capitalize(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_casefold(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_center(str: string, width: number, fillChar?: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_count(str: string, sub: string, start?: number, end?: number): number {\n nullCheck(str);\n return 0;\n }\n\n export function py_string_endswith(str: string, suffix: string, start?: number, end?: number): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_find(str: string, sub: string, start?: number, end?: number): number {\n nullCheck(str);\n return 0;\n }\n\n export function py_string_index(str: string, sub: string, start?: number, end?: number): number {\n nullCheck(str);\n return 0;\n }\n\n export function py_string_isalnum(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isalpha(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isascii(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isdigit(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isnumeric(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isspace(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isdecimal(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isidentifier(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_islower(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isprintable(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_istitle(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_isupper(str: string): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_join(str: string, iterable: any[]): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_ljust(str: string, width: number, fillChar?: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_lower(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_rfind(str: string, sub: string, start?: number, end?: number): number {\n nullCheck(str);\n return 0;\n }\n\n export function py_string_rindex(str: string, sub: string, start?: number, end?: number): number {\n nullCheck(str);\n return 0;\n }\n\n export function py_string_rjust(str: string, width: number, fillChar?: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_rsplit(str: string, sep?: string, maxsplit?: number): string[] {\n nullCheck(str);\n\n if (sep === \"\") {\n throw VALUE_ERROR;\n }\n\n if (maxsplit === 0) return [str]\n if (!maxsplit || maxsplit < 0) maxsplit = str.length;\n\n const out: string[] = [];\n\n let currentChar: string;\n let splitEnd: number;\n let previousSplit = str.length;\n\n if (!sep) {\n for (let i = str.length - 1; i >= 0; i--) {\n currentChar = str.charAt(i);\n if (isWhitespace(currentChar)) {\n if (splitEnd === undefined) splitEnd = i;\n }\n else if (splitEnd !== undefined) {\n if (previousSplit !== splitEnd + 1) out.push(str.substr(splitEnd + 1, previousSplit - (splitEnd + 1)));\n previousSplit = i + 1;\n splitEnd = undefined;\n\n }\n\n if (out.length === maxsplit) break;\n }\n\n if (out.length < maxsplit + 1) {\n if (splitEnd !== undefined) {\n if (previousSplit !== splitEnd + 1)\n out.push(str.substr(splitEnd + 1, previousSplit - (splitEnd + 1)));\n }\n else {\n out.push(str.substr(0, previousSplit))\n }\n }\n }\n else {\n let separatorIndex = 0;\n for (let i = str.length; i >= 0; i--) {\n currentChar = str.charAt(i);\n if (currentChar === sep.charAt(sep.length - separatorIndex - 1)) {\n separatorIndex++;\n if (splitEnd === undefined) splitEnd = i;\n }\n else {\n separatorIndex = 0;\n splitEnd = undefined;\n }\n\n if (separatorIndex === sep.length) {\n out.push(str.substr(splitEnd + 1, previousSplit - (splitEnd + 1)));\n previousSplit = i;\n separatorIndex = 0;\n splitEnd = undefined;\n }\n\n if (out.length === maxsplit) break;\n }\n\n if (out.length < maxsplit + 1) {\n out.push(str.substr(0, previousSplit))\n }\n }\n\n out.reverse();\n return out;\n }\n\n export function py_string_split(str: string, sep?: string, maxsplit?: number): string[] {\n nullCheck(str);\n\n if (sep === \"\") {\n throw VALUE_ERROR;\n }\n\n if (maxsplit === 0) return [str]\n if (!maxsplit || maxsplit < 0) maxsplit = str.length;\n\n const out: string[] = [];\n\n let currentChar: string;\n let splitStart: number;\n let previousSplit = 0;\n\n if (!sep) {\n for (let i = 0; i < str.length; i++) {\n currentChar = str.charAt(i);\n if (isWhitespace(currentChar)) {\n if (splitStart === undefined) splitStart = i;\n }\n else if (splitStart !== undefined) {\n if (previousSplit !== splitStart) out.push(str.substr(previousSplit, splitStart - previousSplit));\n previousSplit = i;\n splitStart = undefined;\n\n }\n\n if (out.length === maxsplit) break;\n }\n\n if (out.length < maxsplit + 1) {\n if (splitStart !== undefined) {\n if (previousSplit !== splitStart)\n out.push(str.substr(previousSplit, splitStart - previousSplit));\n }\n else {\n out.push(str.substr(previousSplit))\n }\n }\n }\n else {\n let separatorIndex = 0;\n for (let i = 0; i < str.length; i++) {\n currentChar = str.charAt(i);\n if (currentChar === sep.charAt(separatorIndex)) {\n separatorIndex++;\n if (splitStart === undefined) splitStart = i;\n }\n else {\n separatorIndex = 0;\n splitStart = undefined;\n }\n\n if (separatorIndex === sep.length) {\n out.push(str.substr(previousSplit, splitStart - previousSplit));\n previousSplit = i + 1;\n separatorIndex = 0;\n splitStart = undefined;\n }\n\n if (out.length === maxsplit) break;\n }\n\n if (out.length < maxsplit + 1) {\n out.push(str.substr(previousSplit))\n }\n }\n\n return out;\n }\n\n\n function isWhitespace(char: string) {\n // TODO Figure out everything python considers whitespace.\n // the \\s character class in JS regexes also includes these: \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff\n return char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\v\" || char === \"\\r\" || char === \"\\f\";\n }\n\n export function py_string_splitlines(str: string, keepends?: boolean): string[] {\n nullCheck(str);\n return [];\n }\n\n export function py_string_startswith(str: string, prefix: string, start?: number, end?: number): boolean {\n nullCheck(str);\n return false;\n }\n\n export function py_string_rstrip(str: string, chars?: string): string {\n nullCheck(str);\n\n for (let i = str.length - 1; i >= 0; i--) {\n if (chars != undefined) {\n if (chars.indexOf(str.charAt(i)) === -1) {\n return str.substr(0, i + 1);\n }\n }\n else if (!isWhitespace(str.charAt(i))) {\n return str.substr(0, i + 1);\n }\n }\n\n return \"\";\n }\n\n export function py_string_lstrip(str: string, chars?: string): string {\n nullCheck(str);\n\n for (let i = 0; i < str.length; i++) {\n if (chars != undefined) {\n if (chars.indexOf(str.charAt(i)) === -1) {\n return str.substr(i);\n }\n }\n else if (!isWhitespace(str.charAt(i))) {\n return str.substr(i);\n }\n }\n\n return \"\";\n }\n\n export function py_string_strip(str: string, chars?: string): string {\n return py_string_rstrip(py_string_lstrip(str, chars), chars);\n }\n\n export function py_string_swapcase(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_title(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_upper(str: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_zfill(str: string, width: number): string {\n nullCheck(str);\n return str;\n }\n\n export function py_array_pop(arr: any[], index?: number): any {\n nullCheck(arr);\n\n if (arr.length === 0) {\n throw INDEX_ERROR;\n }\n\n if (index == undefined) {\n return arr.pop();\n }\n else if (index > 0 && index < arr.length) {\n return arr.removeAt(index | 0);\n }\n\n throw INDEX_ERROR;\n }\n\n export function py_array_clear(arr: any[]): void {\n nullCheck(arr);\n\n arr.length = 0;\n }\n\n export function py_array_index(arr: any[], value: any, start?: number, end?: number): number {\n nullCheck(arr);\n\n start = fixIndex(arr, start);\n end = fixIndex(arr, end);\n\n if (start == null) {\n start = 0;\n }\n\n if (end == null) {\n // end is exclusive\n end = arr.length;\n }\n\n for (let i = start; i < end; i++) {\n if (arr[i] === value) {\n return i;\n }\n }\n\n throw VALUE_ERROR;\n }\n\n export function py_array_count(arr: any[], value: any): number {\n nullCheck(arr);\n\n let count = 0;\n\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] === value) count++;\n }\n\n return count;\n }\n\n function nullCheck(arg: any) {\n if (arg == null) {\n throw ATTRIBUTE_ERROR;\n }\n }\n\n function fixIndex(arr: any[], index: number) {\n if (index != null && arr.length) {\n index = index | 0;\n while (index < 0) index += arr.length;\n }\n return index;\n }\n\n /**\n * Returns a sequence of numbers up to but not including the limit\n * @param first The value to end the sequence before. This value will not show up in the result.\n * If more than one argument is passed, this argument is instead used for the first value in the range\n * @param stop The value to end the sequence before. This value will not show up in the result\n * @param step The value to increase or decrease by for each step in the range. Must be a nonzero integer\n */\n export function range(first: number, stop?: number, step?: number) {\n if (step === undefined) step = 1\n // step must be a nonzero integer (can be negative)\n if (step === 0 || (step | 0) !== step) {\n throw VALUE_ERROR;\n }\n\n // If only one argument is given, then start is actually stop\n if (stop === undefined) {\n stop = first;\n first = 0;\n }\n\n const res: number[] = [];\n if (step > 0 && first >= stop || step < 0 && first <= stop) return res;\n\n let index = first;\n\n while (step < 0 ? index > stop : index < stop) {\n res.push(index);\n index += step\n }\n\n return res;\n }\n\n function sliceRange(valueLength: number, start?: number, stop?: number, step?: number) {\n if (step == null) step = 1\n\n // step must be a nonzero integer (can be negative)\n if (step === 0 || (step | 0) !== step) {\n throw _py.VALUE_ERROR;\n }\n\n if (step < 0) {\n if (start == null) {\n start = valueLength - 1;\n }\n if (stop == null) {\n stop = -1;\n }\n }\n else {\n if (start == null) {\n start = 0;\n }\n if (stop == null) {\n stop = valueLength;\n }\n }\n\n return range(start, stop, step)\n }\n\n /**\n * Returns a section of an array according to python's extended slice syntax\n */\n export function slice<U>(value: U[], start?: number, stop?: number, step?: number): U[] {\n if (value == null) {\n throw TYPE_ERROR;\n }\n return sliceRange(value.length, start, stop, step).map(index => value[index]);\n }\n\n /**\n * Returns a section of a string according to python's extended slice syntax\n */\n export function stringSlice(value: string, start?: number, stop?: number, step?: number): string {\n if (value == null) {\n throw TYPE_ERROR;\n }\n return sliceRange(value.length, start, stop, step).map(index => value.charAt(index)).join(\"\");\n }\n}",
5
5
  "pxt-python.d.ts": "/// <reference no-default-lib=\"true\"/>\n\ndeclare namespace _py {\n interface Array {\n //% py2tsOverride=\"push($0)\"\n append(value: any): void;\n\n //% py2tsOverride=\"concat($0)\"\n extend(other: Array): void;\n\n //% py2tsOverride=\"insertAt($0, $1)\"\n insert(index: number, value: any): void;\n\n //% py2tsOverride=\"removeElement($0)\"\n remove(value: any): void;\n\n //% py2tsOverride=\"sort($0?)\"\n sort(sorter?: (a: any, b: any) => number): void;\n\n //% py2tsOverride=\"reverse()\"\n reverse(): void;\n\n //% py2tsOverride=\"slice()\"\n copy(): void;\n\n //% pyHelper=\"py_array_pop\"\n pop(index?: number): any;\n\n //% pyHelper=\"py_array_clear\"\n clear(): void;\n\n //% pyHelper=\"py_array_index\"\n index(value: any, start?: number, end?: number): number;\n\n //% pyHelper=\"py_array_count\"\n count(value: any): number;\n }\n\n interface String {\n //% pyHelper=\"py_string_capitalize\"\n capitalize(): string;\n\n //% pyHelper=\"py_string_casefold\"\n casefold(): string;\n\n //% pyHelper=\"py_string_center\"\n center(width: number, fillChar?: string): string;\n\n //% pyHelper=\"py_string_count\"\n count(sub: string, start?: number, end?: number): number;\n\n //% pyHelper=\"py_string_endswith\"\n endswith(suffix: string, start?: number, end?: number): boolean;\n\n //% pyHelper=\"py_string_find\"\n find(sub: string, start?: number, end?: number): number;\n\n //% pyHelper=\"py_string_index\"\n index(sub: string, start?: number, end?: number): number;\n\n //% pyHelper=\"py_string_isalnum\"\n isalnum(): boolean;\n\n //% pyHelper=\"py_string_isalpha\"\n isalpha(): boolean;\n\n //% pyHelper=\"py_string_isascii\"\n isascii(): boolean;\n\n //% pyHelper=\"py_string_isdigit\"\n isdigit(): boolean;\n\n //% pyHelper=\"py_string_isnumeric\"\n isnumeric(): boolean;\n\n //% pyHelper=\"py_string_isspace\"\n isspace(): boolean;\n\n //% pyHelper=\"py_string_isdecimal\"\n isdecimal(): boolean;\n\n //% pyHelper=\"py_string_isidentifier\"\n isidentifier(): boolean;\n\n //% pyHelper=\"py_string_islower\"\n islower(): boolean;\n\n //% pyHelper=\"py_string_isprintable\"\n isprintable(): boolean;\n\n //% pyHelper=\"py_string_istitle\"\n istitle(): boolean;\n\n //% pyHelper=\"py_string_isupper\"\n isupper(): boolean;\n\n //% pyHelper=\"py_string_join\"\n join(iterable: any[]): string;\n\n //% pyHelper=\"py_string_ljust\"\n ljust(width: number, fillChar?: string): string;\n\n //% pyHelper=\"py_string_lower\"\n lower(): string;\n\n //% pyHelper=\"py_string_lstrip\"\n lstrip(chars?: string): string;\n\n //% py2tsOverride=\"replace($0, $1)\"\n replace(oldString: string, newString: string): string;\n\n //% pyHelper=\"py_string_rfind\"\n rfind(sub: string, start?: number, end?: number): number;\n\n //% pyHelper=\"py_string_rindex\"\n rindex(sub: string, start?: number, end?: number): number;\n\n //% pyHelper=\"py_string_rjust\"\n rjust(width: number, fillChar?: string): string;\n\n //% pyHelper=\"py_string_rsplit\"\n rsplit(sep?: string, maxSplit?: number): string[];\n\n //% pyHelper=\"py_string_rstrip\"\n rstrip(chars?: string): string;\n\n //% pyHelper=\"py_string_split\"\n split(sep?: string, maxsplit?: number): string[];\n\n //% pyHelper=\"py_string_splitlines\"\n splitlines(keepends?: boolean): string[];\n\n //% pyHelper=\"py_string_startswith\"\n startswith(prefix: string, start?: number, end?: number): boolean;\n\n //% pyHelper=\"py_string_strip\"\n strip(chars?: string): string;\n\n //% pyHelper=\"py_string_swapcase\"\n swapcase(): string;\n\n //% pyHelper=\"py_string_title\"\n title(): string;\n\n //% pyHelper=\"py_string_upper\"\n upper(): string;\n\n //% pyHelper=\"py_string_zfill\"\n zfill(width: number): string;\n }\n\n interface Dict {\n clear(): void;\n copy(): void;\n get(key: string, defaultValue?: any): any;\n // items(): [string, any][];\n keys(): string[];\n pop(key: string, defaultValue?: any): any;\n // popitem(): [string, any];\n setdefault(key: string, defaultValue?: any): any;\n update(other: Dict): void;\n values(): any[];\n }\n\n interface Set {\n isdisjoint(other: Set): boolean;\n issubset(other: Set): boolean;\n issuperset(other: Set): boolean;\n union(other: Set): Set;\n intersection(other: Set): Set;\n difference(other: Set): Set;\n symmetric_difference(other: Set): Set;\n copy(): Set;\n update(other: Set): void;\n intersection_update(other: Set): void;\n difference_update(other: Set): void;\n symmetric_difference_update(other: Set): void;\n add(elem: any): void;\n remove(elem: any): void;\n discard(elem: any): void;\n pop(): any;\n clear(): void;\n }\n}"
6
6
  }
package/built/pxt.js CHANGED
@@ -101796,7 +101796,7 @@ var pxt;
101796
101796
  MATH_ADDITION_SYMBOL: pxt.Util.lf("{id:op}+"),
101797
101797
  MATH_SUBTRACTION_SYMBOL: pxt.Util.lf("{id:op}-"),
101798
101798
  MATH_MULTIPLICATION_SYMBOL: pxt.Util.lf("{id:op}×"),
101799
- MATH_DIVISION_SYMBOL: pxt.Util.lf("{id:op}÷"),
101799
+ MATH_DIVISION_SYMBOL: pxt.Util.lf("{id:op}/"),
101800
101800
  MATH_POWER_SYMBOL: pxt.Util.lf("{id:op}**")
101801
101801
  }
101802
101802
  },
@@ -101806,7 +101806,7 @@ var pxt;
101806
101806
  url: '/blocks/math',
101807
101807
  category: 'math',
101808
101808
  block: {
101809
- MATH_MODULO_TITLE: pxt.Util.lf("remainder of %1 ÷ %2")
101809
+ MATH_MODULO_TITLE: pxt.Util.lf("remainder of %1 / %2")
101810
101810
  }
101811
101811
  },
101812
101812
  'math_js_op': {
@@ -101835,7 +101835,7 @@ var pxt;
101835
101835
  "acos": pxt.Util.lf("{id:op}acos"),
101836
101836
  "tan": pxt.Util.lf("{id:op}tan"),
101837
101837
  "atan2": pxt.Util.lf("{id:op}atan2"),
101838
- "idiv": pxt.Util.lf("{id:op}integer ÷"),
101838
+ "idiv": pxt.Util.lf("{id:op}integer /"),
101839
101839
  "imul": pxt.Util.lf("{id:op}integer ×"),
101840
101840
  }
101841
101841
  },
@@ -117004,6 +117004,7 @@ var pxt;
117004
117004
  diffify(steps, activities);
117005
117005
  }
117006
117006
  const assetFiles = parseAssetJson(assetJson);
117007
+ const globalBlockConfig = parseTutorialBlockConfig("global", tutorialmd);
117007
117008
  // strip hidden snippets
117008
117009
  steps.forEach(step => {
117009
117010
  step.contentMd = stripHiddenSnippets(step.contentMd);
@@ -117023,7 +117024,8 @@ var pxt;
117023
117024
  jres,
117024
117025
  assetFiles,
117025
117026
  customTs,
117026
- tutorialValidationRules
117027
+ tutorialValidationRules,
117028
+ globalBlockConfig
117027
117029
  };
117028
117030
  }
117029
117031
  tutorial.parseTutorial = parseTutorial;
@@ -117050,6 +117052,8 @@ var pxt;
117050
117052
  switch (m1) {
117051
117053
  case "block":
117052
117054
  case "blocks":
117055
+ case "blockconfig.local":
117056
+ case "blockconfig.global":
117053
117057
  case "requiredTutorialBlock":
117054
117058
  case "filterblocks":
117055
117059
  if (!checkTutorialEditor(pxt.BLOCKS_PROJECT_NAME))
@@ -117202,6 +117206,7 @@ ${code}
117202
117206
  markdown.replace(stepRegex, function (match, flags, step) {
117203
117207
  step = step.trim();
117204
117208
  let { header, hint, requiredBlocks } = parseTutorialHint(step, metadata && metadata.explicitHints, metadata.tutorialCodeValidation);
117209
+ const blockConfig = parseTutorialBlockConfig("local", step);
117205
117210
  // if title is not hidden ("{TITLE HERE}"), strip flags
117206
117211
  const title = !flags.match(/^\{.*\}$/)
117207
117212
  ? flags.replace(/@(fullscreen|unplugged|showdialog|showhint|tutorialCompleted|resetDiff)/gi, "").trim()
@@ -117209,7 +117214,8 @@ ${code}
117209
117214
  let info = {
117210
117215
  title,
117211
117216
  contentMd: step,
117212
- headerContentMd: header
117217
+ headerContentMd: header,
117218
+ localBlockConfig: blockConfig
117213
117219
  };
117214
117220
  if (/@(fullscreen|unplugged|showdialog|showhint)/i.test(flags))
117215
117221
  info.showHint = true;
@@ -117264,6 +117270,18 @@ ${code}
117264
117270
  }
117265
117271
  return { header, hint, requiredBlocks };
117266
117272
  }
117273
+ function parseTutorialBlockConfig(scope, content) {
117274
+ let blockConfig = {
117275
+ md: "",
117276
+ blocks: [],
117277
+ };
117278
+ const regex = new RegExp(`\`\`\`\\s*blockconfig\\.${scope}\\s*\\n([\\s\\S]*?)\\n\`\`\``, "gmi");
117279
+ content.replace(regex, (m0, m1) => {
117280
+ blockConfig.md += `${m1}\n`;
117281
+ return "";
117282
+ });
117283
+ return blockConfig;
117284
+ }
117267
117285
  function categorizingValidationRules(listOfRules, title) {
117268
117286
  const ruleNames = Object.keys(listOfRules);
117269
117287
  for (let i = 0; i < ruleNames.length; i++) {
@@ -117279,7 +117297,7 @@ ${code}
117279
117297
  function stripHiddenSnippets(str) {
117280
117298
  if (!str)
117281
117299
  return str;
117282
- const hiddenSnippetRegex = /```(filterblocks|package|ghost|config|template|jres|assetjson|customts)\s*\n([\s\S]*?)\n```/gmi;
117300
+ const hiddenSnippetRegex = /```(filterblocks|package|ghost|config|template|jres|assetjson|customts|blockconfig\.local|blockconfig\.global)\s*\n([\s\S]*?)\n```/gmi;
117283
117301
  return str.replace(hiddenSnippetRegex, '').trim();
117284
117302
  }
117285
117303
  /*
@@ -117361,7 +117379,8 @@ ${code}
117361
117379
  jres: tutorialInfo.jres,
117362
117380
  assetFiles: tutorialInfo.assetFiles,
117363
117381
  customTs: tutorialInfo.customTs,
117364
- tutorialValidationRules: tutorialInfo.tutorialValidationRules
117382
+ tutorialValidationRules: tutorialInfo.tutorialValidationRules,
117383
+ globalBlockConfig: tutorialInfo.globalBlockConfig
117365
117384
  };
117366
117385
  return { options: tutorialOptions, editor: tutorialInfo.editor };
117367
117386
  }
@@ -127519,7 +127538,7 @@ ${output}</xml>`;
127519
127538
  if (alias) {
127520
127539
  info.decompilerBlockAlias = env.aliasBlocks[info.qName];
127521
127540
  }
127522
- else {
127541
+ else if (!env.opts.snippetMode) {
127523
127542
  return pxtc.Util.lf("No output expressions as statements");
127524
127543
  }
127525
127544
  }
@@ -135078,7 +135097,7 @@ var ts;
135078
135097
  snippetMode: opts.snippetMode || false,
135079
135098
  alwaysEmitOnStart: opts.alwaysDecompileOnStart,
135080
135099
  includeGreyBlockMessages,
135081
- generateSourceMap: !!opts.ast,
135100
+ generateSourceMap: opts.generateSourceMap !== undefined ? opts.generateSourceMap : !!opts.ast,
135082
135101
  allowedArgumentTypes: opts.allowedArgumentTypes || ["number", "boolean", "string"],
135083
135102
  errorOnGreyBlocks: !!opts.errorOnGreyBlocks
135084
135103
  };
@@ -135096,7 +135115,7 @@ var ts;
135096
135115
  snippetMode: opts.snippetMode || false,
135097
135116
  alwaysEmitOnStart: opts.alwaysDecompileOnStart,
135098
135117
  includeGreyBlockMessages,
135099
- generateSourceMap: !!opts.ast,
135118
+ generateSourceMap: opts.generateSourceMap !== undefined ? opts.generateSourceMap : !!opts.ast,
135100
135119
  allowedArgumentTypes: opts.allowedArgumentTypes || ["number", "boolean", "string"],
135101
135120
  errorOnGreyBlocks: !!opts.errorOnGreyBlocks
135102
135121
  };
@@ -140897,6 +140916,13 @@ var pxt;
140897
140916
  error(a, 9503, pxt.U.lf("No module named '{0}'", name));
140898
140917
  return sym;
140899
140918
  }
140919
+ function getHelperVariableName() {
140920
+ const scope = currentScope();
140921
+ if (scope.nextHelperVariableId === undefined) {
140922
+ scope.nextHelperVariableId = 0;
140923
+ }
140924
+ return "___tempvar" + scope.nextHelperVariableId++;
140925
+ }
140900
140926
  function defvar(name, opts, modifier, scope) {
140901
140927
  if (!scope)
140902
140928
  scope = currentScope();
@@ -142203,20 +142229,7 @@ var pxt;
142203
142229
  return B.mkStmt(B.mkText(pref), B.mkInfix(expr(target), "=", expr(value)));
142204
142230
  }
142205
142231
  if (!pref && target.kind == "Tuple") {
142206
- let tup = target;
142207
- let targs = [B.mkText("let "), B.mkText("[")];
142208
- let nonNames = tup.elts.filter(e => e.kind !== "Name");
142209
- if (nonNames.length) {
142210
- error(n, 9556, pxt.U.lf("non-trivial tuple assignment unsupported"));
142211
- return stmtTODO(n);
142212
- }
142213
- let tupNames = tup.elts
142214
- .map(e => e)
142215
- .map(convertName);
142216
- targs.push(B.mkCommaSep(tupNames));
142217
- targs.push(B.mkText("]"));
142218
- let res = B.mkStmt(B.mkInfix(B.mkGroup(targs), "=", expr(value)));
142219
- return res;
142232
+ return convertDestructuring(n, target, value);
142220
142233
  }
142221
142234
  if (target.kind === "Name") {
142222
142235
  const scopeSym = currentScope().vars[nm];
@@ -142244,12 +142257,41 @@ var pxt;
142244
142257
  if (!lExp)
142245
142258
  lExp = expr(target);
142246
142259
  return B.mkStmt(B.mkText(pref), B.mkInfix(lExp, "=", expr(value)));
142247
- function convertName(n) {
142260
+ }
142261
+ function convertDestructuring(parent, targets, value) {
142262
+ let nonNames = targets.elts.filter(e => e.kind !== "Name");
142263
+ if (nonNames.length) {
142264
+ error(parent, 9556, pxt.U.lf("non-trivial tuple assignment unsupported"));
142265
+ return stmtTODO(parent);
142266
+ }
142267
+ const names = targets.elts.map(tryGetName);
142268
+ const symbols = names
142269
+ .map(nm => nm ? currentScope().vars[nm] : undefined);
142270
+ if (symbols.some(s => (s === null || s === void 0 ? void 0 : s.modifier) !== undefined)) {
142271
+ const helperVar = getHelperVariableName();
142272
+ const valueAssign = B.mkStmt(B.mkInfix(B.mkGroup([B.mkText("let "), B.mkText(helperVar)]), "=", expr(value)));
142273
+ const assignStatements = [valueAssign];
142274
+ for (let i = 0; i < symbols.length; i++) {
142275
+ const name = convertName(targets.elts[i]);
142276
+ assignStatements.push(B.mkStmt(B.mkInfix(name, "=", B.mkGroup([B.mkText(helperVar), B.mkText(`[${i}]`)]))));
142277
+ }
142278
+ return B.mkGroup(assignStatements);
142279
+ }
142280
+ else {
142281
+ let targs = [B.mkText("let "), B.mkText("[")];
142282
+ let tupNames = targets.elts
142283
+ .map(e => e)
142284
+ .map(e => convertName(e, true));
142285
+ targs.push(B.mkCommaSep(tupNames));
142286
+ targs.push(B.mkText("]"));
142287
+ return B.mkStmt(B.mkInfix(B.mkGroup(targs), "=", expr(value)));
142288
+ }
142289
+ function convertName(n, excludeLet = false) {
142248
142290
  // TODO resuse with Name expr
142249
142291
  markInfoNode(n, "identifierCompletion");
142250
142292
  typeOf(n);
142251
142293
  let v = lookupName(n);
142252
- return possibleDef(n, /*excludeLet*/ true);
142294
+ return possibleDef(n, excludeLet);
142253
142295
  }
142254
142296
  }
142255
142297
  function possibleDef(n, excludeLet = false) {
@@ -159815,6 +159857,7 @@ async function buildTargetCoreAsync(options = {}) {
159815
159857
  updateTOC(cfg);
159816
159858
  cfg.bundledpkgs = {};
159817
159859
  pxt.setAppTarget(cfg);
159860
+ pxt.reloadAppTargetVariant();
159818
159861
  dirsToWatch = cfg.bundleddirs.slice();
159819
159862
  if (pxt.appTarget.id != "core") {
159820
159863
  if (fs.existsSync("theme")) {
@@ -6747,6 +6747,22 @@ var pxt;
6747
6747
  }
6748
6748
  });
6749
6749
  }
6750
+ function validateAllReferencedBlocksExist(xml) {
6751
+ pxt.U.assert(!!(Blockly === null || Blockly === void 0 ? void 0 : Blockly.Blocks), "Called validateAllReferencedBlocksExist before initializing Blockly");
6752
+ const dom = Blockly.Xml.textToDom(xml);
6753
+ const blocks = dom.querySelectorAll("block");
6754
+ for (let i = 0; i < blocks.length; i++) {
6755
+ if (!Blockly.Blocks[blocks.item(i).getAttribute("type")])
6756
+ return false;
6757
+ }
6758
+ const shadows = dom.querySelectorAll("shadow");
6759
+ for (let i = 0; i < shadows.length; i++) {
6760
+ if (!Blockly.Blocks[shadows.item(i).getAttribute("type")])
6761
+ return false;
6762
+ }
6763
+ return true;
6764
+ }
6765
+ blocks_2.validateAllReferencedBlocksExist = validateAllReferencedBlocksExist;
6750
6766
  })(blocks = pxt.blocks || (pxt.blocks = {}));
6751
6767
  })(pxt || (pxt = {}));
6752
6768
  var pxt;
@@ -6768,6 +6784,7 @@ var pxt;
6768
6784
  const newDom = Blockly.Xml.workspaceToDom(newWs, true);
6769
6785
  pxt.Util.toArray(oldDom.childNodes)
6770
6786
  .filter((n) => n.nodeType == Node.ELEMENT_NODE && n.localName == "block" && n.getAttribute("disabled") == "true")
6787
+ .filter((n) => !!Blockly.Blocks[n.getAttribute("type")])
6771
6788
  .forEach(n => newDom.appendChild(newDom.ownerDocument.importNode(n, true)));
6772
6789
  const updatedXml = Blockly.Xml.domToText(newDom);
6773
6790
  return updatedXml;
@@ -7713,6 +7730,14 @@ var pxt;
7713
7730
  blocksXml: `<xml xmlns="http://www.w3.org/1999/xhtml">${cleanOuterHTML(blockXml)}</xml>`,
7714
7731
  };
7715
7732
  }
7733
+ function attachCardInfo(blockInfo, qName) {
7734
+ const toModify = blockInfo.apis.byQName[qName];
7735
+ if (toModify) {
7736
+ const comp = blocks_4.compileInfo(toModify);
7737
+ const xml = createToolboxBlock(blockInfo, toModify, comp);
7738
+ return mkCard(toModify, xml);
7739
+ }
7740
+ }
7716
7741
  function isSubtype(apis, specific, general) {
7717
7742
  if (specific == general)
7718
7743
  return true;
@@ -8060,7 +8085,7 @@ var pxt;
8060
8085
  * Used by pxtrunner to initialize blocks in the docs
8061
8086
  */
8062
8087
  function initializeAndInject(blockInfo) {
8063
- init();
8088
+ init(blockInfo);
8064
8089
  injectBlocks(blockInfo);
8065
8090
  }
8066
8091
  blocks_4.initializeAndInject = initializeAndInject;
@@ -8069,12 +8094,12 @@ var pxt;
8069
8094
  * Blocks are injected separately by called injectBlocks
8070
8095
  */
8071
8096
  function initialize(blockInfo) {
8072
- init();
8097
+ init(blockInfo);
8073
8098
  initJresIcons(blockInfo);
8074
8099
  }
8075
8100
  blocks_4.initialize = initialize;
8076
8101
  let blocklyInitialized = false;
8077
- function init() {
8102
+ function init(blockInfo) {
8078
8103
  if (blocklyInitialized)
8079
8104
  return;
8080
8105
  blocklyInitialized = true;
@@ -8085,7 +8110,7 @@ var pxt;
8085
8110
  blocks_4.initFieldEditors();
8086
8111
  initContextMenu();
8087
8112
  initOnStart();
8088
- initMath();
8113
+ initMath(blockInfo);
8089
8114
  initVariables();
8090
8115
  initFunctions();
8091
8116
  initLists();
@@ -8945,9 +8970,10 @@ var pxt;
8945
8970
  }
8946
8971
  };
8947
8972
  }
8948
- function initMath() {
8973
+ function initMath(blockInfo) {
8949
8974
  // math_op2
8950
8975
  const mathOp2Id = "math_op2";
8976
+ const mathOp2qName = "Math.min"; // TODO: implement logic so that this changes based on which is used (min or max)
8951
8977
  const mathOp2Def = pxt.blocks.getBlockDefinition(mathOp2Id);
8952
8978
  const mathOp2Tooltips = mathOp2Def.tooltip;
8953
8979
  Blockly.Blocks[mathOp2Id] = {
@@ -8983,11 +9009,13 @@ var pxt;
8983
9009
  setHelpResources(this, mathOp2Id, mathOp2Def.name, function (block) {
8984
9010
  return mathOp2Tooltips[block.getFieldValue('op')];
8985
9011
  }, mathOp2Def.url, pxt.toolbox.getNamespaceColor(mathOp2Def.category));
8986
- }
9012
+ },
9013
+ codeCard: attachCardInfo(blockInfo, mathOp2qName)
8987
9014
  };
8988
9015
  // math_op3
8989
9016
  const mathOp3Id = "math_op3";
8990
9017
  const mathOp3Def = pxt.blocks.getBlockDefinition(mathOp3Id);
9018
+ const mathOp3qName = "Math.abs";
8991
9019
  Blockly.Blocks[mathOp3Id] = {
8992
9020
  init: function () {
8993
9021
  this.jsonInit({
@@ -9005,7 +9033,8 @@ var pxt;
9005
9033
  "colour": pxt.toolbox.getNamespaceColor('math')
9006
9034
  });
9007
9035
  setBuiltinHelpInfo(this, mathOp3Id);
9008
- }
9036
+ },
9037
+ codeCard: attachCardInfo(blockInfo, mathOp3qName)
9009
9038
  };
9010
9039
  // builtin math_number, math_integer, math_whole_number, math_number_minmax
9011
9040
  //XXX Integer validation needed.
@@ -11951,7 +11980,6 @@ var pxtblockly;
11951
11980
  this.fieldGroup_.appendChild(bg.el);
11952
11981
  const icon = new svg.Text("\uf008")
11953
11982
  .at(X_PADDING, 5 + (TOTAL_HEIGHT >> 1))
11954
- .fill(this.sourceBlock_.getColourSecondary())
11955
11983
  .setClass("semanticIcon");
11956
11984
  this.fieldGroup_.appendChild(icon.el);
11957
11985
  if (this.asset) {
@@ -13469,7 +13497,7 @@ var pxtblockly;
13469
13497
  return function () {
13470
13498
  const res = [];
13471
13499
  const that = this;
13472
- if (that.sourceBlock_ && that.sourceBlock_.workspace) {
13500
+ if (that.sourceBlock_ && that.sourceBlock_.workspace && !that.sourceBlock_.isInFlyout) {
13473
13501
  const options = that.sourceBlock_.workspace.getVariablesOfType(kindType(opts.name));
13474
13502
  options.forEach(model => {
13475
13503
  res.push([model.name, model.name]);
@@ -157,6 +157,7 @@ declare namespace pxt.blocks {
157
157
  */
158
158
  let extensionBlocklyPatch: (pkgTargetVersion: string, dom: Element) => void;
159
159
  function importXml(pkgTargetVersion: string, xml: string, info: pxtc.BlocksInfo, skipReport?: boolean): string;
160
+ function validateAllReferencedBlocksExist(xml: string): boolean;
160
161
  }
161
162
  declare namespace pxt.blocks.layout {
162
163
  interface FlowOptions {