pxt-core 7.5.13 → 7.5.16

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.
@@ -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
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, eg: #ff0000\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='#ff0000'\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
- "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_lstrip(str: string, chars?: 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 return [];\n }\n\n export function py_string_rstrip(str: string, chars?: string): string {\n nullCheck(str);\n return str;\n }\n\n export function py_string_split(str: string, sep?: string, maxsplit?: number): string[] {\n nullCheck(str);\n return [];\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_strip(str: string, chars?: string): string {\n nullCheck(str);\n return str;\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}",
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
@@ -101446,7 +101446,7 @@ var pxt;
101446
101446
  // The JS Math functions supported in the blocks. The order of this array
101447
101447
  // determines the order of the dropdown in the math_js_op block
101448
101448
  blocks.MATH_FUNCTIONS = {
101449
- unary: ["sqrt", "sin", "cos", "tan"],
101449
+ unary: ["sqrt", "sin", "cos", "tan", "asin", "acos"],
101450
101450
  binary: ["atan2"],
101451
101451
  infix: ["idiv", "imul"]
101452
101452
  };
@@ -101813,6 +101813,8 @@ var pxt;
101813
101813
  "sqrt": pxt.Util.lf("Returns the square root of the argument"),
101814
101814
  "sin": pxt.Util.lf("Returns the sine of the argument"),
101815
101815
  "cos": pxt.Util.lf("Returns the cosine of the argument"),
101816
+ "acos": pxt.Util.lf("Returns the arccosine of the argument"),
101817
+ "asine": pxt.Util.lf("Returns the arcsine of the argument"),
101816
101818
  "tan": pxt.Util.lf("Returns the tangent of the argument"),
101817
101819
  "atan2": pxt.Util.lf("Returns the arctangent of the quotient of the two arguments"),
101818
101820
  "idiv": pxt.Util.lf("Returns the integer portion of the division operation on the two arguments"),
@@ -101827,6 +101829,8 @@ var pxt;
101827
101829
  "sqrt": pxt.Util.lf("{id:op}square root"),
101828
101830
  "sin": pxt.Util.lf("{id:op}sin"),
101829
101831
  "cos": pxt.Util.lf("{id:op}cos"),
101832
+ "asin": pxt.Util.lf("{id:op}asin"),
101833
+ "acos": pxt.Util.lf("{id:op}acos"),
101830
101834
  "tan": pxt.Util.lf("{id:op}tan"),
101831
101835
  "atan2": pxt.Util.lf("{id:op}atan2"),
101832
101836
  "idiv": pxt.Util.lf("{id:op}integer ÷"),
@@ -153622,9 +153626,19 @@ var pxsim;
153622
153626
  let _mute = false; //mute audio
153623
153627
  // for playing WAV
153624
153628
  let audio;
153629
+ const channels = [];
153630
+ // All other nodes get connected to this node which is connected to the actual
153631
+ // destination. Used for muting
153632
+ let destination;
153625
153633
  function context() {
153626
- if (!_context)
153634
+ if (!_context) {
153627
153635
  _context = freshContext();
153636
+ if (_context) {
153637
+ destination = _context.createGain();
153638
+ destination.connect(_context.destination);
153639
+ destination.gain.setValueAtTime(1, 0);
153640
+ }
153641
+ }
153628
153642
  return _context;
153629
153643
  }
153630
153644
  function freshContext() {
@@ -153641,8 +153655,13 @@ var pxsim;
153641
153655
  }
153642
153656
  function mute(mute) {
153643
153657
  _mute = mute;
153644
- stopAll();
153645
153658
  const ctx = context();
153659
+ if (mute) {
153660
+ destination.gain.setTargetAtTime(0, ctx.currentTime, 0.015);
153661
+ }
153662
+ else {
153663
+ destination.gain.setTargetAtTime(1, ctx.currentTime, 0.015);
153664
+ }
153646
153665
  if (!mute && ctx && ctx.state === "suspended")
153647
153666
  ctx.resume();
153648
153667
  }
