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(); + }); +});