diff --git a/HISTORY.md b/HISTORY.md index f5575bf9..dbd4ce39 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -95,11 +95,20 @@ * **New Features:** * new extension primitives * **Notable Changes:** - * libraries no longer rely on the JSF primitive, project may need to re-import their libraries to run without having to enable JS exstensions + * libraries no longer rely on the JSF primitive, project may need to re-import their libraries to run without having to enable JS extensions + * retired Leap Motion library, took out Hummingbird library (get the current one from Birdbrain) * **Notable Fixes:** * fixed occasional invisible error messages -### 2021-06-19 +### 2021-06-23 +* updated bignums library +* pushed dev version to 6.10 +* took out device libraries (Hummingbird blocks and Leap Motion) + +### 2021-06-22 +* extensions: added script-loading extension primitive + +### 2021-06-20 * updated extensions documentation ### 2021-06-19 diff --git a/libraries/LIBRARIES b/libraries/LIBRARIES index ac5be833..847fc7a3 100644 --- a/libraries/LIBRARIES +++ b/libraries/LIBRARIES @@ -28,9 +28,3 @@ stream-tools.xml Streams (lazy lists) A variation on the list data type in which bar-charts.xml Bar charts Takes a table (typically from a CSV data set) as input and reports a summary of the table grouped by the field in the specified column number. The remaining three inputs are used only if the field values are numbers, in which case they can be grouped into buckets (e.g., decades, centuries, etc.). Those three inputs specify the smallest and largest values of interest and, most importantly, the width of a bucket (10 for decades, 100 for centuries). If the field isn't numeric, leave these three inputs empty or set them to zero. In that case, each string value of the field is its own bucket, and they appear sorted alphabetically. The block reports a new table with three columns. The first column contains the bucket name or smallest number. The second column contains a nonnegative integer that says how many records in the input table fall into this bucket. The third column is a subtable containing the actual records from the original table that fall into the bucket. If your buckets aren't of constant width, or you want to group by some function of more than one field, load the "Frequency Distribution Analysis" library instead. httpBlocks.xml Web services access (https) An extended version of the URL block that allows POST, PUT, and DELETE as well as GET requests, allows using the secure HTTPS protocol, and gives control over headers, etc. Also parses JSON data. make-variables.xml Create variables Create and manage global/sprite/script variables in a script -~ ~ -~ ~ -~ ~ -HummingbirdBlocks.xml Hummingbird robotics Control the Hummingbird robotics kit processor -leap-library.xml LEAP Motion controller Report hand positions from LEAP Motion controller (leapmotion.com). - diff --git a/libraries/bignumbers.xml b/libraries/bignumbers.xml index 586c28e8..9ab6ac84 100644 --- a/libraries/bignumbers.xml +++ b/libraries/bignumbers.xml @@ -1 +1 @@ -call with True to turn on the entire Scheme numeric tower, including infinite-precision integers, exact rationals, and complex numbers; call with False to restore native JavaScript arithmetic.
pt:altera utilização de aritmética do Scheme para _ useBigNumsvar done = false; function initialize (callback) { var bigScript = document.createElement('script'); bigScript.src = '//snap.berkeley.edu/snap/libraries/biginteger.js'; bigScript.onload = loadScheme; document.head.appendChild(bigScript); function loadScheme () { var schemeScript = document.createElement('script'); schemeScript.src = '//snap.berkeley.edu/snap/libraries/schemeNumber.js'; schemeScript.onload = finish; document.head.appendChild(schemeScript); } function finish () { makeGlobalObject(); callback(); } } function makeGlobalObject () { window.bigNumbers = { originalEvaluate: InputSlotMorph.prototype.evaluate, originalChangeVar: VariableFrame.prototype.changeVar, originalPrims: { reportBasicSum: Process.prototype.reportBasicSum, reportBasicDifference: Process.prototype.reportBasicDifference, reportBasicProduct: Process.prototype.reportBasicProduct, reportBasicQuotient: Process.prototype.reportBasicQuotient, reportBasicPower: Process.prototype.reportBasicPower, reportBasicModulus: Process.prototype.reportBasicModulus, reportBasicAtan2: Process.prototype.reportBasicAtan2, reportRound: Process.prototype.reportRound, reportBasicMin: Process.prototype.reportBasicMin, reportBasicMax: Process.prototype.reportBasicMax, reportBasicRandom: Process.prototype.reportBasicRandom, reportBasicLessThan: Process.prototype.reportBasicLessThan, reportBasicGreaterThan: Process.prototype.reportBasicGreaterThan, reportEquals: Process.prototype.reportEquals, reportIsIdentical: Process.prototype.reportIsIdentical, reportMonadic: Process.prototype.reportMonadic } }; } function loadBlocks () { var fn = SchemeNumber.fn; var originalPrims = window.bigNumbers.originalPrims; if (useBigNums) { InputSlotMorph.prototype.evaluate = function () { var contents = this.contents(); if (this.selectedBlock) { return this.selectedBlock; } if (this.constant) { return this.constant; } if (this.isNumeric) { return parseNumber(contents.text || '0'); } return contents.text; }; VariableFrame.prototype.changeVar = function (name, delta, sender) { var frame = this.find(name), value, newValue; if (frame) { value = parseNumber(frame.vars[name].value); newValue = Number.isNaN(value) ? delta : fn['+'](value, parseNumber(delta)); if (sender instanceof SpriteMorph && (frame.owner instanceof SpriteMorph) && (sender !== frame.owner)) { sender.shadowVar(name, newValue); } else { frame.vars[name].value = newValue; } } }; Object.assign(Process.prototype, { reportBasicSum: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; return fn['+'](a, b); }, reportBasicDifference: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; return fn['-'](a, b); }, reportBasicProduct: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; return fn['*'](a, b); }, reportBasicQuotient: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (fn['='](b, '0') && !fn['='](a, '0')) { return (fn['<'](a, '0') ? SchemeNumber('-inf.0') : SchemeNumber('+inf.0')) }; if (Number.isNaN(a) || Number.isNaN(b) || fn['='](b, '0')) return NaN; return fn['/'](a, b); }, reportBasicPower: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; return fn['expt'](a, b); }, reportBasicModulus: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; var result = fn.mod(a, b); if (fn['<'](b, '0') && fn['>'](result, '0')) { result = fn['+'](result, b); } return result; }, reportBasicAtan2: function (a, b) { a = parseNumber(a); b = parseNumber(b); if (Number.isNaN(a) || Number.isNaN(b)) return NaN; return degrees(fn.atan2(a, b)); }, reportRound: function (n) { if (this.enableHyperOps) { if (n instanceof List) { return n.map(each => this.reportRound(each)); } } n = parseNumber(n); if (Number.isNaN(n)) return NaN; x = fn.round(n); if (fn["integer?"](x)) return fn["exact"](x); return x; }, reportBasicMin: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) { return a<b ? a : b; } return fn['<'](x, y) ? x : y; }, reportBasicMax: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) { return a>b ? a : b; } return fn['>'](x, y) ? x : y; }, reportBasicRandom: function (min, max) { var floor = parseNumber(min), ceil = parseNumber(max); if (Number.isNaN(floor) || Number.isNaN(ceil)) return NaN; if (!fn['='](fn.mod(floor, '1'), '0') || !fn['='](fn.mod(ceil, '1'), '0')) { // One of the numbers isn't whole. Include the decimal. return fn['+']( fn['*']( Math.random(), fn['-'](ceil, floor) ), floor ); } var size = Math.ceil(max.toString(10).length/14); const array = new Uint32Array(size); window.crypto.getRandomValues(array); var digits=""; for (i=0;i<size;i++) { digits = digits + array[i].toString(); } return fn.floor( fn['+']( // fn['*']( // Math.random(), fn.mod(parseNumber(digits), fn['+']( fn['-'](ceil, floor), '1' ) ), floor ) ); }, reportBasicLessThan: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) return a<b; return fn['<'](x, y); }, reportBasicGreaterThan: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) return a>b; return fn['>'](x, y); }, reportEquals: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) return snapEquals(a, b); return fn['='](x, y); }, reportIsIdentical: function (a, b) { x = parseNumber(a); y = parseNumber(b); if (Number.isNaN(x) || Number.isNaN(y)) return originalPrims.reportIsIdentical(a, b); return fn['='](x, y); }, reportMonadic: function (fname, n) { if (this.enableHyperOps) { if (n instanceof List) { return n.map(each => this.reportMonadic(fname, each)); } } n = parseNumber(n); if (Number.isNaN(n)) return NaN; switch (Process.prototype.inputOption(fname)) { case 'abs': return fn.abs(n); case 'neg': return fn['-'](n); case 'sign': if (fn['='](n,SchemeNumber('0'))) return SchemeNumber('0'); return fn['/'](n, fn.abs(n)); case 'ceiling': return fn.ceiling(n); case 'floor': return fn.floor(n); case 'sqrt': return sqrt(n); case 'sin': return fn.sin(radians(n)); case 'cos': return fn.cos(radians(n)); case 'tan': return fn.tan(radians(n)); case 'asin': return degrees(fn.asin(n)); case 'acos': return degrees(fn.acos(n)); case 'atan': return degrees(fn.atan(n)); case 'ln': return fn.log(n); case 'log': return fn.log(n, '10'); case 'lg': return fn.log(n, '2'); case 'e^': return fn.exp(n); case '10^': return fn.expt('10', n); case '2^': return fn.expt('2', n); case 'id': return n; default: return SchemeNumber('0'); } } }); } else { InputSlotMorph.prototype.evaluate = window.bigNumbers.originalEvaluate; VariableFrame.prototype.changeVar = window.bigNumbers.originalChangeVar; Object.assign(Process.prototype, originalPrims); } done = true; } function parseNumber (n) { var fn = SchemeNumber.fn; if (!fn['number?'](n)) { n = '' + n; try { return parseENotation(n) || SchemeNumber(n); } catch (err) { return NaN; } } return n; } function parseENotation (n) { var fn = SchemeNumber.fn; var numbers = n.match(/^(-?\d+\.?\d*|-?\.\d+)e(-?\d+)$/i); if (!numbers) return null; var coefficient = numbers[1]; var exponent = numbers[2]; return fn['*']( coefficient, fn.expt('10', exponent) ); } function sqrt (n) { var fn = SchemeNumber.fn; if (!fn['exact?'](n) || !fn['rational?'](n) || fn['<'](n,'0')) return fn.sqrt(n); var rootNumerator = fn['exact-integer-sqrt'](fn.numerator(n)); if (!fn['='](rootNumerator[1], '0')) return fn.sqrt(n); var rootDenominator = fn['exact-integer-sqrt'](fn.denominator(n)); if (!fn['='](rootDenominator[1], '0')) return fn.sqrt(n); return fn['/'](rootNumerator[0], rootDenominator[0]); } function isDone () { return done; } if (window.bigNumbers) { loadBlocks(); } else { initialize(loadBlocks); } return isDone;
The factorial function, to make very large numbers, to demo bignums.
The identity function: reports its input. It's useful to get things like 3/4 or 5-2i into numeric input slots.
Provides Scheme arithmetic functions not in JavaScript
pt:_ de _ number? complex? real? rational? integer? exact? inexact? exact inexact finite? infinite? nan? numerator denominator real-part imag-part magnitude angle
\ No newline at end of file +call with True to turn on the entire Scheme numeric tower, including infinite-precision integers, exact rationals, and complex numbers; call with False to restore native JavaScript arithmetic.
pt:altera utilização de aritmética do Scheme para _
The factorial function, to make very large numbers, to demo bignums.
The identity function: reports its input. It's useful to get things like 3/4 or 5-2i into numeric input slots.
Provides Scheme arithmetic functions not in JavaScript
pt:_ de _ number? complex? real? rational? integer? exact? inexact? exact inexact finite? infinite? nan? numerator denominator real-part imag-part magnitude angle
\ No newline at end of file diff --git a/libraries/bignums.js b/libraries/bignums.js new file mode 100644 index 00000000..8e4ef91f --- /dev/null +++ b/libraries/bignums.js @@ -0,0 +1,375 @@ +makeGlobalObject(); + +SnapExtensions.primitives.set( + 'big_switch(bool)', + loadBlocks +); + +SnapExtensions.primitives.set( + 'big_scheme(fn, num)', + function (which, num) { + function parseNumber (n) { + var fn = SchemeNumber.fn; + if (!fn['number?'](n)) { + n = '' + n; + try { + return parseENotation(n) || SchemeNumber(n); + } catch (err) { + return NaN; + } + } + return n; + } + + function parseENotation (n) { + var fn = SchemeNumber.fn; + + var numbers = n.match(/^(-?\d+\.?\d*|-?\.\d+)e(-?\d+)$/i); + if (!numbers) return null; + + var coefficient = numbers[1]; + var exponent = numbers[2]; + return fn['*']( + coefficient, + fn.expt('10', exponent) + ); + } + var fn=SchemeNumber.fn, + number=parseNumber(num); + + switch (which) { + case 'number?': + case 'complex?': + return (fn['number?'](number)); + case 'real?': + return (fn['real?'](number) || fn['real-valued?'](number)); + case 'rational?': + return (fn['rational?'](number) || (fn['='](number, fn.rationalize(number, parseNumber('1.0e-5'))))); + case 'integer?': + return (fn['integer?'](number) || fn['integer-valued?'](number)); + case 'exact?': + case 'inexact?': + case 'finite?': + case 'infinite?': + case 'nan?': + case 'real-part': + case 'imag-part': + return (fn[which](number)); + case 'magnitude': + return (fn.magnitude(number)); + case 'angle': + return (fn.angle(number)); + case 'numerator': + return (fn.numerator(number)); + case 'denominator': + return (fn.denominator(number)); + case 'exact': + return (fn.exact(number)); + case 'inexact': + return (fn.inexact(number)); + } + } +); + +function makeGlobalObject () { + window.bigNumbers = { + originalEvaluate: InputSlotMorph.prototype.evaluate, + originalChangeVar: VariableFrame.prototype.changeVar, + originalPrims: { + reportBasicSum: Process.prototype.reportBasicSum, + reportBasicDifference: Process.prototype.reportBasicDifference, + reportBasicProduct: Process.prototype.reportBasicProduct, + reportBasicQuotient: Process.prototype.reportBasicQuotient, + reportBasicPower: Process.prototype.reportBasicPower, + reportBasicModulus: Process.prototype.reportBasicModulus, + reportBasicAtan2: Process.prototype.reportBasicAtan2, + reportRound: Process.prototype.reportRound, + reportBasicMin: Process.prototype.reportBasicMin, + reportBasicMax: Process.prototype.reportBasicMax, + reportBasicRandom: Process.prototype.reportBasicRandom, + reportBasicLessThan: Process.prototype.reportBasicLessThan, + reportBasicGreaterThan: Process.prototype.reportBasicGreaterThan, + reportEquals: Process.prototype.reportEquals, + reportIsIdentical: Process.prototype.reportIsIdentical, + reportMonadic: Process.prototype.reportMonadic + } + }; +} + +function loadBlocks (useBigNums) { + var fn = SchemeNumber.fn; + var originalPrims = window.bigNumbers.originalPrims; + if (useBigNums) { + InputSlotMorph.prototype.evaluate = function () { + var contents = this.contents(); + + if (this.selectedBlock) { + return this.selectedBlock; + } + + if (this.constant) { + return this.constant; + } + if (this.isNumeric) { + return parseNumber(contents.text || '0'); + } + return contents.text; + }; + VariableFrame.prototype.changeVar = function (name, delta, sender) { + var frame = this.find(name), + value, + newValue; + if (frame) { + value = parseNumber(frame.vars[name].value); + newValue = Number.isNaN(value) ? delta : fn['+'](value, parseNumber(delta)); + if (sender instanceof SpriteMorph && + (frame.owner instanceof SpriteMorph) && + (sender !== frame.owner)) { + sender.shadowVar(name, newValue); + } else { + frame.vars[name].value = newValue; + } + + } + }; + Object.assign(Process.prototype, { + reportBasicSum: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + return fn['+'](a, b); + }, + reportBasicDifference: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + return fn['-'](a, b); + }, + reportBasicProduct: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + return fn['*'](a, b); + }, + reportBasicQuotient: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (fn['='](b, '0') && !fn['='](a, '0')) { + return (fn['<'](a, '0') ? SchemeNumber('-inf.0') : SchemeNumber('+inf.0')) + }; + if (Number.isNaN(a) || Number.isNaN(b) || fn['='](b, '0')) return NaN; + return fn['/'](a, b); + }, + reportBasicPower: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + return fn['expt'](a, b); + }, + reportBasicModulus: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + var result = fn.mod(a, b); + if (fn['<'](b, '0') && fn['>'](result, '0')) { + result = fn['+'](result, b); + } + return result; + }, + reportBasicAtan2: function (a, b) { + a = parseNumber(a); + b = parseNumber(b); + if (Number.isNaN(a) || Number.isNaN(b)) return NaN; + return degrees(fn.atan2(a, b)); + }, + reportRound: function (n) { + if (this.enableHyperOps) { + if (n instanceof List) { + return n.map(each => this.reportRound(each)); + } + } + n = parseNumber(n); + if (Number.isNaN(n)) return NaN; + x = fn.round(n); + if (fn["integer?"](x)) return fn["exact"](x); + return x; + }, + reportBasicMin: function (a, b) { + x = parseNumber(a); + y = parseNumber(b); + if (Number.isNaN(x) || Number.isNaN(y)) { + return ab ? a : b; + } + return fn['>'](x, y) ? x : y; + }, + reportBasicRandom: function (min, max) { + var floor = parseNumber(min), + ceil = parseNumber(max); + if (Number.isNaN(floor) || Number.isNaN(ceil)) return NaN; + if (!fn['='](fn.mod(floor, '1'), '0') || !fn['='](fn.mod(ceil, '1'), '0')) { + // One of the numbers isn't whole. Include the decimal. + return fn['+']( + fn['*']( + Math.random(), + fn['-'](ceil, floor) + ), + floor + ); + } + var size = Math.ceil(max.toString(10).length/14); + const array = new Uint32Array(size); + window.crypto.getRandomValues(array); + var digits=""; + for (i=0;ib; + return fn['>'](x, y); + }, + reportEquals: function (a, b) { + x = parseNumber(a); + y = parseNumber(b); + if (Number.isNaN(x) || Number.isNaN(y)) return snapEquals(a, b); + return fn['='](x, y); + }, + reportIsIdentical: function (a, b) { + x = parseNumber(a); + y = parseNumber(b); + if (Number.isNaN(x) || Number.isNaN(y)) return originalPrims.reportIsIdentical(a, b); + return fn['='](x, y); + }, + reportMonadic: function (fname, n) { + if (this.enableHyperOps) { + if (n instanceof List) { + return n.map(each => this.reportMonadic(fname, each)); + } + } + + n = parseNumber(n); + if (Number.isNaN(n)) return NaN; + + switch (Process.prototype.inputOption(fname)) { + case 'abs': + return fn.abs(n); + case 'neg': + return fn['-'](n); + case 'sign': + if (fn['='](n,SchemeNumber('0'))) return SchemeNumber('0'); + return fn['/'](n, fn.abs(n)); + case 'ceiling': + return fn.ceiling(n); + case 'floor': + return fn.floor(n); + case 'sqrt': + return sqrt(n); + case 'sin': + return fn.sin(radians(n)); + case 'cos': + return fn.cos(radians(n)); + case 'tan': + return fn.tan(radians(n)); + case 'asin': + return degrees(fn.asin(n)); + case 'acos': + return degrees(fn.acos(n)); + case 'atan': + return degrees(fn.atan(n)); + case 'ln': + return fn.log(n); + case 'log': + return fn.log(n, '10'); + case 'lg': + return fn.log(n, '2'); + case 'e^': + return fn.exp(n); + case '10^': + return fn.expt('10', n); + case '2^': + return fn.expt('2', n); + case 'id': + return n; + default: + return SchemeNumber('0'); + } + } + }); + } else { + InputSlotMorph.prototype.evaluate = window.bigNumbers.originalEvaluate; + VariableFrame.prototype.changeVar = window.bigNumbers.originalChangeVar; + Object.assign(Process.prototype, originalPrims); + } + // +++ done = true; +} + +function parseNumber (n) { + var fn = SchemeNumber.fn; + if (!fn['number?'](n)) { + n = '' + n; + try { + return parseENotation(n) || SchemeNumber(n); + } catch (err) { + return NaN; + } + } + return n; +} + +function parseENotation (n) { + var fn = SchemeNumber.fn; + + var numbers = n.match(/^(-?\d+\.?\d*|-?\.\d+)e(-?\d+)$/i); + if (!numbers) return null; + + var coefficient = numbers[1]; + var exponent = numbers[2]; + return fn['*']( + coefficient, + fn.expt('10', exponent) + ); +} + +function sqrt (n) { + var fn = SchemeNumber.fn; + + if (!fn['exact?'](n) || !fn['rational?'](n) || fn['<'](n,'0')) return fn.sqrt(n); + + var rootNumerator = fn['exact-integer-sqrt'](fn.numerator(n)); + if (!fn['='](rootNumerator[1], '0')) return fn.sqrt(n); + + var rootDenominator = fn['exact-integer-sqrt'](fn.denominator(n)); + if (!fn['='](rootDenominator[1], '0')) return fn.sqrt(n); + + return fn['/'](rootNumerator[0], rootDenominator[0]); +} + diff --git a/snap.html b/snap.html index a1d6f183..a3221d3d 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/src/extensions.js b/src/extensions.js index fd43ff04..816c44e5 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -31,13 +31,17 @@ IDE_Morph, CamSnapshotDialogMorph, SoundRecorderDialogMorph, isSnapObject, nop, Color, contains*/ -modules.extensions = '2021-June-20'; +modules.extensions = '2021-June-23'; // Global stuff var SnapExtensions = { primitives: new Map(), - menus: new Map() + menus: new Map(), + scripts: [], + urls: [ + 'libraries/' + ] }; /* @@ -108,6 +112,8 @@ var SnapExtensions = { Whatever you do, please use these extension capabilities sensibly. */ +// Primitives + // errors & exceptions (err_): SnapExtensions.primitives.set( @@ -674,6 +680,38 @@ SnapExtensions.primitives.set( } ); +// loading external scripts (scr_) + +SnapExtensions.primitives.set( + 'scr_load(url)', + function (url, proc) { + var scriptElement; + if (!proc.context.accumulator) { + proc.context.accumulator = {done: false}; + if (contains(SnapExtensions.scripts, url)) { + return; + } + if (!(SnapExtensions.urls.some(any => url.indexOf(any) === 0))) { + throw new Error('unlisted extension url:\n"' + url + '"'); + } + scriptElement = document.createElement('script'); + scriptElement.onload = () => { + SnapExtensions.scripts.push(url); + proc.context.accumulator.done = true; + }; + document.head.appendChild(scriptElement); + scriptElement.src = url; + } else if (proc.context.accumulator.done) { + return; + } + proc.pushContext('doYield'); + proc.pushContext(); + } + +); + +// Menus + SnapExtensions.menus.set( 'clr_numbers', // Brian's browns and oranges, sigh... function () { diff --git a/src/gui.js b/src/gui.js index 2c996b26..9a10657c 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-June-14'; +modules.gui = '2021-June-23'; // Declarations