@@ -153836,9 +153855,8 @@ var pxsim;
153836
153855
  node.playbackRate.value = hz / (context().sampleRate / 1024);
153837
153856
  return node;
153838
153857
  }
153839
- const channels = [];
153840
153858
  class Channel {
153841
- mute() {
153859
+ disconnectNodes() {
153842
153860
  if (this.gain)
153843
153861
  disconnectVca(this.gain, this.generator);
153844
153862
  else if (this.generator) {
@@ -153852,7 +153870,7 @@ var pxsim;
153852
153870
  const idx = channels.indexOf(this);
153853
153871
  if (idx >= 0)
153854
153872
  channels.splice(idx, 1);
153855
- this.mute();
153873
+ this.disconnectNodes();
153856
153874
  }
153857
153875
  }
153858
153876
  let instrStopId = 1;
@@ -153886,7 +153904,7 @@ var pxsim;
153886
153904
  /** Square waves are perceved as much louder than other sounds, so scale it down a bit to make it less jarring **/
153887
153905
  const scaleVol = (n, isSqWave) => (n / 1024) / 4 * (isSqWave ? .5 : 1);
153888
153906
  const finish = () => {
153889
- ch.mute();
153907
+ ch.disconnectNodes();
153890
153908
  timeOff = 0;
153891
153909
  currWave = -1;
153892
153910
  currFreq = -1;
@@ -153914,7 +153932,7 @@ var pxsim;
153914
153932
  return loopAsync();
153915
153933
  });
153916
153934
  }
153917
- ch.generator = _mute ? null : getGenerator(soundWaveIdx, freq);
153935
+ ch.generator = getGenerator(soundWaveIdx, freq);
153918
153936
  if (!ch.generator)
153919
153937
  return pxsim.U.delay(duration);
153920
153938
  currWave = soundWaveIdx;
@@ -153936,7 +153954,7 @@ var pxsim;
153936
153954
  }
153937
153955
  }
153938
153956
  ch.generator.connect(ch.gain);
153939
- ch.gain.connect(ctx.destination);
153957
+ ch.gain.connect(destination);
153940
153958
  ch.generator.start();
153941
153959
  }
153942
153960
  idx += 12;
@@ -153969,7 +153987,7 @@ var pxsim;
153969
153987
  _vca.gain.value = 0;
153970
153988
  _vco.type = 'triangle';
153971
153989
  _vco.connect(_vca);
153972
- _vca.connect(ctx.destination);
153990
+ _vca.connect(destination);
153973
153991
  _vco.start(0);
153974
153992
  }
153975
153993
  setCurrentToneGain(gain);
@@ -153985,7 +154003,7 @@ var pxsim;
153985
154003
  AudioContextManager.tone = tone;
153986
154004
  function setCurrentToneGain(gain) {
153987
154005
  if (_vca === null || _vca === void 0 ? void 0 : _vca.gain) {
153988
- _vca.gain.setTargetAtTime(_mute ? 0 : gain, _context.currentTime, 0.015);
154006
+ _vca.gain.setTargetAtTime(gain, _context.currentTime, 0.015);
153989
154007
  }
153990
154008
  }
153991
154009
  AudioContextManager.setCurrentToneGain = setCurrentToneGain;
@@ -154022,13 +154040,30 @@ var pxsim;
154022
154040
  let nodes = [];
154023
154041
  let nextTime = context().currentTime;
154024
154042
  let allScheduled = false;
