init
286
scratch-gui/src/lib/alerts/index.jsx
Normal file
@@ -0,0 +1,286 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import keyMirror from 'keymirror';
|
||||
|
||||
import successImage from '../assets/icon--success.svg';
|
||||
|
||||
const AlertTypes = keyMirror({
|
||||
STANDARD: null,
|
||||
EXTENSION: null,
|
||||
INLINE: null
|
||||
});
|
||||
|
||||
const AlertLevels = {
|
||||
SUCCESS: 'success',
|
||||
INFO: 'info',
|
||||
WARN: 'warn'
|
||||
};
|
||||
|
||||
const alerts = [
|
||||
{
|
||||
alertId: 'createSuccess',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="New project created."
|
||||
description="Message indicating that project was successfully created"
|
||||
id="gui.alerts.createsuccess"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 5
|
||||
},
|
||||
{
|
||||
alertId: 'createCopySuccess',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Project saved as a copy."
|
||||
description="Message indicating that project was successfully created"
|
||||
id="gui.alerts.createcopysuccess"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 5
|
||||
},
|
||||
{
|
||||
alertId: 'createRemixSuccess',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Project saved as a remix."
|
||||
description="Message indicating that project was successfully created"
|
||||
id="gui.alerts.createremixsuccess"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 5
|
||||
},
|
||||
{
|
||||
alertId: 'creating',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Creating new…"
|
||||
description="Message indicating that project is in process of creating"
|
||||
id="gui.alerts.creating"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.SUCCESS
|
||||
},
|
||||
{
|
||||
alertId: 'creatingCopy',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Copying project…"
|
||||
description="Message indicating that project is in process of copying"
|
||||
id="gui.alerts.creatingCopy"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.SUCCESS
|
||||
},
|
||||
{
|
||||
alertId: 'creatingRemix',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Remixing project…"
|
||||
description="Message indicating that project is in process of remixing"
|
||||
id="gui.alerts.creatingRemix"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.SUCCESS
|
||||
},
|
||||
{
|
||||
alertId: 'creatingError',
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
closeButton: true,
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Could not create the project. Please try again!"
|
||||
description="Message indicating that project could not be created"
|
||||
id="gui.alerts.creatingError"
|
||||
/>
|
||||
),
|
||||
level: AlertLevels.WARN
|
||||
},
|
||||
{
|
||||
alertId: 'savingError',
|
||||
clearList: ['createSuccess', 'creating', 'createCopySuccess', 'creatingCopy',
|
||||
'createRemixSuccess', 'creatingRemix', 'saveSuccess', 'saving'],
|
||||
showDownload: true,
|
||||
// showSaveNow: true,
|
||||
closeButton: true,
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Project could not save."
|
||||
description="Message indicating that project could not be saved"
|
||||
id="gui.alerts.savingError"
|
||||
/>
|
||||
),
|
||||
level: AlertLevels.WARN
|
||||
},
|
||||
{
|
||||
alertId: 'saveSuccess',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['saveSuccess', 'saving', 'savingError', 'twSaveToDiskSuccess',
|
||||
'twCreatingRestorePoint', 'twRestorePointSuccess', 'twRestorePointError'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Project saved."
|
||||
description="Message indicating that project was successfully saved"
|
||||
id="gui.alerts.savesuccess"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 3
|
||||
},
|
||||
{
|
||||
alertId: 'twSaveToDiskSuccess',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['saveSuccess', 'saving', 'savingError', 'twCreatingRestorePoint',
|
||||
'twRestorePointSuccess', 'twRestorePointError'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Saved to your computer."
|
||||
description="Message indicating that project was successfully saved to the user's disk"
|
||||
id="tw.alerts.savedToDisk"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 3
|
||||
},
|
||||
{
|
||||
alertId: 'saving',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['saveSuccess', 'saving', 'savingError', 'twSaveToDiskSuccess',
|
||||
'twCreatingRestorePoint', 'twRestorePointSuccess', 'twRestorePointError'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Saving project…"
|
||||
description="Message indicating that project is in process of saving"
|
||||
id="gui.alerts.saving"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.INFO
|
||||
},
|
||||
{
|
||||
alertId: 'twCreatingRestorePoint',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['twRestorePointSuccess', 'twRestorePointError'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Creating restore point…"
|
||||
description="Menu bar message indicating that a restore point is being automatically created"
|
||||
id="tw.alerts.creatingRestorePoint"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.INFO
|
||||
},
|
||||
{
|
||||
alertId: 'twRestorePointSuccess',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['twCreatingRestorePoint', 'twRestorePointError'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Access restore points in "File""
|
||||
// eslint-disable-next-line max-len
|
||||
description="Menu bar message indicating that a restore point was successfully created. File refers to the file dropdown menu."
|
||||
id="tw.alerts.restorePointSuccess"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 3
|
||||
},
|
||||
{
|
||||
alertId: 'twRestorePointError',
|
||||
alertType: AlertTypes.INLINE,
|
||||
clearList: ['twCreatingRestorePoint', 'twRestorePointSuccess'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Could not create restore point"
|
||||
// eslint-disable-next-line max-len
|
||||
description="Menu bar message indicating that a restore point could not be created."
|
||||
id="tw.alerts.restorePointError"
|
||||
/>
|
||||
),
|
||||
iconURL: successImage,
|
||||
level: AlertLevels.WARN,
|
||||
maxDisplaySecs: 5
|
||||
},
|
||||
{
|
||||
alertId: 'cloudInfo',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: ['cloudInfo'],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Please note, cloud variables only support numbers, not letters or symbols. {learnMoreLink}" // eslint-disable-line max-len
|
||||
description="Info about cloud variable limitations"
|
||||
id="gui.alerts.cloudInfo"
|
||||
values={{
|
||||
learnMoreLink: (
|
||||
<a
|
||||
href="https://scratch.mit.edu/info/faq/#clouddata"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more."
|
||||
description="Link text to cloud var faq"
|
||||
id="gui.alerts.cloudInfoLearnMore"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
),
|
||||
closeButton: true,
|
||||
level: AlertLevels.SUCCESS,
|
||||
maxDisplaySecs: 15
|
||||
},
|
||||
{
|
||||
alertId: 'importingAsset',
|
||||
alertType: AlertTypes.STANDARD,
|
||||
clearList: [],
|
||||
content: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Importing…"
|
||||
description="Message indicating that project is in process of importing"
|
||||
id="gui.alerts.importing"
|
||||
/>
|
||||
),
|
||||
iconSpinner: true,
|
||||
level: AlertLevels.SUCCESS
|
||||
}
|
||||
];
|
||||
|
||||
export {
|
||||
alerts as default,
|
||||
AlertLevels,
|
||||
AlertTypes
|
||||
};
|
||||
6
scratch-gui/src/lib/analytics.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const GoogleAnalytics = {
|
||||
// Disable Scratch's analytics
|
||||
event () {}
|
||||
};
|
||||
|
||||
export default GoogleAnalytics;
|
||||
136
scratch-gui/src/lib/app-state-hoc.jsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Provider} from 'react-redux';
|
||||
import {createStore, combineReducers, compose} from 'redux';
|
||||
import ConnectedIntlProvider from './connected-intl-provider.jsx';
|
||||
import AddonHooks from '../addons/hooks';
|
||||
|
||||
import localesReducer, {initLocale, localesInitialState} from '../reducers/locales';
|
||||
|
||||
import {setPlayer, setFullScreen} from '../reducers/mode.js';
|
||||
|
||||
import locales from '@turbowarp/scratch-l10n';
|
||||
import {detectLocale} from './detect-locale';
|
||||
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
/*
|
||||
* Higher Order Component to provide redux state. If an `intl` prop is provided
|
||||
* it will override the internal `intl` redux state
|
||||
* @param {React.Component} WrappedComponent - component to provide state for
|
||||
* @param {boolean} localesOnly - only provide the locale state, not everything
|
||||
* required by the GUI. Used to exclude excess state when
|
||||
only rendering modals, not the GUI.
|
||||
* @returns {React.Component} component with redux and intl state provided
|
||||
*/
|
||||
const AppStateHOC = function (WrappedComponent, localesOnly) {
|
||||
class AppStateWrapper extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
let initialState = {};
|
||||
let reducers = {};
|
||||
let enhancer;
|
||||
|
||||
let initializedLocales = localesInitialState;
|
||||
const locale = detectLocale(Object.keys(locales));
|
||||
if (locale !== 'en') {
|
||||
initializedLocales = initLocale(initializedLocales, locale);
|
||||
}
|
||||
if (localesOnly) {
|
||||
// Used for instantiating minimal state for the unsupported
|
||||
// browser modal
|
||||
reducers = {locales: localesReducer};
|
||||
initialState = {locales: initializedLocales};
|
||||
enhancer = composeEnhancers();
|
||||
} else {
|
||||
// You are right, this is gross. But it's necessary to avoid
|
||||
// importing unneeded code that will crash unsupported browsers.
|
||||
const guiRedux = require('../reducers/gui');
|
||||
const guiReducer = guiRedux.default;
|
||||
const {
|
||||
guiInitialState,
|
||||
guiMiddleware,
|
||||
initFullScreen,
|
||||
initPlayer,
|
||||
initEmbedded,
|
||||
initTelemetryModal
|
||||
} = guiRedux;
|
||||
const {ScratchPaintReducer} = require('./tw-scratch-paint');
|
||||
|
||||
let initializedGui = guiInitialState;
|
||||
if (props.isFullScreen || props.isPlayerOnly) {
|
||||
if (props.isFullScreen) {
|
||||
initializedGui = initFullScreen(initializedGui);
|
||||
}
|
||||
if (props.isPlayerOnly) {
|
||||
initializedGui = initPlayer(initializedGui);
|
||||
}
|
||||
} else if (props.showTelemetryModal) {
|
||||
initializedGui = initTelemetryModal(initializedGui);
|
||||
}
|
||||
if (props.isEmbedded) {
|
||||
initializedGui = initEmbedded(initializedGui);
|
||||
}
|
||||
reducers = {
|
||||
locales: localesReducer,
|
||||
scratchGui: guiReducer,
|
||||
scratchPaint: ScratchPaintReducer
|
||||
};
|
||||
initialState = {
|
||||
locales: initializedLocales,
|
||||
scratchGui: initializedGui
|
||||
};
|
||||
enhancer = composeEnhancers(guiMiddleware);
|
||||
}
|
||||
const reducer = combineReducers(reducers);
|
||||
const reducer2 = (previousState, action) => {
|
||||
const nextState = reducer(previousState, action);
|
||||
AddonHooks.appStateReducer(action, previousState, nextState);
|
||||
return nextState;
|
||||
};
|
||||
this.store = createStore(
|
||||
reducer2,
|
||||
initialState,
|
||||
enhancer
|
||||
);
|
||||
window.ReduxStore = this.store;
|
||||
AddonHooks.appStateStore = this.store;
|
||||
}
|
||||
componentDidUpdate (prevProps) {
|
||||
if (localesOnly) return;
|
||||
if (prevProps.isPlayerOnly !== this.props.isPlayerOnly) {
|
||||
this.store.dispatch(setPlayer(this.props.isPlayerOnly));
|
||||
}
|
||||
if (prevProps.isFullScreen !== this.props.isFullScreen) {
|
||||
this.store.dispatch(setFullScreen(this.props.isFullScreen));
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
isFullScreen, // eslint-disable-line no-unused-vars
|
||||
isPlayerOnly, // eslint-disable-line no-unused-vars
|
||||
showTelemetryModal, // eslint-disable-line no-unused-vars
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<Provider store={this.store}>
|
||||
<ConnectedIntlProvider>
|
||||
<WrappedComponent
|
||||
{...componentProps}
|
||||
/>
|
||||
</ConnectedIntlProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
AppStateWrapper.propTypes = {
|
||||
isFullScreen: PropTypes.bool,
|
||||
isPlayerOnly: PropTypes.bool,
|
||||
isTelemetryEnabled: PropTypes.bool,
|
||||
showTelemetryModal: PropTypes.bool,
|
||||
isEmbedded: PropTypes.bool
|
||||
};
|
||||
return AppStateWrapper;
|
||||
};
|
||||
|
||||
export default AppStateHOC;
|
||||
12
scratch-gui/src/lib/assets/icon--back.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>back-icon</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="back-icon" fill="#FFFFFF">
|
||||
<path d="M4.10804912,8.57765055 L9.33203638,3.35366329 C9.63820413,3.04558199 10.1165912,3.04558199 10.422759,3.35366329 L15.6467462,8.57765055 C16.1442688,9.07517314 15.7806946,9.89608543 15.0918172,9.89608543 L12.4893913,9.89608543 L10.9394171,16.0385759 C10.7863332,16.6336895 10.1739977,16.9953502 9.58079768,16.8422663 C9.15981702,16.7485024 8.85364927,16.4231992 8.75797184,16.0385759 L7.2079976,9.89608543 L4.66297817,9.89608543 C3.97410073,9.89608543 3.61052653,9.07517314 4.10804912,8.57765055" id="Fill-1" transform="translate(9.877398, 10.000000) rotate(-90.000000) translate(-9.877398, -10.000000) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
10
scratch-gui/src/lib/assets/icon--help.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>help</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="help" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M9.99905449,18 C5.58160974,18 2,14.4173461 2,10.0009453 C2,5.58265391 5.58160974,2 9.99905449,2 C14.4183903,2 18,5.58265391 18,10.0009453 C18,14.4173461 14.4183903,18 9.99905449,18 Z M9.85325612,12.3615266 C9.26892802,12.3615266 8.80562581,12.8360629 8.80562581,13.4221434 C8.80562581,13.9930994 9.26892802,14.4676356 9.85325612,14.4676356 C10.4394752,14.4676356 10.9141236,13.9930994 10.9141236,13.4221434 C10.9141236,12.8360629 10.4394752,12.3615266 9.85325612,12.3615266 Z M9.46181302,11.6431053 L10.1596029,11.6431053 C10.3487058,11.6431053 10.5245716,11.5239986 10.5699563,11.3406121 C10.6588347,10.9700579 10.9141236,10.7167198 11.2545089,10.4614912 L11.7159201,10.1211863 C12.4458575,9.57291741 12.8978135,8.90554177 12.8978135,7.93000118 C12.8978135,6.71435661 11.9106961,5.53274253 9.93835244,5.53274253 C8.16078478,5.53274253 7.1018083,6.7370436 7.1018083,8.16065225 C7.1018083,8.1984639 7.10369933,8.23627555 7.10369933,8.27597779 C7.11315447,8.49717594 7.28902021,8.67867187 7.51027065,8.6862342 L8.38392625,8.71648352 C8.62408699,8.72593643 8.81886302,8.53309701 8.81886302,8.29299303 L8.81886302,8.28354012 C8.81886302,7.71258419 9.23299846,7.13973768 9.93835244,7.13973768 C10.6701808,7.13973768 11.0219123,7.60103982 11.0219123,8.07557604 C11.0219123,8.3931939 10.9141236,8.6862342 10.5850845,8.93011934 L9.96482685,9.39331206 C9.26892802,9.91511284 9.0363314,10.5352239 9.0363314,11.1080704 C9.0363314,11.1515538 9.03822243,11.1950372 9.03822243,11.2347395 C9.04578655,11.4634999 9.23299846,11.6431053 9.46181302,11.6431053 Z" id="help-icon" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
9
scratch-gui/src/lib/assets/icon--success.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Alerts/Check</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Alerts/Check" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M15.224782,4.51424732 C15.8244301,3.86519582 16.8367012,3.82514645 17.4857527,4.42479455 C18.1348042,5.02444265 18.1748535,6.03671368 17.5752055,6.68576518 L9.4450369,15.4857566 C8.81157357,16.1714093 7.72807682,16.1714093 7.09461349,15.4857566 L2.42479455,10.4312037 C1.82514645,9.78215224 1.86519582,8.76988121 2.51424732,8.17023311 C3.16329883,7.57058501 4.17556986,7.61063439 4.77521796,8.25968589 L8.26982519,12.0422045 L15.224782,4.51424732 Z" id="Check" fill="#0EBD8C" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 962 B |
10
scratch-gui/src/lib/assets/icon--tutorials.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Tutorials</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Tutorials" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M19.0000002,9.48986925 C19.0000002,9.86977638 18.6906473,10.1592294 18.3125492,10.1592294 L17.2813727,10.1592294 C16.9014655,10.1592294 16.5939217,9.86977638 16.5939217,9.48986925 C16.5939217,9.10996211 16.9014655,8.80241823 17.2813727,8.80241823 L18.3125492,8.80241823 C18.6906473,8.80241823 19.0000002,9.10996211 19.0000002,9.48986925 Z M5.32243869,14.1574807 C5.59380093,14.428843 5.59380093,14.8449317 5.32243869,15.116294 L4.60061513,15.8580174 C4.4558886,15.9846531 4.29307126,16.0570164 4.11035401,16.0570164 C3.92944585,16.0570164 3.76662851,15.9846531 3.62371106,15.8580174 C3.36863055,15.5866552 3.36863055,15.1524756 3.62371106,14.8992042 L4.36362544,14.1574807 C4.63498768,13.8861185 5.06916727,13.8861185 5.32243869,14.1574807 Z M16.358922,3.12262561 C16.6121934,3.39217877 16.6121934,3.82816744 16.358922,4.07962978 L15.6171985,4.82135324 C15.4905628,4.94979804 15.3096546,5.0221613 15.1468373,5.0221613 C14.96412,5.0221613 14.8031118,4.94979804 14.6583852,4.82135324 C14.4051138,4.55180008 14.4051138,4.1176205 14.6583852,3.86253999 L15.4001087,3.12262561 C15.6515711,2.85126337 16.0875597,2.85126337 16.358922,3.12262561 Z M12.4138577,17.3409216 C12.5965749,18.5005429 11.9254057,19.477447 9.98968834,19.477447 C8.0557801,19.477447 7.38461082,18.5005429 7.56551898,17.3409216 C7.65597306,16.8343788 8.14623418,17.5236389 9.98968834,17.5580114 C11.8530424,17.5580114 12.3414944,16.8343788 12.4138577,17.3409216 Z M3.62280652,4.08017251 C3.36772601,3.82690108 3.36772601,3.39272149 3.62280652,3.12316833 C3.89235968,2.84999701 4.32834835,2.84999701 4.59971059,3.12316833 L5.32153415,3.86308271 C5.59470547,4.11816322 5.59470547,4.55234281 5.32153415,4.82189597 C5.19670752,4.94853168 5.01579936,5.02270403 4.85298201,5.02270403 C4.67026477,5.02270403 4.48935661,4.94853168 4.3627209,4.82189597 L3.62280652,4.08017251 Z M3.38798773,9.48986925 C3.38798773,9.86977638 3.08044385,10.1592294 2.71862753,10.1592294 L1.6693602,10.1592294 C1.30754387,10.1592294 1,9.86977638 1,9.48986925 C1,9.10996211 1.30754387,8.80241823 1.6693602,8.80241823 L2.71862753,8.80241823 C3.08044385,8.80241823 3.38798773,9.10996211 3.38798773,9.48986925 Z M9.32050905,2.19872763 L9.32050905,1.1693602 C9.32050905,0.789453058 9.60996211,0.5 9.98986925,0.5 C10.3697764,0.5 10.6773203,0.789453058 10.6773203,1.1693602 L10.6773203,2.19872763 C10.6773203,2.57863477 10.3697764,2.88617864 9.98986925,2.88617864 C9.60996211,2.88617864 9.32050905,2.57863477 9.32050905,2.19872763 Z M15.0021108,8.80259914 C15.1269374,10.1594103 14.7307485,11.4257675 13.9709342,12.3827716 C13.2635833,13.3072124 12.8113129,13.5062113 12.7932221,14.6278419 C12.7932221,15.206748 12.5037691,15.6771093 12.0334078,15.8941991 L12.0171261,15.8941991 C10.7326782,16.5273776 9.24742215,16.5092868 7.96297421,15.8941991 C7.51070381,15.6771093 7.22125075,15.206748 7.22125075,14.7002052 C7.22125075,13.5243021 6.75088953,13.343394 6.02725689,12.4026715 C5.35970577,11.5524032 4.95989874,10.4669542 4.95989874,9.30914199 C4.95989874,6.32415734 7.54869452,3.97235124 10.5879516,4.31607675 C12.9035761,4.58743899 14.785021,6.48697468 15.0021108,8.80259914 Z M16.358922,14.8993851 C16.6121934,15.1526565 16.6121934,15.5868361 16.358922,15.8581983 C16.2322863,15.984834 16.0513781,16.0571973 15.8686608,16.0571973 C15.7076526,16.0571973 15.5249353,15.984834 15.4001087,15.8581983 L14.6583852,15.1164749 C14.4051138,14.8451126 14.4051138,14.4290239 14.6583852,14.1576616 C14.9297475,13.8844903 15.362118,13.8844903 15.6171985,14.1576616 L16.358922,14.8993851 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
10
scratch-gui/src/lib/assets/placeholder.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49.3 (51167) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Artboard</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="icon" stroke="#FFFFFF" stroke-width="2" x="1" y="1" width="18" height="18" rx="4"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 559 B |
59
scratch-gui/src/lib/audio/audio-buffer-player.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import SharedAudioContext from './shared-audio-context.js';
|
||||
|
||||
class AudioBufferPlayer {
|
||||
constructor (samples, sampleRate) {
|
||||
this.audioContext = new SharedAudioContext();
|
||||
this.buffer = this.audioContext.createBuffer(1, samples.length, sampleRate);
|
||||
this.buffer.getChannelData(0).set(samples);
|
||||
this.source = null;
|
||||
|
||||
this.startTime = null;
|
||||
this.updateCallback = null;
|
||||
this.trimStart = null;
|
||||
this.trimEnd = null;
|
||||
}
|
||||
|
||||
play (trimStart, trimEnd, onUpdate, onEnded) {
|
||||
this.updateCallback = onUpdate;
|
||||
this.trimStart = trimStart;
|
||||
this.trimEnd = trimEnd;
|
||||
this.startTime = Date.now();
|
||||
|
||||
const trimStartTime = this.buffer.duration * trimStart;
|
||||
const trimmedDuration = (this.buffer.duration * trimEnd) - trimStartTime;
|
||||
|
||||
this.source = this.audioContext.createBufferSource();
|
||||
this.source.onended = onEnded;
|
||||
this.source.buffer = this.buffer;
|
||||
this.source.connect(this.audioContext.destination);
|
||||
this.source.start(0, trimStartTime, trimmedDuration);
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
update () {
|
||||
const timeSinceStart = (Date.now() - this.startTime) / 1000;
|
||||
const percentage = timeSinceStart / this.buffer.duration;
|
||||
if (percentage + this.trimStart < this.trimEnd && this.source.onended) {
|
||||
requestAnimationFrame(this.update.bind(this));
|
||||
this.updateCallback(percentage + this.trimStart);
|
||||
} else {
|
||||
this.updateCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
stop () {
|
||||
if (this.source) {
|
||||
this.source.onended = null; // Do not call onEnded callback if manually stopped
|
||||
try {
|
||||
this.source.stop();
|
||||
} catch (e) {
|
||||
// This is probably Safari, which dies when you call stop more than once
|
||||
// which the spec says is allowed: https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode
|
||||
console.log('Caught error while stopping buffer source node.'); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AudioBufferPlayer;
|
||||
161
scratch-gui/src/lib/audio/audio-effects.js
Normal file
@@ -0,0 +1,161 @@
|
||||
import EchoEffect from './effects/echo-effect.js';
|
||||
import RobotEffect from './effects/robot-effect.js';
|
||||
import VolumeEffect from './effects/volume-effect.js';
|
||||
import FadeEffect from './effects/fade-effect.js';
|
||||
import MuteEffect from './effects/mute-effect.js';
|
||||
|
||||
const effectTypes = {
|
||||
ROBOT: 'robot',
|
||||
REVERSE: 'reverse',
|
||||
LOUDER: 'higher',
|
||||
SOFTER: 'lower',
|
||||
FASTER: 'faster',
|
||||
SLOWER: 'slower',
|
||||
ECHO: 'echo',
|
||||
FADEIN: 'fade in',
|
||||
FADEOUT: 'fade out',
|
||||
MUTE: 'mute'
|
||||
};
|
||||
|
||||
class AudioEffects {
|
||||
static get effectTypes () {
|
||||
return effectTypes;
|
||||
}
|
||||
constructor (buffer, name, trimStart, trimEnd) {
|
||||
this.trimStartSeconds = (trimStart * buffer.length) / buffer.sampleRate;
|
||||
this.trimEndSeconds = (trimEnd * buffer.length) / buffer.sampleRate;
|
||||
this.adjustedTrimStartSeconds = this.trimStartSeconds;
|
||||
this.adjustedTrimEndSeconds = this.trimEndSeconds;
|
||||
|
||||
// Some effects will modify the playback rate and/or number of samples.
|
||||
// Need to precompute those values to create the offline audio context.
|
||||
const pitchRatio = Math.pow(2, 4 / 12); // A major third
|
||||
let sampleCount = buffer.length;
|
||||
const affectedSampleCount = Math.floor((this.trimEndSeconds - this.trimStartSeconds) *
|
||||
buffer.sampleRate);
|
||||
let adjustedAffectedSampleCount = affectedSampleCount;
|
||||
const unaffectedSampleCount = sampleCount - affectedSampleCount;
|
||||
|
||||
this.playbackRate = 1;
|
||||
switch (name) {
|
||||
case effectTypes.ECHO:
|
||||
sampleCount = Math.max(sampleCount,
|
||||
Math.floor((this.trimEndSeconds + EchoEffect.TAIL_SECONDS) * buffer.sampleRate));
|
||||
break;
|
||||
case effectTypes.FASTER:
|
||||
this.playbackRate = pitchRatio;
|
||||
adjustedAffectedSampleCount = Math.floor(affectedSampleCount / this.playbackRate);
|
||||
sampleCount = unaffectedSampleCount + adjustedAffectedSampleCount;
|
||||
|
||||
break;
|
||||
case effectTypes.SLOWER:
|
||||
this.playbackRate = 1 / pitchRatio;
|
||||
adjustedAffectedSampleCount = Math.floor(affectedSampleCount / this.playbackRate);
|
||||
sampleCount = unaffectedSampleCount + adjustedAffectedSampleCount;
|
||||
break;
|
||||
}
|
||||
|
||||
const durationSeconds = sampleCount / buffer.sampleRate;
|
||||
this.adjustedTrimEndSeconds = this.trimStartSeconds +
|
||||
(adjustedAffectedSampleCount / buffer.sampleRate);
|
||||
this.adjustedTrimStart = this.adjustedTrimStartSeconds / durationSeconds;
|
||||
this.adjustedTrimEnd = this.adjustedTrimEndSeconds / durationSeconds;
|
||||
|
||||
if (window.OfflineAudioContext) {
|
||||
this.audioContext = new window.OfflineAudioContext(1, sampleCount, buffer.sampleRate);
|
||||
} else {
|
||||
// Need to use webkitOfflineAudioContext, which doesn't support all sample rates.
|
||||
// Resample by adjusting sample count to make room and set offline context to desired sample rate.
|
||||
const sampleScale = 44100 / buffer.sampleRate;
|
||||
this.audioContext = new window.webkitOfflineAudioContext(1, sampleScale * sampleCount, 44100);
|
||||
}
|
||||
|
||||
// For the reverse effect we need to manually reverse the data into a new audio buffer
|
||||
// to prevent overwriting the original, so that the undo stack works correctly.
|
||||
// Doing buffer.reverse() would mutate the original data.
|
||||
if (name === effectTypes.REVERSE) {
|
||||
const originalBufferData = buffer.getChannelData(0);
|
||||
const newBuffer = this.audioContext.createBuffer(1, buffer.length, buffer.sampleRate);
|
||||
const newBufferData = newBuffer.getChannelData(0);
|
||||
const bufferLength = buffer.length;
|
||||
|
||||
const startSamples = Math.floor(this.trimStartSeconds * buffer.sampleRate);
|
||||
const endSamples = Math.floor(this.trimEndSeconds * buffer.sampleRate);
|
||||
let counter = 0;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
if (i >= startSamples && i < endSamples) {
|
||||
newBufferData[i] = originalBufferData[endSamples - counter - 1];
|
||||
counter++;
|
||||
} else {
|
||||
newBufferData[i] = originalBufferData[i];
|
||||
}
|
||||
}
|
||||
this.buffer = newBuffer;
|
||||
} else {
|
||||
// All other effects use the original buffer because it is not modified.
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
this.source = this.audioContext.createBufferSource();
|
||||
this.source.buffer = this.buffer;
|
||||
this.name = name;
|
||||
}
|
||||
process (done) {
|
||||
// Some effects need to use more nodes and must expose an input and output
|
||||
let input;
|
||||
let output;
|
||||
switch (this.name) {
|
||||
case effectTypes.FASTER:
|
||||
case effectTypes.SLOWER:
|
||||
this.source.playbackRate.setValueAtTime(this.playbackRate, this.adjustedTrimStartSeconds);
|
||||
this.source.playbackRate.setValueAtTime(1.0, this.adjustedTrimEndSeconds);
|
||||
break;
|
||||
case effectTypes.LOUDER:
|
||||
({input, output} = new VolumeEffect(this.audioContext, 1.25,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.SOFTER:
|
||||
({input, output} = new VolumeEffect(this.audioContext, 0.75,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.ECHO:
|
||||
({input, output} = new EchoEffect(this.audioContext,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.ROBOT:
|
||||
({input, output} = new RobotEffect(this.audioContext,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.FADEIN:
|
||||
({input, output} = new FadeEffect(this.audioContext, true,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.FADEOUT:
|
||||
({input, output} = new FadeEffect(this.audioContext, false,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
case effectTypes.MUTE:
|
||||
({input, output} = new MuteEffect(this.audioContext,
|
||||
this.adjustedTrimStartSeconds, this.adjustedTrimEndSeconds));
|
||||
break;
|
||||
}
|
||||
|
||||
if (input && output) {
|
||||
this.source.connect(input);
|
||||
output.connect(this.audioContext.destination);
|
||||
} else {
|
||||
// No effects nodes are needed, wire directly to the output
|
||||
this.source.connect(this.audioContext.destination);
|
||||
}
|
||||
|
||||
this.source.start();
|
||||
|
||||
this.audioContext.startRendering();
|
||||
this.audioContext.oncomplete = ({renderedBuffer}) => {
|
||||
done(renderedBuffer, this.adjustedTrimStart, this.adjustedTrimEnd);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default AudioEffects;
|
||||
139
scratch-gui/src/lib/audio/audio-recorder.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import 'get-float-time-domain-data';
|
||||
import getUserMedia from 'get-user-media-promise';
|
||||
import SharedAudioContext from './shared-audio-context.js';
|
||||
import {computeRMS, computeChunkedRMS} from './audio-util.js';
|
||||
|
||||
class AudioRecorder {
|
||||
constructor () {
|
||||
this.audioContext = new SharedAudioContext();
|
||||
this.bufferLength = 8192;
|
||||
|
||||
this.userMediaStream = null;
|
||||
this.mediaStreamSource = null;
|
||||
this.sourceNode = null;
|
||||
this.scriptProcessorNode = null;
|
||||
|
||||
this.recordedSamples = 0;
|
||||
this.recording = false;
|
||||
this.started = false;
|
||||
this.buffers = [];
|
||||
|
||||
this.disposed = false;
|
||||
}
|
||||
|
||||
startListening (onStarted, onUpdate, onError) {
|
||||
try {
|
||||
getUserMedia({audio: true})
|
||||
.then(userMediaStream => {
|
||||
if (!this.disposed) {
|
||||
this.started = true;
|
||||
onStarted();
|
||||
this.attachUserMediaStream(userMediaStream, onUpdate);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
if (!this.disposed) {
|
||||
onError(e);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!this.disposed) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startRecording () {
|
||||
this.recording = true;
|
||||
}
|
||||
|
||||
attachUserMediaStream (userMediaStream, onUpdate) {
|
||||
this.userMediaStream = userMediaStream;
|
||||
this.mediaStreamSource = this.audioContext.createMediaStreamSource(userMediaStream);
|
||||
this.sourceNode = this.audioContext.createGain();
|
||||
this.scriptProcessorNode = this.audioContext.createScriptProcessor(this.bufferLength, 1, 1);
|
||||
|
||||
this.scriptProcessorNode.onaudioprocess = processEvent => {
|
||||
if (this.recording && !this.disposed) {
|
||||
this.buffers.push(new Float32Array(processEvent.inputBuffer.getChannelData(0)));
|
||||
}
|
||||
};
|
||||
|
||||
this.analyserNode = this.audioContext.createAnalyser();
|
||||
|
||||
this.analyserNode.fftSize = 2048;
|
||||
|
||||
const bufferLength = this.analyserNode.frequencyBinCount;
|
||||
const dataArray = new Float32Array(bufferLength);
|
||||
|
||||
const update = () => {
|
||||
if (this.disposed) return;
|
||||
this.analyserNode.getFloatTimeDomainData(dataArray);
|
||||
onUpdate(computeRMS(dataArray));
|
||||
requestAnimationFrame(update);
|
||||
};
|
||||
|
||||
requestAnimationFrame(update);
|
||||
|
||||
// Wire everything together, ending in the destination
|
||||
this.mediaStreamSource.connect(this.sourceNode);
|
||||
this.sourceNode.connect(this.analyserNode);
|
||||
this.analyserNode.connect(this.scriptProcessorNode);
|
||||
this.scriptProcessorNode.connect(this.audioContext.destination);
|
||||
}
|
||||
|
||||
stop () {
|
||||
const buffer = new Float32Array(this.buffers.length * this.bufferLength);
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0; i < this.buffers.length; i++) {
|
||||
const bufferChunk = this.buffers[i];
|
||||
buffer.set(bufferChunk, offset);
|
||||
offset += bufferChunk.length;
|
||||
}
|
||||
|
||||
const chunkLevels = computeChunkedRMS(buffer);
|
||||
const maxRMS = Math.max.apply(null, chunkLevels);
|
||||
const threshold = maxRMS / 8;
|
||||
|
||||
let firstChunkAboveThreshold = null;
|
||||
let lastChunkAboveThreshold = null;
|
||||
for (let i = 0; i < chunkLevels.length; i++) {
|
||||
if (chunkLevels[i] > threshold) {
|
||||
if (firstChunkAboveThreshold === null) firstChunkAboveThreshold = i + 1;
|
||||
lastChunkAboveThreshold = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let trimStart = Math.max(2, firstChunkAboveThreshold - 2) / this.buffers.length;
|
||||
let trimEnd = Math.min(this.buffers.length - 2, lastChunkAboveThreshold + 2) / this.buffers.length;
|
||||
|
||||
// With very few samples, the automatic trimming can produce invalid values
|
||||
if (trimStart >= trimEnd) {
|
||||
trimStart = 0;
|
||||
trimEnd = 1;
|
||||
}
|
||||
|
||||
return {
|
||||
levels: chunkLevels,
|
||||
samples: buffer,
|
||||
sampleRate: this.audioContext.sampleRate,
|
||||
trimStart: trimStart,
|
||||
trimEnd: trimEnd
|
||||
};
|
||||
}
|
||||
|
||||
dispose () {
|
||||
if (this.started) {
|
||||
this.scriptProcessorNode.onaudioprocess = null;
|
||||
this.scriptProcessorNode.disconnect();
|
||||
this.analyserNode.disconnect();
|
||||
this.sourceNode.disconnect();
|
||||
this.mediaStreamSource.disconnect();
|
||||
this.userMediaStream.getAudioTracks()[0].stop();
|
||||
}
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default AudioRecorder;
|
||||
113
scratch-gui/src/lib/audio/audio-util.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import WavEncoder from 'wav-encoder';
|
||||
|
||||
export const SOUND_BYTE_LIMIT = 10 * 1000 * 1000; // 10mb
|
||||
|
||||
const _computeRMS = function (samples, start, end, scaling = 0.55) {
|
||||
const length = end - start;
|
||||
if (length === 0) return 0;
|
||||
// Calculate RMS, adapted from https://github.com/Tonejs/Tone.js/blob/master/Tone/component/Meter.js#L88
|
||||
let sum = 0;
|
||||
for (let i = start; i < end; i++) {
|
||||
const sample = samples[i];
|
||||
sum += sample ** 2;
|
||||
}
|
||||
const rms = Math.sqrt(sum / length);
|
||||
const val = rms / scaling;
|
||||
return Math.sqrt(val);
|
||||
};
|
||||
|
||||
const computeRMS = (samples, scaling) => _computeRMS(samples, 0, samples.length, scaling);
|
||||
|
||||
const computeChunkedRMS = function (samples, chunkSize = 1024) {
|
||||
const sampleCount = samples.length;
|
||||
const chunkLevels = [];
|
||||
for (let i = 0; i < sampleCount; i += chunkSize) {
|
||||
const maxIndex = Math.min(sampleCount, i + chunkSize);
|
||||
chunkLevels.push(_computeRMS(samples, i, maxIndex));
|
||||
}
|
||||
return chunkLevels;
|
||||
};
|
||||
|
||||
const encodeAndAddSoundToVM = function (vm, samples, sampleRate, name, callback) {
|
||||
WavEncoder.encode({
|
||||
sampleRate: sampleRate,
|
||||
channelData: [samples]
|
||||
}).then(wavBuffer => {
|
||||
const vmSound = {
|
||||
format: '',
|
||||
dataFormat: 'wav',
|
||||
rate: sampleRate,
|
||||
sampleCount: samples.length
|
||||
};
|
||||
|
||||
// Create an asset from the encoded .wav and get resulting md5
|
||||
const storage = vm.runtime.storage;
|
||||
vmSound.asset = storage.createAsset(
|
||||
storage.AssetType.Sound,
|
||||
storage.DataFormat.WAV,
|
||||
new Uint8Array(wavBuffer),
|
||||
null,
|
||||
true // generate md5
|
||||
);
|
||||
vmSound.assetId = vmSound.asset.assetId;
|
||||
|
||||
// update vmSound object with md5 property
|
||||
vmSound.md5 = `${vmSound.assetId}.${vmSound.dataFormat}`;
|
||||
// The VM will update the sound name to a fresh name
|
||||
vmSound.name = name;
|
||||
|
||||
vm.addSound(vmSound).then(() => {
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@typedef SoundBuffer
|
||||
@type {Object}
|
||||
@property {Float32Array} samples Array of audio samples
|
||||
@property {number} sampleRate Audio sample rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Downsample the given buffer to try to reduce file size below SOUND_BYTE_LIMIT
|
||||
* @param {SoundBuffer} buffer - Buffer to resample
|
||||
* @param {function(SoundBuffer):Promise<SoundBuffer>} resampler - resampler function
|
||||
* @returns {SoundBuffer} Downsampled buffer with half the sample rate
|
||||
*/
|
||||
const downsampleIfNeeded = (buffer, resampler) => {
|
||||
const {samples, sampleRate} = buffer;
|
||||
const encodedByteLength = samples.length * 2; /* bitDepth 16 bit */
|
||||
// Resolve immediately if already within byte limit
|
||||
if (encodedByteLength < SOUND_BYTE_LIMIT) {
|
||||
return Promise.resolve({samples, sampleRate});
|
||||
}
|
||||
// TW: Don't check if the sound will still fit at this reduced sample rate.
|
||||
// Instead the GUI will show a warning if it's too large.
|
||||
return resampler({samples, sampleRate}, 22050);
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop every other sample of an audio buffer as a last-resort way of downsampling.
|
||||
* @param {SoundBuffer} buffer - Buffer to resample
|
||||
* @returns {SoundBuffer} Downsampled buffer with half the sample rate
|
||||
*/
|
||||
const dropEveryOtherSample = buffer => {
|
||||
const newLength = Math.floor(buffer.samples.length / 2);
|
||||
const newSamples = new Float32Array(newLength);
|
||||
for (let i = 0; i < newLength; i++) {
|
||||
newSamples[i] = buffer.samples[i * 2];
|
||||
}
|
||||
return {
|
||||
samples: newSamples,
|
||||
sampleRate: buffer.sampleRate / 2
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
computeRMS,
|
||||
computeChunkedRMS,
|
||||
encodeAndAddSoundToVM,
|
||||
downsampleIfNeeded,
|
||||
dropEveryOtherSample
|
||||
};
|
||||
41
scratch-gui/src/lib/audio/effects/echo-effect.js
Normal file
@@ -0,0 +1,41 @@
|
||||
class EchoEffect {
|
||||
static get DELAY_TIME () {
|
||||
return 0.25;
|
||||
}
|
||||
static get TAIL_SECONDS () {
|
||||
return 0.75;
|
||||
}
|
||||
constructor (audioContext, startTime, endTime) {
|
||||
this.audioContext = audioContext;
|
||||
this.input = this.audioContext.createGain();
|
||||
this.output = this.audioContext.createGain();
|
||||
|
||||
this.effectInput = this.audioContext.createGain();
|
||||
this.effectInput.gain.value = 0;
|
||||
|
||||
this.effectInput.gain.setValueAtTime(0.75, startTime);
|
||||
this.effectInput.gain.setValueAtTime(0, endTime);
|
||||
|
||||
this.delay = this.audioContext.createDelay(1);
|
||||
this.delay.delayTime.value = EchoEffect.DELAY_TIME;
|
||||
this.decay = this.audioContext.createGain();
|
||||
this.decay.gain.value = 0.3;
|
||||
|
||||
this.compressor = this.audioContext.createDynamicsCompressor();
|
||||
this.compressor.threshold.value = -5;
|
||||
this.compressor.knee.value = 15;
|
||||
this.compressor.ratio.value = 12;
|
||||
this.compressor.attack.value = 0;
|
||||
this.compressor.release.value = 0.25;
|
||||
|
||||
this.input.connect(this.effectInput);
|
||||
this.effectInput.connect(this.delay);
|
||||
this.delay.connect(this.compressor);
|
||||
this.input.connect(this.compressor);
|
||||
this.delay.connect(this.decay);
|
||||
this.decay.connect(this.delay);
|
||||
this.compressor.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchoEffect;
|
||||
27
scratch-gui/src/lib/audio/effects/fade-effect.js
Normal file
@@ -0,0 +1,27 @@
|
||||
class FadeEffect {
|
||||
constructor (audioContext, fadeIn, startSeconds, endSeconds) {
|
||||
this.audioContext = audioContext;
|
||||
|
||||
this.input = this.audioContext.createGain();
|
||||
this.output = this.audioContext.createGain();
|
||||
|
||||
this.gain = this.audioContext.createGain();
|
||||
|
||||
this.gain.gain.setValueAtTime(1, 0);
|
||||
|
||||
if (fadeIn) {
|
||||
this.gain.gain.setValueAtTime(0, startSeconds);
|
||||
this.gain.gain.linearRampToValueAtTime(1, endSeconds);
|
||||
} else {
|
||||
this.gain.gain.setValueAtTime(1, startSeconds);
|
||||
this.gain.gain.linearRampToValueAtTime(0, endSeconds);
|
||||
}
|
||||
|
||||
this.gain.gain.setValueAtTime(1, endSeconds);
|
||||
|
||||
this.input.connect(this.gain);
|
||||
this.gain.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
export default FadeEffect;
|
||||
22
scratch-gui/src/lib/audio/effects/mute-effect.js
Normal file
@@ -0,0 +1,22 @@
|
||||
class MuteEffect {
|
||||
constructor (audioContext, startSeconds, endSeconds) {
|
||||
this.audioContext = audioContext;
|
||||
|
||||
this.input = this.audioContext.createGain();
|
||||
this.output = this.audioContext.createGain();
|
||||
|
||||
this.gain = this.audioContext.createGain();
|
||||
|
||||
// Smoothly ramp the gain down before the start time, and up after the end time.
|
||||
this.rampLength = 0.001;
|
||||
this.gain.gain.setValueAtTime(1.0, Math.max(0, startSeconds - this.rampLength));
|
||||
this.gain.gain.linearRampToValueAtTime(0, startSeconds);
|
||||
this.gain.gain.setValueAtTime(0, endSeconds);
|
||||
this.gain.gain.linearRampToValueAtTime(1.0, endSeconds + this.rampLength);
|
||||
|
||||
this.input.connect(this.gain);
|
||||
this.gain.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
export default MuteEffect;
|
||||
117
scratch-gui/src/lib/audio/effects/robot-effect.js
Normal file
@@ -0,0 +1,117 @@
|
||||
class RobotEffect {
|
||||
constructor (audioContext, startTime, endTime) {
|
||||
this.audioContext = audioContext;
|
||||
|
||||
this.input = this.audioContext.createGain();
|
||||
this.output = this.audioContext.createGain();
|
||||
this.passthrough = this.audioContext.createGain();
|
||||
this.effectInput = this.audioContext.createGain();
|
||||
|
||||
this.passthrough.gain.value = 1;
|
||||
this.effectInput.gain.value = 0;
|
||||
|
||||
this.passthrough.gain.setValueAtTime(0, startTime);
|
||||
this.passthrough.gain.setValueAtTime(1, endTime);
|
||||
|
||||
this.effectInput.gain.setValueAtTime(1, startTime);
|
||||
this.effectInput.gain.setValueAtTime(0, endTime);
|
||||
|
||||
// Ring modulator inspired by BBC Dalek voice
|
||||
// http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf
|
||||
// https://github.com/bbc/webaudio.prototyping.bbc.co.uk
|
||||
|
||||
// > There are four parallel signal paths, two which process the
|
||||
// > combination Vc + Vin / 2 and two which process Vc - Vin/2.
|
||||
// > Each branch consists of a non-linearity [diode]...
|
||||
const createDiodeNode = () => {
|
||||
const node = this.audioContext.createWaveShaper();
|
||||
|
||||
// Piecewise function given by (2) in Parker paper
|
||||
const transform = (v, vb = 0.2, vl = 0.4, h = 0.65) => {
|
||||
if (v <= vb) return 0;
|
||||
if (v <= vl) return h * (Math.pow(v - vb, 2) / ((2 * vl) - (2 * vb)));
|
||||
return (h * v) - (h * vl) + (h * (Math.pow(v - vb, 2) / ((2 * vl) - (2 * vb))));
|
||||
};
|
||||
|
||||
// Create the waveshaper curve with the voltage transform above
|
||||
const bufferLength = 1024;
|
||||
const curve = new Float32Array(bufferLength);
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const voltage = (2 * (i / bufferLength)) - 1;
|
||||
curve[i] = transform(voltage);
|
||||
}
|
||||
node.curve = curve;
|
||||
return node;
|
||||
};
|
||||
|
||||
const oscillator = this.audioContext.createOscillator();
|
||||
oscillator.frequency.value = 50;
|
||||
oscillator.start(0);
|
||||
|
||||
const vInGain = this.audioContext.createGain();
|
||||
vInGain.gain.value = 0.5;
|
||||
|
||||
const vInInverter1 = this.audioContext.createGain();
|
||||
vInInverter1.gain.value = -1;
|
||||
|
||||
const vInInverter2 = this.audioContext.createGain();
|
||||
vInInverter2.gain.value = -1;
|
||||
|
||||
const vInDiode1 = createDiodeNode(this.audioContext);
|
||||
const vInDiode2 = createDiodeNode(this.audioContext);
|
||||
|
||||
const vInInverter3 = this.audioContext.createGain();
|
||||
vInInverter3.gain.value = -1;
|
||||
|
||||
const vcInverter1 = this.audioContext.createGain();
|
||||
vcInverter1.gain.value = -1;
|
||||
|
||||
const vcDiode3 = createDiodeNode(this.audioContext);
|
||||
const vcDiode4 = createDiodeNode(this.audioContext);
|
||||
|
||||
const compressor = this.audioContext.createDynamicsCompressor();
|
||||
compressor.threshold.value = -5;
|
||||
compressor.knee.value = 15;
|
||||
compressor.ratio.value = 12;
|
||||
compressor.attack.value = 0;
|
||||
compressor.release.value = 0.25;
|
||||
|
||||
const biquadFilter = this.audioContext.createBiquadFilter();
|
||||
biquadFilter.type = 'highpass';
|
||||
biquadFilter.frequency.value = 1000;
|
||||
biquadFilter.gain.value = 1.25;
|
||||
|
||||
this.input.connect(this.effectInput);
|
||||
this.input.connect(this.passthrough);
|
||||
|
||||
this.passthrough.connect(this.output);
|
||||
|
||||
this.effectInput.connect(vcInverter1);
|
||||
this.effectInput.connect(vcDiode4);
|
||||
|
||||
vcInverter1.connect(vcDiode3);
|
||||
|
||||
oscillator.connect(vInGain);
|
||||
vInGain.connect(vInInverter1);
|
||||
vInGain.connect(vcInverter1);
|
||||
vInGain.connect(vcDiode4);
|
||||
|
||||
vInInverter1.connect(vInInverter2);
|
||||
vInInverter1.connect(vInDiode2);
|
||||
vInInverter2.connect(vInDiode1);
|
||||
|
||||
vInDiode1.connect(vInInverter3);
|
||||
vInDiode2.connect(vInInverter3);
|
||||
|
||||
vInInverter3.connect(compressor);
|
||||
vcDiode3.connect(compressor);
|
||||
vcDiode4.connect(compressor);
|
||||
|
||||
this.effectInput.connect(biquadFilter);
|
||||
biquadFilter.connect(compressor);
|
||||
|
||||
compressor.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
export default RobotEffect;
|
||||
22
scratch-gui/src/lib/audio/effects/volume-effect.js
Normal file
@@ -0,0 +1,22 @@
|
||||
class VolumeEffect {
|
||||
constructor (audioContext, volume, startSeconds, endSeconds) {
|
||||
this.audioContext = audioContext;
|
||||
|
||||
this.input = this.audioContext.createGain();
|
||||
this.output = this.audioContext.createGain();
|
||||
|
||||
this.gain = this.audioContext.createGain();
|
||||
|
||||
// Smoothly ramp the gain up before the start time, and down after the end time.
|
||||
this.rampLength = 0.01;
|
||||
this.gain.gain.setValueAtTime(1.0, Math.max(0, startSeconds - this.rampLength));
|
||||
this.gain.gain.exponentialRampToValueAtTime(volume, startSeconds);
|
||||
this.gain.gain.setValueAtTime(volume, endSeconds);
|
||||
this.gain.gain.exponentialRampToValueAtTime(1.0, endSeconds + this.rampLength);
|
||||
|
||||
this.input.connect(this.gain);
|
||||
this.gain.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
export default VolumeEffect;
|
||||
34
scratch-gui/src/lib/audio/shared-audio-context.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import StartAudioContext from '@turbowarp/startaudiocontext';
|
||||
import bowser from 'bowser';
|
||||
import log from '../log';
|
||||
|
||||
let AUDIO_CONTEXT;
|
||||
|
||||
if (!bowser.msie) {
|
||||
/**
|
||||
* AudioContext can be initialized only when user interaction event happens
|
||||
*/
|
||||
const event =
|
||||
typeof document.ontouchstart === 'undefined' ?
|
||||
'mousedown' :
|
||||
'touchstart';
|
||||
const initAudioContext = () => {
|
||||
document.removeEventListener(event, initAudioContext);
|
||||
|
||||
try {
|
||||
AUDIO_CONTEXT = new (window.AudioContext || window.webkitAudioContext)();
|
||||
StartAudioContext(AUDIO_CONTEXT);
|
||||
} catch (e) {
|
||||
log.error('could not create shared audio context; sound-related features will not be available', e);
|
||||
}
|
||||
};
|
||||
document.addEventListener(event, initAudioContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap browser AudioContext because we shouldn't create more than one
|
||||
* @return {AudioContext} The singleton AudioContext
|
||||
*/
|
||||
export default function () {
|
||||
return AUDIO_CONTEXT;
|
||||
}
|
||||
140
scratch-gui/src/lib/backpack-api.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import xhr from 'xhr';
|
||||
import costumePayload from './backpack/costume-payload';
|
||||
import soundPayload from './backpack/sound-payload';
|
||||
import spritePayload from './backpack/sprite-payload';
|
||||
import codePayload from './backpack/code-payload';
|
||||
import localBackpackAPI from './tw-local-backpack-api';
|
||||
|
||||
export const LOCAL_API = '_local_';
|
||||
|
||||
// Add a new property for the full thumbnail url, which includes the host.
|
||||
// Also include a full body url for loading sprite zips
|
||||
// TODO retreiving the images through storage would allow us to remove this.
|
||||
const includeFullUrls = (item, host) => Object.assign({}, item, {
|
||||
thumbnailUrl: `${host}/${item.thumbnail}`,
|
||||
bodyUrl: `${host}/${item.body}`
|
||||
});
|
||||
|
||||
const getBackpackContents = ({
|
||||
host,
|
||||
username,
|
||||
token,
|
||||
limit,
|
||||
offset
|
||||
}) => new Promise((resolve, reject) => {
|
||||
if (host === LOCAL_API) {
|
||||
return resolve(localBackpackAPI.getBackpackContents({
|
||||
limit,
|
||||
offset
|
||||
}));
|
||||
}
|
||||
xhr({
|
||||
method: 'GET',
|
||||
uri: `${host}/${username}?limit=${limit}&offset=${offset}`,
|
||||
headers: {'x-token': token},
|
||||
json: true
|
||||
}, (error, response) => {
|
||||
if (error || response.statusCode !== 200) {
|
||||
return reject(new Error(response.status));
|
||||
}
|
||||
return resolve(response.body.map(item => includeFullUrls(item, host)));
|
||||
});
|
||||
});
|
||||
|
||||
const saveBackpackObject = ({
|
||||
host,
|
||||
username,
|
||||
token,
|
||||
type, // Type of object being saved to the backpack
|
||||
mime, // Mime-type of the object being saved
|
||||
name, // User-facing name of the object being saved
|
||||
body, // Base64-encoded body of the object being saved
|
||||
thumbnail // Base64-encoded JPEG thumbnail of the object being saved
|
||||
}) => new Promise((resolve, reject) => {
|
||||
if (host === LOCAL_API) {
|
||||
return resolve(localBackpackAPI.saveBackpackObject({
|
||||
type,
|
||||
mime,
|
||||
name,
|
||||
body,
|
||||
thumbnail
|
||||
}));
|
||||
}
|
||||
xhr({
|
||||
method: 'POST',
|
||||
uri: `${host}/${username}`,
|
||||
headers: {'x-token': token},
|
||||
json: {type, mime, name, body, thumbnail}
|
||||
}, (error, response) => {
|
||||
if (error || response.statusCode !== 200) {
|
||||
return reject(new Error(response.status));
|
||||
}
|
||||
return resolve(includeFullUrls(response.body, host));
|
||||
});
|
||||
});
|
||||
|
||||
const deleteBackpackObject = ({
|
||||
host,
|
||||
username,
|
||||
token,
|
||||
id
|
||||
}) => new Promise((resolve, reject) => {
|
||||
if (host === LOCAL_API) {
|
||||
return resolve(localBackpackAPI.deleteBackpackObject({
|
||||
id
|
||||
}));
|
||||
}
|
||||
xhr({
|
||||
method: 'DELETE',
|
||||
uri: `${host}/${username}/${id}`,
|
||||
headers: {'x-token': token}
|
||||
}, (error, response) => {
|
||||
if (error || response.statusCode !== 200) {
|
||||
return reject(new Error(response.status));
|
||||
}
|
||||
return resolve(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
const updateBackpackObject = ({
|
||||
host,
|
||||
id,
|
||||
name
|
||||
}) => new Promise((resolve, reject) => {
|
||||
if (host === LOCAL_API) {
|
||||
return resolve(localBackpackAPI.updateBackpackObject({
|
||||
id,
|
||||
name
|
||||
}));
|
||||
}
|
||||
reject(new Error('updateBackpackObject not supported'));
|
||||
});
|
||||
|
||||
// Two types of backpack items are not retreivable through storage
|
||||
// code, as json and sprite3 as arraybuffer zips.
|
||||
const fetchAs = (responseType, uri) => new Promise((resolve, reject) => {
|
||||
xhr({uri, responseType}, (error, response) => {
|
||||
if (error || response.statusCode !== 200) {
|
||||
return reject(new Error(response.status));
|
||||
}
|
||||
return resolve(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// These two helpers allow easy fetching of backpack code and sprite zips
|
||||
// Use the curried fetchAs here so the consumer does not worry about XHR responseTypes
|
||||
const fetchCode = fetchAs.bind(null, 'json');
|
||||
const fetchSprite = fetchAs.bind(null, 'arraybuffer');
|
||||
|
||||
export {
|
||||
getBackpackContents,
|
||||
saveBackpackObject,
|
||||
deleteBackpackObject,
|
||||
updateBackpackObject,
|
||||
costumePayload,
|
||||
soundPayload,
|
||||
spritePayload,
|
||||
codePayload,
|
||||
fetchCode,
|
||||
fetchSprite
|
||||
};
|
||||
62
scratch-gui/src/lib/backpack/block-to-image.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import computedStyleToInlineStyle from 'computed-style-to-inline-style';
|
||||
import LazyScratchBlocks from '../tw-lazy-scratch-blocks';
|
||||
|
||||
/**
|
||||
* Given a blockId, return a data-uri image that can be used to create a thumbnail.
|
||||
* @param {string} blockId the ID of the block to imagify
|
||||
* @return {Promise} resolves to a data-url of a picture of the blocks
|
||||
*/
|
||||
export default function (blockId) {
|
||||
// Not sure any better way to access the scratch-blocks workspace than this...
|
||||
const ScratchBlocks = LazyScratchBlocks.get();
|
||||
const block = ScratchBlocks.getMainWorkspace().getBlockById(blockId);
|
||||
const blockSvg = block.getSvgRoot().cloneNode(true /* deep */);
|
||||
|
||||
// Once we have the cloned SVG, do the rest in a setTimeout to prevent
|
||||
// blocking the drag end from finishing promptly.
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
// TW: Fixes issues caused by scratch-blocks block hiding
|
||||
blockSvg.style.display = '';
|
||||
|
||||
// Strip entities that cannot be inlined
|
||||
blockSvg.innerHTML = blockSvg.innerHTML.replace(/ /g, ' ');
|
||||
|
||||
// Create an <svg> element to put the cloned blockSvg inside
|
||||
const NS = 'http://www.w3.org/2000/svg';
|
||||
const svg = document.createElementNS(NS, 'svg');
|
||||
svg.appendChild(blockSvg);
|
||||
|
||||
// Needs to be on the DOM to get CSS properties and correct sizing
|
||||
document.body.appendChild(svg);
|
||||
|
||||
const padding = 10;
|
||||
const extraHatPadding = 16;
|
||||
const topPadding = padding + (blockSvg.getAttribute('data-shapes') === 'hat' ? extraHatPadding : 0);
|
||||
const leftPadding = padding;
|
||||
blockSvg.setAttribute('transform', `translate(${leftPadding} ${topPadding})`);
|
||||
|
||||
const bounds = blockSvg.getBoundingClientRect();
|
||||
svg.setAttribute('width', bounds.width + (2 * padding));
|
||||
svg.setAttribute('height', bounds.height + (2 * padding));
|
||||
|
||||
// We need to inline the styles set by CSS rules because
|
||||
// not all the styles are set directly on the SVG. This makes the
|
||||
// image styled the same way the block actually appears.
|
||||
// TODO this doesn't handle images that are xlink:href in the SVG
|
||||
computedStyleToInlineStyle(svg, {
|
||||
recursive: true,
|
||||
// Enumerate the specific properties we need to inline.
|
||||
// Specifically properties that are set from CSS in scratch-blocks
|
||||
properties: ['fill', 'font-family', 'font-size', 'font-weight']
|
||||
});
|
||||
|
||||
const svgString = (new XMLSerializer()).serializeToString(svg);
|
||||
|
||||
// Once we have the svg as a string, remove it from the DOM
|
||||
svg.parentNode.removeChild(svg);
|
||||
|
||||
resolve(`data:image/svg+xml;utf-8,${encodeURIComponent(svgString)}`);
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
57
scratch-gui/src/lib/backpack/code-payload.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import blockToImage from './block-to-image';
|
||||
import createThumbnail from './thumbnail';
|
||||
import {BLOCKS_DEFAULT_SCALE} from '../layout-constants';
|
||||
import {Base64} from 'js-base64';
|
||||
|
||||
const codePayload = ({blockObjects, topBlockId}) => {
|
||||
const payload = {
|
||||
type: 'script', // Needs to match backpack-server type name
|
||||
name: 'code', // All code currently gets the same name
|
||||
mime: 'application/json',
|
||||
// Backpack expects a base64 encoded string to store. Cannot use btoa because
|
||||
// the code can contain characters outside the 0-255 code-point range supported by btoa
|
||||
body: Base64.encode(JSON.stringify(blockObjects)) // Base64 encode the json
|
||||
};
|
||||
|
||||
return blockToImage(topBlockId)
|
||||
.then(createThumbnail)
|
||||
.then(thumbnail => {
|
||||
payload.thumbnail = thumbnail;
|
||||
return payload;
|
||||
});
|
||||
};
|
||||
|
||||
const findTopBlock = payload => {
|
||||
const blocks = payload.extensionURLs ? payload.blocks : payload;
|
||||
return blocks.find(i => i.topLevel);
|
||||
};
|
||||
|
||||
const placeInViewport = (payload, workspaceMetrics, isRtl) => {
|
||||
const topBlock = findTopBlock(payload);
|
||||
if (topBlock) {
|
||||
const {scrollX, scrollY, scale} = workspaceMetrics || {
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
scale: BLOCKS_DEFAULT_SCALE
|
||||
};
|
||||
|
||||
const posY = -scrollY + 30;
|
||||
let posX;
|
||||
if (isRtl) {
|
||||
posX = scrollX + 30;
|
||||
} else {
|
||||
posX = -scrollX + 30;
|
||||
}
|
||||
|
||||
topBlock.x = posX / scale;
|
||||
topBlock.y = posY / scale;
|
||||
}
|
||||
|
||||
return payload;
|
||||
};
|
||||
|
||||
export {
|
||||
codePayload as default,
|
||||
findTopBlock,
|
||||
placeInViewport
|
||||
};
|
||||
43
scratch-gui/src/lib/backpack/costume-payload.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import createThumbnail from './thumbnail';
|
||||
import getCostumeUrl from '../get-costume-url';
|
||||
|
||||
const costumePayload = (costume, vm) => {
|
||||
// TODO is it ok to base64 encode SVGs? What about unicode text inside them?
|
||||
const assetDataUrl = vm.getExportedCostumeBase64(costume);
|
||||
const assetDataFormat = costume.dataFormat;
|
||||
const payload = {
|
||||
type: 'costume',
|
||||
name: costume.name,
|
||||
// Params to be filled in below
|
||||
mime: '',
|
||||
body: '',
|
||||
thumbnail: ''
|
||||
};
|
||||
|
||||
switch (assetDataFormat) {
|
||||
case 'svg':
|
||||
payload.mime = 'image/svg+xml';
|
||||
payload.body = assetDataUrl;
|
||||
break;
|
||||
case 'png':
|
||||
payload.mime = 'image/png';
|
||||
payload.body = assetDataUrl;
|
||||
break;
|
||||
case 'jpg':
|
||||
payload.mime = 'image/jpeg';
|
||||
payload.body = assetDataUrl;
|
||||
break;
|
||||
default:
|
||||
alert(`Cannot serialize for format: ${assetDataFormat}`); // eslint-disable-line
|
||||
}
|
||||
|
||||
// Do not generate the thumbnail from the raw asset. Instead use the getCostumeUrl
|
||||
// utility which inlines the fonts to make the thumbnail show the right fonts.
|
||||
const inlinedFontDataUrl = getCostumeUrl(costume.asset);
|
||||
return createThumbnail(inlinedFontDataUrl).then(thumbnail => {
|
||||
payload.thumbnail = thumbnail;
|
||||
return payload;
|
||||
});
|
||||
};
|
||||
|
||||
export default costumePayload;
|
||||
39
scratch-gui/src/lib/backpack/sound-payload.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import soundThumbnail from '!base64-loader!./sound-thumbnail.png';
|
||||
|
||||
const soundPayload = sound => {
|
||||
const assetDataUrl = sound.asset.encodeDataURI();
|
||||
const assetDataFormat = sound.dataFormat;
|
||||
const payload = {
|
||||
type: 'sound',
|
||||
name: sound.name,
|
||||
thumbnail: soundThumbnail,
|
||||
// Params to be filled in below
|
||||
mime: '',
|
||||
body: ''
|
||||
};
|
||||
|
||||
switch (assetDataFormat) {
|
||||
case 'wav':
|
||||
payload.mime = 'audio/x-wav';
|
||||
payload.body = assetDataUrl.replace('data:audio/x-wav;base64,', '');
|
||||
break;
|
||||
case 'mp3':
|
||||
payload.mime = 'audio/mp3';
|
||||
// TODO scratch-storage should be fixed so that encodeDataURI does not
|
||||
// always prepend the wave format header; Once that is fixed, the following
|
||||
// line will have to change.
|
||||
payload.body = assetDataUrl.replace('data:audio/x-wav;base64,', '');
|
||||
break;
|
||||
default:
|
||||
alert(`Cannot serialize for format: ${assetDataFormat}`); // eslint-disable-line
|
||||
}
|
||||
|
||||
// Return a promise to make it consistent with other payload constructors like costume-payload
|
||||
return new Promise(resolve => resolve(payload));
|
||||
};
|
||||
|
||||
export {
|
||||
soundPayload as default,
|
||||
soundThumbnail
|
||||
};
|
||||
BIN
scratch-gui/src/lib/backpack/sound-thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
scratch-gui/src/lib/backpack/sound-thumbnail.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
29
scratch-gui/src/lib/backpack/sprite-payload.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import createThumbnail from './thumbnail';
|
||||
|
||||
const spritePayload = (id, vm) => {
|
||||
const target = vm.runtime.getTargetById(id);
|
||||
if (!target) return null;
|
||||
|
||||
return vm.exportSprite(
|
||||
id,
|
||||
'base64'
|
||||
).then(zippedSprite => {
|
||||
const payload = {
|
||||
type: 'sprite',
|
||||
name: target.sprite.name,
|
||||
mime: 'application/zip',
|
||||
body: zippedSprite,
|
||||
// Filled in below
|
||||
thumbnail: ''
|
||||
};
|
||||
|
||||
const costumeDataUrl = target.sprite.costumes[target.currentCostume].asset.encodeDataURI();
|
||||
|
||||
return createThumbnail(costumeDataUrl).then(thumbnail => {
|
||||
payload.thumbnail = thumbnail;
|
||||
return payload;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default spritePayload;
|
||||
37
scratch-gui/src/lib/backpack/thumbnail.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const jpegThumbnail = dataUrl => new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const maxDimension = 96; // 3x the maximum displayed size of 32px
|
||||
|
||||
// TW: After setting canvas width/height, the canvas is automatically cleared.
|
||||
|
||||
if (image.height < 1 || image.width < 1) {
|
||||
canvas.width = canvas.height = maxDimension;
|
||||
// drawImage can fail if image height/width is less than 1
|
||||
// Use blank image; the costume is too small to render anyway
|
||||
} else {
|
||||
if (image.height > image.width) {
|
||||
canvas.height = maxDimension;
|
||||
canvas.width = (maxDimension / image.height) * image.width;
|
||||
} else {
|
||||
canvas.width = maxDimension;
|
||||
canvas.height = (maxDimension / image.width) * image.height;
|
||||
}
|
||||
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
// TW: PNG allows using transparency while JPEG does not.
|
||||
// A white background looks quite ugly in dark mode.
|
||||
const dataURL = canvas.toDataURL('image/png').replace('data:image/png;base64,', '');
|
||||
resolve(dataURL);
|
||||
};
|
||||
image.onerror = err => {
|
||||
reject(err);
|
||||
};
|
||||
image.src = dataUrl;
|
||||
});
|
||||
|
||||
export default jpegThumbnail;
|
||||
362
scratch-gui/src/lib/blocks.js
Normal file
@@ -0,0 +1,362 @@
|
||||
import LazyScratchBlocks from './tw-lazy-scratch-blocks';
|
||||
|
||||
/**
|
||||
* Connect scratch blocks with the vm
|
||||
* @param {VirtualMachine} vm - The scratch vm
|
||||
* @return {ScratchBlocks} ScratchBlocks connected with the vm
|
||||
*/
|
||||
export default function (vm) {
|
||||
const ScratchBlocks = LazyScratchBlocks.get();
|
||||
const jsonForMenuBlock = function (name, menuOptionsFn, colors, start) {
|
||||
return {
|
||||
message0: '%1',
|
||||
args0: [
|
||||
{
|
||||
type: 'field_dropdown',
|
||||
name: name,
|
||||
options: function () {
|
||||
return start.concat(menuOptionsFn());
|
||||
}
|
||||
}
|
||||
],
|
||||
inputsInline: true,
|
||||
output: 'String',
|
||||
colour: colors.secondary,
|
||||
colourSecondary: colors.secondary,
|
||||
colourTertiary: colors.tertiary,
|
||||
colourQuaternary: colors.quaternary,
|
||||
outputShape: ScratchBlocks.OUTPUT_SHAPE_ROUND
|
||||
};
|
||||
};
|
||||
|
||||
const jsonForHatBlockMenu = function (hatName, name, menuOptionsFn, colors, start) {
|
||||
return {
|
||||
message0: hatName,
|
||||
args0: [
|
||||
{
|
||||
type: 'field_dropdown',
|
||||
name: name,
|
||||
options: function () {
|
||||
return start.concat(menuOptionsFn());
|
||||
}
|
||||
}
|
||||
],
|
||||
colour: colors.primary,
|
||||
colourSecondary: colors.secondary,
|
||||
colourTertiary: colors.tertiary,
|
||||
colourQuaternary: colors.quaternary,
|
||||
extensions: ['shape_hat']
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const jsonForSensingMenus = function (menuOptionsFn) {
|
||||
return {
|
||||
message0: ScratchBlocks.Msg.SENSING_OF,
|
||||
args0: [
|
||||
{
|
||||
type: 'field_dropdown',
|
||||
name: 'PROPERTY',
|
||||
options: function () {
|
||||
return menuOptionsFn();
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
type: 'input_value',
|
||||
name: 'OBJECT'
|
||||
}
|
||||
],
|
||||
output: true,
|
||||
colour: ScratchBlocks.Colours.sensing.primary,
|
||||
colourSecondary: ScratchBlocks.Colours.sensing.secondary,
|
||||
colourTertiary: ScratchBlocks.Colours.sensing.tertiary,
|
||||
colourQuaternary: ScratchBlocks.Colours.sensing.quaternary,
|
||||
outputShape: ScratchBlocks.OUTPUT_SHAPE_ROUND
|
||||
};
|
||||
};
|
||||
|
||||
const soundsMenu = function () {
|
||||
let menu = [['', '']];
|
||||
if (vm.editingTarget && vm.editingTarget.sprite.sounds.length > 0) {
|
||||
menu = vm.editingTarget.sprite.sounds.map(sound => [sound.name, sound.name]);
|
||||
}
|
||||
menu.push([
|
||||
ScratchBlocks.ScratchMsgs.translate('SOUND_RECORD', 'record...'),
|
||||
ScratchBlocks.recordSoundCallback
|
||||
]);
|
||||
return menu;
|
||||
};
|
||||
|
||||
const costumesMenu = function () {
|
||||
if (vm.editingTarget && vm.editingTarget.getCostumes().length > 0) {
|
||||
return vm.editingTarget.getCostumes().map(costume => [costume.name, costume.name]);
|
||||
}
|
||||
return [['', '']];
|
||||
};
|
||||
|
||||
const backdropsMenu = function () {
|
||||
const next = ScratchBlocks.ScratchMsgs.translate('LOOKS_NEXTBACKDROP', 'next backdrop');
|
||||
const previous = ScratchBlocks.ScratchMsgs.translate('LOOKS_PREVIOUSBACKDROP', 'previous backdrop');
|
||||
const random = ScratchBlocks.ScratchMsgs.translate('LOOKS_RANDOMBACKDROP', 'random backdrop');
|
||||
if (vm.runtime.targets[0] && vm.runtime.targets[0].getCostumes().length > 0) {
|
||||
return vm.runtime.targets[0].getCostumes().map(costume => [costume.name, costume.name])
|
||||
.concat([[next, 'next backdrop'],
|
||||
[previous, 'previous backdrop'],
|
||||
[random, 'random backdrop']]);
|
||||
}
|
||||
return [['', '']];
|
||||
};
|
||||
|
||||
const backdropNamesMenu = function () {
|
||||
const stage = vm.runtime.getTargetForStage();
|
||||
if (stage && stage.getCostumes().length > 0) {
|
||||
return stage.getCostumes().map(costume => [costume.name, costume.name]);
|
||||
}
|
||||
return [['', '']];
|
||||
};
|
||||
|
||||
const spriteMenu = function () {
|
||||
const sprites = [];
|
||||
for (const targetId in vm.runtime.targets) {
|
||||
if (!Object.prototype.hasOwnProperty.call(vm.runtime.targets, targetId)) continue;
|
||||
if (vm.runtime.targets[targetId].isOriginal) {
|
||||
if (!vm.runtime.targets[targetId].isStage) {
|
||||
if (vm.runtime.targets[targetId] === vm.editingTarget) {
|
||||
continue;
|
||||
}
|
||||
sprites.push([vm.runtime.targets[targetId].sprite.name, vm.runtime.targets[targetId].sprite.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sprites;
|
||||
};
|
||||
|
||||
const cloneMenu = function () {
|
||||
if (vm.editingTarget && vm.editingTarget.isStage) {
|
||||
const menu = spriteMenu();
|
||||
if (menu.length === 0) {
|
||||
return [['', '']]; // Empty menu matches Scratch 2 behavior
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
const myself = ScratchBlocks.ScratchMsgs.translate('CONTROL_CREATECLONEOF_MYSELF', 'myself');
|
||||
return [[myself, '_myself_']].concat(spriteMenu());
|
||||
};
|
||||
|
||||
const soundColors = ScratchBlocks.Colours.sounds;
|
||||
|
||||
const looksColors = ScratchBlocks.Colours.looks;
|
||||
|
||||
const motionColors = ScratchBlocks.Colours.motion;
|
||||
|
||||
const sensingColors = ScratchBlocks.Colours.sensing;
|
||||
|
||||
const controlColors = ScratchBlocks.Colours.control;
|
||||
|
||||
const eventColors = ScratchBlocks.Colours.event;
|
||||
|
||||
ScratchBlocks.Blocks.sound_sounds_menu.init = function () {
|
||||
const json = jsonForMenuBlock('SOUND_MENU', soundsMenu, soundColors, []);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.looks_costume.init = function () {
|
||||
const json = jsonForMenuBlock('COSTUME', costumesMenu, looksColors, []);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.looks_backdrops.init = function () {
|
||||
const json = jsonForMenuBlock('BACKDROP', backdropsMenu, looksColors, []);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.event_whenbackdropswitchesto.init = function () {
|
||||
const json = jsonForHatBlockMenu(
|
||||
ScratchBlocks.Msg.EVENT_WHENBACKDROPSWITCHESTO,
|
||||
'BACKDROP', backdropNamesMenu, eventColors, []);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.motion_pointtowards_menu.init = function () {
|
||||
const random = ScratchBlocks.ScratchMsgs.translate('MOTION_POINTTOWARDS_RANDOM', 'random direction');
|
||||
const mouse = ScratchBlocks.ScratchMsgs.translate('MOTION_POINTTOWARDS_POINTER', 'mouse-pointer');
|
||||
const json = jsonForMenuBlock('TOWARDS', spriteMenu, motionColors, [
|
||||
[mouse, '_mouse_'],
|
||||
[random, '_random_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.motion_goto_menu.init = function () {
|
||||
const random = ScratchBlocks.ScratchMsgs.translate('MOTION_GOTO_RANDOM', 'random position');
|
||||
const mouse = ScratchBlocks.ScratchMsgs.translate('MOTION_GOTO_POINTER', 'mouse-pointer');
|
||||
const json = jsonForMenuBlock('TO', spriteMenu, motionColors, [
|
||||
[random, '_random_'],
|
||||
[mouse, '_mouse_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.motion_glideto_menu.init = function () {
|
||||
const random = ScratchBlocks.ScratchMsgs.translate('MOTION_GLIDETO_RANDOM', 'random position');
|
||||
const mouse = ScratchBlocks.ScratchMsgs.translate('MOTION_GLIDETO_POINTER', 'mouse-pointer');
|
||||
const json = jsonForMenuBlock('TO', spriteMenu, motionColors, [
|
||||
[random, '_random_'],
|
||||
[mouse, '_mouse_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.sensing_of_object_menu.init = function () {
|
||||
const stage = ScratchBlocks.ScratchMsgs.translate('SENSING_OF_STAGE', 'Stage');
|
||||
const json = jsonForMenuBlock('OBJECT', spriteMenu, sensingColors, [
|
||||
[stage, '_stage_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.sensing_of.init = function () {
|
||||
const blockId = this.id;
|
||||
const blockType = this.type;
|
||||
|
||||
// Get the sensing_of block from vm.
|
||||
let defaultSensingOfBlock;
|
||||
const blocks = vm.runtime.flyoutBlocks._blocks;
|
||||
Object.keys(blocks).forEach(id => {
|
||||
const block = blocks[id];
|
||||
if (id === blockType || (block && block.opcode === blockType)) {
|
||||
defaultSensingOfBlock = block;
|
||||
}
|
||||
});
|
||||
|
||||
// Function that fills in menu for the first input in the sensing block.
|
||||
// Called every time it opens since it depends on the values in the other block input.
|
||||
const menuFn = function () {
|
||||
const stageOptions = [
|
||||
[ScratchBlocks.Msg.SENSING_OF_BACKDROPNUMBER, 'backdrop #'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_BACKDROPNAME, 'backdrop name'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_VOLUME, 'volume']
|
||||
];
|
||||
const spriteOptions = [
|
||||
[ScratchBlocks.Msg.SENSING_OF_XPOSITION, 'x position'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_YPOSITION, 'y position'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_DIRECTION, 'direction'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_COSTUMENUMBER, 'costume #'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_COSTUMENAME, 'costume name'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_SIZE, 'size'],
|
||||
[ScratchBlocks.Msg.SENSING_OF_VOLUME, 'volume']
|
||||
];
|
||||
if (vm.editingTarget) {
|
||||
let lookupBlocks = vm.editingTarget.blocks;
|
||||
let sensingOfBlock = lookupBlocks.getBlock(blockId);
|
||||
|
||||
// The block doesn't exist, but should be in the flyout. Look there.
|
||||
if (!sensingOfBlock) {
|
||||
sensingOfBlock = vm.runtime.flyoutBlocks.getBlock(blockId) || defaultSensingOfBlock;
|
||||
// If we still don't have a block, just return an empty list . This happens during
|
||||
// scratch blocks construction.
|
||||
if (!sensingOfBlock) {
|
||||
return [['', '']];
|
||||
}
|
||||
// The block was in the flyout so look up future block info there.
|
||||
lookupBlocks = vm.runtime.flyoutBlocks;
|
||||
}
|
||||
const sort = function (options) {
|
||||
options.sort(ScratchBlocks.scratchBlocksUtils.compareStrings);
|
||||
};
|
||||
// Get all the stage variables (no lists) so we can add them to menu when the stage is selected.
|
||||
const stageVariableOptions = vm.runtime.getTargetForStage().getAllVariableNamesInScopeByType('');
|
||||
sort(stageVariableOptions);
|
||||
const stageVariableMenuItems = stageVariableOptions.map(variable => [variable, variable]);
|
||||
if (sensingOfBlock.inputs.OBJECT.shadow !== sensingOfBlock.inputs.OBJECT.block) {
|
||||
// There's a block dropped on top of the menu. It'd be nice to evaluate it and
|
||||
// return the correct list, but that is tricky. Scratch2 just returns stage options
|
||||
// so just do that here too.
|
||||
return stageOptions.concat(stageVariableMenuItems);
|
||||
}
|
||||
const menuBlock = lookupBlocks.getBlock(sensingOfBlock.inputs.OBJECT.shadow);
|
||||
const selectedItem = menuBlock.fields.OBJECT.value;
|
||||
if (selectedItem === '_stage_') {
|
||||
return stageOptions.concat(stageVariableMenuItems);
|
||||
}
|
||||
// Get all the local variables (no lists) and add them to the menu.
|
||||
const target = vm.runtime.getSpriteTargetByName(selectedItem);
|
||||
let spriteVariableOptions = [];
|
||||
// The target should exist, but there are ways for it not to (e.g. #4203).
|
||||
if (target) {
|
||||
spriteVariableOptions = target.getAllVariableNamesInScopeByType('', true);
|
||||
sort(spriteVariableOptions);
|
||||
}
|
||||
const spriteVariableMenuItems = spriteVariableOptions.map(variable => [variable, variable]);
|
||||
return spriteOptions.concat(spriteVariableMenuItems);
|
||||
}
|
||||
return [['', '']];
|
||||
};
|
||||
|
||||
const json = jsonForSensingMenus(menuFn);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.sensing_distancetomenu.init = function () {
|
||||
const mouse = ScratchBlocks.ScratchMsgs.translate('SENSING_DISTANCETO_POINTER', 'mouse-pointer');
|
||||
const json = jsonForMenuBlock('DISTANCETOMENU', spriteMenu, sensingColors, [
|
||||
[mouse, '_mouse_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.sensing_touchingobjectmenu.init = function () {
|
||||
const mouse = ScratchBlocks.ScratchMsgs.translate('SENSING_TOUCHINGOBJECT_POINTER', 'mouse-pointer');
|
||||
const edge = ScratchBlocks.ScratchMsgs.translate('SENSING_TOUCHINGOBJECT_EDGE', 'edge');
|
||||
const json = jsonForMenuBlock('TOUCHINGOBJECTMENU', spriteMenu, sensingColors, [
|
||||
[mouse, '_mouse_'],
|
||||
[edge, '_edge_']
|
||||
]);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.Blocks.control_create_clone_of_menu.init = function () {
|
||||
const json = jsonForMenuBlock('CLONE_OPTION', cloneMenu, controlColors, []);
|
||||
this.jsonInit(json);
|
||||
};
|
||||
|
||||
ScratchBlocks.VerticalFlyout.getCheckboxState = function (blockId) {
|
||||
const monitoredBlock = vm.runtime.monitorBlocks._blocks[blockId];
|
||||
return monitoredBlock ? monitoredBlock.isMonitored : false;
|
||||
};
|
||||
|
||||
ScratchBlocks.FlyoutExtensionCategoryHeader.getExtensionState = function (extensionId) {
|
||||
if (vm.getPeripheralIsConnected(extensionId)) {
|
||||
return ScratchBlocks.StatusButtonState.READY;
|
||||
}
|
||||
return ScratchBlocks.StatusButtonState.NOT_READY;
|
||||
};
|
||||
|
||||
ScratchBlocks.FieldNote.playNote_ = function (noteNum, extensionId) {
|
||||
vm.runtime.emit('PLAY_NOTE', noteNum, extensionId);
|
||||
};
|
||||
|
||||
// Use a collator's compare instead of localeCompare which internally
|
||||
// creates a collator. Using this is a lot faster in browsers that create a
|
||||
// collator for every localeCompare call.
|
||||
const collator = new Intl.Collator([], {
|
||||
sensitivity: 'base',
|
||||
numeric: true
|
||||
});
|
||||
ScratchBlocks.scratchBlocksUtils.compareStrings = function (str1, str2) {
|
||||
return collator.compare(str1, str2);
|
||||
};
|
||||
|
||||
// Blocks wants to know if 3D CSS transforms are supported. The cross
|
||||
// section of browsers Scratch supports and browsers that support 3D CSS
|
||||
// transforms will make the return always true.
|
||||
//
|
||||
// Shortcutting to true lets us skip an expensive style recalculation when
|
||||
// first loading the Scratch editor.
|
||||
ScratchBlocks.utils.is3dSupported = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
return ScratchBlocks;
|
||||
}
|
||||
27
scratch-gui/src/lib/bmp-converter.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export default (bmpImage, type = 'image/bmp') => new Promise(resolve => {
|
||||
// If the input is an ArrayBuffer, we need to convert it to a `Blob` and give it a URL so we can use it as an <img>
|
||||
// `src`. If it's a data URI, we can use it as-is.
|
||||
const imageUrl = bmpImage instanceof String ?
|
||||
bmpImage :
|
||||
window.URL.createObjectURL(new Blob([bmpImage], {type}));
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const image = document.createElement('img');
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/png');
|
||||
|
||||
// Revoke URL. If a blob URL was generated earlier, this allows the blob to be GC'd and prevents a memory leak.
|
||||
window.URL.revokeObjectURL(imageUrl);
|
||||
|
||||
resolve(dataUrl);
|
||||
});
|
||||
|
||||
image.setAttribute('src', imageUrl);
|
||||
});
|
||||
5
scratch-gui/src/lib/brand.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// Legacy export format because this is used by some build-time scripts stuck in the past.
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
module.exports = {
|
||||
APP_NAME: '001CODE'
|
||||
};
|
||||
225
scratch-gui/src/lib/cloud-manager-hoc.jsx
Normal file
@@ -0,0 +1,225 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
|
||||
import VM from 'scratch-vm';
|
||||
import CloudProvider from '../lib/cloud-provider';
|
||||
|
||||
import {
|
||||
getIsShowingWithId
|
||||
} from '../reducers/project-state';
|
||||
|
||||
import {
|
||||
showAlertWithTimeout
|
||||
} from '../reducers/alerts';
|
||||
import {openUsernameModal} from '../reducers/modals';
|
||||
import {setUsernameInvalid, setCloudHost} from '../reducers/tw';
|
||||
|
||||
/**
|
||||
* TW: Our scratch-vm has an alternative fix to the cloud variable and video sensing privacy concerns.
|
||||
*/
|
||||
const DISABLE_WITH_VIDEO_SENSING = false;
|
||||
|
||||
/*
|
||||
* Higher Order Component to manage the connection to the cloud server.
|
||||
* @param {React.Component} WrappedComponent component to manage VM events for
|
||||
* @returns {React.Component} connected component with vm events bound to redux
|
||||
*/
|
||||
const cloudManagerHOC = function (WrappedComponent) {
|
||||
class CloudManager extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.cloudProvider = null;
|
||||
bindAll(this, [
|
||||
'handleCloudDataUpdate',
|
||||
'handleExtensionAdded'
|
||||
]);
|
||||
|
||||
this.props.vm.on('HAS_CLOUD_DATA_UPDATE', this.handleCloudDataUpdate);
|
||||
this.props.vm.on('EXTENSION_ADDED', this.handleExtensionAdded);
|
||||
}
|
||||
componentDidMount () {
|
||||
if (this.shouldConnect(this.props)) {
|
||||
this.connectToCloud();
|
||||
}
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (this.props.reduxCloudHost !== nextProps.cloudHost) {
|
||||
this.props.onSetReduxCloudHost(nextProps.cloudHost);
|
||||
}
|
||||
}
|
||||
componentDidUpdate (prevProps) {
|
||||
// TODO need to add cloud provider disconnection logic and cloud data clearing logic
|
||||
// when loading a new project e.g. via file upload
|
||||
// (and eventually move it out of the vm.clear function)
|
||||
|
||||
if (this.shouldReconnect(this.props, prevProps)) {
|
||||
this.disconnectFromCloud();
|
||||
if (this.shouldConnect(this.props)) {
|
||||
this.connectToCloud();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.shouldConnect(this.props) && !this.shouldConnect(prevProps)) {
|
||||
this.connectToCloud();
|
||||
}
|
||||
|
||||
if (this.shouldDisconnect(this.props, prevProps)) {
|
||||
this.disconnectFromCloud();
|
||||
}
|
||||
}
|
||||
componentWillUnmount () {
|
||||
this.props.vm.off('HAS_CLOUD_DATA_UPDATE', this.handleCloudDataUpdate);
|
||||
this.props.vm.off('EXTENSION_ADDED', this.handleExtensionAdded);
|
||||
this.disconnectFromCloud();
|
||||
}
|
||||
canUseCloud (props) {
|
||||
return !!(
|
||||
props.reduxCloudHost &&
|
||||
props.username &&
|
||||
props.vm &&
|
||||
props.projectId &&
|
||||
props.hasCloudPermission
|
||||
);
|
||||
}
|
||||
shouldConnect (props) {
|
||||
return !this.isConnected() && this.canUseCloud(props) &&
|
||||
props.isShowingWithId && props.vm.runtime.hasCloudData() &&
|
||||
props.canModifyCloudData;
|
||||
}
|
||||
shouldDisconnect (props, prevProps) {
|
||||
return this.isConnected() &&
|
||||
( // Can no longer use cloud or cloud provider info is now stale
|
||||
!this.canUseCloud(props) ||
|
||||
!props.vm.runtime.hasCloudData() ||
|
||||
(props.projectId !== prevProps.projectId) ||
|
||||
// tw: username changes are handled in "reconnect"
|
||||
// (props.username !== prevProps.username) ||
|
||||
// Editing someone else's project
|
||||
!props.canModifyCloudData
|
||||
);
|
||||
}
|
||||
shouldReconnect (props, prevProps) {
|
||||
return this.isConnected() && (
|
||||
props.username !== prevProps.username ||
|
||||
props.reduxCloudHost !== prevProps.reduxCloudHost
|
||||
);
|
||||
}
|
||||
isConnected () {
|
||||
return this.cloudProvider && !!this.cloudProvider.connection;
|
||||
}
|
||||
connectToCloud () {
|
||||
this.cloudProvider = new CloudProvider(
|
||||
this.props.reduxCloudHost,
|
||||
this.props.vm,
|
||||
this.props.username,
|
||||
this.props.projectId);
|
||||
this.cloudProvider.onInvalidUsername = this.props.onInvalidUsername;
|
||||
this.props.vm.setCloudProvider(this.cloudProvider);
|
||||
}
|
||||
disconnectFromCloud () {
|
||||
if (this.cloudProvider) {
|
||||
this.cloudProvider.requestCloseConnection();
|
||||
this.cloudProvider = null;
|
||||
this.props.vm.setCloudProvider(null);
|
||||
}
|
||||
}
|
||||
handleCloudDataUpdate (projectHasCloudData) {
|
||||
if (this.isConnected() && !projectHasCloudData) {
|
||||
this.disconnectFromCloud();
|
||||
} else if (this.shouldConnect(this.props)) {
|
||||
this.props.onShowCloudInfo();
|
||||
this.connectToCloud();
|
||||
}
|
||||
}
|
||||
handleExtensionAdded (categoryInfo) {
|
||||
// Note that props.vm.extensionManager.isExtensionLoaded('videoSensing') is still false
|
||||
// at the point of this callback, so it is difficult to reuse the canModifyCloudData logic.
|
||||
if (DISABLE_WITH_VIDEO_SENSING && categoryInfo.id === 'videoSensing' && this.isConnected()) {
|
||||
this.disconnectFromCloud();
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
canModifyCloudData,
|
||||
cloudHost,
|
||||
reduxCloudHost,
|
||||
onSetReduxCloudHost,
|
||||
projectId,
|
||||
username,
|
||||
hasCloudPermission,
|
||||
isShowingWithId,
|
||||
onShowCloudInfo,
|
||||
onInvalidUsername,
|
||||
/* eslint-enable no-unused-vars */
|
||||
vm,
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
canUseCloud={this.canUseCloud(this.props)}
|
||||
vm={vm}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CloudManager.propTypes = {
|
||||
canModifyCloudData: PropTypes.bool.isRequired,
|
||||
cloudHost: PropTypes.string,
|
||||
reduxCloudHost: PropTypes.string,
|
||||
onSetReduxCloudHost: PropTypes.func,
|
||||
hasCloudPermission: PropTypes.bool,
|
||||
isShowingWithId: PropTypes.bool.isRequired,
|
||||
onInvalidUsername: PropTypes.func,
|
||||
onShowCloudInfo: PropTypes.func,
|
||||
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
username: PropTypes.string,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
|
||||
CloudManager.defaultProps = {
|
||||
cloudHost: null,
|
||||
onShowCloudInfo: () => {},
|
||||
username: null
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const loadingState = state.scratchGui.projectState.loadingState;
|
||||
return {
|
||||
reduxCloudHost: state.scratchGui.tw.cloudHost,
|
||||
isShowingWithId: getIsShowingWithId(loadingState),
|
||||
projectId: state.scratchGui.projectState.projectId,
|
||||
// if you're editing someone else's project, you can't modify cloud data
|
||||
canModifyCloudData: (!state.scratchGui.mode.hasEverEnteredEditor || ownProps.canSave) &&
|
||||
// possible security concern if the program attempts to encode webcam data over cloud variables
|
||||
!(DISABLE_WITH_VIDEO_SENSING && ownProps.vm.extensionManager.isExtensionLoaded('videoSensing'))
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onSetReduxCloudHost: cloudHost => dispatch(setCloudHost(cloudHost)),
|
||||
onShowCloudInfo: () => showAlertWithTimeout(dispatch, 'cloudInfo'),
|
||||
onInvalidUsername: () => {
|
||||
dispatch(setUsernameInvalid(true));
|
||||
dispatch(openUsernameModal());
|
||||
}
|
||||
});
|
||||
|
||||
// Allow incoming props to override redux-provided props. Used to mock in tests.
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
|
||||
{}, stateProps, dispatchProps, ownProps
|
||||
);
|
||||
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(CloudManager);
|
||||
};
|
||||
|
||||
export default cloudManagerHOC;
|
||||
259
scratch-gui/src/lib/cloud-provider.js
Normal file
@@ -0,0 +1,259 @@
|
||||
import log from './log.js';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
const anonymizeUsername = username => {
|
||||
if (/^player\d{2,7}$/i.test(username)) {
|
||||
return 'player';
|
||||
}
|
||||
return username;
|
||||
};
|
||||
|
||||
class CloudProvider {
|
||||
/**
|
||||
* A cloud data provider which creates and manages a web socket connection
|
||||
* to the Scratch cloud data server. This provider is responsible for
|
||||
* interfacing with the VM's cloud io device.
|
||||
* @param {string} cloudHost The url for the cloud data server
|
||||
* @param {VirtualMachine} vm The Scratch virtual machine to interface with
|
||||
* @param {string} username The username to associate cloud data updates with
|
||||
* @param {string} projectId The id associated with the project containing
|
||||
* cloud data.
|
||||
*/
|
||||
constructor (cloudHost, vm, username, projectId) {
|
||||
this.vm = vm;
|
||||
this.username = anonymizeUsername(username);
|
||||
this.projectId = projectId;
|
||||
this.cloudHost = cloudHost;
|
||||
|
||||
this.connectionAttempts = 0;
|
||||
|
||||
// A queue of messages to send which were received before the
|
||||
// connection was ready
|
||||
this.queuedData = [];
|
||||
|
||||
this.openConnection();
|
||||
|
||||
// Send a message to the cloud server at a rate of no more
|
||||
// than 10 messages/sec.
|
||||
// tw: we let cloud variables change at a greater rate
|
||||
this.sendCloudData = throttle(this._sendCloudData, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new websocket connection to the clouddata server.
|
||||
* @param {string} cloudHost The cloud data server to connect to.
|
||||
*/
|
||||
openConnection () {
|
||||
this.connectionAttempts += 1;
|
||||
|
||||
try {
|
||||
// tw: only add ws:// or wss:// if it not already present in the cloudHost
|
||||
if (!this.cloudHost || (!this.cloudHost.includes('ws://') && !this.cloudHost.includes('wss://'))) {
|
||||
this.cloudHost = (location.protocol === 'http:' ? 'ws://' : 'wss://') + this.cloudHost;
|
||||
}
|
||||
this.connection = new WebSocket(this.cloudHost);
|
||||
} catch (e) {
|
||||
log.warn('Websocket support is not available in this browser', e);
|
||||
this.connection = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.connection.onerror = this.onError.bind(this);
|
||||
this.connection.onmessage = this.onMessage.bind(this);
|
||||
this.connection.onopen = this.onOpen.bind(this);
|
||||
this.connection.onclose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
onError (event) {
|
||||
log.error(`Websocket connection error: ${JSON.stringify(event)}`);
|
||||
// Error is always followed by close, which handles reconnect logic.
|
||||
}
|
||||
|
||||
onMessage (event) {
|
||||
const messageString = event.data;
|
||||
// Multiple commands can be received, newline separated
|
||||
messageString.split('\n').forEach(message => {
|
||||
if (message) { // .split can also contain '' in the array it returns
|
||||
const parsedData = this.parseMessage(JSON.parse(message));
|
||||
this.vm.postIOData('cloud', parsedData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onOpen () {
|
||||
// Reset connection attempts to 1 to make sure any subsequent reconnects
|
||||
// use connectionAttempts=1 to calculate timeout
|
||||
this.connectionAttempts = 1;
|
||||
this.writeToServer('handshake');
|
||||
log.info(`Successfully connected to clouddata server.`);
|
||||
|
||||
// Go through the queued data and send off messages that we weren't
|
||||
// ready to send before
|
||||
this.queuedData.forEach(data => {
|
||||
this.sendCloudData(data);
|
||||
});
|
||||
// Reset the queue
|
||||
this.queuedData = [];
|
||||
}
|
||||
|
||||
onClose (e) {
|
||||
// tw: code 4002 is "Username Error" -- do not try to reconnect
|
||||
if (e && e.code === 4002) {
|
||||
log.info('Cloud username is invalid. Not reconnecting.');
|
||||
this.onInvalidUsername();
|
||||
return;
|
||||
}
|
||||
// tw: code 4004 is "Project Unavailable" -- do not try to reconnect
|
||||
if (e && e.code === 4004) {
|
||||
log.info('Cloud variables are disabled for this project. Not reconnecting.');
|
||||
return;
|
||||
}
|
||||
log.info(`Closed connection to websocket`);
|
||||
const randomizedTimeout = this.randomizeDuration(this.exponentialTimeout());
|
||||
this.setTimeout(this.openConnection.bind(this), randomizedTimeout);
|
||||
}
|
||||
|
||||
// tw: method called when username is invalid
|
||||
onInvalidUsername () { /* no-op */ }
|
||||
|
||||
exponentialTimeout () {
|
||||
return (Math.pow(2, Math.min(this.connectionAttempts, 5)) - 1) * 1000;
|
||||
}
|
||||
|
||||
randomizeDuration (t) {
|
||||
return Math.random() * t;
|
||||
}
|
||||
|
||||
setTimeout (fn, time) {
|
||||
log.info(`Reconnecting in ${(time / 1000).toFixed(1)}s, attempt ${this.connectionAttempts}`);
|
||||
this._connectionTimeout = window.setTimeout(fn, time);
|
||||
}
|
||||
|
||||
parseMessage (message) {
|
||||
const varData = {};
|
||||
switch (message.method) {
|
||||
case 'set': {
|
||||
varData.varUpdate = {
|
||||
name: message.name,
|
||||
value: message.value
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return varData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send a message to the cloud data server.
|
||||
* @param {string} methodName The message method, indicating the action to perform.
|
||||
* @param {string} dataName The name of the cloud variable this message pertains to
|
||||
* @param {string | number} dataValue The value to set the cloud variable to
|
||||
* @param {string} dataNewName The new name for the cloud variable (if renaming)
|
||||
*/
|
||||
writeToServer (methodName, dataName, dataValue, dataNewName) {
|
||||
const msg = {};
|
||||
msg.method = methodName;
|
||||
msg.user = this.username;
|
||||
msg.project_id = this.projectId;
|
||||
|
||||
// Optional string params can use simple falsey undefined check
|
||||
if (dataName) msg.name = dataName;
|
||||
if (dataNewName) msg.new_name = dataNewName;
|
||||
|
||||
// Optional number params need different undefined check
|
||||
if (typeof dataValue !== 'undefined' && dataValue !== null) msg.value = dataValue;
|
||||
|
||||
const dataToWrite = JSON.stringify(msg);
|
||||
if (this.connection && this.connection.readyState === WebSocket.OPEN) {
|
||||
this.sendCloudData(dataToWrite);
|
||||
} else if (msg.method === 'create' || msg.method === 'delete' || msg.method === 'rename') {
|
||||
// Save data for sending when connection is open, iff the data
|
||||
// is a create, rename, or delete
|
||||
this.queuedData.push(dataToWrite);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a formatted message to the cloud data server.
|
||||
* @param {string} data The formatted message to send.
|
||||
*/
|
||||
_sendCloudData (data) {
|
||||
this.connection.send(`${data}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for the VM's cloud IO device to create
|
||||
* a new cloud variable on the server.
|
||||
* @param {string} name The name of the variable to create
|
||||
* @param {string | number} value The value of the new cloud variable.
|
||||
*/
|
||||
createVariable (name, value) {
|
||||
this.writeToServer('create', name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for the VM's cloud IO device to update
|
||||
* a cloud variable on the server.
|
||||
* @param {string} name The name of the variable to update
|
||||
* @param {string | number} value The new value for the variable
|
||||
*/
|
||||
updateVariable (name, value) {
|
||||
this.writeToServer('set', name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for the VM's cloud IO device to rename
|
||||
* a cloud variable on the server.
|
||||
* @param {string} oldName The old name of the variable to rename
|
||||
* @param {string} newName The new name for the cloud variable.
|
||||
*/
|
||||
renameVariable (oldName, newName) {
|
||||
this.writeToServer('rename', oldName, null, newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for the VM's cloud IO device to delete
|
||||
* a cloud variable on the server.
|
||||
* @param {string} name The name of the variable to delete
|
||||
*/
|
||||
deleteVariable (name) {
|
||||
this.writeToServer('delete', name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the web socket and clears the cloud
|
||||
* provider of references related to the cloud data project.
|
||||
*/
|
||||
requestCloseConnection () {
|
||||
if (this.connection &&
|
||||
this.connection.readyState !== WebSocket.CLOSING &&
|
||||
this.connection.readyState !== WebSocket.CLOSED) {
|
||||
log.info('Request close cloud connection without reconnecting');
|
||||
// Remove listeners, after this point we do not want to react to connection updates
|
||||
this.connection.onclose = () => {};
|
||||
this.connection.onerror = () => {};
|
||||
this.connection.close();
|
||||
}
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear this provider of references related to the project
|
||||
* and current state.
|
||||
*/
|
||||
clear () {
|
||||
this.connection = null;
|
||||
this.vm = null;
|
||||
this.username = null;
|
||||
this.projectId = null;
|
||||
if (this._connectionTimeout) {
|
||||
clearTimeout(this._connectionTimeout);
|
||||
this._connectionTimeout = null;
|
||||
}
|
||||
this.connectionAttempts = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CloudProvider;
|
||||
39
scratch-gui/src/lib/collect-metadata.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Report a telemetry event.
|
||||
* @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded`
|
||||
*/
|
||||
// TODO make a telemetry HOC and move this stuff there
|
||||
const collectMetadata = function (vm, projectName = '', locale = '') {
|
||||
// TODO move most or all of this into a collectMetadata() method on the VM/Runtime
|
||||
const metadata = {
|
||||
projectName: projectName,
|
||||
language: locale,
|
||||
spriteCount: 0,
|
||||
blocksCount: 0,
|
||||
costumesCount: 0,
|
||||
listsCount: 0,
|
||||
scriptCount: 0,
|
||||
soundsCount: 0,
|
||||
variablesCount: 0
|
||||
};
|
||||
|
||||
for (const target of vm.runtime.targets) {
|
||||
++metadata.spriteCount;
|
||||
metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length;
|
||||
metadata.costumesCount += target.sprite.costumes_.length;
|
||||
metadata.scriptCount += target.sprite.blocks._scripts.length;
|
||||
metadata.soundsCount += target.sprite.sounds.length;
|
||||
for (const variableName in target.variables) {
|
||||
const variable = target.variables[variableName];
|
||||
if (variable.type === 'list') {
|
||||
++metadata.listsCount;
|
||||
} else {
|
||||
++metadata.variablesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
};
|
||||
|
||||
export default collectMetadata;
|
||||
10
scratch-gui/src/lib/connected-intl-provider.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import {IntlProvider as ReactIntlProvider} from 'react-intl';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
key: state.locales.locale,
|
||||
locale: state.locales.locale,
|
||||
messages: state.locales.messages
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ReactIntlProvider);
|
||||
17
scratch-gui/src/lib/data-uri-to-blob.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Utility to convert data URIs to blobs
|
||||
* Adapted from https://stackoverflow.com/questions/12168909/blob-from-dataurl
|
||||
* @param {string} dataURI the data uri to blobify
|
||||
* @return {Blob} a blob representing the data uri
|
||||
*/
|
||||
export default function dataURItoBlob (dataURI) {
|
||||
const byteString = atob(dataURI.split(',')[1]);
|
||||
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
||||
const arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
const uintArray = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
uintArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
const blob = new Blob([arrayBuffer], {type: mimeString});
|
||||
return blob;
|
||||
}
|
||||
1
scratch-gui/src/lib/default-project/1dango-cat.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="61.49876" height="117.72954" viewBox="0,0,61.49876,117.72954"><g transform="translate(-209.25062,-121.13523)"><g data-paper-data="{"isPaintingLayer":true}" fill-rule="nonzero" stroke="#000000" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal"><path d="M212.45298,121.45117c4.27378,-1.68745 3.99111,1.73524 23.39137,52.09733c11.02954,29.4711 19.76067,53.61865 16.55011,54.62697c-4.68511,1.80854 -12.36182,-22.62622 -23.39136,-52.09733c-16.89172,-48.16374 -21.64214,-52.78237 -16.55012,-54.62697z" data-paper-data="{"index":null}" fill="#d99e82" stroke-width="0"/><path d="M231.66366,188.60508c-9.14277,0 -16.55443,-7.24756 -16.55443,-16.18792c0,-0.78587 -6.31352,-9.26285 -5.83245,-12.90786c0.40452,-2.76293 9.55433,-0.3003 11.53093,-0.54877c4.86102,-1.00187 5.4542,-2.73129 10.85596,-2.73129c1.57876,0 3.53694,-7.03389 6.07355,-5.48884c4.69159,2.85764 10.48089,15.87384 10.48089,21.67674c0,8.94036 -7.41168,16.18792 -16.55443,16.18792z" data-paper-data="{"index":null}" fill="#fcb1e3" stroke-width="0"/><path d="M254.49773,207.54343c-3.95019,2.74614 -8.14073,4.33933 -12.55592,4.76162c-4.55084,0.43525 -8.46053,0.76116 -12.24363,-2.3874c-7.45344,-6.20327 -8.34307,-19.21037 -2.19171,-24.55014c1.44594,-1.25547 3.26814,-2.45373 4.53075,-3.07615c1.76698,-0.87105 1.91986,-1.00055 5.71606,-1.94996c6.04389,-0.9383 9.92917,-0.90806 14.55274,2.94c7.45344,6.20327 8.43469,16.90662 2.19171,24.26203z" data-paper-data="{"index":null}" fill="#ffd983" stroke-width="0"/><path d="M232.32721,232.90573c-2.56114,-5.68425 -1.68892,-8.14742 -0.86406,-12.66379c1.34684,-6.26958 3.91259,-8.71043 9.84623,-10.86565c2.22542,-0.80831 7.74255,-2.38249 10.8511,-2.73285c3.62404,-0.35301 6.7831,0.58103 9.40006,2.45432c2.34915,1.68157 3.03827,2.18205 6.332,6.90816c2.62231,4.47889 4.4898,11.43587 0.7492,16.90554c-3.6011,5.26566 -11.02377,5.95331 -18.58348,5.95331c-8.33056,0 -15.04386,-0.62903 -17.73105,-5.95905z" data-paper-data="{"index":null}" fill="#a6d388" stroke-width="0"/><path d="M231.87007,170.77208c-1.41148,0 -2.55572,-1.14423 -2.55572,-2.55573c0,-1.41149 1.14423,-2.55572 2.55572,-2.55572c1.4115,0 2.55572,1.14422 2.55572,2.55572c0,1.4115 -1.14422,2.55573 -2.55572,2.55573z" data-paper-data="{"index":null}" fill="#000000" stroke-width="1.5"/><path d="M243.83381,164.03088z" data-paper-data="{"index":null}" fill="#000000" stroke-width="0"/><path d="M245.61169,164.9569c0,1.37058 -0.22211,1.74084 -1.59269,1.74084c-0.68528,0 -0.93531,-0.35186 -1.3844,-0.80094c-0.44908,-0.44908 -0.80093,-0.99542 -0.80093,-1.68071c0,-1.37058 -0.07421,-1.74085 1.29638,-1.74085c1.37058,0 2.48163,1.11107 2.48163,2.48165z" data-paper-data="{"index":null}" fill="#000000" stroke-width="1.5"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
3
scratch-gui/src/lib/default-project/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
`dango-cat.svg` was created by https://scratch.mit.edu/users/littlebunny06/ 🍡🐱
|
||||
|
||||
If `default-project.sb3` is replaced with a non-empty file, it will be used instead of the costumes.
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg version="1.1" width="2" height="2" viewBox="-1 -1 2 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Exported by Scratch - http://scratch.mit.edu/ -->
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 202 B |
88
scratch-gui/src/lib/default-project/dango-cat.svg
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve"> <image id="image0" width="64" height="64" x="0" y="0"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo
|
||||
AAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxIAAAsSAdLd
|
||||
fvwAAAAHdElNRQfoChwNJx5Ort+cAAARXklEQVR42s2beXDdV3XHP+f+fm/Xk/S0W9632I6XxAuQ
|
||||
EOMkxLGzQmfSkhmXDBRoCzSFAtNlOnTaKcOwBEihDTNMSssQKKWlEEMoWUoWCCEkISvEiS3LsqRI
|
||||
1i69Re+933JP/3iSIjmyrS0OZ+b98d697/7O+d7vOffc+zsXzpO4VXUk11yMuFGqd+wXcaNvx5hH
|
||||
MOZBcaOXL7vpkyJulPSO/bjphvOl1usvkbrlAJhoHACJxNaJcW5DpA9QQBHpFeN8ViKxtZW+CQCi
|
||||
9SveaPUXLsl1uxA3holXVQCIJevEuLci8tKU4ad/RF4Ux/2QiSVrQTDxKkwkTnLtrjfanLlL8w0f
|
||||
x0mkcavqEMfFqcpExYm8AzEPA+GksWJcrdr8Nq3adJmKcaYDESDmQXEi17vp+og4Lm66ASeRfqNN
|
||||
O7e41Q18SBVxowCIG92NMXeB5KcZqPHlm3XFe76k2+44odv+pV1X3PIFjbdecBojJIcx3xA3unNi
|
||||
rMozljg+yFIMEmteR7mvHRNNYL0iJhJfpaH/J6r2fagumwKopom6vX9I/RXvIda4BlVbUUIM5f4T
|
||||
DD307wz/4jsE2YFpGkqPiPlXcSJ3Wr/UXXlGiVjTGsr9Jxatu7OYP6cv3Ecw1o/6JTT0ETdaDXqL
|
||||
hv6XUfv7QBoqQa32Te9k+aHPUvfWm3FSGZgwHgBVnFSG9IWXU3XBpdhSHm+gAw0DgDSql6uGV4nj
|
||||
lsWNHNcw8LABYhyqtl6B19d+/gFwkjWE41lsKY+TqHHVL+/XwLsdG/45UJl1MaQuuITWm/+Rpmtu
|
||||
Jda4GnSS5adL5bdo/UqqdxwgvmIL/kgv/kjPZIcW1F6nYbhbjNPnpGpP2lJObXkcMQb1ywuyY94u
|
||||
4NY0EYz1I5EY6pcRN7pdbfARrL4LtHqyX6x5PQ1XvZ/Mpe/CTTeg1p7B8NnVEmPwswOMPPZdhh78
|
||||
+ml0lzEx5j8xzj9r4P12UpdIbQv+6Kn5TeRcO8aa12FLObAhGvoYN7IM1Y+qDb+I6pVADCoJT/0V
|
||||
76X10KepvuhgZU2fTve5iiomliS14c2kt70dRCj3taN+CSCO6h7UXi/GiYvjHNMwKEw+J75sI0Fu
|
||||
aGkAqNl9I/5wN+qXsEEZ48ZSqL1ZQ/8rqH03UAMgkRg1O69l+aHPUL/vlkq0Xojhr0UCt7qB6q1X
|
||||
ktrwJsLCKN5gJ9gQoBbV/Wrt5eI4eeNG2zXwfA08xHHJvOUmil2/WTgATqqWcHyMsDBGpK7V2PHc
|
||||
PhuUv4ANPwGsnOyXXLeL1j/4e5qu/xjxlvW8upotkaiCCLHGNVRffJBYy0b8oe5pdNflqL1Bw2C7
|
||||
OM4rseZ13cFYv9ryOKhOsmZWmTUGTPqSicSxfglxY5uwwa2q9hCqdZP9og2rqL/ivdTtPUSktmWe
|
||||
fr5AEUHE4I/0MvzzbzP0yDfwhrqntw+JmG/huHeoXz42aUMkswx/pPe1kzz9S2LVNsL8CDrh5+JG
|
||||
GlD9oNrgn1B7DZCAygpQd9khlh/6DDW7b6ykuwuguxFFBHS+sVgVk0iT2nQp6QsvBxvg9bejgQeQ
|
||||
RPUS1F4jxnHEcY9pGBQnWZRYvoUg2/9aBrjpRlKbLyf76x9g4lVxWy7coNZ+DLWXTvYTJ0J66xU0
|
||||
HvwwqU2XIW4E7ML8PFRhb/1JQhV+ObwKRxbGHDEONvDIH/k5A/fdQf7IzybzBwCLmMfEmC+ZWNX/
|
||||
2lKu/LdhwOfqVhKMdJ8OQBNBrh9xo7s1DP4K1RtBE9PZ0XD1B6nd/Q6cZDVaCUILlqgJuW3bTwjU
|
||||
8Ne/uYZAzaLGE+MQjo8y+uRhBh74GqXuF6e3jiPyQ3Hcz2vgPeNmVkwB8KoLOFGcZK1jS7nbUfsu
|
||||
IAIQySyj8eCHab35U6Q3763M+iKje6jCztoePrD617Qmcjw1upyeUjVmMYm5KhKJk1yzk+odV2Ni
|
||||
Kbz+dmwpT8UW3YZqOtqy+W7rF1XL+ZkASCQO4KhfPAS6CSC9/QAr/+grZC65CSdRvUTLGrhG+cCa
|
||||
J7motoeYE+Jbl8eGVrMkWxNVnGQtVVveRtXmvXjDPXj9E6myyFGJVX2P0J8CYDbeTTijEFn+Vqzb
|
||||
gi17LFV0typsSA2xr6EDVEDhioZ21qWGsbokezNQi/UDqN1IdOP+swJ7ZscTsJ5H/mQ3I0faKPUP
|
||||
oqEFWZySIso1zUdpiBUmFBOa4nkONh9b7NATtiulbIFszwCFoRFsEJy1/zkij4BAkC+QbTvJ2LF2
|
||||
vLHcVNN8xaqwIpHl6qa217QdaDrGsnh2USzwi2VyfUPkB0YIPH9OSs4t9IqgqpSHRhl76Ti5E12E
|
||||
xfKC2HBVYxsrE2MV+k+KCmuSo7y94fiCHC30AwoDI2R7B/HyxYkd59xkfmuPCDYIGO/pZ+RIG+O9
|
||||
fWgQzgkIRWiIFbiu+SjMtuaLcl3Ly9RHx+eWGAloaCmO5sj2DFAcy1cy0XnOycIWX4FwvEjuRBej
|
||||
Lx+nPDw6la+fSawKb6vv4IKqwZmzP4WQsCU9yFvrOs/tBqp4+SLZU4MUBkcJ/bP7+dIDAEzksHij
|
||||
WcaOtpM93klQGJ9dX6DaLXFDy0sYc+YEyjEBNy47QpVbPqMrBGWfXP8Iub4h/OLCDkGWBoBpQGho
|
||||
KfYNMHqkjUL3Kazvz2BDYB32ZF5hR80pOFvGp4adNb3squ0hsDM3qjYMGR/Oku0doJwroHZplmV3
|
||||
SUaZACIse+Q7X6E8PEqytZlIJkM0omxJ9/Lulc8Sc4LZ6T9N4q7PLSufYchLcjTfgBcagkKB0miO
|
||||
oOwtmbpLD8CEKFDKjmNKbWxdU+bmXcNcvaafTKx4TuMrAwiX1HXx1arDPHyqgf860sqTg02U/QjG
|
||||
CLLE2+0lA0ARrEJNpMyeTA8HW9q4uKaX2lGL7anBX5YhEo/MIaEUNCyR9ju4PnWKy7a5PN6ykR90
|
||||
7ubxgQ2M+cnKNnqJgFg0AJMRuzFe4LL6Lg40H2dz9QBxE2JV8DwY6h4hN1wg05oh3ViNcZ1Z1mpB
|
||||
NSAsDeIXe7BBJaCmIwEHW59nX/NLPDe8iru7dvPwqS30lyrnr2aB2+hFA2BVMKKsSo1xZeMJrmo6
|
||||
wZrUKK5YQhXC0+juFTz6j/eTH8qTWZ4hWZMEIxNuYQm9UfzxHkJvFLBMLuhKZayoCbiksY3d9Sc4
|
||||
ml3GPd0Xc1/PDk4W6lE1GFnYRm1BALhi2Vg9xP6mdvY2dLIsnkOkAkp4Jj8XUFXyw3mKuSLVDdXU
|
||||
tmZwoyFBsYegNIjqZPo6+xihGowoF9Z2s6XmFW5e8zgP9G7nx90Xc2SslXABZwrzBkCBuBNweeNJ
|
||||
DjQfpy5axCJzzuFFBBtYRnpHKeZy1LdmERk/q+Gni50AYm16gN9zn6IcunTkG8gF8XlvUeYNgAD5
|
||||
IMqd7bt4sH8t+5va2dfYQWs8j4jOAwgIyh42KONE5q62Ixarho58A/f17OCe7os5lm0hVLOg04QF
|
||||
x4BQDS9lGziaq+dwzyauaOzgquZ21k2LA3NCc47djFh86/Li2HLu6drJ/b3b6SzUoyoL9v9FAQCv
|
||||
RuCu8RruOnkR957ayKUNXRxsbmNLepC4E2B14QuWoBhRxoMoz46s5u7O3TzSt4WBUjVMtMkbtQrM
|
||||
BsSgl+TwK5t4qH8NuzO9XNPSxs7aXtKuNy8gJg0f9ZL8cmAjd3dVcoCsn8CILmrGXxcApivuSCVG
|
||||
PNS/hseHVrC1up+DLcfZ13CSKtc7JwiCMuYnufeVHRzu2s3zIysphlGMKM4SGv66APCqEeCI4lmH
|
||||
p0ZaeXZ0GUPrEtyy6jnm4vTfOP427njpakI1OGJfF8MnZfG7wXOII0qowv196+kvV501hTWidI/X
|
||||
cU/XTqzK62r4eQNg0rDOQi2PDq4669m/oNzfs52ThYZFp7i/UwBA5WXIA33rGfVnT1YEZaBUzY+6
|
||||
dy4oo/udB8CI8nKunieHW2edXSPKz/o28dJY63mh/nkHAKBsHe49tYHxMDLjdxEl6ye4u2sPnn1d
|
||||
4vLvBgBGlOfGWnh+rGnG22CD8sTgOp4eXr2ka/ySACCydBgJUAgi3HdqA551pmJBKYxwd+cexoPY
|
||||
0hQuTtf9HEf2Z7ZOlVL7w/gDL1dUX4r3VlSOAJ4YXsHRXN1UcvPC6EoeG9i4dJFfDCB4PS9QfPE+
|
||||
znYMNdPhKjW7UyePpY5f4A+8TGLzdSQ3XYtJNZ6lzm+OuqGMeHEe6F/Ptvo+AutwuGs3I15qCYKf
|
||||
gDGEYz0Unvk+4899nzA3ML2DJ5GYTq8Zmjp7NrEUYX5AxbgngDrQdYCr/jhe73N4Pc8hTgS3ehni
|
||||
xhcFAiIMlZPsbeqkEI3z5ZcPVui/GJIZBy3nKL7wY8bu/xzFI/ej3tR7iiJivi/GuS3M9fc4iVps
|
||||
KXsaANEksZVvJhjp6JFI4keofRFYMfERWxzG63oCf/g4TqIWp6oJjLMgICpnCjHqEkWOByt5eGDL
|
||||
wukvBmxAueNXjP3fF8g/+R/Y/NSsKyKPYdy/NLHkbRp6XevvtIzdf/sUADMwd+vWEo52gXHRoIS4
|
||||
sUYNg/eg9kMTjJgAq4r4+itJbn0nbmb1xKPmZ4Cq0JQaRzIZBv2a+Z/yTtDFH2ij8OR3KL54L3ai
|
||||
6GGivQ0xXxUncpf6pUGJJiAMcBs34J86MmMyXiNOqoGwMIg4UTT0ECeyWW14K6qHQDNT/dItJLfc
|
||||
QGLjAUyyfqKCZO6GqOOoU5tBnPkUx1T83Ob6KTx3mMIz/004Nr38TYYR+ZY47h0aeEfFjaFBGSfd
|
||||
RJjrn220M4uJpZFIEjs+hEk1GlsY2KdqP4HaA0B0sl+kaTOpbTcRW31pJT7MtZTGcdTJZMDMEQDj
|
||||
oOUCpaMPkX/iW3i9v53e6iHmJ2KcLzmZlY+GI13WqW7BFsem6D6rCmedodAjtnwPYa4Xtb5q6HeI
|
||||
G/8h2GPAaiaqwm1hkHLnrwhGOnCS9ThVjSBziA/GYBKJyknpWSfdgFq8rqfJ/vR28r/6JmF2WlG0
|
||||
yFMY928kGv+0Bl4b1leA2Nq34PecvVR2ztRza1YQZHsqbhGUECfaqjZ8P2r/GHSqbNbEa0hs3E9y
|
||||
y404NSsqIJwpPpyLAVI5NAyGOij8+ruM/+YebHFsensnYr4mxv03DcqnJJJAQx83s4JgqGNOds25
|
||||
WtyWs4BiYukKAMbksMHPxIn8lEql+AYgpkEZv/8IXvdTlYqt6lYkkmRWNpyRAQLGwY4PU3j6e4w9
|
||||
cBul44+iweTrcMki5pti3I9ig/8R4+SxIU6yFi3nscXRuZq18MxToinEiWLLWUys2rXl3H40/PhE
|
||||
6bw7OYPR5u2ktt9EdOUexInNjA+zMcA4qF+k1PYo+Sfuwut+djqDAsT8VIz5oklkHgrHRwInlUH9
|
||||
8ln9fEkY8BoJfSINGypoq7WEXptEYj8E7QDWAk0AYb6PcufjhGPdOFWNOMn6iVRVZzJg4jfvlRfI
|
||||
PvRl8r/8OuHIjCLo5zHO30kk/g8aekcQsSIQadpEMLTwu0NLkuA76RbC3CkmlxxxoqvUBn+K6vtA
|
||||
W6YmN1lH8oKDJDZfj5NuQYxRk8mA60o40kXh6e9ReP4wtjD9soP0YMzXxbh3alDukkgc9Uu4dasI
|
||||
hjuXQv2lE5OoJXPgU4hT2e+Lcfcg5ttAgWlX4tzMGq3Z+zFtft+P7LK/eMTWXvtJdRvWnX5tLo+Y
|
||||
b4oT2QVMjWlSdQvQ7DxK6qKbkWgSE68GcTCxdAzjvhORR5h2cRLjaHT5Lo2t2qPMvDgZIuYhMe6N
|
||||
JlETxTiYZAaJpd5o0+YnkcZNiBNBIpWCc3HjdRjnI4i8zIyZnnF19gjG+TOJJDIAEk0iTpRo6/Y3
|
||||
2pyFi1PVVAHAmbhN6kTWI+aLMO3yNHIKMZ8XN7quAlas8t/q5jda/aUTE0vj1q1FjEu0eauIca9C
|
||||
zM8R87A4kSsTW68VcSJEl+/AJGrOm17/D+V6zZQnRNw7AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0
|
||||
LTEwLTI4VDEzOjM5OjMwKzAwOjAwWcgGrAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0xMC0yOFQx
|
||||
MzozOTozMCswMDowMCiVvhAAAAAodEVYdGRhdGU6dGltZXN0YW1wADIwMjQtMTAtMjhUMTM6Mzk6
|
||||
MzArMDA6MDB/gJ/PAAAAAElFTkSuQmCC" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
47
scratch-gui/src/lib/default-project/index.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import projectData from './project-data';
|
||||
|
||||
/* eslint-disable import/no-unresolved */
|
||||
import overrideDefaultProject from '!arraybuffer-loader!./override-default-project.sb3';
|
||||
import backdrop from '!raw-loader!./cd21514d0531fdffb22204e0ec5ed84a.svg';
|
||||
import costume1 from '!raw-loader!./dango-cat.svg';
|
||||
/* eslint-enable import/no-unresolved */
|
||||
import {TextEncoder} from '../tw-text-encoder';
|
||||
|
||||
const defaultProject = translator => {
|
||||
if (overrideDefaultProject.byteLength > 0) {
|
||||
return [{
|
||||
id: 0,
|
||||
assetType: 'Project',
|
||||
dataFormat: 'JSON',
|
||||
data: overrideDefaultProject
|
||||
}];
|
||||
}
|
||||
|
||||
let _TextEncoder;
|
||||
if (typeof TextEncoder === 'undefined') {
|
||||
_TextEncoder = require('text-encoding').TextEncoder;
|
||||
} else {
|
||||
_TextEncoder = TextEncoder;
|
||||
}
|
||||
const encoder = new _TextEncoder();
|
||||
|
||||
const projectJson = projectData(translator);
|
||||
return [{
|
||||
id: 0,
|
||||
assetType: 'Project',
|
||||
dataFormat: 'JSON',
|
||||
data: JSON.stringify(projectJson)
|
||||
}, {
|
||||
id: 'cd21514d0531fdffb22204e0ec5ed84a',
|
||||
assetType: 'ImageVector',
|
||||
dataFormat: 'SVG',
|
||||
data: encoder.encode(backdrop)
|
||||
}, {
|
||||
id: '927d672925e7b99f7813735c484c6922',
|
||||
assetType: 'ImageVector',
|
||||
dataFormat: 'SVG',
|
||||
data: encoder.encode(costume1)
|
||||
}];
|
||||
};
|
||||
|
||||
export default defaultProject;
|
||||
92
scratch-gui/src/lib/default-project/project-data.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import {defineMessages} from 'react-intl';
|
||||
import sharedMessages from '../shared-messages';
|
||||
|
||||
let messages = defineMessages({
|
||||
variable: {
|
||||
defaultMessage: 'my variable',
|
||||
description: 'Name for the default variable',
|
||||
id: 'gui.defaultProject.variable'
|
||||
}
|
||||
});
|
||||
|
||||
messages = {...messages, ...sharedMessages};
|
||||
|
||||
// use the default message if a translation function is not passed
|
||||
const defaultTranslator = msgObj => msgObj.defaultMessage;
|
||||
|
||||
/**
|
||||
* Generate a localized version of the default project
|
||||
* @param {function} translateFunction a function to use for translating the default names
|
||||
* @return {object} the project data json for the default project
|
||||
*/
|
||||
const projectData = translateFunction => {
|
||||
const translator = translateFunction || defaultTranslator;
|
||||
return ({
|
||||
targets: [
|
||||
{
|
||||
isStage: true,
|
||||
name: 'Stage',
|
||||
variables: {
|
||||
'`jEk@4|i[#Fk?(8x)AV.-my variable': [
|
||||
translator(messages.variable),
|
||||
0
|
||||
]
|
||||
},
|
||||
lists: {},
|
||||
broadcasts: {},
|
||||
blocks: {},
|
||||
currentCostume: 0,
|
||||
costumes: [
|
||||
{
|
||||
assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
|
||||
name: translator(messages.backdrop, {index: 1}),
|
||||
md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
|
||||
dataFormat: 'svg',
|
||||
rotationCenterX: 240,
|
||||
rotationCenterY: 180
|
||||
}
|
||||
],
|
||||
sounds: [],
|
||||
volume: 100
|
||||
},
|
||||
{
|
||||
isStage: false,
|
||||
name: translator(messages.sprite, {index: 1}),
|
||||
variables: {},
|
||||
lists: {},
|
||||
broadcasts: {},
|
||||
blocks: {},
|
||||
comments: {},
|
||||
currentCostume: 0,
|
||||
costumes: [
|
||||
{
|
||||
assetId: '927d672925e7b99f7813735c484c6922',
|
||||
name: translator(messages.costume, {index: 1}),
|
||||
bitmapResolution: 1,
|
||||
md5ext: '927d672925e7b99f7813735c484c6922.svg',
|
||||
dataFormat: 'svg',
|
||||
rotationCenterX: 30.74937882782359,
|
||||
rotationCenterY: 58.864768144346826
|
||||
}
|
||||
],
|
||||
sounds: [],
|
||||
volume: 100,
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 100,
|
||||
direction: 90,
|
||||
draggable: false,
|
||||
rotationStyle: 'all around'
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
semver: '3.0.0',
|
||||
vm: '0.1.0',
|
||||
agent: ''
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export default projectData;
|
||||
112
scratch-gui/src/lib/define-dynamic-block.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// TODO: access `BlockType` and `ArgumentType` without reaching into VM
|
||||
// Should we move these into a new extension support module or something?
|
||||
import ArgumentType from 'scratch-vm/src/extension-support/argument-type';
|
||||
import BlockType from 'scratch-vm/src/extension-support/block-type';
|
||||
import {injectExtensionBlockTheme} from './themes/blockHelpers';
|
||||
|
||||
/**
|
||||
* Define a block using extension info which has the ability to dynamically determine (and update) its layout.
|
||||
* This functionality is used for extension blocks which can change its properties based on different state
|
||||
* information. For example, the `control_stop` block changes its shape based on which menu item is selected
|
||||
* and a variable block changes its text to reflect the variable name without using an editable field.
|
||||
* @param {object} ScratchBlocks - The ScratchBlocks name space.
|
||||
* @param {object} categoryInfo - Information about this block's extension category, including any menus and icons.
|
||||
* @param {object} staticBlockInfo - The base block information before any dynamic changes.
|
||||
* @param {string} extendedOpcode - The opcode for the block (including the extension ID).
|
||||
* @param {Theme} theme - the current theme
|
||||
*/
|
||||
// TODO: grow this until it can fully replace `_convertForScratchBlocks` in the VM runtime
|
||||
const defineDynamicBlock = (ScratchBlocks, categoryInfo, staticBlockInfo, extendedOpcode, theme) => ({
|
||||
init: function () {
|
||||
const colors = injectExtensionBlockTheme(staticBlockInfo.json, theme);
|
||||
const blockJson = {
|
||||
type: extendedOpcode,
|
||||
inputsInline: true,
|
||||
category: categoryInfo.name,
|
||||
colour: colors.colour,
|
||||
colourSecondary: colors.colourSecondary,
|
||||
colourTertiary: colors.colourTertiary,
|
||||
colourQuaternary: colors.colourQuaternary
|
||||
};
|
||||
// There is a scratch-blocks / Blockly extension called "scratch_extension" which adjusts the styling of
|
||||
// blocks to allow for an icon, a feature of Scratch extension blocks. However, Scratch "core" extension
|
||||
// blocks don't have icons and so they should not use 'scratch_extension'. Adding a scratch-blocks / Blockly
|
||||
// extension after `jsonInit` isn't fully supported (?), so we decide now whether there will be an icon.
|
||||
if (staticBlockInfo.blockIconURI || categoryInfo.blockIconURI) {
|
||||
blockJson.extensions = ['scratch_extension'];
|
||||
}
|
||||
// initialize the basics of the block, to be overridden & extended later by `domToMutation`
|
||||
this.jsonInit(blockJson);
|
||||
// initialize the cached block info used to carry block info from `domToMutation` to `mutationToDom`
|
||||
this.blockInfoText = '{}';
|
||||
// we need a block info update (through `domToMutation`) before we have a completely initialized block
|
||||
this.needsBlockInfoUpdate = true;
|
||||
},
|
||||
mutationToDom: function () {
|
||||
const container = document.createElement('mutation');
|
||||
container.setAttribute('blockInfo', this.blockInfoText);
|
||||
return container;
|
||||
},
|
||||
domToMutation: function (xmlElement) {
|
||||
const blockInfoText = xmlElement.getAttribute('blockInfo');
|
||||
if (!blockInfoText) return;
|
||||
if (!this.needsBlockInfoUpdate) {
|
||||
throw new Error('Attempted to update block info twice');
|
||||
}
|
||||
delete this.needsBlockInfoUpdate;
|
||||
this.blockInfoText = blockInfoText;
|
||||
const blockInfo = JSON.parse(blockInfoText);
|
||||
|
||||
switch (blockInfo.blockType) {
|
||||
case BlockType.COMMAND:
|
||||
case BlockType.CONDITIONAL:
|
||||
case BlockType.LOOP:
|
||||
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(!blockInfo.isTerminal);
|
||||
break;
|
||||
case BlockType.REPORTER:
|
||||
this.setOutput(true);
|
||||
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_ROUND);
|
||||
if (!blockInfo.disableMonitor) {
|
||||
this.setCheckboxInFlyout(true);
|
||||
}
|
||||
break;
|
||||
case BlockType.BOOLEAN:
|
||||
this.setOutput(true);
|
||||
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_HEXAGONAL);
|
||||
break;
|
||||
case BlockType.HAT:
|
||||
case BlockType.EVENT:
|
||||
this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
|
||||
this.setNextStatement(true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (blockInfo.color1 || blockInfo.color2 || blockInfo.color3) {
|
||||
// `setColour` handles undefined parameters by adjusting defined colors
|
||||
this.setColour(blockInfo.color1, blockInfo.color2, blockInfo.color3);
|
||||
}
|
||||
|
||||
// Layout block arguments
|
||||
// TODO handle E/C Blocks
|
||||
const blockText = blockInfo.text;
|
||||
const args = [];
|
||||
let argCount = 0;
|
||||
const scratchBlocksStyleText = blockText.replace(/\[(.+?)]/g, (match, argName) => {
|
||||
const arg = blockInfo.arguments[argName];
|
||||
switch (arg.type) {
|
||||
case ArgumentType.STRING:
|
||||
args.push({type: 'input_value', name: argName});
|
||||
break;
|
||||
case ArgumentType.BOOLEAN:
|
||||
args.push({type: 'input_value', name: argName, check: 'Boolean'});
|
||||
break;
|
||||
}
|
||||
return `%${++argCount}`;
|
||||
});
|
||||
this.interpolate_(scratchBlocksStyleText, args);
|
||||
}
|
||||
});
|
||||
|
||||
export default defineDynamicBlock;
|
||||
59
scratch-gui/src/lib/detect-locale.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* Utility function to detect locale from the browser setting or paramenter on the URL.
|
||||
*/
|
||||
|
||||
import queryString from 'query-string';
|
||||
|
||||
// tw: read language from localStorage
|
||||
export const LANGUAGE_KEY = 'tw:language';
|
||||
|
||||
/**
|
||||
* look for language setting in the browser. Check against supported locales.
|
||||
* If there's a parameter in the URL, override the browser setting
|
||||
* @param {Array.string} supportedLocales An array of supported locale codes.
|
||||
* @return {string} the preferred locale
|
||||
*/
|
||||
const detectLocale = supportedLocales => {
|
||||
// tw: read language from localStorage
|
||||
try {
|
||||
const storedLanguage = localStorage.getItem(LANGUAGE_KEY);
|
||||
if (storedLanguage && supportedLocales.includes(storedLanguage)) {
|
||||
return storedLanguage;
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 默认设置为中文简体,不再根据系统语言自动检测
|
||||
let locale = 'zh-cn'; // default changed to Chinese Simplified
|
||||
|
||||
// 注释掉根据系统语言自动检测的逻辑
|
||||
// let browserLocale = window.navigator.userLanguage || window.navigator.language;
|
||||
// browserLocale = browserLocale.toLowerCase();
|
||||
// // try to set locale from browserLocale
|
||||
// if (supportedLocales.includes(browserLocale)) {
|
||||
// locale = browserLocale;
|
||||
// } else {
|
||||
// browserLocale = browserLocale.split('-')[0];
|
||||
// if (supportedLocales.includes(browserLocale)) {
|
||||
// locale = browserLocale;
|
||||
// }
|
||||
// }
|
||||
|
||||
const queryParams = queryString.parse(location.search);
|
||||
// Flatten potential arrays and remove falsy values
|
||||
const potentialLocales = [].concat(queryParams.locale, queryParams.lang).filter(l => l);
|
||||
if (!potentialLocales.length) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
const urlLocale = potentialLocales[0].toLowerCase();
|
||||
if (supportedLocales.includes(urlLocale)) {
|
||||
return urlLocale;
|
||||
}
|
||||
|
||||
return locale;
|
||||
};
|
||||
|
||||
export {
|
||||
detectLocale
|
||||
};
|
||||
33
scratch-gui/src/lib/download-blob.js
Normal file
@@ -0,0 +1,33 @@
|
||||
export default (filename, blob) => {
|
||||
const downloadLink = document.createElement('a');
|
||||
document.body.appendChild(downloadLink);
|
||||
|
||||
// Use special ms version if available to get it working on Edge.
|
||||
if (navigator.msSaveOrOpenBlob) {
|
||||
navigator.msSaveOrOpenBlob(blob, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('download' in HTMLAnchorElement.prototype) {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
downloadLink.href = url;
|
||||
downloadLink.download = filename;
|
||||
downloadLink.type = blob.type;
|
||||
downloadLink.click();
|
||||
// remove the link after a timeout to prevent a crash on iOS 13 Safari
|
||||
window.setTimeout(() => {
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
} else {
|
||||
// iOS 12 Safari, open a new page and set href to data-uri
|
||||
let popup = window.open('', '_blank');
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
popup.location.href = reader.result;
|
||||
popup = null;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
|
||||
};
|
||||
11
scratch-gui/src/lib/drag-constants.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
SOUND: 'SOUND',
|
||||
COSTUME: 'COSTUME',
|
||||
SPRITE: 'SPRITE',
|
||||
CODE: 'CODE',
|
||||
|
||||
BACKPACK_SOUND: 'BACKPACK_SOUND',
|
||||
BACKPACK_COSTUME: 'BACKPACK_COSTUME',
|
||||
BACKPACK_SPRITE: 'BACKPACK_SPRITE',
|
||||
BACKPACK_CODE: 'BACKPACK_CODE'
|
||||
};
|
||||
130
scratch-gui/src/lib/drag-recognizer.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import bindAll from 'lodash.bindall';
|
||||
import {getEventXY} from '../lib/touch-utils';
|
||||
|
||||
class DragRecognizer {
|
||||
/* Gesture states */
|
||||
static get STATE_UNIDENTIFIED () {
|
||||
return 'unidentified';
|
||||
}
|
||||
static get STATE_SCROLL () {
|
||||
return 'scroll';
|
||||
}
|
||||
static get STATE_DRAG () {
|
||||
return 'drag';
|
||||
}
|
||||
|
||||
constructor ({
|
||||
onDrag = (() => {}),
|
||||
onDragEnd = (() => {}),
|
||||
touchDragAngle = 70, // Angle and distance thresholds are the same as scratch-blocks
|
||||
distanceThreshold = 3
|
||||
}) {
|
||||
this._onDrag = onDrag;
|
||||
this._onDragEnd = onDragEnd;
|
||||
this._touchDragAngle = touchDragAngle;
|
||||
this._distanceThreshold = distanceThreshold;
|
||||
|
||||
this._initialOffset = null;
|
||||
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
|
||||
|
||||
bindAll(this, [
|
||||
'start',
|
||||
'gestureInProgress',
|
||||
'reset',
|
||||
'_handleMove',
|
||||
'_handleEnd'
|
||||
]);
|
||||
}
|
||||
|
||||
start (event) {
|
||||
if (typeof event.button === 'number' && event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
this._initialOffset = getEventXY(event);
|
||||
this._bindListeners();
|
||||
}
|
||||
|
||||
gestureInProgress () {
|
||||
return this._gestureState !== DragRecognizer.STATE_UNIDENTIFIED;
|
||||
}
|
||||
|
||||
reset () {
|
||||
this._unbindListeners();
|
||||
this._initialOffset = null;
|
||||
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
|
||||
}
|
||||
|
||||
//
|
||||
// Internal functions
|
||||
//
|
||||
|
||||
_bindListeners () {
|
||||
window.addEventListener('mouseup', this._handleEnd);
|
||||
window.addEventListener('mousemove', this._handleMove);
|
||||
window.addEventListener('touchend', this._handleEnd);
|
||||
// touchmove must be marked as non-passive, or else it cannot prevent scrolling
|
||||
window.addEventListener('touchmove', this._handleMove, {passive: false});
|
||||
}
|
||||
|
||||
_unbindListeners () {
|
||||
window.removeEventListener('mouseup', this._handleEnd);
|
||||
window.removeEventListener('mousemove', this._handleMove);
|
||||
window.removeEventListener('touchend', this._handleEnd);
|
||||
window.removeEventListener('touchmove', this._handleMove, {passive: false});
|
||||
}
|
||||
|
||||
_handleMove (event) {
|
||||
// For gestures identified as vertical scrolls, do not process movement events
|
||||
if (this._isScroll()) return;
|
||||
|
||||
const currentOffset = getEventXY(event);
|
||||
|
||||
// Try to identify this gesture if it hasn't been identified already
|
||||
if (!this.gestureInProgress()) {
|
||||
const dx = currentOffset.x - this._initialOffset.x;
|
||||
const dy = currentOffset.y - this._initialOffset.y;
|
||||
const dragDistance = Math.sqrt((dx * dx) + (dy * dy));
|
||||
if (dragDistance < this._distanceThreshold) return;
|
||||
|
||||
// For touch moves, additionally check if the angle suggests drag vs. scroll
|
||||
if (event.type === 'touchmove') {
|
||||
// Direction goes from -180 to 180, with 0 toward the right.
|
||||
let angle = Math.atan2(dy, dx) / Math.PI * 180;
|
||||
// Fold over horizontal axis, range now 0 to 180
|
||||
angle = Math.abs(angle);
|
||||
// Fold over vertical axis, range now 0 to 90
|
||||
if (angle > 90) angle = 180 - angle;
|
||||
if (angle > this._touchDragAngle) {
|
||||
this._gestureState = DragRecognizer.STATE_SCROLL;
|
||||
} else {
|
||||
this._gestureState = DragRecognizer.STATE_DRAG;
|
||||
}
|
||||
} else {
|
||||
// Mouse moves are always considered drags
|
||||
this._gestureState = DragRecognizer.STATE_DRAG;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._isDrag()) {
|
||||
this._onDrag(currentOffset, this._initialOffset);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
_handleEnd () {
|
||||
this.reset();
|
||||
// Call the callback after reset to make sure if gestureInProgress()
|
||||
// is used in response, it get the correct value (i.e. no gesture in progress)
|
||||
this._onDragEnd();
|
||||
}
|
||||
|
||||
_isDrag () {
|
||||
return this._gestureState === DragRecognizer.STATE_DRAG;
|
||||
}
|
||||
|
||||
_isScroll () {
|
||||
return this._gestureState === DragRecognizer.STATE_SCROLL;
|
||||
}
|
||||
}
|
||||
|
||||
export default DragRecognizer;
|
||||
55
scratch-gui/src/lib/drag-utils.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* Utility functions for drag interactions, e.g. sorting items in a grid/list.
|
||||
*/
|
||||
|
||||
/**
|
||||
* From an xy position and a list of boxes {top, left, bottom, right}, return there
|
||||
* corresponding box index the position is over. The boxes are in a (possibly wrapped)
|
||||
* list, the only requirement being all boxes are flush against the edges, that is,
|
||||
* if they are along an outer edge, the position of that edge is identical.
|
||||
* This functionality works for a single column of items, a wrapped list with
|
||||
* many rows, or a single row of items.
|
||||
* @param {{x: number, y: number}} position The xy coordinates to retreive the corresponding index of.
|
||||
* @param {Array.<DOMRect>} boxes The rects of the items, returned from `getBoundingClientRect`
|
||||
* @param {bool} isRtl are the boxes in RTL order.
|
||||
* @return {?number} index of the corresponding box, or null if one could not be found.
|
||||
*/
|
||||
const indexForPositionOnList = ({x, y}, boxes, isRtl) => {
|
||||
if (boxes.length === 0) return null;
|
||||
let index = null;
|
||||
const leftEdge = Math.min.apply(null, boxes.map(b => b.left));
|
||||
const rightEdge = Math.max.apply(null, boxes.map(b => b.right));
|
||||
const topEdge = Math.min.apply(null, boxes.map(b => b.top));
|
||||
const bottomEdge = Math.max.apply(null, boxes.map(b => b.bottom));
|
||||
for (let n = 0; n < boxes.length; n++) {
|
||||
const box = boxes[n];
|
||||
// Construct an "extended" box for each, extending out to infinity if
|
||||
// the box is along a boundary.
|
||||
let minX = box.left === leftEdge ? -Infinity : box.left;
|
||||
let maxX = box.right === rightEdge ? Infinity : box.right;
|
||||
const minY = box.top === topEdge ? -Infinity : box.top;
|
||||
const maxY = box.bottom === bottomEdge ? Infinity : box.bottom;
|
||||
// The last item in the wrapped list gets a right edge at infinity, even
|
||||
// if it isn't the farthest right, in RTL mode. In LTR mode, it gets a
|
||||
// left edge at infinity.
|
||||
if (n === boxes.length - 1) {
|
||||
if (isRtl) {
|
||||
minX = -Infinity;
|
||||
} else {
|
||||
maxX = Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the point is in the bounds.
|
||||
if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
|
||||
index = n;
|
||||
break; // No need to keep looking.
|
||||
}
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
export {
|
||||
indexForPositionOnList
|
||||
};
|
||||
119
scratch-gui/src/lib/drop-area-hoc.jsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import bindAll from 'lodash.bindall';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import omit from 'lodash.omit';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
/**
|
||||
* Higher Order Component to give components the ability to react to drag overs
|
||||
* and drops of objects stored in the assetDrag redux state.
|
||||
*
|
||||
* Example: You want to enable MyComponent to receive drops from a drag type
|
||||
* Wrapped = DropAreaHOC([...dragTypes])(
|
||||
* <MyComponent />
|
||||
* )
|
||||
*
|
||||
* MyComponent now receives 2 new props
|
||||
* containerRef: a ref that must be set on the container element
|
||||
* dragOver: boolean if an asset is being dragged above the component
|
||||
*
|
||||
* Use the wrapped component:
|
||||
* <Wrapped onDrop={yourDropHandler} />
|
||||
*
|
||||
* NB: This HOC _only_ works with objects that drag using the assetDrag reducer.
|
||||
* This _does not_ handle drags for blocks coming from the workspace.
|
||||
*
|
||||
* @param {Array.<string>} dragTypes Types to respond to, from DragConstants
|
||||
* @returns {function} The HOC, specialized for those drag types
|
||||
*/
|
||||
const DropAreaHOC = function (dragTypes) {
|
||||
/**
|
||||
* Return the HOC, specialized for the dragTypes
|
||||
* @param {React.Component} WrappedComponent component to receive drop behaviors
|
||||
* @returns {React.Component} component with drag over/drop behavior
|
||||
*/
|
||||
return function (WrappedComponent) {
|
||||
class DropAreaWrapper extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'setRef'
|
||||
]);
|
||||
|
||||
this.state = {
|
||||
dragOver: false
|
||||
};
|
||||
|
||||
this.ref = null;
|
||||
this.containerBox = null;
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
// If `dragging` becomes true, record the drop area rectangle
|
||||
if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) {
|
||||
this.dropAreaRect = this.ref && this.ref.getBoundingClientRect();
|
||||
// If `dragging` becomes false, call the drop handler
|
||||
} else if (!newProps.dragInfo.dragging && this.props.dragInfo.dragging && this.state.dragOver) {
|
||||
this.props.onDrop(this.props.dragInfo);
|
||||
this.setState({dragOver: false});
|
||||
}
|
||||
|
||||
// If a drag is in progress (currentOffset) and it matches the relevant drag types,
|
||||
// test if the drag is within the drop area rect and set the state accordingly.
|
||||
if (this.dropAreaRect && newProps.dragInfo.currentOffset &&
|
||||
dragTypes.includes(newProps.dragInfo.dragType)) {
|
||||
const {x, y} = newProps.dragInfo.currentOffset;
|
||||
const {top, right, bottom, left} = this.dropAreaRect;
|
||||
if (x > left && x < right && y > top && y < bottom) {
|
||||
this.setState({dragOver: true});
|
||||
} else {
|
||||
this.setState({dragOver: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
setRef (el) {
|
||||
this.ref = el;
|
||||
if (this.props.componentRef) {
|
||||
this.props.componentRef(this.ref);
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const componentProps = omit(this.props, ['onDrop', 'dragInfo', 'componentRef']);
|
||||
return (
|
||||
<WrappedComponent
|
||||
containerRef={this.setRef}
|
||||
dragOver={this.state.dragOver}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DropAreaWrapper.propTypes = {
|
||||
componentRef: PropTypes.func,
|
||||
dragInfo: PropTypes.shape({
|
||||
currentOffset: PropTypes.shape({
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number
|
||||
}),
|
||||
dragType: PropTypes.string,
|
||||
dragging: PropTypes.bool,
|
||||
index: PropTypes.number
|
||||
}),
|
||||
onDrop: PropTypes.func
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
dragInfo: state.scratchGui.assetDrag
|
||||
});
|
||||
|
||||
const mapDispatchToProps = () => ({});
|
||||
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DropAreaWrapper);
|
||||
};
|
||||
};
|
||||
|
||||
export default DropAreaHOC;
|
||||
55
scratch-gui/src/lib/empty-assets.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* Utility functions to return json corresponding to default empty assets.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate a blank costume object for vm.addCostume with the provided name.
|
||||
* @param {string} name the name to use for the costume, caller should localize
|
||||
* @return {object} vm costume object
|
||||
*/
|
||||
const emptyCostume = name => ({
|
||||
name: name,
|
||||
md5: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
|
||||
rotationCenterX: 0,
|
||||
rotationCenterY: 0,
|
||||
bitmapResolution: 1,
|
||||
skinId: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Generate a new empty sprite. The caller should provide localized versions of the
|
||||
* default names.
|
||||
* @param {string} name the name to use for the sprite
|
||||
* @param {string} soundName the name to use for the default sound
|
||||
* @param {string} costumeName the name to use for the default costume
|
||||
* @return {object} object expected by vm.addSprite
|
||||
*/
|
||||
const emptySprite = (name, soundName, costumeName) => ({
|
||||
objName: name,
|
||||
sounds: [],
|
||||
costumes: [
|
||||
{
|
||||
costumeName: costumeName,
|
||||
baseLayerID: -1,
|
||||
baseLayerMD5: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
|
||||
bitmapResolution: 1,
|
||||
rotationCenterX: 0,
|
||||
rotationCenterY: 0
|
||||
}
|
||||
],
|
||||
currentCostumeIndex: 0,
|
||||
scratchX: 36,
|
||||
scratchY: 28,
|
||||
scale: 1,
|
||||
direction: 90,
|
||||
rotationStyle: 'normal',
|
||||
isDraggable: false,
|
||||
visible: true,
|
||||
spriteInfo: {}
|
||||
});
|
||||
|
||||
export {
|
||||
emptyCostume,
|
||||
emptySprite
|
||||
};
|
||||
26
scratch-gui/src/lib/error-boundary-hoc.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import ErrorBoundary from '../containers/error-boundary.jsx';
|
||||
|
||||
/*
|
||||
* Higher Order Component to provide error boundary for wrapped component.
|
||||
* A curried function, call like errorHOC(<tracking label>)(<Component>).
|
||||
* @param {string} action - Label for GA tracking of errors.
|
||||
* @returns {function} a function that accepts a component to wrap.
|
||||
*/
|
||||
const ErrorBoundaryHOC = function (action){
|
||||
/**
|
||||
* The function to be called with a React component to wrap it.
|
||||
* @param {React.ComponentType} WrappedComponent - Component to wrap with an error boundary.
|
||||
* @returns {React.ComponentType} the component wrapped with an error boundary.
|
||||
*/
|
||||
return function (WrappedComponent) {
|
||||
const ErrorBoundaryWrapper = props => (
|
||||
<ErrorBoundary action={action}>
|
||||
<WrappedComponent {...props} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
return ErrorBoundaryWrapper;
|
||||
};
|
||||
};
|
||||
|
||||
export default ErrorBoundaryHOC;
|
||||
290
scratch-gui/src/lib/file-uploader.js
Normal file
@@ -0,0 +1,290 @@
|
||||
import {BitmapAdapter, sanitizeSvg, fixForVanilla} from '@turbowarp/scratch-svg-renderer';
|
||||
import randomizeSpritePosition from './randomize-sprite-position.js';
|
||||
import bmpConverter from './bmp-converter';
|
||||
import gifDecoder from './gif-decoder';
|
||||
import convertAudioToWav from './tw-convert-audio-wav.js';
|
||||
import log from './log.js';
|
||||
|
||||
/**
|
||||
* Extract the file name given a string of the form fileName + ext
|
||||
* @param {string} nameExt File name + extension (e.g. 'my_image.png')
|
||||
* @return {string} The name without the extension, or the full name if
|
||||
* there was no '.' in the string (e.g. 'my_image')
|
||||
*/
|
||||
const extractFileName = function (nameExt) {
|
||||
// There could be multiple dots, but get the stuff before the first .
|
||||
const nameParts = nameExt.split('.', 1); // we only care about the first .
|
||||
return nameParts[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a file upload given the input element that contains the file,
|
||||
* and a function to handle loading the file.
|
||||
* @param {Input} fileInput The <input/> element that contains the file being loaded
|
||||
* @param {Function} onload The function that handles loading the file
|
||||
* @param {Function} onerror The function that handles any error loading the file
|
||||
*/
|
||||
const handleFileUpload = function (fileInput, onload, onerror) {
|
||||
const readFile = (i, files) => {
|
||||
if (i === files.length) {
|
||||
// Reset the file input value now that we have everything we need
|
||||
// so that the user can upload the same sound multiple times if
|
||||
// they choose
|
||||
fileInput.value = null;
|
||||
return;
|
||||
}
|
||||
const file = files[i];
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const fileType = file.type;
|
||||
const fileName = extractFileName(file.name);
|
||||
onload(reader.result, fileType, fileName, i, files.length);
|
||||
readFile(i + 1, files);
|
||||
};
|
||||
reader.onerror = onerror;
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
readFile(0, fileInput.files);
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef VMAsset
|
||||
* @property {string} name The user-readable name of this asset - This will
|
||||
* automatically get translated to a fresh name if this one already exists in the
|
||||
* scope of this vm asset (e.g. if a sound already exists with the same name for
|
||||
* the same target)
|
||||
* @property {string} dataFormat The data format of this asset, typically
|
||||
* the extension to be used for that particular asset, e.g. 'svg' for vector images
|
||||
* @property {string} md5 The md5 hash of the asset data, followed by '.'' and dataFormat
|
||||
* @property {string} The md5 hash of the asset data // TODO remove duplication....
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create an asset (costume, sound) with storage and return an object representation
|
||||
* of the asset to track in the VM.
|
||||
* @param {ScratchStorage} storage The storage to cache the asset in
|
||||
* @param {AssetType} assetType A ScratchStorage AssetType indicating what kind of
|
||||
* asset this is.
|
||||
* @param {string} dataFormat The format of this data (typically the file extension)
|
||||
* @param {UInt8Array} data The asset data buffer
|
||||
* @return {VMAsset} An object representing this asset and relevant information
|
||||
* which can be used to look up the data in storage
|
||||
*/
|
||||
const createVMAsset = function (storage, assetType, dataFormat, data) {
|
||||
const asset = storage.createAsset(
|
||||
assetType,
|
||||
dataFormat,
|
||||
data,
|
||||
null,
|
||||
true // generate md5
|
||||
);
|
||||
|
||||
return {
|
||||
name: null, // Needs to be set by caller
|
||||
dataFormat: dataFormat,
|
||||
asset: asset,
|
||||
md5: `${asset.assetId}.${dataFormat}`,
|
||||
assetId: asset.assetId
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles loading a costume or a backdrop using the provided, context-relevant information.
|
||||
* @param {ArrayBuffer | string} fileData The costume data to load (this can be a base64 string
|
||||
* iff the image is a bitmap)
|
||||
* @param {string} fileType The MIME type of this file
|
||||
* @param {VM} vm The ScratchStorage instance to cache the costume data
|
||||
* @param {Function} handleCostume The function to execute on the costume object returned after
|
||||
* caching this costume in storage - This function should be responsible for
|
||||
* adding the costume to the VM and handling other UI flow that should come after adding the costume
|
||||
* @param {Function} handleError The function to execute if there is an error parsing the costume
|
||||
*/
|
||||
const costumeUpload = function (fileData, fileType, vm, handleCostume, handleError = () => {}) {
|
||||
const storage = vm.runtime.storage;
|
||||
let costumeFormat = null;
|
||||
let assetType = null;
|
||||
switch (fileType) {
|
||||
case 'image/svg+xml': {
|
||||
// fix any vanilla compatibility issues in the SVG first
|
||||
try {
|
||||
fileData = fixForVanilla(fileData);
|
||||
} catch (e) {
|
||||
log.error('fixForVanilla error', e);
|
||||
}
|
||||
|
||||
// run svg bytes through scratch-svg-renderer's sanitization code
|
||||
fileData = sanitizeSvg.sanitizeByteStream(fileData);
|
||||
|
||||
costumeFormat = storage.DataFormat.SVG;
|
||||
assetType = storage.AssetType.ImageVector;
|
||||
break;
|
||||
}
|
||||
case 'image/jpeg': {
|
||||
costumeFormat = storage.DataFormat.JPG;
|
||||
assetType = storage.AssetType.ImageBitmap;
|
||||
break;
|
||||
}
|
||||
case 'image/bmp': {
|
||||
// Convert .bmp files to .png to compress them. .bmps are completely uncompressed,
|
||||
// and would otherwise take up a lot of storage space and take much longer to upload and download.
|
||||
bmpConverter(fileData).then(dataUrl => {
|
||||
costumeUpload(dataUrl, 'image/png', vm, handleCostume);
|
||||
});
|
||||
return; // Return early because we're triggering another proper costumeUpload
|
||||
}
|
||||
case 'image/png': {
|
||||
costumeFormat = storage.DataFormat.PNG;
|
||||
assetType = storage.AssetType.ImageBitmap;
|
||||
break;
|
||||
}
|
||||
case 'image/webp': {
|
||||
// Scratch does not natively support webp, so convert to png
|
||||
// see image/bmp logic above
|
||||
bmpConverter(fileData, 'image/webp').then(dataUrl => {
|
||||
costumeUpload(dataUrl, 'image/png', vm, handleCostume);
|
||||
});
|
||||
return;
|
||||
}
|
||||
case 'image/gif': {
|
||||
let costumes = [];
|
||||
gifDecoder(fileData, (frameNumber, dataUrl, numFrames) => {
|
||||
costumeUpload(dataUrl, 'image/png', vm, costumes_ => {
|
||||
costumes = costumes.concat(costumes_);
|
||||
if (frameNumber === numFrames - 1) {
|
||||
handleCostume(costumes);
|
||||
}
|
||||
}, handleError);
|
||||
});
|
||||
return; // Abandon this load, do not try to load gif itself
|
||||
}
|
||||
default:
|
||||
handleError(`Encountered unexpected file type: ${fileType}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const bitmapAdapter = new BitmapAdapter();
|
||||
if (bitmapAdapter.setStageSize) {
|
||||
const width = vm.runtime.stageWidth;
|
||||
const height = vm.runtime.stageHeight;
|
||||
bitmapAdapter.setStageSize(width, height);
|
||||
}
|
||||
const addCostumeFromBuffer = function (dataBuffer) {
|
||||
const vmCostume = createVMAsset(
|
||||
storage,
|
||||
assetType,
|
||||
costumeFormat,
|
||||
dataBuffer
|
||||
);
|
||||
handleCostume([vmCostume]);
|
||||
};
|
||||
|
||||
if (costumeFormat === storage.DataFormat.SVG) {
|
||||
// Must pass in file data as a Uint8Array,
|
||||
// passing in an array buffer causes the sprite/costume
|
||||
// thumbnails to not display because the data URI for the costume
|
||||
// is invalid
|
||||
addCostumeFromBuffer(new Uint8Array(fileData));
|
||||
} else {
|
||||
// otherwise it's a bitmap
|
||||
bitmapAdapter.importBitmap(fileData, fileType).then(addCostumeFromBuffer)
|
||||
.catch(handleError);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles loading a sound using the provided, context-relevant information.
|
||||
* @param {ArrayBuffer} fileData The sound data to load
|
||||
* @param {string} fileType The MIME type of this file; This function will exit
|
||||
* early if the fileType is unexpected.
|
||||
* @param {ScratchStorage} storage The ScratchStorage instance to cache the sound data
|
||||
* @param {Function} handleSound The function to execute on the sound object of type VMAsset
|
||||
* This function should be responsible for adding the sound to the VM
|
||||
* as well as handling other UI flow that should come after adding the sound
|
||||
* @param {Function} handleError The function to execute if there is an error parsing the sound
|
||||
*/
|
||||
const soundUpload = function (fileData, fileType, storage, handleSound, handleError) {
|
||||
let soundFormat;
|
||||
switch (fileType) {
|
||||
case 'audio/mp3':
|
||||
case 'audio/mpeg': {
|
||||
soundFormat = storage.DataFormat.MP3;
|
||||
break;
|
||||
}
|
||||
case 'audio/wav':
|
||||
case 'audio/wave':
|
||||
case 'audio/x-wav':
|
||||
case 'audio/x-pn-wav': {
|
||||
soundFormat = storage.DataFormat.WAV;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
convertAudioToWav(fileData)
|
||||
.then(fixed => {
|
||||
soundUpload(fixed, 'audio/wav', storage, handleSound, handleError);
|
||||
})
|
||||
.catch(handleError);
|
||||
return;
|
||||
}
|
||||
|
||||
const vmSound = createVMAsset(
|
||||
storage,
|
||||
storage.AssetType.Sound,
|
||||
soundFormat,
|
||||
new Uint8Array(fileData));
|
||||
|
||||
handleSound(vmSound);
|
||||
};
|
||||
|
||||
const spriteUpload = function (fileData, fileType, spriteName, vm, handleSprite, handleError = () => {}) {
|
||||
switch (fileType) {
|
||||
case '':
|
||||
case 'application/zip': { // We think this is a .sprite2 or .sprite3 file
|
||||
handleSprite(new Uint8Array(fileData));
|
||||
return;
|
||||
}
|
||||
case 'image/svg+xml':
|
||||
case 'image/png':
|
||||
case 'image/bmp':
|
||||
case 'image/jpeg':
|
||||
case 'image/webp':
|
||||
case 'image/gif': {
|
||||
// Make a sprite from an image by making it a costume first
|
||||
costumeUpload(fileData, fileType, vm, vmCostumes => {
|
||||
vmCostumes.forEach((costume, i) => {
|
||||
costume.name = `${spriteName}${i ? i + 1 : ''}`;
|
||||
});
|
||||
const newSprite = {
|
||||
name: spriteName,
|
||||
isStage: false,
|
||||
x: 0, // x/y will be randomized below
|
||||
y: 0,
|
||||
visible: true,
|
||||
size: 100,
|
||||
rotationStyle: 'all around',
|
||||
direction: 90,
|
||||
draggable: false,
|
||||
currentCostume: 0,
|
||||
blocks: {},
|
||||
variables: {},
|
||||
costumes: vmCostumes,
|
||||
sounds: [] // TODO are all of these necessary?
|
||||
};
|
||||
randomizeSpritePosition(newSprite);
|
||||
// TODO probably just want sprite upload to handle this object directly
|
||||
handleSprite(JSON.stringify(newSprite));
|
||||
}, handleError);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
handleError(`Encountered unexpected file type: ${fileType}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
handleFileUpload,
|
||||
costumeUpload,
|
||||
soundUpload,
|
||||
spriteUpload
|
||||
};
|
||||
48
scratch-gui/src/lib/font-loader-hoc.jsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import omit from 'lodash.omit';
|
||||
import {connect} from 'react-redux';
|
||||
import {setFontsLoaded} from '../reducers/fonts-loaded';
|
||||
import {loadFonts} from 'scratch-render-fonts';
|
||||
|
||||
/* Higher Order Component to provide behavior for loading fonts.
|
||||
* @param {React.Component} WrappedComponent component to receive fontsLoaded prop
|
||||
* @returns {React.Component} component with font loading behavior
|
||||
*/
|
||||
const FontLoaderHOC = function (WrappedComponent) {
|
||||
class FontLoaderComponent extends React.Component {
|
||||
componentDidMount () {
|
||||
if (this.props.fontsLoaded) return;
|
||||
|
||||
loadFonts().then(() => this.props.onSetFontsLoaded());
|
||||
}
|
||||
render () {
|
||||
const componentProps = omit(this.props, ['onSetFontsLoaded']);
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FontLoaderComponent.propTypes = {
|
||||
fontsLoaded: PropTypes.bool.isRequired,
|
||||
onSetFontsLoaded: PropTypes.func.isRequired
|
||||
};
|
||||
const mapStateToProps = state => ({
|
||||
fontsLoaded: state.scratchGui.fontsLoaded
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onSetFontsLoaded: () => dispatch(setFontsLoaded())
|
||||
});
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(FontLoaderComponent);
|
||||
};
|
||||
|
||||
export {
|
||||
FontLoaderHOC as default
|
||||
};
|
||||
40
scratch-gui/src/lib/get-costume-url.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import storage from './storage';
|
||||
import {inlineSvgFonts} from '@turbowarp/scratch-svg-renderer';
|
||||
|
||||
// Contains 'font-family', but doesn't only contain 'font-family="none"'
|
||||
const HAS_FONT_REGEXP = 'font-family(?!="none")';
|
||||
|
||||
const getCostumeUrl = (function () {
|
||||
let cachedAssetId;
|
||||
let cachedUrl;
|
||||
|
||||
return function (asset) {
|
||||
|
||||
if (cachedAssetId === asset.assetId) {
|
||||
return cachedUrl;
|
||||
}
|
||||
|
||||
cachedAssetId = asset.assetId;
|
||||
|
||||
// If the SVG refers to fonts, they must be inlined in order to display correctly in the img tag.
|
||||
// Avoid parsing the SVG when possible, since it's expensive.
|
||||
if (asset.assetType === storage.AssetType.ImageVector) {
|
||||
const svgString = asset.decodeText();
|
||||
if (svgString.match(HAS_FONT_REGEXP)) {
|
||||
const svgText = inlineSvgFonts(svgString);
|
||||
cachedUrl = `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`;
|
||||
} else {
|
||||
cachedUrl = asset.encodeDataURI();
|
||||
}
|
||||
} else {
|
||||
cachedUrl = asset.encodeDataURI();
|
||||
}
|
||||
|
||||
return cachedUrl;
|
||||
};
|
||||
}());
|
||||
|
||||
export {
|
||||
getCostumeUrl as default,
|
||||
HAS_FONT_REGEXP
|
||||
};
|
||||
60
scratch-gui/src/lib/gif-decoder.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import {GifReader} from 'omggif';
|
||||
|
||||
export default (arrayBuffer, onFrame) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const gifReader = new GifReader(new Uint8Array(arrayBuffer));
|
||||
const numFrames = gifReader.numFrames();
|
||||
canvas.width = gifReader.width;
|
||||
canvas.height = gifReader.height;
|
||||
|
||||
let imageData = ctx.createImageData(canvas.width, canvas.height);
|
||||
let previousData = ctx.createImageData(canvas.width, canvas.height);
|
||||
|
||||
const loadFrame = i => {
|
||||
const framePixels = [];
|
||||
gifReader.decodeAndBlitFrameRGBA(i, framePixels);
|
||||
const {x, y, width, height, disposal} = gifReader.frameInfo(i);
|
||||
for (let row = 0; row < height; row++) {
|
||||
for (let column = 0; column < width; column++) {
|
||||
const indexOffset = 4 * (x + (y * canvas.width));
|
||||
const j = indexOffset + (4 * (column + (row * canvas.width)));
|
||||
if (framePixels[j + 3]) {
|
||||
imageData.data[j + 0] = framePixels[j + 0];
|
||||
imageData.data[j + 1] = framePixels[j + 1];
|
||||
imageData.data[j + 2] = framePixels[j + 2];
|
||||
imageData.data[j + 3] = framePixels[j + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
|
||||
const dataUrl = canvas.toDataURL();
|
||||
|
||||
switch (disposal) {
|
||||
case 2: // "Return to background", blank out the current frame
|
||||
ctx.clearRect(x, y, width, height);
|
||||
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
break;
|
||||
case 3: // "Restore to previous", copy previous data to current
|
||||
imageData = ctx.createImageData(canvas.width, canvas.height);
|
||||
imageData.data.set(previousData.data);
|
||||
break;
|
||||
default: // 0 and 1, as well as 4+ modes = do-not-dispose, so cache frame
|
||||
previousData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
break;
|
||||
|
||||
}
|
||||
onFrame(i, dataUrl, numFrames);
|
||||
|
||||
if (i < numFrames - 1) {
|
||||
setTimeout(() => {
|
||||
loadFrame(i + 1);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
loadFrame(0);
|
||||
};
|
||||
90
scratch-gui/src/lib/hash-parser-hoc.jsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import bindAll from 'lodash.bindall';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {
|
||||
defaultProjectId,
|
||||
getIsFetchingWithoutId,
|
||||
setProjectId
|
||||
} from '../reducers/project-state';
|
||||
|
||||
/* Higher Order Component to get the project id from location.hash
|
||||
* @param {React.Component} WrappedComponent: component to render
|
||||
* @returns {React.Component} component with hash parsing behavior
|
||||
*/
|
||||
const HashParserHOC = function (WrappedComponent) {
|
||||
class HashParserComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleHashChange'
|
||||
]);
|
||||
}
|
||||
componentDidMount () {
|
||||
window.addEventListener('hashchange', this.handleHashChange);
|
||||
this.handleHashChange();
|
||||
}
|
||||
componentDidUpdate (prevProps) {
|
||||
// if we are newly fetching a non-hash project...
|
||||
if (this.props.isFetchingWithoutId && !prevProps.isFetchingWithoutId) {
|
||||
// ...clear the hash from the url
|
||||
history.pushState('new-project', 'new-project',
|
||||
window.location.pathname + window.location.search);
|
||||
}
|
||||
}
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('hashchange', this.handleHashChange);
|
||||
}
|
||||
handleHashChange () {
|
||||
const hashMatch = window.location.hash.match(/#(\d+)/);
|
||||
const hashProjectId = hashMatch === null ? defaultProjectId : hashMatch[1];
|
||||
this.props.setProjectId(hashProjectId.toString());
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
isFetchingWithoutId: isFetchingWithoutIdProp,
|
||||
reduxProjectId,
|
||||
setProjectId: setProjectIdProp,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
HashParserComponent.propTypes = {
|
||||
isFetchingWithoutId: PropTypes.bool,
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
setProjectId: PropTypes.func
|
||||
};
|
||||
const mapStateToProps = state => {
|
||||
const loadingState = state.scratchGui.projectState.loadingState;
|
||||
return {
|
||||
isFetchingWithoutId: getIsFetchingWithoutId(loadingState),
|
||||
reduxProjectId: state.scratchGui.projectState.projectId
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setProjectId: projectId => {
|
||||
dispatch(setProjectId(projectId));
|
||||
}
|
||||
});
|
||||
// Allow incoming props to override redux-provided props. Used to mock in tests.
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
|
||||
{}, stateProps, dispatchProps, ownProps
|
||||
);
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(HashParserComponent);
|
||||
};
|
||||
|
||||
export {
|
||||
HashParserHOC as default
|
||||
};
|
||||
34
scratch-gui/src/lib/import-csv.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import Papa from 'papaparse';
|
||||
|
||||
export default () => new Promise((resolve, reject) => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.setAttribute('type', 'file');
|
||||
fileInput.setAttribute('accept', '.csv, .tsv, .txt'); // parser auto-detects delimiter
|
||||
fileInput.onchange = e => {
|
||||
const file = e.target.files[0];
|
||||
const fr = new FileReader();
|
||||
fr.onload = () => {
|
||||
document.body.removeChild(fileInput);
|
||||
const text = fr.result;
|
||||
Papa.parse(text, {
|
||||
header: false,
|
||||
complete: results => {
|
||||
resolve({
|
||||
rows: results.data,
|
||||
text
|
||||
});
|
||||
},
|
||||
error: err => {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
fr.onerror = () => {
|
||||
document.body.removeChild(fileInput);
|
||||
reject(new Error('Cannot read file'));
|
||||
};
|
||||
fr.readAsText(file);
|
||||
};
|
||||
document.body.appendChild(fileInput);
|
||||
fileInput.click();
|
||||
});
|
||||
35
scratch-gui/src/lib/isScratchDesktop.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Internal stored state. Not valid until after at least one call to `setIsScratchDesktop()`.
|
||||
* @type {boolean}
|
||||
*/
|
||||
let _isScratchDesktop; // undefined = not ready yet
|
||||
|
||||
/**
|
||||
* Tell the `isScratchDesktop()` whether or not the GUI is running under Scratch Desktop.
|
||||
* @param {boolean} value - the new value which `isScratchDesktop()` should return in the future.
|
||||
*/
|
||||
const setIsScratchDesktop = function (value) {
|
||||
_isScratchDesktop = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} - true if it seems like the GUI is running under Scratch Desktop; false otherwise.
|
||||
* If `setIsScratchDesktop()` has not yet been called, this can return `undefined`.
|
||||
*/
|
||||
const isScratchDesktop = function () {
|
||||
return _isScratchDesktop;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} - false if it seems like the GUI is running under Scratch Desktop; true otherwise.
|
||||
*/
|
||||
const notScratchDesktop = function () {
|
||||
return !isScratchDesktop();
|
||||
};
|
||||
|
||||
export default isScratchDesktop;
|
||||
export {
|
||||
isScratchDesktop,
|
||||
notScratchDesktop,
|
||||
setIsScratchDesktop
|
||||
};
|
||||
70
scratch-gui/src/lib/layout-constants.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import keyMirror from 'keymirror';
|
||||
|
||||
/**
|
||||
* Names for each state of the stage size toggle
|
||||
* @enum {string}
|
||||
*/
|
||||
const STAGE_SIZE_MODES = keyMirror({
|
||||
/**
|
||||
* Display the stage at a large (but fixed) width.
|
||||
*/
|
||||
large: null,
|
||||
|
||||
/**
|
||||
* Display the stage at a small (but fixed) width.
|
||||
*/
|
||||
small: null,
|
||||
|
||||
/**
|
||||
* Display the stage at its full size.
|
||||
*/
|
||||
full: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Names for each stage render size
|
||||
* @enum {string}
|
||||
*/
|
||||
const STAGE_DISPLAY_SIZES = keyMirror({
|
||||
large: null,
|
||||
|
||||
small: null,
|
||||
|
||||
constrained: null,
|
||||
|
||||
full: null
|
||||
});
|
||||
|
||||
// zoom level to start with
|
||||
const BLOCKS_DEFAULT_SCALE = 0.675;
|
||||
|
||||
const FIXED_WIDTH = 480;
|
||||
|
||||
/**
|
||||
* Minimum amount of screen width (excluding the width used by the stage itself) to display constrained.
|
||||
*/
|
||||
const UNCONSTRAINED_NON_STAGE_WIDTH = 1096 - FIXED_WIDTH;
|
||||
|
||||
const STAGE_DISPLAY_SCALE_METADATA = {
|
||||
[STAGE_DISPLAY_SIZES.large]: {
|
||||
width: FIXED_WIDTH
|
||||
},
|
||||
[STAGE_DISPLAY_SIZES.small]: {
|
||||
width: FIXED_WIDTH * 0.5
|
||||
},
|
||||
[STAGE_DISPLAY_SIZES.constrained]: {
|
||||
scale: 0.85
|
||||
},
|
||||
[STAGE_DISPLAY_SIZES.full]: {
|
||||
scale: 1
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
BLOCKS_DEFAULT_SCALE,
|
||||
STAGE_DISPLAY_SCALE_METADATA,
|
||||
STAGE_DISPLAY_SIZES,
|
||||
STAGE_SIZE_MODES,
|
||||
FIXED_WIDTH,
|
||||
UNCONSTRAINED_NON_STAGE_WIDTH
|
||||
};
|
||||
7
scratch-gui/src/lib/libraries/.eslintrc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
rules: {
|
||||
// These manifest files use duplicate imports to make things easier to follow
|
||||
// by providing clear parallel structure. Turn the error off for this folder.
|
||||
'no-duplicate-imports': 0
|
||||
}
|
||||
};
|
||||
11
scratch-gui/src/lib/libraries/backdrop-tags.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import messages from './tag-messages.js';
|
||||
export default [
|
||||
{tag: 'fantasy', intlLabel: messages.fantasy},
|
||||
{tag: 'music', intlLabel: messages.music},
|
||||
{tag: 'sports', intlLabel: messages.sports},
|
||||
{tag: 'outdoors', intlLabel: messages.outdoors},
|
||||
{tag: 'indoors', intlLabel: messages.indoors},
|
||||
{tag: 'space', intlLabel: messages.space},
|
||||
{tag: 'underwater', intlLabel: messages.underwater},
|
||||
{tag: 'patterns', intlLabel: messages.patterns}
|
||||
];
|
||||
1184
scratch-gui/src/lib/libraries/backdrops.json
Normal file
12421
scratch-gui/src/lib/libraries/costumes.json
Normal file
405
scratch-gui/src/lib/libraries/decks/am-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.am.gif';
|
||||
import introSay from './steps/intro-2-say.am.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.am.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.am.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.am.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.am.png';
|
||||
import speechMoveAround from './steps/speech-move-around.am.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.am.png';
|
||||
import speechChangeColor from './steps/speech-change-color.am.png';
|
||||
import speechSpin from './steps/speech-spin.am.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.am.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.am.png';
|
||||
import cnGlide from './steps/cn-glide.am.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.am.png';
|
||||
import cnVariable from './steps/add-variable.am.gif';
|
||||
import cnScore from './steps/cn-score.am.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.am.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.am.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.am.png';
|
||||
import nameSpin from './steps/name-spin.am.png';
|
||||
import nameGrow from './steps/name-grow.am.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.am.png';
|
||||
import musicMakeSong from './steps/music-make-song.am.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.am.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.am.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.am.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.am.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.am.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.am.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.am.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.am.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.am.png';
|
||||
import popGameAddScore from './steps/add-variable.am.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.am.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.am.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.am.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.am.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.am.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.am.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.am.png';
|
||||
import animateCharMove from './steps/animate-char-move.am.png';
|
||||
import animateCharJump from './steps/animate-char-jump.am.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.am.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.am.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.am.gif';
|
||||
import storyConversation from './steps/story-conversation.am.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.am.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.am.png';
|
||||
import storyShowCharacter from './steps/story-show-character.am.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.am.gif';
|
||||
import videoPet from './steps/video-pet.am.png';
|
||||
import videoAnimate from './steps/video-animate.am.png';
|
||||
import videoPop from './steps/video-pop.am.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.am.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.am.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.am.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.am.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.am.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.am.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.am.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.am.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.am.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.am.png';
|
||||
import pongAddAScore from './steps/add-variable.am.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.am.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.am.png';
|
||||
import pongResetScore from './steps/pong-reset-score.am.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.am.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.am.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.am.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.am.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.am.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.am.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.am.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.am.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.am.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.am.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.am.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.am.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.am.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.am.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.am.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.am.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.am.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.am.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.am.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.am.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.am.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.am.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.am.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.am.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.am.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.am.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.am.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.am.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.am.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.am.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.am.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.am.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.am.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.am.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.am.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.am.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.am.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.am.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.am.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.am.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.am.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.am.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.am.png';
|
||||
|
||||
const amImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {amImages};
|
||||
405
scratch-gui/src/lib/libraries/decks/ar-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.ar.gif';
|
||||
import introSay from './steps/intro-2-say.ar.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.ar.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.ar.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.ar.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.ar.png';
|
||||
import speechMoveAround from './steps/speech-move-around.ar.png';
|
||||
import speechAddBackdrop from './steps/add-backdrop.RTL.png';
|
||||
import speechAddSprite from './steps/speech-add-sprite.RTL.gif';
|
||||
import speechSong from './steps/speech-song.ar.png';
|
||||
import speechChangeColor from './steps/speech-change-color.ar.png';
|
||||
import speechSpin from './steps/speech-spin.ar.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.ar.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.ar.png';
|
||||
import cnGlide from './steps/cn-glide.ar.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.RTL.gif';
|
||||
import cnCollect from './steps/cn-collect.ar.png';
|
||||
import cnVariable from './steps/add-variable.ar.gif';
|
||||
import cnScore from './steps/cn-score.ar.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.ar.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.RTL.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.RTL.gif';
|
||||
import namePlaySound from './steps/name-play-sound.ar.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.RTL.gif';
|
||||
import nameChangeColor from './steps/name-change-color.ar.png';
|
||||
import nameSpin from './steps/name-spin.ar.png';
|
||||
import nameGrow from './steps/name-grow.ar.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.RTL.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.ar.png';
|
||||
import musicMakeSong from './steps/music-make-song.ar.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.ar.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.ar.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.RTL.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.RTL.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.ar.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.ar.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.RTL.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.ar.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.ar.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.ar.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.ar.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.RTL.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.ar.png';
|
||||
import popGameAddScore from './steps/add-variable.ar.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.ar.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.ar.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.ar.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.ar.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/animate-char-pick-backdrop.RTL.png';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.RTL.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.ar.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.ar.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.ar.png';
|
||||
import animateCharMove from './steps/animate-char-move.ar.png';
|
||||
import animateCharJump from './steps/animate-char-jump.ar.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.ar.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.RTL.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.RTL.gif';
|
||||
import storySaySomething from './steps/story-say-something.ar.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.RTL.gif';
|
||||
import storyFlip from './steps/story-flip.ar.gif';
|
||||
import storyConversation from './steps/story-conversation.ar.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.RTL.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.ar.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.ar.png';
|
||||
import storyShowCharacter from './steps/story-show-character.ar.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.ar.gif';
|
||||
import videoPet from './steps/video-pet.ar.png';
|
||||
import videoAnimate from './steps/video-animate.ar.png';
|
||||
import videoPop from './steps/video-pop.ar.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.RTL.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.RTL.png';
|
||||
import flySaySomething from './steps/fly-say-something.ar.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.ar.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.RTL.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.ar.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.RTL.png';
|
||||
import flyAddScore from './steps/add-variable.ar.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.ar.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.RTL.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.ar.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.ar.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.RTL.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.RTL.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.ar.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.RTL.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.ar.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.RTL.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.ar.png';
|
||||
import pongAddAScore from './steps/add-variable.ar.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.ar.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.ar.png';
|
||||
import pongResetScore from './steps/pong-reset-score.ar.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.RTL.gif';
|
||||
import pongGameOver from './steps/pong-game-over.ar.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.ar.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.ar.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.RTL.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.RTL.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.ar.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.RTL.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.ar.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.ar.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.ar.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.ar.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.ar.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.RTL.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.ar.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.ar.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.ar.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.RTL.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.ar.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.ar.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.ar.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.ar.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.ar.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.ar.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.ar.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.ar.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.ar.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.ar.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.ar.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.ar.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.ar.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.ar.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.ar.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.ar.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.ar.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.RTL.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.ar.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.ar.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.RTL.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.ar.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.ar.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.ar.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.ar.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.RTL.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.ar.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.RTL.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.ar.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.RTL.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.ar.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.RTL.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.ar.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.RTL.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.ar.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.RTL.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.ar.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.ar.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.ar.png';
|
||||
|
||||
const arImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {arImages};
|
||||
405
scratch-gui/src/lib/libraries/decks/en-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.en.gif';
|
||||
import introSay from './steps/intro-2-say.en.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.en.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.en.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.en.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.en.png';
|
||||
import speechMoveAround from './steps/speech-move-around.en.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.en.png';
|
||||
import speechChangeColor from './steps/speech-change-color.en.png';
|
||||
import speechSpin from './steps/speech-spin.en.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.en.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.en.png';
|
||||
import cnGlide from './steps/cn-glide.en.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.en.png';
|
||||
import cnVariable from './steps/add-variable.en.gif';
|
||||
import cnScore from './steps/cn-score.en.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.en.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.en.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.en.png';
|
||||
import nameSpin from './steps/name-spin.en.png';
|
||||
import nameGrow from './steps/name-grow.en.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.en.png';
|
||||
import musicMakeSong from './steps/music-make-song.en.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.en.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.en.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.en.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.en.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.en.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.en.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.en.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.en.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.en.png';
|
||||
import popGameAddScore from './steps/add-variable.en.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.en.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.en.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.en.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.en.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.en.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.en.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.en.png';
|
||||
import animateCharMove from './steps/animate-char-move.en.png';
|
||||
import animateCharJump from './steps/animate-char-jump.en.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.en.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.en.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.en.gif';
|
||||
import storyConversation from './steps/story-conversation.en.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.en.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.en.png';
|
||||
import storyShowCharacter from './steps/story-show-character.en.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.en.gif';
|
||||
import videoPet from './steps/video-pet.en.png';
|
||||
import videoAnimate from './steps/video-animate.en.png';
|
||||
import videoPop from './steps/video-pop.en.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.en.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.en.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.en.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.en.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.en.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.en.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.en.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.en.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.en.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.en.png';
|
||||
import pongAddAScore from './steps/add-variable.en.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.en.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.en.png';
|
||||
import pongResetScore from './steps/pong-reset-score.en.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.en.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.en.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.en.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.en.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.en.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.en.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.en.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.en.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.en.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.en.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.en.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.en.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.en.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.en.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.en.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.en.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.en.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.en.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.en.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.en.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.en.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.en.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.en.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.en.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.en.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.en.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.en.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.en.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.en.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.en.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.en.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.en.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.en.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.en.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.en.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.en.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.en.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.en.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.en.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.en.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.en.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.en.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.en.png';
|
||||
|
||||
const enImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {enImages};
|
||||
405
scratch-gui/src/lib/libraries/decks/es-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.es.gif';
|
||||
import introSay from './steps/intro-2-say.es.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.es.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.es.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.es.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.es.png';
|
||||
import speechMoveAround from './steps/speech-move-around.es.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.es.png';
|
||||
import speechChangeColor from './steps/speech-change-color.es.png';
|
||||
import speechSpin from './steps/speech-spin.es.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.es.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.es.png';
|
||||
import cnGlide from './steps/cn-glide.es.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.es.png';
|
||||
import cnVariable from './steps/add-variable.es.gif';
|
||||
import cnScore from './steps/cn-score.es.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.es.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.es.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.es.png';
|
||||
import nameSpin from './steps/name-spin.es.png';
|
||||
import nameGrow from './steps/name-grow.es.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.es.png';
|
||||
import musicMakeSong from './steps/music-make-song.es.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.es.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.es.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.es.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.es.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.es.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.es.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.es.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.es.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.es.png';
|
||||
import popGameAddScore from './steps/add-variable.es.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.es.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.es.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.es.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.es.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.es.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.es.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.es.png';
|
||||
import animateCharMove from './steps/animate-char-move.es.png';
|
||||
import animateCharJump from './steps/animate-char-jump.es.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.es.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.es.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.es.gif';
|
||||
import storyConversation from './steps/story-conversation.es.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.es.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.es.png';
|
||||
import storyShowCharacter from './steps/story-show-character.es.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.es.gif';
|
||||
import videoPet from './steps/video-pet.es.png';
|
||||
import videoAnimate from './steps/video-animate.es.png';
|
||||
import videoPop from './steps/video-pop.es.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.es.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.es.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.es.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.es.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.es.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.es.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.es.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.es.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.es.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.es.png';
|
||||
import pongAddAScore from './steps/add-variable.es.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.es.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.es.png';
|
||||
import pongResetScore from './steps/pong-reset-score.es.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.es.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.es.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.es.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.es.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.es.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.es.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.es.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.es.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.es.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.es.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.es.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.es.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.es.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.es.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.es.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.es.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.es.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.es.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.es.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.es.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.es.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.es.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.es.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.es.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.es.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.es.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.es.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.es.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.es.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.es.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.es.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.es.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.es.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.es.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.es.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.es.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.es.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.es.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.es.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.es.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.es.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.es.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.es.png';
|
||||
|
||||
const esImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {esImages};
|
||||
405
scratch-gui/src/lib/libraries/decks/fr-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.fr.gif';
|
||||
import introSay from './steps/intro-2-say.fr.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.fr.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.fr.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.fr.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.fr.png';
|
||||
import speechMoveAround from './steps/speech-move-around.fr.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.fr.png';
|
||||
import speechChangeColor from './steps/speech-change-color.fr.png';
|
||||
import speechSpin from './steps/speech-spin.fr.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.fr.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.fr.png';
|
||||
import cnGlide from './steps/cn-glide.fr.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.fr.png';
|
||||
import cnVariable from './steps/add-variable.fr.gif';
|
||||
import cnScore from './steps/cn-score.fr.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.fr.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.fr.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.fr.png';
|
||||
import nameSpin from './steps/name-spin.fr.png';
|
||||
import nameGrow from './steps/name-grow.fr.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.fr.png';
|
||||
import musicMakeSong from './steps/music-make-song.fr.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.fr.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.fr.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.fr.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.fr.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.fr.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.fr.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.fr.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.fr.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.fr.png';
|
||||
import popGameAddScore from './steps/add-variable.fr.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.fr.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.fr.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.fr.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.fr.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.fr.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.fr.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.fr.png';
|
||||
import animateCharMove from './steps/animate-char-move.fr.png';
|
||||
import animateCharJump from './steps/animate-char-jump.fr.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.fr.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.fr.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.fr.gif';
|
||||
import storyConversation from './steps/story-conversation.fr.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.fr.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.fr.png';
|
||||
import storyShowCharacter from './steps/story-show-character.fr.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.fr.gif';
|
||||
import videoPet from './steps/video-pet.fr.png';
|
||||
import videoAnimate from './steps/video-animate.fr.png';
|
||||
import videoPop from './steps/video-pop.fr.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.fr.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.fr.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.fr.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.fr.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.fr.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.fr.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.fr.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.fr.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.fr.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.fr.png';
|
||||
import pongAddAScore from './steps/add-variable.fr.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.fr.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.fr.png';
|
||||
import pongResetScore from './steps/pong-reset-score.fr.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.fr.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.fr.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.fr.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.fr.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.fr.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.fr.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.fr.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.fr.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.fr.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.fr.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.fr.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.fr.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.fr.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.fr.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.fr.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.fr.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.fr.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.fr.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.fr.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.fr.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.fr.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.fr.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.fr.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.fr.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.fr.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.fr.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.fr.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.fr.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.fr.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.fr.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.fr.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.fr.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.fr.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.fr.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.fr.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.fr.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.fr.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.fr.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.fr.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.fr.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.fr.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.fr.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.fr.png';
|
||||
|
||||
const frImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {frImages};
|
||||
3
scratch-gui/src/lib/libraries/decks/index.jsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
// tw: remove decks. we don't use them.
|
||||
};
|
||||
405
scratch-gui/src/lib/libraries/decks/ja-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.ja.gif';
|
||||
import introSay from './steps/intro-2-say.ja.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.ja.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.ja.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.ja.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.ja.png';
|
||||
import speechMoveAround from './steps/speech-move-around.ja.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.ja.png';
|
||||
import speechChangeColor from './steps/speech-change-color.ja.png';
|
||||
import speechSpin from './steps/speech-spin.ja.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.ja.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.ja.png';
|
||||
import cnGlide from './steps/cn-glide.ja.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.ja.png';
|
||||
import cnVariable from './steps/add-variable.ja.gif';
|
||||
import cnScore from './steps/cn-score.ja.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.ja.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.ja.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.ja.png';
|
||||
import nameSpin from './steps/name-spin.ja.png';
|
||||
import nameGrow from './steps/name-grow.ja.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.ja.png';
|
||||
import musicMakeSong from './steps/music-make-song.ja.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.ja.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.ja.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.ja.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.ja.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.ja.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.ja.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.ja.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.ja.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.ja.png';
|
||||
import popGameAddScore from './steps/add-variable.ja.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.ja.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.ja.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.ja.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.ja.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.ja.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.ja.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.ja.png';
|
||||
import animateCharMove from './steps/animate-char-move.ja.png';
|
||||
import animateCharJump from './steps/animate-char-jump.ja.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.ja.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.ja.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.ja.gif';
|
||||
import storyConversation from './steps/story-conversation.ja.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.ja.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.ja.png';
|
||||
import storyShowCharacter from './steps/story-show-character.ja.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.ja.gif';
|
||||
import videoPet from './steps/video-pet.ja.png';
|
||||
import videoAnimate from './steps/video-animate.ja.png';
|
||||
import videoPop from './steps/video-pop.ja.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.ja.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.ja.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.ja.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.ja.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.ja.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.ja.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.ja.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.ja.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.ja.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.ja.png';
|
||||
import pongAddAScore from './steps/add-variable.ja.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.ja.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.ja.png';
|
||||
import pongResetScore from './steps/pong-reset-score.ja.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.ja.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.ja.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.ja.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.ja.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.ja.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.ja.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.ja.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.ja.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.ja.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.ja.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.ja.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.ja.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.ja.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.ja.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.ja.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.ja.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.ja.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.ja.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.ja.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.ja.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.ja.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.ja.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.ja.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.ja.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.ja.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.ja.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.ja.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.ja.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.ja.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.ja.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.ja.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.ja.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.ja.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.ja.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.ja.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.ja.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.ja.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.ja.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.ja.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.ja.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.ja.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.ja.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.ja.png';
|
||||
|
||||
const jaImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {jaImages};
|
||||
405
scratch-gui/src/lib/libraries/decks/pt_BR-steps.js
Normal file
@@ -0,0 +1,405 @@
|
||||
// Intro
|
||||
import introMove from './steps/intro-1-move.pt_BR.gif';
|
||||
import introSay from './steps/intro-2-say.pt_BR.gif';
|
||||
import introGreenFlag from './steps/intro-3-green-flag.pt_BR.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.pt_BR.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.pt_BR.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.pt_BR.png';
|
||||
import speechMoveAround from './steps/speech-move-around.pt_BR.png';
|
||||
import speechAddBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.LTR.gif';
|
||||
import speechSong from './steps/speech-song.pt_BR.png';
|
||||
import speechChangeColor from './steps/speech-change-color.pt_BR.png';
|
||||
import speechSpin from './steps/speech-spin.pt_BR.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.pt_BR.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.LTR.gif';
|
||||
import cnSay from './steps/cn-say.pt_BR.png';
|
||||
import cnGlide from './steps/cn-glide.pt_BR.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.LTR.gif';
|
||||
import cnCollect from './steps/cn-collect.pt_BR.png';
|
||||
import cnVariable from './steps/add-variable.pt_BR.gif';
|
||||
import cnScore from './steps/cn-score.pt_BR.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.pt_BR.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.LTR.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.LTR.gif';
|
||||
import namePlaySound from './steps/name-play-sound.pt_BR.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.LTR.gif';
|
||||
import nameChangeColor from './steps/name-change-color.pt_BR.png';
|
||||
import nameSpin from './steps/name-spin.pt_BR.png';
|
||||
import nameGrow from './steps/name-grow.pt_BR.png';
|
||||
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.LTR.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.pt_BR.png';
|
||||
import musicMakeSong from './steps/music-make-song.pt_BR.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.pt_BR.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.pt_BR.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.LTR.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.LTR.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.pt_BR.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.pt_BR.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.LTR.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.pt_BR.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.pt_BR.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.pt_BR.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.pt_BR.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.LTR.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.pt_BR.png';
|
||||
import popGameAddScore from './steps/add-variable.pt_BR.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.pt_BR.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.pt_BR.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.pt_BR.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.pt_BR.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/pick-backdrop.LTR.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.LTR.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.pt_BR.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.pt_BR.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.pt_BR.png';
|
||||
import animateCharMove from './steps/animate-char-move.pt_BR.png';
|
||||
import animateCharJump from './steps/animate-char-jump.pt_BR.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.pt_BR.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.LTR.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.LTR.gif';
|
||||
import storySaySomething from './steps/story-say-something.pt_BR.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.LTR.gif';
|
||||
import storyFlip from './steps/story-flip.pt_BR.gif';
|
||||
import storyConversation from './steps/story-conversation.pt_BR.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.LTR.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.pt_BR.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.pt_BR.png';
|
||||
import storyShowCharacter from './steps/story-show-character.pt_BR.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.pt_BR.gif';
|
||||
import videoPet from './steps/video-pet.pt_BR.png';
|
||||
import videoAnimate from './steps/video-animate.pt_BR.png';
|
||||
import videoPop from './steps/video-pop.pt_BR.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.LTR.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.LTR.png';
|
||||
import flySaySomething from './steps/fly-say-something.pt_BR.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.pt_BR.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.LTR.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.pt_BR.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.LTR.png';
|
||||
import flyAddScore from './steps/add-variable.pt_BR.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.pt_BR.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.LTR.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.pt_BR.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.pt_BR.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.LTR.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.LTR.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.pt_BR.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.LTR.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.pt_BR.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.LTR.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.pt_BR.png';
|
||||
import pongAddAScore from './steps/add-variable.pt_BR.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.pt_BR.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.pt_BR.png';
|
||||
import pongResetScore from './steps/pong-reset-score.pt_BR.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.LTR.gif';
|
||||
import pongGameOver from './steps/pong-game-over.pt_BR.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.pt_BR.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.pt_BR.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.LTR.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.LTR.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.pt_BR.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.LTR.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.pt_BR.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.pt_BR.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.pt_BR.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.pt_BR.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.pt_BR.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.LTR.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.pt_BR.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.pt_BR.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.pt_BR.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.LTR.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.pt_BR.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.pt_BR.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.pt_BR.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.pt_BR.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.pt_BR.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.pt_BR.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.pt_BR.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.pt_BR.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.pt_BR.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.pt_BR.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.pt_BR.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.pt_BR.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.pt_BR.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.pt_BR.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.pt_BR.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.pt_BR.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.pt_BR.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.LTR.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.pt_BR.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.pt_BR.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.LTR.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.pt_BR.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.pt_BR.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.pt_BR.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.pt_BR.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.LTR.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.pt_BR.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.LTR.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.pt_BR.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.LTR.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.pt_BR.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.LTR.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.pt_BR.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.LTR.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.pt_BR.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.LTR.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.pt_BR.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.pt_BR.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.pt_BR.png';
|
||||
|
||||
const ptBrImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSay: introSay,
|
||||
introGreenFlag: introGreenFlag,
|
||||
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
|
||||
// Animate a name
|
||||
namePickLetter: namePickLetter,
|
||||
namePlaySound: namePlaySound,
|
||||
namePickLetter2: namePickLetter2,
|
||||
nameChangeColor: nameChangeColor,
|
||||
nameSpin: nameSpin,
|
||||
nameGrow: nameGrow,
|
||||
|
||||
// Make-Music
|
||||
musicPickInstrument: musicPickInstrument,
|
||||
musicPlaySound: musicPlaySound,
|
||||
musicMakeSong: musicMakeSong,
|
||||
musicMakeBeat: musicMakeBeat,
|
||||
musicMakeBeatbox: musicMakeBeatbox,
|
||||
|
||||
// Chase-Game
|
||||
chaseGameAddBackdrop: chaseGameAddBackdrop,
|
||||
chaseGameAddSprite1: chaseGameAddSprite1,
|
||||
chaseGameRightLeft: chaseGameRightLeft,
|
||||
chaseGameUpDown: chaseGameUpDown,
|
||||
chaseGameAddSprite2: chaseGameAddSprite2,
|
||||
chaseGameMoveRandomly: chaseGameMoveRandomly,
|
||||
chaseGamePlaySound: chaseGamePlaySound,
|
||||
chaseGameAddVariable: chaseGameAddVariable,
|
||||
chaseGameChangeScore: chaseGameChangeScore,
|
||||
|
||||
// Make-A-Pop/Clicker Game
|
||||
popGamePickSprite: popGamePickSprite,
|
||||
popGamePlaySound: popGamePlaySound,
|
||||
popGameAddScore: popGameAddScore,
|
||||
popGameChangeScore: popGameChangeScore,
|
||||
popGameRandomPosition: popGameRandomPosition,
|
||||
popGameChangeColor: popGameChangeColor,
|
||||
popGameResetScore: popGameResetScore,
|
||||
|
||||
// Animate A Character
|
||||
animateCharPickBackdrop: animateCharPickBackdrop,
|
||||
animateCharPickSprite: animateCharPickSprite,
|
||||
animateCharSaySomething: animateCharSaySomething,
|
||||
animateCharAddSound: animateCharAddSound,
|
||||
animateCharTalk: animateCharTalk,
|
||||
animateCharMove: animateCharMove,
|
||||
animateCharJump: animateCharJump,
|
||||
animateCharChangeColor: animateCharChangeColor,
|
||||
|
||||
// Tell A Story
|
||||
storyPickBackdrop: storyPickBackdrop,
|
||||
storyPickSprite: storyPickSprite,
|
||||
storySaySomething: storySaySomething,
|
||||
storyPickSprite2: storyPickSprite2,
|
||||
storyFlip: storyFlip,
|
||||
storyConversation: storyConversation,
|
||||
storyPickBackdrop2: storyPickBackdrop2,
|
||||
storySwitchBackdrop: storySwitchBackdrop,
|
||||
storyHideCharacter: storyHideCharacter,
|
||||
storyShowCharacter: storyShowCharacter,
|
||||
|
||||
// Video Sensing
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {ptBrImages};
|
||||
BIN
scratch-gui/src/lib/libraries/decks/steps/add-backdrop.LTR.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-backdrop.RTL.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.am.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.ar.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.en.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.es.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.fr.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.ja.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.pt_BR.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.sw.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.tr.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.uk.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.zh_CN.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.zh_TW.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-effects.zu.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-sprite.LTR.gif
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-sprite.RTL.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.am.gif
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.ar.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.en.gif
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.es.gif
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.fr.gif
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.ja.gif
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.pt_BR.gif
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.sw.gif
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.tr.gif
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.uk.gif
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.zh_CN.gif
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
scratch-gui/src/lib/libraries/decks/steps/add-variable.zh_TW.gif
Normal file
|
After Width: | Height: | Size: 41 KiB |