diff --git a/package.json b/package.json
index 4ccd4e206..291bb4cef 100644
--- a/package.json
+++ b/package.json
@@ -105,9 +105,9 @@
"redux-throttle": "0.1.1",
"rimraf": "^2.6.1",
"scratch-audio": "0.1.0-prerelease.20190114210212",
- "scratch-l10n": "3.3.20190612144059",
+ "scratch-l10n": "3.3.20190619145905",
"scratch-blocks": "0.1.0-prerelease.1559136797",
- "scratch-paint": "0.2.0-prerelease.20190524133325",
+ "scratch-paint": "0.2.0-prerelease.20190624133806",
"scratch-render": "0.1.0-prerelease.20190605151415",
"scratch-storage": "1.3.2",
"scratch-svg-renderer": "0.2.0-prerelease.20190523193400",
diff --git a/src/components/cards/card.css b/src/components/cards/card.css
index 265ca22d1..ad712e339 100644
--- a/src/components/cards/card.css
+++ b/src/components/cards/card.css
@@ -170,6 +170,9 @@
align-items: center;
position: relative;
text-align: center;
+
+ /* Min height prevents layout changing when images change */
+ min-height: 256px;
}
.step-video {
diff --git a/src/components/cards/cards.jsx b/src/components/cards/cards.jsx
index 9bb147f13..b69169800 100644
--- a/src/components/cards/cards.jsx
+++ b/src/components/cards/cards.jsx
@@ -104,6 +104,9 @@ class VideoStep extends React.Component {
// We use the Wistia API here to update or pause the video dynamically:
// https://wistia.com/support/developers/player-api
componentDidUpdate (prevProps) {
+ // Ensure the wistia API is loaded and available
+ if (!(window.Wistia && window.Wistia.api)) return;
+
// Get a handle on the currently loaded video
const video = window.Wistia.api(prevProps.video);
@@ -155,6 +158,7 @@ const ImageStep = ({title, image}) => (
diff --git a/src/components/loupe/loupe.jsx b/src/components/loupe/loupe.jsx
index 4c2c1b2d6..b4b97d807 100644
--- a/src/components/loupe/loupe.jsx
+++ b/src/components/loupe/loupe.jsx
@@ -91,7 +91,8 @@ LoupeComponent.propTypes = {
color: PropTypes.shape({
r: PropTypes.number,
g: PropTypes.number,
- b: PropTypes.number
+ b: PropTypes.number,
+ a: PropTypes.number
}),
data: PropTypes.instanceOf(Uint8Array),
width: PropTypes.number,
diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css
index abb4db315..6844ef1c5 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.css
+++ b/src/components/sprite-selector-item/sprite-selector-item.css
@@ -20,7 +20,6 @@
cursor: pointer;
user-select: none;
- touch-action: none;
}
.sprite-selector-item.is-selected {
diff --git a/src/components/sprite-selector-item/sprite-selector-item.jsx b/src/components/sprite-selector-item/sprite-selector-item.jsx
index 930f655fd..bcccb7fe7 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.jsx
+++ b/src/components/sprite-selector-item/sprite-selector-item.jsx
@@ -23,7 +23,7 @@ const SpriteSelectorItem = props => (
onMouseDown: props.onMouseDown,
onTouchStart: props.onMouseDown
}}
- disable={props.dragging}
+ disable={props.preventContextMenu}
id={`${props.name}-${contextMenuId}`}
ref={props.componentRef}
>
@@ -93,7 +93,6 @@ SpriteSelectorItem.propTypes = {
componentRef: PropTypes.func,
costumeURL: PropTypes.string,
details: PropTypes.string,
- dragging: PropTypes.bool,
name: PropTypes.string.isRequired,
number: PropTypes.number,
onClick: PropTypes.func,
@@ -103,6 +102,7 @@ SpriteSelectorItem.propTypes = {
onMouseDown: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
+ preventContextMenu: PropTypes.bool,
selected: PropTypes.bool.isRequired
};
diff --git a/src/components/sprite-selector/sprite-list.jsx b/src/components/sprite-selector/sprite-list.jsx
index e89898e47..3d1910293 100644
--- a/src/components/sprite-selector/sprite-list.jsx
+++ b/src/components/sprite-selector/sprite-list.jsx
@@ -110,7 +110,8 @@ SpriteList.propTypes = {
editingTarget: PropTypes.string,
hoveredTarget: PropTypes.shape({
hoveredSprite: PropTypes.string,
- receivedBlocks: PropTypes.bool
+ receivedBlocks: PropTypes.bool,
+ sprite: PropTypes.string
}),
items: PropTypes.arrayOf(PropTypes.shape({
costume: PropTypes.shape({
diff --git a/src/components/sprite-selector/sprite-selector.css b/src/components/sprite-selector/sprite-selector.css
index ffade4750..c871e4517 100644
--- a/src/components/sprite-selector/sprite-selector.css
+++ b/src/components/sprite-selector/sprite-selector.css
@@ -68,6 +68,8 @@
padding-left: calc($space / 2);
padding-right: calc($space / 2);
padding-bottom: $space;
+
+ overflow-x: hidden;
}
.add-button {
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 173f1218a..2012c61e5 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -52,6 +52,7 @@ class GUI extends React.Component {
setIsScratchDesktop(this.props.isScratchDesktop);
this.setReduxTitle(this.props.projectTitle);
this.props.onStorageInit(storage);
+ this.props.onVmInit(this.props.vm);
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
@@ -92,6 +93,7 @@ class GUI extends React.Component {
onStorageInit,
onUpdateProjectId,
onUpdateReduxProjectTitle,
+ onVmInit,
projectHost,
projectId,
projectTitle,
@@ -131,6 +133,7 @@ GUI.propTypes = {
onUpdateProjectId: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
onUpdateReduxProjectTitle: PropTypes.func,
+ onVmInit: PropTypes.func,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
projectTitle: PropTypes.string,
@@ -142,7 +145,8 @@ GUI.defaultProps = {
isScratchDesktop: false,
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(),
onProjectLoaded: () => {},
- onUpdateProjectId: () => {}
+ onUpdateProjectId: () => {},
+ onVmInit: (/* vm */) => {}
};
const mapStateToProps = state => {
diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx
index 07d25ae7e..b800eafe4 100644
--- a/src/containers/sprite-selector-item.jsx
+++ b/src/containers/sprite-selector-item.jsx
@@ -6,14 +6,12 @@ import {connect} from 'react-redux';
import {setHoveredSprite} from '../reducers/hovered-target';
import {updateAssetDrag} from '../reducers/asset-drag';
import storage from '../lib/storage';
-import {getEventXY} from '../lib/touch-utils';
import VM from 'scratch-vm';
import getCostumeUrl from '../lib/get-costume-url';
+import DragRecognizer from '../lib/drag-recognizer';
import SpriteSelectorItemComponent from '../components/sprite-selector-item/sprite-selector-item.jsx';
-const dragThreshold = 3; // Same as the block drag threshold
-
class SpriteSelectorItem extends React.PureComponent {
constructor (props) {
super(props);
@@ -27,10 +25,18 @@ class SpriteSelectorItem extends React.PureComponent {
'handleMouseEnter',
'handleMouseLeave',
'handleMouseDown',
- 'handleMouseMove',
- 'handleMouseUp',
+ 'handleDragEnd',
+ 'handleDrag',
'handleTouchEnd'
]);
+
+ this.dragRecognizer = new DragRecognizer({
+ onDrag: this.handleDrag,
+ onDragEnd: this.handleDragEnd
+ });
+ }
+ componentWillUnmount () {
+ this.dragRecognizer.reset();
}
componentDidMount () {
document.addEventListener('touchend', this.handleTouchEnd);
@@ -44,19 +50,7 @@ class SpriteSelectorItem extends React.PureComponent {
return getCostumeUrl(this.props.asset);
}
- handleTouchEnd (e) {
- const {x, y} = getEventXY(e);
- const {top, left, bottom, right} = this.ref.getBoundingClientRect();
- if (x >= left && x <= right && y >= top && y <= bottom) {
- this.handleMouseEnter();
- }
- }
- handleMouseUp () {
- this.initialOffset = null;
- window.removeEventListener('mouseup', this.handleMouseUp);
- window.removeEventListener('mousemove', this.handleMouseMove);
- window.removeEventListener('touchend', this.handleMouseUp);
- window.removeEventListener('touchmove', this.handleMouseMove);
+ handleDragEnd () {
if (this.props.dragging) {
this.props.onDrag({
img: null,
@@ -70,29 +64,26 @@ class SpriteSelectorItem extends React.PureComponent {
this.noClick = false;
});
}
- handleMouseMove (e) {
- const currentOffset = getEventXY(e);
- const dx = currentOffset.x - this.initialOffset.x;
- const dy = currentOffset.y - this.initialOffset.y;
- if (Math.sqrt((dx * dx) + (dy * dy)) > dragThreshold) {
- this.props.onDrag({
- img: this.getCostumeData(),
- currentOffset: currentOffset,
- dragging: true,
- dragType: this.props.dragType,
- index: this.props.index,
- payload: this.props.dragPayload
- });
- this.noClick = true;
+ handleDrag (currentOffset) {
+ this.props.onDrag({
+ img: this.getCostumeData(),
+ currentOffset: currentOffset,
+ dragging: true,
+ dragType: this.props.dragType,
+ index: this.props.index,
+ payload: this.props.dragPayload
+ });
+ this.noClick = true;
+ }
+ handleTouchEnd (e) {
+ const {x, y} = getEventXY(e);
+ const {top, left, bottom, right} = this.ref.getBoundingClientRect();
+ if (x >= left && x <= right && y >= top && y <= bottom) {
+ this.handleMouseEnter();
}
- e.preventDefault();
}
handleMouseDown (e) {
- this.initialOffset = getEventXY(e);
- window.addEventListener('mouseup', this.handleMouseUp);
- window.addEventListener('mousemove', this.handleMouseMove);
- window.addEventListener('touchend', this.handleMouseUp);
- window.addEventListener('touchmove', this.handleMouseMove);
+ this.dragRecognizer.start(e);
}
handleClick (e) {
e.preventDefault();
@@ -143,6 +134,7 @@ class SpriteSelectorItem extends React.PureComponent {
{}),
+ 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) {
+ 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._onDragEnd();
+ this.reset();
+ }
+
+ _isDrag () {
+ return this._gestureState === DragRecognizer.STATE_DRAG;
+ }
+
+ _isScroll () {
+ return this._gestureState === DragRecognizer.STATE_SCROLL;
+ }
+}
+
+export default DragRecognizer;
diff --git a/src/lib/libraries/decks/en-steps.js b/src/lib/libraries/decks/en-steps.js
index ce302ae65..f98794e83 100644
--- a/src/lib/libraries/decks/en-steps.js
+++ b/src/lib/libraries/decks/en-steps.js
@@ -90,6 +90,86 @@ import videoPet from './steps/video-pet.en.gif';
import videoAnimate from './steps/video-animate.en.gif';
import videoPop from './steps/video-pop.en.gif';
+// Make it Fly
+import flyChooseBackdrop from './steps/fly-choose-backdrop.en.gif';
+import flyChooseCharacter from './steps/fly-choose-character.en.gif';
+import flySaySomething from './steps/fly-say-something.en.gif';
+import flyMoveArrows from './steps/fly-make-interactive.en.gif';
+import flyChooseObject from './steps/fly-object-to-collect.en.gif';
+import flyFlyingObject from './steps/fly-flying-heart.en.gif';
+import flySelectFlyingSprite from './steps/fly-select-flyer.en.jpg';
+import flyAddScore from './steps/fly-add-score.en.gif';
+import flyKeepScore from './steps/fly-keep-score.en.gif';
+import flyAddScenery from './steps/fly-choose-scenery.en.gif';
+import flyMoveScenery from './steps/fly-move-scenery.en.gif';
+import flySwitchLooks from './steps/fly-switch-costume.en.gif';
+
+// Pong
+import pongAddBackdrop from './steps/pong-add-backdrop.en.png';
+import pongAddBallSprite from './steps/pong-add-ball-sprite.en.png';
+import pongBounceAround from './steps/pong-bounce-around.en.gif';
+import pongAddPaddle from './steps/pong-add-a-paddle.en.gif';
+import pongMoveThePaddle from './steps/pong-move-the-paddle.en.gif';
+import pongSelectBallSprite from './steps/pong-select-ball.en.png';
+import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.en.png';
+import pongAddAScore from './steps/pong-add-score.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.en.gif';
+import pongGameOver from './steps/pong-game-over.en.png';
+
+// Imagine If
+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.en.png';
+import imagineChooseSprite from './steps/imagine-choose-any-sprite.en.png';
+import imagineFlyAround from './steps/imagine-fly-around.en.png';
+import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.en.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.en.png';
+import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.en.gif';
+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.en.png';
+
+// Add Effectsf
+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';
+
const enImages = {
// Intro
introMove: introMove,
@@ -181,8 +261,89 @@ const enImages = {
videoAddExtension: videoAddExtension,
videoPet: videoPet,
videoAnimate: videoAnimate,
- videoPop: videoPop
+ 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 If
+ 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
+
};
+
export {enImages};
diff --git a/src/lib/libraries/decks/index.jsx b/src/lib/libraries/decks/index.jsx
index 92dcb643d..0e81893da 100644
--- a/src/lib/libraries/decks/index.jsx
+++ b/src/lib/libraries/decks/index.jsx
@@ -36,6 +36,15 @@ import libraryStory from './thumbnails/tell-a-story.jpg';
// Video Sensing
import libraryVideoSens from './thumbnails/video-sensing.jpg';
+// Make-it-Fly
+import libraryMakeFly from './thumbnails/make-it-fly.jpg';
+
+// Pong
+import libraryPong from './thumbnails/pong.jpg';
+
+// Imagine If
+import libraryImagineIf from './thumbnails/imagine-if.jpg';
+
// Videos
import recordASound from './thumbnails/record-a-sound.jpg';
import glideAroundThumb from './thumbnails/glide-around.jpg';
@@ -89,220 +98,6 @@ export default {
urlId: 'getStarted'
},
- 'say-it-out-loud': {
- name: (
-
- ),
- img: libraryTXTSpeech,
- steps: [{
- video: 'k54n8uwcty',
- trackingPixel: (
-
- )
- }, {
- title: (
-
- ),
- image: 'speechAddExtension'
- }, {
- title: (
-
- ),
- image: 'speechSaySomething'
- }, {
- title: (
-
- ),
- image: 'speechSetVoice'
- }, {
- title: (
-
- ),
- image: 'speechMoveAround'
- }, {
- title: (
-
- ),
- image: 'speechAddBackdrop'
- }, {
- title: (
-
- ),
- image: 'speechAddSprite'
- }, {
- title: (
-
- ),
- image: 'speechSong'
- }, {
- title: (
-
- ),
- image: 'speechChangeColor'
- }, {
- title: (
-
- ),
- image: 'speechSpin'
- }, {
- title: (
-
- ),
- image: 'speechGrowShrink'
- }, {
- deckIds: [
- 'animate-a-name',
- 'Make-Music'
- ]
- }
- ],
- urlId: 'animations-that-talk'
- },
-
- 'cartoon-network': {
- name: (
-
- ),
- requiredProjectId: '249143200',
- img: libraryCartoonNetwork,
- steps: [{
- video: 'uz5oz5h9yg',
- trackingPixel: (
-
- )
- }, {
- title: (
-
- ),
- image: 'cnShowCharacter'
- }, {
- title: (
-
- ),
- image: 'cnSay'
- }, {
- title: (
-
- ),
- image: 'cnGlide'
- }, {
- title: (
-
- ),
- image: 'cnPickSprite'
- }, {
- title: (
-
- ),
- image: 'cnCollect'
- }, {
- title: (
-
- ),
- image: 'cnVariable'
- }, {
- title: (
-
- ),
- image: 'cnScore'
- }, {
- title: (
-
- ),
- image: 'cnBackdrop'
- },
- {
- video: '6o76f5ivo1'
- },
- {
- deckIds: [
- 'switch-costume',
- 'add-effects'
- ]
- }
- ],
- urlId: 'animate-an-adventure-game'
- },
'animate-a-name': {
name: (
+ ),
+ tags: ['imagine-if'],
+ img: libraryImagineIf,
+ steps: [
+ {
+ title: (
+
+ ),
+ image: 'imagineTypeWhatYouWant'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineClickGreenFlag'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChooseBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChooseSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineFlyAround'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChooseAnotherSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineLeftRight'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineUpDown'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChangeCostumes'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineGlideToPoint'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineGrowShrink'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChooseAnotherBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineSwitchBackdrops'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineRecordASound'
+ }, {
+ title: (
+
+ ),
+ image: 'imagineChooseSound'
+ }, {
+ deckIds: [
+ 'hide-and-show',
+ 'Chase-Game'
+ ]
+ }
+ ],
+ urlId: 'imagine-if'
+ },
+
'Make-Music': {
name: (
- ),
- img: libraryMakeAGame,
- tags: ['games', 'click', 'clicked', 'score'],
- steps: [{
- video: 'Make-A-Game'
- },
- {
- title: (
-
- ),
- image: 'popGamePickSprite'
- }, {
- title: (
-
- ),
- image: 'popGamePlaySound'
- }, {
- title: (
-
- ),
- image: 'popGameAddScore'
- }, {
- title: (
-
- ),
-
- image: 'popGameChangeScore'
- }, {
- title: (
-
- ),
- image: 'popGameRandomPosition'
- }, {
- title: (
-
- ),
- image: 'popGameChangeColor'
- }, {
- title: (
-
- ),
- image: 'popGameResetScore'
- }, {
- deckIds: [
- 'add-a-backdrop',
- 'move-around-with-arrow-keys'
- ]
- }
- ],
- urlId: 'clicker-game'
- },
-
- 'Chase-Game': {
- name: (
-
- ),
- img: libraryChaseGame,
- tags: ['games', 'arrow', 'keyboard', 'score'],
- steps: [{
- video: 'Chase-Game'
- },
- {
- title: (
-
- ),
- image: 'chaseGameAddBackdrop'
- }, {
- title: (
-
- ),
- image: 'chaseGameAddSprite1'
- }, {
- title: (
-
- ),
- image: 'chaseGameRightLeft'
- }, {
- title: (
-
- ),
- image: 'chaseGameUpDown'
- }, {
- title: (
-
- ),
- image: 'chaseGameAddSprite2'
- }, {
- title: (
-
- ),
- image: 'chaseGameMoveRandomly'
- }, {
- title: (
-
- ),
- image: 'chaseGamePlaySound'
- }, {
- title: (
-
- ),
- image: 'chaseGameAddVariable'
- }, {
- title: (
-
- ),
- image: 'chaseGameChangeScore'
- }, {
- deckIds: [
- 'add-effects',
- 'move-around-with-arrow-keys'
- ]
- }
- ],
- urlId: 'chase-game'
- },
- 'Animate-A-Character': {
- name: (
-
- ),
- img: libraryAnimateChar,
- steps: [{
- video: 's228u3g5u9'
- },
- {
- title: (
-
- ),
- image: 'animateCharPickBackdrop'
- }, {
- title: (
-
- ),
- image: 'animateCharPickSprite'
- }, {
- title: (
-
- ),
- image: 'animateCharSaySomething'
- }, {
- title: (
-
- ),
- image: 'animateCharAddSound'
- }, {
- title: (
-
- ),
- image: 'animateCharTalk'
- }, {
- title: (
-
- ),
- image: 'animateCharMove'
- }, {
- title: (
-
- ),
- image: 'animateCharJump'
- }, {
- title: (
-
- ),
- image: 'animateCharChangeColor'
- }, {
- deckIds: [
- 'Chase-Game',
- 'Tell-A-Story'
- ]
- }
- ],
- urlId: 'animate-a-character'
- },
+
'Tell-A-Story': {
name: (
+ ),
+ img: libraryChaseGame,
+ tags: ['games', 'arrow', 'keyboard', 'score'],
+ steps: [{
+ video: 'Chase-Game'
+ },
+ {
+ title: (
+
+ ),
+ image: 'chaseGameAddBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameAddSprite1'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameRightLeft'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameUpDown'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameAddSprite2'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameMoveRandomly'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGamePlaySound'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameAddVariable'
+ }, {
+ title: (
+
+ ),
+ image: 'chaseGameChangeScore'
+ }, {
+ deckIds: [
+ 'add-effects',
+ 'move-around-with-arrow-keys'
+ ]
+ }
+ ],
+ urlId: 'chase-game'
+ },
+
+ 'Animate-A-Character': {
+ name: (
+
+ ),
+ img: libraryAnimateChar,
+ steps: [{
+ video: 's228u3g5u9'
+ },
+ {
+ title: (
+
+ ),
+ image: 'animateCharPickBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharPickSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharSaySomething'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharAddSound'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharTalk'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharMove'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharJump'
+ }, {
+ title: (
+
+ ),
+ image: 'animateCharChangeColor'
+ }, {
+ deckIds: [
+ 'Chase-Game',
+ 'Tell-A-Story'
+ ]
+ }
+ ],
+ urlId: 'animate-a-character'
+ },
+
+ 'Make-A-Game': {
+ name: (
+
+ ),
+ img: libraryMakeAGame,
+ tags: ['games', 'click', 'clicked', 'score'],
+ steps: [{
+ video: 'Make-A-Game'
+ },
+ {
+ title: (
+
+ ),
+ image: 'popGamePickSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'popGamePlaySound'
+ }, {
+ title: (
+
+ ),
+ image: 'popGameAddScore'
+ }, {
+ title: (
+
+ ),
+
+ image: 'popGameChangeScore'
+ }, {
+ title: (
+
+ ),
+ image: 'popGameRandomPosition'
+ }, {
+ title: (
+
+ ),
+ image: 'popGameChangeColor'
+ }, {
+ title: (
+
+ ),
+ image: 'popGameResetScore'
+ }, {
+ deckIds: [
+ 'add-a-backdrop',
+ 'move-around-with-arrow-keys'
+ ]
+ }
+ ],
+ urlId: 'clicker-game'
+ },
+
+ 'make-it-fly': {
+ name: (
+
+ ),
+ tags: ['game', 'fly', 'how', 'can', 'animation'],
+ img: libraryMakeFly,
+ steps: [{
+ video: 'zbtdx2dem9'
+ }, {
+ title: (
+
+ ),
+ image: 'flyChooseBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'flyChooseCharacter'
+ }, {
+ title: (
+
+ ),
+ image: 'flySaySomething'
+ }, {
+ title: (
+
+ ),
+ image: 'flyMoveArrows'
+ }, {
+ title: (
+
+ ),
+ image: 'flyChooseObject'
+ }, {
+ title: (
+
+ ),
+ image: 'flyFlyingObject'
+ }, {
+ title: (
+
+ ),
+ image: 'flySelectFlyingSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'flyAddScore'
+ }, {
+ title: (
+
+ ),
+ image: 'flyKeepScore'
+ }, {
+ title: (
+
+ ),
+ image: 'flyAddScenery'
+ }, {
+ title: (
+
+ ),
+ image: 'flyMoveScenery'
+ }, {
+ title: (
+
+ ),
+ image: 'flySwitchLooks'
+ }, {
+ deckIds: [
+ 'change-size',
+ 'spin-video'
+ ]
+ }
+ ],
+ urlId: 'make-it-fly'
+ },
+
+ 'pong': {
+ name: (
+
+ ),
+ tags: ['pong', 'game'],
+ img: libraryPong,
+ steps: [{
+ video: 'pong-game'
+ },
+ {
+ title: (
+
+ ),
+ image: 'pongAddBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'pongAddBallSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'pongBounceAround'
+ }, {
+ title: (
+
+ ),
+ image: 'pongAddPaddle'
+ }, {
+ title: (
+
+ ),
+ image: 'pongMoveThePaddle'
+ }, {
+ title: (
+
+ ),
+ image: 'pongSelectBallSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'pongAddMoreCodeToBall'
+ }, {
+ title: (
+
+ ),
+ image: 'pongAddAScore'
+ }, {
+ title: (
+
+ ),
+ image: 'pongChooseScoreFromMenu'
+ }, {
+ title: (
+
+ ),
+ image: 'pongInsertChangeScoreBlock'
+ }, {
+ title: (
+
+ ),
+ image: 'pongResetScore'
+ }, {
+ title: (
+
+ ),
+ image: 'pongAddLineSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'pongGameOver'
+ }, {
+ deckIds: [
+ 'add-effects',
+ 'Video-Sensing'
+ ]
+ }
+ ],
+ urlId: 'pong'
+ },
+
+ 'say-it-out-loud': {
+ name: (
+
+ ),
+ img: libraryTXTSpeech,
+ steps: [{
+ video: 'k54n8uwcty',
+ trackingPixel: (
+
+ )
+ }, {
+ title: (
+
+ ),
+ image: 'speechAddExtension'
+ }, {
+ title: (
+
+ ),
+ image: 'speechSaySomething'
+ }, {
+ title: (
+
+ ),
+ image: 'speechSetVoice'
+ }, {
+ title: (
+
+ ),
+ image: 'speechMoveAround'
+ }, {
+ title: (
+
+ ),
+ image: 'speechAddBackdrop'
+ }, {
+ title: (
+
+ ),
+ image: 'speechAddSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'speechSong'
+ }, {
+ title: (
+
+ ),
+ image: 'speechChangeColor'
+ }, {
+ title: (
+
+ ),
+ image: 'speechSpin'
+ }, {
+ title: (
+
+ ),
+ image: 'speechGrowShrink'
+ }, {
+ deckIds: [
+ 'animate-a-name',
+ 'Make-Music'
+ ]
+ }
+ ],
+ urlId: 'animations-that-talk'
+ },
+
'Video-Sensing': {
name: (
+ ),
+ requiredProjectId: '249143200',
+ img: libraryCartoonNetwork,
+ steps: [{
+ video: 'uz5oz5h9yg',
+ trackingPixel: (
+
+ )
+ }, {
+ title: (
+
+ ),
+ image: 'cnShowCharacter'
+ }, {
+ title: (
+
+ ),
+ image: 'cnSay'
+ }, {
+ title: (
+
+ ),
+ image: 'cnGlide'
+ }, {
+ title: (
+
+ ),
+ image: 'cnPickSprite'
+ }, {
+ title: (
+
+ ),
+ image: 'cnCollect'
+ }, {
+ title: (
+
+ ),
+ image: 'cnVariable'
+ }, {
+ title: (
+
+ ),
+ image: 'cnScore'
+ }, {
+ title: (
+
+ ),
+ image: 'cnBackdrop'
+ },
+ {
+ video: '6o76f5ivo1'
+ },
+ {
+ deckIds: [
+ 'switch-costume',
+ 'add-effects'
+ ]
+ }
+ ],
+ urlId: 'animate-an-adventure-game'
+ },
+
'add-sprite': {
name: (
+ ),
+ image: 'addBackdrop'
+ },
+
+ {
deckIds: [
'change-size',
'switch-costume'
@@ -949,6 +1403,7 @@ export default {
}],
urlId: 'add-a-backdrop'
},
+
'change-size': {
name: (
+ ),
+ image: 'changeSize'
}, {
deckIds: [
'glide-around',
@@ -969,6 +1433,7 @@ export default {
}],
urlId: 'change-size'
},
+
'glide-around': {
name: (
+ ),
+ image: 'glideAroundBackAndForth'
+ }, {
+ title: (
+
+ ),
+ image: 'glideAroundPoint'
}, {
deckIds: [
'add-a-backdrop',
@@ -1002,6 +1485,51 @@ export default {
img: recordASound,
steps: [{
video: 'record-a-sound'
+ }, {
+ title: (
+
+ ),
+ image: 'recordASoundSoundsTab'
+ }, {
+ title: (
+
+ ),
+ image: 'recordASoundClickRecord'
+ }, {
+ title: (
+
+ ),
+ image: 'recordASoundPressRecordButton'
+ }, {
+ title: (
+
+ ),
+ image: 'recordASoundChooseSound'
+ }, {
+ title: (
+
+ ),
+ image: 'recordASoundPlayYourSound'
}, {
deckIds: [
'Make-Music',
@@ -1010,6 +1538,7 @@ export default {
}],
urlId: 'record-a-sound'
},
+
'spin-video': {
name: (
+ ),
+ image: 'spinTurn'
+ }, {
+ title: (
+
+ ),
+ image: 'spinPointInDirection'
}, {
deckIds: [
'add-a-backdrop',
@@ -1030,6 +1577,7 @@ export default {
}],
urlId: 'make-it-spin'
},
+
'hide-and-show': {
name: (
+ ),
+ image: 'hideAndShow'
}, {
deckIds: [
'add-a-backdrop',
@@ -1063,6 +1620,15 @@ export default {
tags: ['animation', 'art', 'games', 'stories', 'paint', 'edit', 'change', 'character', 'sprite'],
steps: [{
video: 'switch-costume'
+ }, {
+ title: (
+
+ ),
+ image: 'switchCostumes'
}, {
deckIds: [
'add-a-backdrop',
@@ -1084,6 +1650,24 @@ export default {
tags: ['games', 'keyboard'],
steps: [{
video: 'move-around-with-arrow-keys'
+ }, {
+ title: (
+
+ ),
+ image: 'moveArrowKeysLeftRight'
+ }, {
+ title: (
+
+ ),
+ image: 'moveArrowKeysUpDown'
}, {
deckIds: [
'add-a-backdrop',
@@ -1092,6 +1676,7 @@ export default {
}],
urlId: 'arrow-keys'
},
+
'add-effects': {
name: (
+ ),
+ image: 'addEffects'
}, {
deckIds: [
'add-a-backdrop',
@@ -1113,6 +1707,7 @@ export default {
}],
urlId: 'add-effects'
},
+
'wedo2-getting-started': {
steps: [{
video: '4im7iizv47'
@@ -1120,6 +1715,7 @@ export default {
urlId: 'wedo',
hidden: true
},
+
'ev3-getting-started': {
steps: [{
video: 'qgu78c5y7d'
@@ -1127,6 +1723,7 @@ export default {
urlId: 'ev3',
hidden: true
},
+
'whats-new': {
steps: [{
video: 'mtqymxg0qq'
diff --git a/src/lib/libraries/decks/steps/add-backdrop.en.png b/src/lib/libraries/decks/steps/add-backdrop.en.png
new file mode 100755
index 000000000..d7e227b07
Binary files /dev/null and b/src/lib/libraries/decks/steps/add-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/add-effects.en.png b/src/lib/libraries/decks/steps/add-effects.en.png
new file mode 100755
index 000000000..6af9ef397
Binary files /dev/null and b/src/lib/libraries/decks/steps/add-effects.en.png differ
diff --git a/src/lib/libraries/decks/steps/change-size.en.png b/src/lib/libraries/decks/steps/change-size.en.png
new file mode 100755
index 000000000..1cecbd09d
Binary files /dev/null and b/src/lib/libraries/decks/steps/change-size.en.png differ
diff --git a/src/lib/libraries/decks/steps/fly-add-score.en.gif b/src/lib/libraries/decks/steps/fly-add-score.en.gif
new file mode 100755
index 000000000..90c6a6671
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-add-score.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-choose-backdrop.en.gif b/src/lib/libraries/decks/steps/fly-choose-backdrop.en.gif
new file mode 100755
index 000000000..f96106c22
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-choose-backdrop.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-choose-character.en.gif b/src/lib/libraries/decks/steps/fly-choose-character.en.gif
new file mode 100755
index 000000000..2d53cbf3e
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-choose-character.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-choose-scenery.en.gif b/src/lib/libraries/decks/steps/fly-choose-scenery.en.gif
new file mode 100644
index 000000000..b917e322e
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-choose-scenery.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-flying-heart.en.gif b/src/lib/libraries/decks/steps/fly-flying-heart.en.gif
new file mode 100644
index 000000000..0580926b5
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-flying-heart.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-keep-score.en.gif b/src/lib/libraries/decks/steps/fly-keep-score.en.gif
new file mode 100644
index 000000000..3345b87d7
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-keep-score.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-make-interactive.en.gif b/src/lib/libraries/decks/steps/fly-make-interactive.en.gif
new file mode 100644
index 000000000..dd21a1888
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-make-interactive.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-move-scenery.en.gif b/src/lib/libraries/decks/steps/fly-move-scenery.en.gif
new file mode 100644
index 000000000..0d460ebaf
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-move-scenery.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-object-to-collect.en.gif b/src/lib/libraries/decks/steps/fly-object-to-collect.en.gif
new file mode 100644
index 000000000..be8ea0eb9
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-object-to-collect.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-say-something.en.gif b/src/lib/libraries/decks/steps/fly-say-something.en.gif
new file mode 100644
index 000000000..1f9404494
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-say-something.en.gif differ
diff --git a/src/lib/libraries/decks/steps/fly-select-flyer.en.jpg b/src/lib/libraries/decks/steps/fly-select-flyer.en.jpg
new file mode 100644
index 000000000..dd0cb9fb2
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-select-flyer.en.jpg differ
diff --git a/src/lib/libraries/decks/steps/fly-switch-costume.en.gif b/src/lib/libraries/decks/steps/fly-switch-costume.en.gif
new file mode 100644
index 000000000..6c756e26e
Binary files /dev/null and b/src/lib/libraries/decks/steps/fly-switch-costume.en.gif differ
diff --git a/src/lib/libraries/decks/steps/glide-around-back-and-forth.en.png b/src/lib/libraries/decks/steps/glide-around-back-and-forth.en.png
new file mode 100755
index 000000000..696be693e
Binary files /dev/null and b/src/lib/libraries/decks/steps/glide-around-back-and-forth.en.png differ
diff --git a/src/lib/libraries/decks/steps/glide-around-point.en.png b/src/lib/libraries/decks/steps/glide-around-point.en.png
new file mode 100755
index 000000000..6d64d0cc6
Binary files /dev/null and b/src/lib/libraries/decks/steps/glide-around-point.en.png differ
diff --git a/src/lib/libraries/decks/steps/hide-show.en.png b/src/lib/libraries/decks/steps/hide-show.en.png
new file mode 100755
index 000000000..6d5ce3a9c
Binary files /dev/null and b/src/lib/libraries/decks/steps/hide-show.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-change-costumes.en.png b/src/lib/libraries/decks/steps/imagine-change-costumes.en.png
new file mode 100755
index 000000000..89c79c0d0
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-change-costumes.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-choose-another-backdrop.en.png b/src/lib/libraries/decks/steps/imagine-choose-another-backdrop.en.png
new file mode 100755
index 000000000..73e35cf29
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-choose-another-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-choose-another-sprite.en.png b/src/lib/libraries/decks/steps/imagine-choose-another-sprite.en.png
new file mode 100644
index 000000000..501987ea9
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-choose-another-sprite.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-choose-any-sprite.en.png b/src/lib/libraries/decks/steps/imagine-choose-any-sprite.en.png
new file mode 100755
index 000000000..a14d4ed8e
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-choose-any-sprite.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-choose-backdrop.en.png b/src/lib/libraries/decks/steps/imagine-choose-backdrop.en.png
new file mode 100755
index 000000000..3a8ae9d19
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-choose-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-choose-sound.en.png b/src/lib/libraries/decks/steps/imagine-choose-sound.en.png
new file mode 100644
index 000000000..88cd22525
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-choose-sound.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-click-green-flag.en.png b/src/lib/libraries/decks/steps/imagine-click-green-flag.en.png
new file mode 100755
index 000000000..201c5cf15
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-click-green-flag.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-fly-around.en.png b/src/lib/libraries/decks/steps/imagine-fly-around.en.png
new file mode 100755
index 000000000..f0b2eec66
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-fly-around.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-glide-to-point.en.png b/src/lib/libraries/decks/steps/imagine-glide-to-point.en.png
new file mode 100755
index 000000000..290a8c9a2
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-glide-to-point.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-grow-shrink.en.png b/src/lib/libraries/decks/steps/imagine-grow-shrink.en.png
new file mode 100755
index 000000000..c67f0ec64
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-grow-shrink.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-left-right.en.png b/src/lib/libraries/decks/steps/imagine-left-right.en.png
new file mode 100644
index 000000000..ae23eaa7c
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-left-right.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-record-a-sound.en.gif b/src/lib/libraries/decks/steps/imagine-record-a-sound.en.gif
new file mode 100644
index 000000000..2d219d654
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-record-a-sound.en.gif differ
diff --git a/src/lib/libraries/decks/steps/imagine-switch-backdrops.en.gif b/src/lib/libraries/decks/steps/imagine-switch-backdrops.en.gif
new file mode 100644
index 000000000..0171639b5
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-switch-backdrops.en.gif differ
diff --git a/src/lib/libraries/decks/steps/imagine-type-what-you-want.en.png b/src/lib/libraries/decks/steps/imagine-type-what-you-want.en.png
new file mode 100755
index 000000000..4194644e0
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-type-what-you-want.en.png differ
diff --git a/src/lib/libraries/decks/steps/imagine-up-down.en.png b/src/lib/libraries/decks/steps/imagine-up-down.en.png
new file mode 100644
index 000000000..950a8c4cc
Binary files /dev/null and b/src/lib/libraries/decks/steps/imagine-up-down.en.png differ
diff --git a/src/lib/libraries/decks/steps/move-arrow-keys-left-right.en.png b/src/lib/libraries/decks/steps/move-arrow-keys-left-right.en.png
new file mode 100755
index 000000000..6d05acc8e
Binary files /dev/null and b/src/lib/libraries/decks/steps/move-arrow-keys-left-right.en.png differ
diff --git a/src/lib/libraries/decks/steps/move-arrow-keys-up-down.en.png b/src/lib/libraries/decks/steps/move-arrow-keys-up-down.en.png
new file mode 100755
index 000000000..c9fab0472
Binary files /dev/null and b/src/lib/libraries/decks/steps/move-arrow-keys-up-down.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-add-a-paddle.en.gif b/src/lib/libraries/decks/steps/pong-add-a-paddle.en.gif
new file mode 100755
index 000000000..ec1f90ebe
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-a-paddle.en.gif differ
diff --git a/src/lib/libraries/decks/steps/pong-add-backdrop.en.png b/src/lib/libraries/decks/steps/pong-add-backdrop.en.png
new file mode 100755
index 000000000..8698dd38b
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-add-ball-sprite.en.png b/src/lib/libraries/decks/steps/pong-add-ball-sprite.en.png
new file mode 100755
index 000000000..8f7f8e0a2
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-ball-sprite.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-add-code-to-ball.en.png b/src/lib/libraries/decks/steps/pong-add-code-to-ball.en.png
new file mode 100755
index 000000000..9001af6a7
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-code-to-ball.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-add-line.en.gif b/src/lib/libraries/decks/steps/pong-add-line.en.gif
new file mode 100644
index 000000000..760aebb50
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-line.en.gif differ
diff --git a/src/lib/libraries/decks/steps/pong-add-score.en.gif b/src/lib/libraries/decks/steps/pong-add-score.en.gif
new file mode 100644
index 000000000..9bfd04ca9
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-add-score.en.gif differ
diff --git a/src/lib/libraries/decks/steps/pong-bounce-around.en.gif b/src/lib/libraries/decks/steps/pong-bounce-around.en.gif
new file mode 100755
index 000000000..61ad989f5
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-bounce-around.en.gif differ
diff --git a/src/lib/libraries/decks/steps/pong-choose-score.en.png b/src/lib/libraries/decks/steps/pong-choose-score.en.png
new file mode 100755
index 000000000..cb02a6343
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-choose-score.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-game-over.en.png b/src/lib/libraries/decks/steps/pong-game-over.en.png
new file mode 100755
index 000000000..90da7458a
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-game-over.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-insert-change-score.en.png b/src/lib/libraries/decks/steps/pong-insert-change-score.en.png
new file mode 100755
index 000000000..ffdea65e4
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-insert-change-score.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-move-the-paddle.en.gif b/src/lib/libraries/decks/steps/pong-move-the-paddle.en.gif
new file mode 100644
index 000000000..21052cb6e
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-move-the-paddle.en.gif differ
diff --git a/src/lib/libraries/decks/steps/pong-reset-score.en.png b/src/lib/libraries/decks/steps/pong-reset-score.en.png
new file mode 100755
index 000000000..13219c4cf
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-reset-score.en.png differ
diff --git a/src/lib/libraries/decks/steps/pong-select-ball.en.png b/src/lib/libraries/decks/steps/pong-select-ball.en.png
new file mode 100755
index 000000000..16ad29d91
Binary files /dev/null and b/src/lib/libraries/decks/steps/pong-select-ball.en.png differ
diff --git a/src/lib/libraries/decks/steps/record-a-sound-choose-sound.en.png b/src/lib/libraries/decks/steps/record-a-sound-choose-sound.en.png
new file mode 100755
index 000000000..88cd22525
Binary files /dev/null and b/src/lib/libraries/decks/steps/record-a-sound-choose-sound.en.png differ
diff --git a/src/lib/libraries/decks/steps/record-a-sound-click-record.en.png b/src/lib/libraries/decks/steps/record-a-sound-click-record.en.png
new file mode 100644
index 000000000..9ce8acb13
Binary files /dev/null and b/src/lib/libraries/decks/steps/record-a-sound-click-record.en.png differ
diff --git a/src/lib/libraries/decks/steps/record-a-sound-play-your-sound.en.png b/src/lib/libraries/decks/steps/record-a-sound-play-your-sound.en.png
new file mode 100755
index 000000000..a31315d62
Binary files /dev/null and b/src/lib/libraries/decks/steps/record-a-sound-play-your-sound.en.png differ
diff --git a/src/lib/libraries/decks/steps/record-a-sound-press-record-button.en.png b/src/lib/libraries/decks/steps/record-a-sound-press-record-button.en.png
new file mode 100644
index 000000000..2cfac3c70
Binary files /dev/null and b/src/lib/libraries/decks/steps/record-a-sound-press-record-button.en.png differ
diff --git a/src/lib/libraries/decks/steps/record-a-sound-sounds-tab.en.png b/src/lib/libraries/decks/steps/record-a-sound-sounds-tab.en.png
new file mode 100755
index 000000000..1edea203c
Binary files /dev/null and b/src/lib/libraries/decks/steps/record-a-sound-sounds-tab.en.png differ
diff --git a/src/lib/libraries/decks/steps/spin-point-in-direction.en.png b/src/lib/libraries/decks/steps/spin-point-in-direction.en.png
new file mode 100755
index 000000000..39e2276f9
Binary files /dev/null and b/src/lib/libraries/decks/steps/spin-point-in-direction.en.png differ
diff --git a/src/lib/libraries/decks/steps/spin-turn.en.png b/src/lib/libraries/decks/steps/spin-turn.en.png
new file mode 100755
index 000000000..7006f68b0
Binary files /dev/null and b/src/lib/libraries/decks/steps/spin-turn.en.png differ
diff --git a/src/lib/libraries/decks/steps/switch-costumes.en.png b/src/lib/libraries/decks/steps/switch-costumes.en.png
new file mode 100755
index 000000000..09921ae73
Binary files /dev/null and b/src/lib/libraries/decks/steps/switch-costumes.en.png differ
diff --git a/src/lib/libraries/decks/thumbnails/imagine-if.jpg b/src/lib/libraries/decks/thumbnails/imagine-if.jpg
new file mode 100644
index 000000000..c419d6e87
Binary files /dev/null and b/src/lib/libraries/decks/thumbnails/imagine-if.jpg differ
diff --git a/src/lib/libraries/decks/thumbnails/make-it-fly.jpg b/src/lib/libraries/decks/thumbnails/make-it-fly.jpg
new file mode 100644
index 000000000..3bce97750
Binary files /dev/null and b/src/lib/libraries/decks/thumbnails/make-it-fly.jpg differ
diff --git a/src/lib/libraries/decks/thumbnails/pong.jpg b/src/lib/libraries/decks/thumbnails/pong.jpg
new file mode 100644
index 000000000..631aa1521
Binary files /dev/null and b/src/lib/libraries/decks/thumbnails/pong.jpg differ
diff --git a/src/lib/libraries/decks/translate-video.js b/src/lib/libraries/decks/translate-video.js
index 6f34771b0..c837dafed 100644
--- a/src/lib/libraries/decks/translate-video.js
+++ b/src/lib/libraries/decks/translate-video.js
@@ -74,6 +74,12 @@ const videos = {
'en': '3jvl8zgjo2',
'ja': 'q7zq9vu0wb',
'ja-Hira': 'q7zq9vu0wb'
+ },
+ 'make-it-fly': {
+ en: 'zbtdx2dem9'
+ },
+ 'pong-game': {
+ en: '8m48dv0ens'
}
};
diff --git a/test/integration/examples.test.js b/test/integration/examples.test.js
index 3554151db..a268455ca 100644
--- a/test/integration/examples.test.js
+++ b/test/integration/examples.test.js
@@ -75,7 +75,8 @@ describe('blocks example', () => {
await expect(projectRequests).toEqual(['https://projects.scratch.mit.edu/96708228']);
});
- test('Change categories', async () => {
+ // skipping per https://github.com/LLK/scratch-gui/issues/4902 until we have better approach
+ test.skip('Change categories', async () => {
await loadUri(`${uri}`);
await clickText('Looks');
await clickText('Sound');
diff --git a/test/unit/util/drag-recognizer.test.js b/test/unit/util/drag-recognizer.test.js
new file mode 100644
index 000000000..825917ba8
--- /dev/null
+++ b/test/unit/util/drag-recognizer.test.js
@@ -0,0 +1,109 @@
+import DragRecognizer from '../../../src/lib/drag-recognizer';
+
+describe('DragRecognizer', () => {
+ let onDrag;
+ let onDragEnd;
+ let dragRecognizer;
+
+ beforeEach(() => {
+ onDrag = jest.fn();
+ onDragEnd = jest.fn();
+ dragRecognizer = new DragRecognizer({onDrag, onDragEnd});
+ });
+
+ afterEach(() => {
+ dragRecognizer.reset();
+ });
+
+ test('start -> small drag', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('mousemove', {clientX: 101, clientY: 101}));
+ expect(onDrag).not.toBeCalled();
+ });
+
+ test('start -> large vertical touch move -> scroll, not drag', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 106, clientY: 150}));
+ expect(onDrag).not.toBeCalled();
+ });
+
+ test('start -> large vertical mouse move -> mouse moves always drag)', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('mousemove', {clientX: 100, clientY: 150}));
+ expect(onDrag).toBeCalled();
+ });
+
+ test('start -> large horizontal touch move -> drag', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
+ expect(onDrag).toBeCalled();
+ });
+
+ test('after starting a scroll, it cannot become a drag', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 100, clientY: 110}));
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 100, clientY: 100}));
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 110, clientY: 100}));
+ expect(onDrag).not.toBeCalled();
+ });
+
+ test('start -> end unbinds', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
+ expect(onDrag).toHaveBeenCalledTimes(1);
+ window.dispatchEvent(new MouseEvent('touchend', {clientX: 150, clientY: 106}));
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
+ expect(onDrag).toHaveBeenCalledTimes(1); // Still 1
+ });
+
+ test('start -> reset unbinds', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
+ expect(onDrag).toHaveBeenCalledTimes(1);
+ dragRecognizer.reset();
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
+ expect(onDrag).toHaveBeenCalledTimes(1); // Still 1
+ });
+
+ test('scrolls do not call prevent default', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ const event = new MouseEvent('touchmove', {clientX: 100, clientY: 110});
+ event.preventDefault = jest.fn();
+ window.dispatchEvent(event);
+ expect(event.preventDefault).toHaveBeenCalledTimes(0);
+ });
+
+ test('confirmed drags have preventDefault called on them', () => {
+ dragRecognizer.start({clientX: 100, clientY: 100});
+ const event = new MouseEvent('touchmove', {clientX: 150, clientY: 106});
+ event.preventDefault = jest.fn();
+ window.dispatchEvent(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ });
+
+ test('multiple horizontal drag angles', () => {
+ // +45 from horizontal => drag
+ dragRecognizer.start({clientX: 0, clientY: 0});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 10, clientY: 10}));
+ expect(onDrag).toHaveBeenCalledTimes(1);
+ dragRecognizer.reset();
+
+ // -45 from horizontal => drag
+ dragRecognizer.start({clientX: 0, clientY: 0});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: 10, clientY: -10}));
+ expect(onDrag).toHaveBeenCalledTimes(2);
+ dragRecognizer.reset();
+
+ // +135 from horizontal => drag
+ dragRecognizer.start({clientX: 0, clientY: 0});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: -10, clientY: 10}));
+ expect(onDrag).toHaveBeenCalledTimes(3);
+ dragRecognizer.reset();
+
+ // -135 from horizontal => drag
+ dragRecognizer.start({clientX: 0, clientY: 0});
+ window.dispatchEvent(new MouseEvent('touchmove', {clientX: -10, clientY: -10}));
+ expect(onDrag).toHaveBeenCalledTimes(4);
+ dragRecognizer.reset();
+ });
+});