154043
+ const channel = new Channel();
154044
+ channel.gain = context().createGain();
154045
+ channel.gain.gain.value = 0;
154046
+ channel.gain.gain.setValueAtTime(volume, context().currentTime);
154047
+ channel.gain.connect(destination);
154048
+ if (channels.length > 5)
154049
+ channels[0].remove();
154050
+ channels.push(channel);
154051
+ const checkCancel = () => {
154052
+ if (isCancelled && isCancelled() || !channel.gain) {
154053
+ if (resolve)
154054
+ resolve();
154055
+ resolve = undefined;
154056
+ channel.remove();
154057
+ return true;
154058
+ }
154059
+ return false;
154060
+ };
154025
154061
  // Every time we pull a buffer, schedule a node in the future to play it.
154026
154062
  // Scheduling the nodes ahead of time sounds much smoother than trying to
154027
154063
  // do it when the previous node completes (which sounds SUPER choppy in
154028
154064
  // FireFox).
154029
154065
  function playNext() {
154030
- const cancelled = isCancelled && isCancelled();
154031
- while (!allScheduled && nodes.length < MAX_SCHEDULED_BUFFER_NODES && !cancelled) {
154066
+ while (!allScheduled && nodes.length < MAX_SCHEDULED_BUFFER_NODES && !checkCancel()) {
154032
154067
  const data = pull();
154033
154068
  if (!data || !data.length) {
154034
154069
  allScheduled = true;
@@ -154044,6 +154079,8 @@ var pxsim;
154044
154079
  }
154045
154080
  }
154046
154081
  function play(data) {
154082
+ if (checkCancel())
154083
+ return;
154047
154084
  const buff = context().createBuffer(1, data.length, sampleRate);
154048
154085
  if (buff.copyToChannel) {
154049
154086
  buff.copyToChannel(data, 0);
@@ -154066,15 +154103,6 @@ var pxsim;
154066
154103
  newNode.start(nextTime);
154067
154104
  nextTime += buff.duration;
154068
154105
  }
154069
- const channel = new Channel();
154070
- channel.gain = context().createGain();
154071
- channel.gain.gain.value = 0;
154072
- if (!_mute)
154073
- channel.gain.gain.setValueAtTime(volume, context().currentTime);
154074
- channel.gain.connect(context().destination);
154075
- if (channels.length > 5)
154076
- channels[0].remove();
154077
- channels.push(channel);
154078
154106
  playNext();
154079
154107
  });
154080
154108
  }
@@ -155099,18 +155127,62 @@ var pxsim;
155099
155127
  result = "0" + result;
155100
155128
  return result;
155101
155129
  }
155102
- let soundPromise;
155130
+ let playing = false;
155131
+ let soundQueue;
155132
+ let cancellationToken = {
155133
+ cancelled: false
155134
+ };
155103
155135
  function __playSoundExpression(notes, waitTillDone) {
155136
+ if (!soundQueue)
155137
+ soundQueue = [];
155104
155138
  const cb = pxsim.getResume();
155105
- if (!soundPromise)
155106
- soundPromise = Promise.resolve();
155107
- soundPromise = soundPromise.then(() => playSoundExpressionAsync(notes));
155139
+ const soundPromise = new Promise((resolve, reject) => {
155140
+ soundQueue.push({
155141
+ notes,
155142
+ onFinished: resolve,
155143
+ onCancelled: reject
155144
+ });
155145
+ });
155146
+ if (!playing) {
155147
+ playNextSoundAsync();
155148
+ }
155108
155149
  if (!waitTillDone)
155109
155150
  cb();
155110
155151
  else
155111
- soundPromise = soundPromise.then(cb);
155152
+ soundPromise.then(cb);
155112
155153
  }
155113
155154
  music.__playSoundExpression = __playSoundExpression;
