mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 15:02:52 -05:00
Merge branch 'develop' into removev2SvgAdapter
This commit is contained in:
commit
e5560b067b
11 changed files with 1525 additions and 1049 deletions
|
@ -146,7 +146,7 @@ return {
|
||||||
blocks: [
|
blocks: [
|
||||||
{
|
{
|
||||||
//...
|
//...
|
||||||
arguments {
|
arguments: {
|
||||||
MY_IMAGE: {
|
MY_IMAGE: {
|
||||||
type: ArgumentType.IMAGE,
|
type: ArgumentType.IMAGE,
|
||||||
dataURI: 'myImageData',
|
dataURI: 'myImageData',
|
||||||
|
|
2396
package-lock.json
generated
2396
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -41,7 +41,6 @@
|
||||||
"immutable": "3.8.1",
|
"immutable": "3.8.1",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
"minilog": "3.1.0",
|
"minilog": "3.1.0",
|
||||||
"nets": "3.2.0",
|
|
||||||
"scratch-parser": "5.0.0",
|
"scratch-parser": "5.0.0",
|
||||||
"scratch-sb1-converter": "0.2.7",
|
"scratch-sb1-converter": "0.2.7",
|
||||||
"scratch-translate-extension-languages": "0.0.20191118205314",
|
"scratch-translate-extension-languages": "0.0.20191118205314",
|
||||||
|
|
|
@ -602,6 +602,15 @@ class Runtime extends EventEmitter {
|
||||||
return 'PERIPHERAL_LIST_UPDATE';
|
return 'PERIPHERAL_LIST_UPDATE';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event name for when the user picks a bluetooth device to connect to
|
||||||
|
* via Companion Device Manager (CDM)
|
||||||
|
* @const {string}
|
||||||
|
*/
|
||||||
|
static get USER_PICKED_PERIPHERAL () {
|
||||||
|
return 'USER_PICKED_PERIPHERAL';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event name for reporting that a peripheral has connected.
|
* Event name for reporting that a peripheral has connected.
|
||||||
* This causes the status button in the blocks menu to indicate 'connected'.
|
* This causes the status button in the blocks menu to indicate 'connected'.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
const formatMessage = require('format-message');
|
const formatMessage = require('format-message');
|
||||||
const nets = require('nets');
|
|
||||||
const languageNames = require('scratch-translate-extension-languages');
|
const languageNames = require('scratch-translate-extension-languages');
|
||||||
|
|
||||||
const ArgumentType = require('../../extension-support/argument-type');
|
const ArgumentType = require('../../extension-support/argument-type');
|
||||||
|
@ -8,6 +7,7 @@ const Cast = require('../../util/cast');
|
||||||
const MathUtil = require('../../util/math-util');
|
const MathUtil = require('../../util/math-util');
|
||||||
const Clone = require('../../util/clone');
|
const Clone = require('../../util/clone');
|
||||||
const log = require('../../util/log');
|
const log = require('../../util/log');
|
||||||
|
const fetchWithTimeout = require('../../util/fetch-with-timeout');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon svg to be displayed in the blocks category menu, encoded as a data URI.
|
* Icon svg to be displayed in the blocks category menu, encoded as a data URI.
|
||||||
|
@ -722,28 +722,24 @@ class Scratch3Text2SpeechBlocks {
|
||||||
path += `&text=${encodeURIComponent(words.substring(0, 128))}`;
|
path += `&text=${encodeURIComponent(words.substring(0, 128))}`;
|
||||||
|
|
||||||
// Perform HTTP request to get audio file
|
// Perform HTTP request to get audio file
|
||||||
return new Promise(resolve => {
|
return fetchWithTimeout(path, {}, SERVER_TIMEOUT)
|
||||||
nets({
|
.then(res => {
|
||||||
url: path,
|
if (res.status !== 200) {
|
||||||
timeout: SERVER_TIMEOUT
|
throw new Error(`HTTP ${res.status} error reaching translation service`);
|
||||||
}, (err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
log.warn(err);
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
log.warn(res.statusCode);
|
|
||||||
return resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res.arrayBuffer();
|
||||||
|
})
|
||||||
|
.then(buffer => {
|
||||||
// Play the sound
|
// Play the sound
|
||||||
const sound = {
|
const sound = {
|
||||||
data: {
|
data: {
|
||||||
buffer: body.buffer
|
buffer
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.runtime.audioEngine.decodeSoundPlayer(sound).then(soundPlayer => {
|
return this.runtime.audioEngine.decodeSoundPlayer(sound);
|
||||||
|
})
|
||||||
|
.then(soundPlayer => {
|
||||||
this._soundPlayers.set(soundPlayer.id, soundPlayer);
|
this._soundPlayers.set(soundPlayer.id, soundPlayer);
|
||||||
|
|
||||||
soundPlayer.setPlaybackRate(playbackRate);
|
soundPlayer.setPlaybackRate(playbackRate);
|
||||||
|
@ -755,12 +751,15 @@ class Scratch3Text2SpeechBlocks {
|
||||||
soundPlayer.connect(chain);
|
soundPlayer.connect(chain);
|
||||||
|
|
||||||
soundPlayer.play();
|
soundPlayer.play();
|
||||||
|
return new Promise(resolve => {
|
||||||
soundPlayer.on('stop', () => {
|
soundPlayer.on('stop', () => {
|
||||||
this._soundPlayers.delete(soundPlayer.id);
|
this._soundPlayers.delete(soundPlayer.id);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
.catch(err => {
|
||||||
|
log.warn(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ const ArgumentType = require('../../extension-support/argument-type');
|
||||||
const BlockType = require('../../extension-support/block-type');
|
const BlockType = require('../../extension-support/block-type');
|
||||||
const Cast = require('../../util/cast');
|
const Cast = require('../../util/cast');
|
||||||
const log = require('../../util/log');
|
const log = require('../../util/log');
|
||||||
const nets = require('nets');
|
const fetchWithTimeout = require('../../util/fetch-with-timeout');
|
||||||
const languageNames = require('scratch-translate-extension-languages');
|
const languageNames = require('scratch-translate-extension-languages');
|
||||||
const formatMessage = require('format-message');
|
const formatMessage = require('format-message');
|
||||||
|
|
||||||
|
@ -247,6 +247,10 @@ class Scratch3TranslateBlocks {
|
||||||
* @return {Promise} - a promise that resolves after the response from the translate server.
|
* @return {Promise} - a promise that resolves after the response from the translate server.
|
||||||
*/
|
*/
|
||||||
getTranslate (args) {
|
getTranslate (args) {
|
||||||
|
// If the text contains only digits 0-9 and nothing else, return it without
|
||||||
|
// making a request.
|
||||||
|
if (/^\d+$/.test(args.WORDS)) return Promise.resolve(args.WORDS);
|
||||||
|
|
||||||
// Don't remake the request if we already have the value.
|
// Don't remake the request if we already have the value.
|
||||||
if (this._lastTextTranslated === args.WORDS &&
|
if (this._lastTextTranslated === args.WORDS &&
|
||||||
this._lastLangTranslated === args.LANGUAGE) {
|
this._lastLangTranslated === args.LANGUAGE) {
|
||||||
|
@ -261,28 +265,21 @@ class Scratch3TranslateBlocks {
|
||||||
urlBase += encodeURIComponent(args.WORDS);
|
urlBase += encodeURIComponent(args.WORDS);
|
||||||
|
|
||||||
const tempThis = this;
|
const tempThis = this;
|
||||||
const translatePromise = new Promise(resolve => {
|
const translatePromise = fetchWithTimeout(urlBase, {}, serverTimeoutMs)
|
||||||
nets({
|
.then(response => response.text())
|
||||||
url: urlBase,
|
.then(responseText => {
|
||||||
timeout: serverTimeoutMs
|
const translated = JSON.parse(responseText).result;
|
||||||
}, (err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
log.warn(`error fetching translate result! ${res}`);
|
|
||||||
resolve('');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const translated = JSON.parse(body).result;
|
|
||||||
tempThis._translateResult = translated;
|
tempThis._translateResult = translated;
|
||||||
// Cache what we just translated so we don't keep making the
|
// Cache what we just translated so we don't keep making the
|
||||||
// same call over and over.
|
// same call over and over.
|
||||||
tempThis._lastTextTranslated = args.WORDS;
|
tempThis._lastTextTranslated = args.WORDS;
|
||||||
tempThis._lastLangTranslated = args.LANGUAGE;
|
tempThis._lastLangTranslated = args.LANGUAGE;
|
||||||
resolve(translated);
|
|
||||||
return translated;
|
return translated;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
log.warn(`error fetching translate result! ${err}`);
|
||||||
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
|
||||||
translatePromise.then(translatedText => translatedText);
|
|
||||||
return translatePromise;
|
return translatePromise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,6 +180,24 @@ class BLE extends JSONRPC {
|
||||||
window.clearTimeout(this._discoverTimeoutID);
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'userDidPickPeripheral':
|
||||||
|
this._availablePeripherals[params.peripheralId] = params;
|
||||||
|
this._runtime.emit(
|
||||||
|
this._runtime.constructor.USER_PICKED_PERIPHERAL,
|
||||||
|
this._availablePeripherals
|
||||||
|
);
|
||||||
|
if (this._discoverTimeoutID) {
|
||||||
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'userDidNotPickPeripheral':
|
||||||
|
this._runtime.emit(
|
||||||
|
this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT
|
||||||
|
);
|
||||||
|
if (this._discoverTimeoutID) {
|
||||||
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'characteristicDidChange':
|
case 'characteristicDidChange':
|
||||||
if (this._characteristicDidChangeCallback) {
|
if (this._characteristicDidChangeCallback) {
|
||||||
this._characteristicDidChangeCallback(params.message);
|
this._characteristicDidChangeCallback(params.message);
|
||||||
|
|
18
src/io/bt.js
18
src/io/bt.js
|
@ -128,6 +128,24 @@ class BT extends JSONRPC {
|
||||||
window.clearTimeout(this._discoverTimeoutID);
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'userDidPickPeripheral':
|
||||||
|
this._availablePeripherals[params.peripheralId] = params;
|
||||||
|
this._runtime.emit(
|
||||||
|
this._runtime.constructor.USER_PICKED_PERIPHERAL,
|
||||||
|
this._availablePeripherals
|
||||||
|
);
|
||||||
|
if (this._discoverTimeoutID) {
|
||||||
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'userDidNotPickPeripheral':
|
||||||
|
this._runtime.emit(
|
||||||
|
this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT
|
||||||
|
);
|
||||||
|
if (this._discoverTimeoutID) {
|
||||||
|
window.clearTimeout(this._discoverTimeoutID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'didReceiveMessage':
|
case 'didReceiveMessage':
|
||||||
this._messageCallback(params); // TODO: refine?
|
this._messageCallback(params); // TODO: refine?
|
||||||
break;
|
break;
|
||||||
|
|
28
src/util/fetch-with-timeout.js
Normal file
28
src/util/fetch-with-timeout.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Fetch a remote resource like `fetch` does, but with a time limit.
|
||||||
|
* @param {Request|string} resource Remote resource to fetch.
|
||||||
|
* @param {?object} init An options object containing any custom settings that you want to apply to the request.
|
||||||
|
* @param {number} timeout The amount of time before the request is canceled, in milliseconds
|
||||||
|
* @returns {Promise<Response>} The response from the server.
|
||||||
|
*/
|
||||||
|
const fetchWithTimeout = (resource, init, timeout) => {
|
||||||
|
let timeoutID = null;
|
||||||
|
// Not supported in Safari <11
|
||||||
|
const controller = window.AbortController ? new window.AbortController() : null;
|
||||||
|
const signal = controller ? controller.signal : null;
|
||||||
|
// The fetch call races a timer.
|
||||||
|
return Promise.race([
|
||||||
|
fetch(resource, Object.assign({signal}, init)).then(response => {
|
||||||
|
clearTimeout(timeoutID);
|
||||||
|
return response;
|
||||||
|
}),
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
timeoutID = setTimeout(() => {
|
||||||
|
if (controller) controller.abort();
|
||||||
|
reject(new Error(`Fetch timed out after ${timeout} ms`));
|
||||||
|
}, timeout);
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = fetchWithTimeout;
|
|
@ -127,6 +127,9 @@ class VirtualMachine extends EventEmitter {
|
||||||
this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => {
|
this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => {
|
||||||
this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info);
|
this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info);
|
||||||
});
|
});
|
||||||
|
this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => {
|
||||||
|
this.emit(Runtime.USER_PICKED_PERIPHERAL, info);
|
||||||
|
});
|
||||||
this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () =>
|
this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () =>
|
||||||
this.emit(Runtime.PERIPHERAL_CONNECTED)
|
this.emit(Runtime.PERIPHERAL_CONNECTED)
|
||||||
);
|
);
|
||||||
|
|
|
@ -77,7 +77,6 @@ module.exports = [
|
||||||
'immutable': true,
|
'immutable': true,
|
||||||
'jszip': true,
|
'jszip': true,
|
||||||
'minilog': true,
|
'minilog': true,
|
||||||
'nets': true,
|
|
||||||
'scratch-parser': true,
|
'scratch-parser': true,
|
||||||
'socket.io-client': true,
|
'socket.io-client': true,
|
||||||
'text-encoding': true
|
'text-encoding': true
|
||||||
|
|
Loading…
Reference in a new issue