155155
+ async function playNextSoundAsync() {
155156
+ if (soundQueue.length) {
155157
+ playing = true;
155158
+ const sound = soundQueue.shift();
155159
+ let currentToken = cancellationToken;
155160
+ try {
155161
+ await playSoundExpressionAsync(sound.notes, () => currentToken.cancelled);
155162
+ if (currentToken.cancelled) {
155163
+ sound.onCancelled();
155164
+ }
155165
+ else {
155166
+ sound.onFinished();
155167
+ }
155168
+ }
155169
+ catch (_a) {
155170
+ sound.onCancelled();
155171
+ }
155172
+ playNextSoundAsync();
155173
+ }
155174
+ else {
155175
+ playing = false;
155176
+ }
155177
+ }
155178
+ function clearSoundQueue() {
155179
+ soundQueue = [];
155180
+ cancellationToken.cancelled = true;
155181
+ cancellationToken = {
155182
+ cancelled: false
155183
+ };
155184
+ }
155185
+ music.clearSoundQueue = clearSoundQueue;
155114
155186
  function playSoundExpressionAsync(notes, isCancelled, onPull) {
155115
155187
  const synth = new music.SoundEmojiSynthesizer(0);
155116
155188
  const soundEffects = parseSoundEffects(notes);
@@ -155142,6 +155214,7 @@ var pxsim;
155142
155214
  }
155143
155215
  music.playSoundExpressionAsync = playSoundExpressionAsync;
155144
155216
  function __stopSoundExpressions() {
155217
+ clearSoundQueue();
155145
155218
  pxsim.AudioContextManager.stopAll();
155146
155219
  }
155147
155220
  music.__stopSoundExpressions = __stopSoundExpressions;
@@ -7828,6 +7828,7 @@ var pxt;
7828
7828
  return;
7829
7829
  }
7830
7830
  }
7831
+ const hasInput = (name) => { var _a; return (_a = block.inputList) === null || _a === void 0 ? void 0 : _a.some(i => i.name === name); };
7831
7832
  inputs.forEach(inputParts => {
7832
7833
  const fields = [];
7833
7834
  let inputName;
@@ -7976,12 +7977,19 @@ var pxt;
7976
7977
  });
7977
7978
  let input;
7978
7979
  if (inputName) {
7980
+ // Don't add duplicate inputs
7981
+ if (hasInput(inputName))
7982
+ return;
7979
7983
  input = block.appendValueInput(inputName);
7980
7984
  input.setAlign(Blockly.ALIGN_LEFT);
7981
7985
  }
7982
7986
  else if (expanded) {
7983
7987
  const prefix = hasParameter ? blocks_4.optionalInputWithFieldPrefix : blocks_4.optionalDummyInputPrefix;
7984
- input = block.appendDummyInput(prefix + (anonIndex++));
7988
+ inputName = prefix + (anonIndex++);
7989
+ // Don't add duplicate inputs
7990
+ if (hasInput(inputName))
7991
+ return;
7992
+ input = block.appendDummyInput(inputName);
7985
7993
  }
7986
7994
  else {
7987
7995
  input = block.appendDummyInput();
@@ -15149,6 +15157,7 @@ var pxtblockly;
15149
15157
  class FieldSoundEffect extends pxtblockly.FieldBase {
15150
15158
  constructor() {
15151
15159
  super(...arguments);
15160
+ this.registeredChangeListener = false;
15152
15161
  this.onWorkspaceChange = (ev) => {
15153
15162
  if (ev.type !== Blockly.Events.CHANGE)
15154
15163
  return;
@@ -15178,10 +15187,19 @@ var pxtblockly;
15178
15187
  if (!this.options.effectFieldName)
15179
15188
  this.options.effectFieldName = "effect";
15180
15189
  this.redrawPreview();
15181
- this.sourceBlock_.workspace.addChangeListener(this.onWorkspaceChange);
15190
+ if (this.sourceBlock_.workspace) {
15191
+ this.workspace = this.sourceBlock_.workspace;
15192
+ if (!this.sourceBlock_.isShadow() && !this.sourceBlock_.isInsertionMarker()) {
15193
+ this.registeredChangeListener = true;
15194
+ this.workspace.addChangeListener(this.onWorkspaceChange);
15195
+ }
15196
+ }
15182
15197
  }
15183
15198
  onDispose() {
15184
- this.sourceBlock_.workspace.removeChangeListener(this.onWorkspaceChange);
15199
+ if (this.workspace && this.registeredChangeListener) {
15200
+ this.workspace.removeChangeListener(this.onWorkspaceChange);
15201
+ this.registeredChangeListener = false;
15202
+ }
15185
15203
  }
15186
15204
  onValueChanged(newValue) {
15187
15205
  return newValue;
@@ -15347,7 +15365,7 @@ var pxtblockly;
15347
15365
  render_() {
15348
15366
  super.render_();
15349
15367
  this.size_.height = TOTAL_HEIGHT + Y_PADDING * 2;
15350
- this.size_.width = TOTAL_WIDTH;
15368
+ this.size_.width = TOTAL_WIDTH + X_PADDING;
15351
15369
  }
15352
15370
  updateSiblingBlocks(sound) {
15353
15371
  this.setNumberInputValue(this.options.durationInputName, sound.duration);
@@ -1145,6 +1145,8 @@ declare namespace pxtblockly {
1145
1145
  class FieldSoundEffect extends FieldBase<FieldSoundEffectParams> {
1146
1146
  protected mostRecentValue: pxt.assets.Sound;
1147
1147
  protected drawnSound: pxt.assets.Sound;
1148
+ protected workspace: Blockly.Workspace;
1149
+ protected registeredChangeListener: boolean;
1148
1150
  protected onInit(): void;
1149
1151
  protected onDispose(): void;
1150
1152
  protected onValueChanged(newValue: string): string;
@@ -4266,6 +4266,7 @@ var pxt;
4266
4266
  return;
4267
4267
  }
4268
4268
  }
4269
+ const hasInput = (name) => { var _a; return (_a = block.inputList) === null || _a === void 0 ? void 0 : _a.some(i => i.name === name); };
4269
4270
  inputs.forEach(inputParts => {
4270
4271
  const fields = [];
4271
4272
  let inputName;
@@ -4414,12 +4415,19 @@ var pxt;
4414
4415
  });
4415
4416
  let input;
4416
4417
  if (inputName) {
4418
+ // Don't add duplicate inputs
4419
+ if (hasInput(inputName))
4420
+ return;
4417
4421
  input = block.appendValueInput(inputName);
4418
4422
  input.setAlign(Blockly.ALIGN_LEFT);
4419
4423
  }
4420
4424
  else if (expanded) {
4421
4425
  const prefix = hasParameter ? blocks_4.optionalInputWithFieldPrefix : blocks_4.optionalDummyInputPrefix;
4422
- input = block.appendDummyInput(prefix + (anonIndex++));
4426
+ inputName = prefix + (anonIndex++);
4427
+ // Don't add duplicate inputs
4428
+ if (hasInput(inputName))
4429
+ return;
4430
+ input = block.appendDummyInput(inputName);
4423
4431
  }
4424
4432
  else {
4425
4433
  input = block.appendDummyInput();
@@ -11587,6 +11595,7 @@ var pxtblockly;
11587
11595
  class FieldSoundEffect extends pxtblockly.FieldBase {
11588
11596
  constructor() {
11589
11597
  super(...arguments);
11598
+ this.registeredChangeListener = false;
11590
11599
  this.onWorkspaceChange = (ev) => {
11591
11600
  if (ev.type !== Blockly.Events.CHANGE)
11592
11601
  return;
@@ -11616,10 +11625,19 @@ var pxtblockly;
11616
11625
  if (!this.options.effectFieldName)
11617
11626
  this.options.effectFieldName = "effect";
11618
11627
  this.redrawPreview();
11619
- this.sourceBlock_.workspace.addChangeListener(this.onWorkspaceChange);
11628
+ if (this.sourceBlock_.workspace) {
11629
+ this.workspace = this.sourceBlock_.workspace;
11630
+ if (!this.sourceBlock_.isShadow() && !this.sourceBlock_.isInsertionMarker()) {
11631
+ this.registeredChangeListener = true;
11632
+ this.workspace.addChangeListener(this.onWorkspaceChange);
11633
+ }
11634
+ }
11620
11635
  }
11621
11636
  onDispose() {
11622
- this.sourceBlock_.workspace.removeChangeListener(this.onWorkspaceChange);
11637
+ if (this.workspace && this.registeredChangeListener) {
11638
+ this.workspace.removeChangeListener(this.onWorkspaceChange);
11639
+ this.registeredChangeListener = false;
11640
+ }
11623
11641
  }
11624
11642
  onValueChanged(newValue) {
11625
11643
  return newValue;
@@ -11785,7 +11803,7 @@ var pxtblockly;
11785
11803
  render_() {
11786
11804
  super.render_();
11787
11805
  this.size_.height = TOTAL_HEIGHT + Y_PADDING * 2;
11788
- this.size_.width = TOTAL_WIDTH;
11806
+ this.size_.width = TOTAL_WIDTH + X_PADDING;
11789
11807
  }
11790
11808
  updateSiblingBlocks(sound) {
11791
11809
  this.setNumberInputValue(this.options.durationInputName, sound.duration);
package/built/pxtlib.js CHANGED
@@ -3760,7 +3760,7 @@ var pxt;
3760
3760
  // The JS Math functions supported in the blocks. The order of this array
3761
3761
  // determines the order of the dropdown in the math_js_op block
3762
3762
  blocks.MATH_FUNCTIONS = {
3763
- unary: ["sqrt", "sin", "cos", "tan"],
3763
+ unary: ["sqrt", "sin", "cos", "tan", "asin", "acos"],
3764
3764
  binary: ["atan2"],
3765
3765
  infix: ["idiv", "imul"]
3766
3766
  };
@@ -4127,6 +4127,8 @@ var pxt;
4127
4127
  "sqrt": pxt.Util.lf("Returns the square root of the argument"),
4128
4128
  "sin": pxt.Util.lf("Returns the sine of the argument"),
4129
4129
  "cos": pxt.Util.lf("Returns the cosine of the argument"),
4130
+ "acos": pxt.Util.lf("Returns the arccosine of the argument"),
4131
+ "asine": pxt.Util.lf("Returns the arcsine of the argument"),
4130
4132
  "tan": pxt.Util.lf("Returns the tangent of the argument"),
4131
4133
  "atan2": pxt.Util.lf("Returns the arctangent of the quotient of the two arguments"),
4132
4134
  "idiv": pxt.Util.lf("Returns the integer portion of the division operation on the two arguments"),
@@ -4141,6 +4143,8 @@ var pxt;
4141
4143
  "sqrt": pxt.Util.lf("{id:op}square root"),
4142
4144
  "sin": pxt.Util.lf("{id:op}sin"),
4143
4145
  "cos": pxt.Util.lf("{id:op}cos"),
4146
+ "asin": pxt.Util.lf("{id:op}asin"),
4147
+ "acos": pxt.Util.lf("{id:op}acos"),
4144
4148
  "tan": pxt.Util.lf("{id:op}tan"),
4145
4149
  "atan2": pxt.Util.lf("{id:op}atan2"),
4146
4150
  "idiv": pxt.Util.lf("{id:op}integer ÷"),
package/built/pxtsim.d.ts CHANGED
@@ -1728,6 +1728,7 @@ declare namespace pxsim.codal.music {
1728
1728
  protected getValue(offset: number, length: number): number;
1729
1729
  }
1730
1730
  function __playSoundExpression(notes: string, waitTillDone: boolean): void;
1731
+ function clearSoundQueue(): void;
1731
1732
  function playSoundExpressionAsync(notes: string, isCancelled?: () => boolean, onPull?: (freq: number, volume: number) => void): Promise<void>;
1732
1733
  function __stopSoundExpressions(): void;
1733
1734
  interface TonePrint {