{
languageMenuOpen: languageMenuOpen(state),
locale: state.locales.locale,
loginMenuOpen: loginMenuOpen(state),
+ modeMenuOpen: modeMenuOpen(state),
projectTitle: state.scratchGui.projectTitle,
sessionExists: state.session && typeof state.session.session !== 'undefined',
username: user ? user.username : null,
userOwnsProject: ownProps.authorUsername && user &&
(ownProps.authorUsername === user.username),
- vm: state.scratchGui.vm
+ vm: state.scratchGui.vm,
+ mode220022BC: isTimeTravel220022BC(state),
+ mode1920: isTimeTravel1920(state),
+ mode1990: isTimeTravel1990(state),
+ mode2020: isTimeTravel2020(state),
+ modeNow: isTimeTravelNow(state)
};
};
@@ -867,13 +979,16 @@ const mapDispatchToProps = dispatch => ({
onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
onClickLogin: () => dispatch(openLoginMenu()),
onRequestCloseLogin: () => dispatch(closeLoginMenu()),
+ onClickMode: () => dispatch(openModeMenu()),
+ onRequestCloseMode: () => dispatch(closeModeMenu()),
onRequestOpenAbout: () => dispatch(openAboutMenu()),
onRequestCloseAbout: () => dispatch(closeAboutMenu()),
onClickNew: needSave => dispatch(requestNewProject(needSave)),
onClickRemix: () => dispatch(remixProject()),
onClickSave: () => dispatch(manualUpdateProject()),
onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()),
- onSeeCommunity: () => dispatch(setPlayer(true))
+ onSeeCommunity: () => dispatch(setPlayer(true)),
+ onSetTimeTravelMode: mode => dispatch(setTimeTravel(mode))
});
export default compose(
diff --git a/src/components/menu-bar/nineties_logo.svg b/src/components/menu-bar/nineties_logo.svg
new file mode 100644
index 000000000..ac798ff4b
--- /dev/null
+++ b/src/components/menu-bar/nineties_logo.svg
@@ -0,0 +1,2389 @@
+
+
+
diff --git a/src/components/menu-bar/oldtimey-logo.svg b/src/components/menu-bar/oldtimey-logo.svg
new file mode 100644
index 000000000..afc34cde2
--- /dev/null
+++ b/src/components/menu-bar/oldtimey-logo.svg
@@ -0,0 +1,76 @@
+
+
+
diff --git a/src/components/menu-bar/prehistoric-logo.svg b/src/components/menu-bar/prehistoric-logo.svg
new file mode 100644
index 000000000..a18055270
--- /dev/null
+++ b/src/components/menu-bar/prehistoric-logo.svg
@@ -0,0 +1,93 @@
+
+
+
diff --git a/src/components/nineties-mode/nineties-mode.css b/src/components/nineties-mode/nineties-mode.css
new file mode 100644
index 000000000..0d09ccef4
--- /dev/null
+++ b/src/components/nineties-mode/nineties-mode.css
@@ -0,0 +1,9 @@
+.nineties-mode {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: transparent url('./shapes.svg');
+ z-index: 50;
+ opacity: .2;
+ pointer-events: none;
+}
diff --git a/src/components/nineties-mode/nineties-mode.jsx b/src/components/nineties-mode/nineties-mode.jsx
new file mode 100644
index 000000000..77e65fda5
--- /dev/null
+++ b/src/components/nineties-mode/nineties-mode.jsx
@@ -0,0 +1,23 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import styles from './nineties-mode.css';
+import {connect} from 'react-redux';
+import {isTimeTravel1990} from '../../reducers/time-travel';
+
+const NinetiesMode = props => {
+ if (!props.show) return null;
+ return
;
+};
+
+NinetiesMode.propTypes = {
+ show: PropTypes.bool
+};
+
+const mapStateToProps = state => ({
+ // This is the button's mode, as opposed to the actual current state
+ show: isTimeTravel1990(state)
+});
+
+export default connect(
+ mapStateToProps
+)(NinetiesMode);
diff --git a/src/components/nineties-mode/shapes.svg b/src/components/nineties-mode/shapes.svg
new file mode 100644
index 000000000..deb2237e1
--- /dev/null
+++ b/src/components/nineties-mode/shapes.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/oldtimey-mode/oldtimey-mode.css b/src/components/oldtimey-mode/oldtimey-mode.css
new file mode 100644
index 000000000..1deb573a4
--- /dev/null
+++ b/src/components/oldtimey-mode/oldtimey-mode.css
@@ -0,0 +1,9 @@
+.oldtimey-mode {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: transparent url('./oldtimey.png') center center;
+ background-size: 100% 100%;
+ z-index: 500;
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/src/components/oldtimey-mode/oldtimey-mode.jsx b/src/components/oldtimey-mode/oldtimey-mode.jsx
new file mode 100644
index 000000000..98d45b37a
--- /dev/null
+++ b/src/components/oldtimey-mode/oldtimey-mode.jsx
@@ -0,0 +1,35 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import styles from './oldtimey-mode.css';
+import oldtimeySound from './projector2.mp3';
+import {connect} from 'react-redux';
+import {isTimeTravel1920} from '../../reducers/time-travel';
+
+const OldTimeyMode = props => {
+ if (!props.show) return null;
+ return (
+
+
+ );
+};
+
+OldTimeyMode.propTypes = {
+ show: PropTypes.bool
+};
+
+const mapStateToProps = state => ({
+ // This is the button's mode, as opposed to the actual current state
+ show: isTimeTravel1920(state)
+});
+
+export default connect(
+ mapStateToProps
+)(OldTimeyMode);
diff --git a/src/components/oldtimey-mode/oldtimey.png b/src/components/oldtimey-mode/oldtimey.png
new file mode 100644
index 000000000..ca0abe691
Binary files /dev/null and b/src/components/oldtimey-mode/oldtimey.png differ
diff --git a/src/components/oldtimey-mode/projector.wav b/src/components/oldtimey-mode/projector.wav
new file mode 100644
index 000000000..ff28aba5e
Binary files /dev/null and b/src/components/oldtimey-mode/projector.wav differ
diff --git a/src/components/oldtimey-mode/projector2.mp3 b/src/components/oldtimey-mode/projector2.mp3
new file mode 100644
index 000000000..a475951ee
Binary files /dev/null and b/src/components/oldtimey-mode/projector2.mp3 differ
diff --git a/src/components/prehistoric-mode/background.png b/src/components/prehistoric-mode/background.png
new file mode 100644
index 000000000..90c68ccb4
Binary files /dev/null and b/src/components/prehistoric-mode/background.png differ
diff --git a/src/components/prehistoric-mode/prehistoric-mode.css b/src/components/prehistoric-mode/prehistoric-mode.css
new file mode 100644
index 000000000..962a3a039
--- /dev/null
+++ b/src/components/prehistoric-mode/prehistoric-mode.css
@@ -0,0 +1,49 @@
+.prehistoric-mode {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+.prehistoric-background{
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 90%;
+ z-index: 50;
+ pointer-events: none;
+ background: transparent url('./background.png') center center;
+ animation:jitter 2.5s linear infinite;
+}
+
+
+@keyframes jitter{
+ 0%{transform:scale(1.0385841106898348);}
+ 5% {transform:scale(1.045596657989466);}
+ 10% {transform:scale(1.029923116529907);}
+ 15% {transform:scale(1.059010318189378);}
+ 20% {transform:scale(1.0868915738701577);}
+ 25% {transform:scale(1.0771183924324506);}
+ 30% {transform:scale(1.0780695942284886);}
+ 35% {transform:scale(1.081268381642509);}
+ 40% {transform:scale(1.0446864380239975);}
+ 45% {transform:scale(1.0822226558561991);}
+ 50% {transform:scale(1.0703574969484024);}
+ 55% {transform:scale(1.0131626458532954);}
+ 60% {transform:scale(1.0638351688496082);}
+ 65% {transform:scale(1.0254684750877292);}
+ 70% {transform:scale(1.0409969735315965);}
+ 75% {transform:scale(1.029863208069294);}
+ 80% {transform:scale(1.043175299252114);}
+ 85% {transform:scale(1.022196668538089);}
+ 90% {transform:scale(1.042465864842523);}
+ 95% {transform:scale(1.04090035265691);}
+ 100% {transform:scale(1.0385841106898348);}
+}
+
+.torch {
+ position: absolute;
+ pointer-events: none;
+ opacity: 80%;
+ z-index: 50;
+}
\ No newline at end of file
diff --git a/src/components/prehistoric-mode/prehistoric-mode.jsx b/src/components/prehistoric-mode/prehistoric-mode.jsx
new file mode 100644
index 000000000..72a49d575
--- /dev/null
+++ b/src/components/prehistoric-mode/prehistoric-mode.jsx
@@ -0,0 +1,68 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import bindAll from 'lodash.bindall';
+import styles from './prehistoric-mode.css';
+import {connect} from 'react-redux';
+import {isTimeTravel220022BC} from '../../reducers/time-travel';
+import torch from './torch.gif';
+
+class PrehistoricMode extends React.Component {
+ constructor (props) {
+ super(props);
+ bindAll(this, [
+ 'handleMouseMove'
+ ]);
+ this.state = {
+ x: window.innerWidth / 2,
+ y: window.innerHeight / 2
+ };
+ }
+
+ componentDidMount () {
+ document.addEventListener('pointermove', this.handleMouseMove);
+ }
+
+ componentWillUnmount () {
+ document.removeEventListener('pointermove', this.handleMouseMove);
+ }
+
+ handleMouseMove (event) {
+ this.setState({x: event.clientX, y: event.clientY});
+ }
+
+ render () {
+ if (!this.props.show) return null;
+ return (
+
+

+
+ );
+ }
+}
+
+PrehistoricMode.propTypes = {
+ show: PropTypes.bool
+};
+
+const mapStateToProps = state => ({
+ // This is the button's mode, as opposed to the actual current state
+ show: isTimeTravel220022BC(state)
+});
+
+export default connect(
+ mapStateToProps
+)(PrehistoricMode);
diff --git a/src/components/prehistoric-mode/torch.gif b/src/components/prehistoric-mode/torch.gif
new file mode 100644
index 000000000..6f5b13927
Binary files /dev/null and b/src/components/prehistoric-mode/torch.gif differ
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index 5756249f7..616d2e090 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -26,6 +26,7 @@ import {closeExtensionLibrary, openSoundRecorder, openConnectionModal} from '../
import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/custom-procedures';
import {setConnectionModalExtensionId} from '../reducers/connection-modal';
import {updateMetrics} from '../reducers/workspace-metrics';
+import {isTimeTravel2020} from '../reducers/time-travel';
import {
activateTab,
@@ -48,7 +49,7 @@ const DroppableBlocks = DropAreaHOC([
class Blocks extends React.Component {
constructor (props) {
super(props);
- this.ScratchBlocks = VMScratchBlocks(props.vm);
+ this.ScratchBlocks = VMScratchBlocks(props.vm, false);
bindAll(this, [
'attachVM',
'detachVM',
@@ -87,6 +88,11 @@ class Blocks extends React.Component {
this.toolboxUpdateQueue = [];
}
componentDidMount () {
+ this.ScratchBlocks = VMScratchBlocks(this.props.vm, this.props.useCatBlocks);
+ this.ScratchBlocks.prompt = this.handlePromptStart;
+ this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart;
+ this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder;
+
this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker;
this.ScratchBlocks.Procedures.externalProcedureDefCallback = this.props.onActivateCustomProcedures;
this.ScratchBlocks.ScratchMsgs.setLocale(this.props.locale);
@@ -553,6 +559,7 @@ class Blocks extends React.Component {
onRequestCloseCustomProcedures,
toolboxXML,
updateMetrics: updateMetricsProp,
+ useCatBlocks,
workspaceMetrics,
...props
} = this.props;
@@ -639,6 +646,7 @@ Blocks.propTypes = {
toolboxXML: PropTypes.string,
updateMetrics: PropTypes.func,
updateToolboxState: PropTypes.func,
+ useCatBlocks: PropTypes.bool,
vm: PropTypes.instanceOf(VM).isRequired,
workspaceMetrics: PropTypes.shape({
targets: PropTypes.objectOf(PropTypes.object)
@@ -689,7 +697,8 @@ const mapStateToProps = state => ({
messages: state.locales.messages,
toolboxXML: state.scratchGui.toolbox.toolboxXML,
customProceduresVisible: state.scratchGui.customProcedures.active,
- workspaceMetrics: state.scratchGui.workspaceMetrics
+ workspaceMetrics: state.scratchGui.workspaceMetrics,
+ useCatBlocks: isTimeTravel2020(state)
});
const mapDispatchToProps = dispatch => ({
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index c10df0fb3..0081c83c5 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -104,6 +104,7 @@ GUI.propTypes = {
isLoading: PropTypes.bool,
isScratchDesktop: PropTypes.bool,
isShowingProject: PropTypes.bool,
+ isTotallyNormal: PropTypes.bool,
loadingStateVisible: PropTypes.bool,
onProjectLoaded: PropTypes.func,
onSeeCommunity: PropTypes.func,
@@ -118,6 +119,7 @@ GUI.propTypes = {
GUI.defaultProps = {
isScratchDesktop: false,
+ isTotallyNormal: false,
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(),
onProjectLoaded: () => {},
onUpdateProjectId: () => {},
diff --git a/src/lib/blocks.js b/src/lib/blocks.js
index 809a77ce2..bb9a94db3 100644
--- a/src/lib/blocks.js
+++ b/src/lib/blocks.js
@@ -1,12 +1,11 @@
-import ScratchBlocks from 'scratch-blocks';
-
/**
* Connect scratch blocks with the vm
* @param {VirtualMachine} vm - The scratch vm
+ * @param {Bool} useCatBlocks - Whether to use cat blocks rendering of ScratchBlocks
* @return {ScratchBlocks} ScratchBlocks connected with the vm
*/
-export default function (vm) {
-
+export default function (vm, useCatBlocks) {
+ const ScratchBlocks = useCatBlocks ? require('cat-blocks') : require('scratch-blocks');
const jsonForMenuBlock = function (name, menuOptionsFn, colors, start) {
return {
message0: '%1',
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index 378a3203c..bf9dda8fd 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -22,6 +22,7 @@ import restoreDeletionReducer, {restoreDeletionInitialState} from './restore-del
import stageSizeReducer, {stageSizeInitialState} from './stage-size';
import targetReducer, {targetsInitialState} from './targets';
import timeoutReducer, {timeoutInitialState} from './timeout';
+import timeTravelReducer, {timeTravelInitialState} from './time-travel';
import toolboxReducer, {toolboxInitialState} from './toolbox';
import vmReducer, {vmInitialState} from './vm';
import vmStatusReducer, {vmStatusInitialState} from './vm-status';
@@ -56,6 +57,7 @@ const guiInitialState = {
restoreDeletion: restoreDeletionInitialState,
targets: targetsInitialState,
timeout: timeoutInitialState,
+ timeTravel: timeTravelInitialState,
toolbox: toolboxInitialState,
vm: vmInitialState,
vmStatus: vmStatusInitialState,
@@ -155,6 +157,7 @@ const guiReducer = combineReducers({
restoreDeletion: restoreDeletionReducer,
targets: targetReducer,
timeout: timeoutReducer,
+ timeTravel: timeTravelReducer,
toolbox: toolboxReducer,
vm: vmReducer,
vmStatus: vmStatusReducer,
diff --git a/src/reducers/menus.js b/src/reducers/menus.js
index fc3687f5a..b756a9e41 100644
--- a/src/reducers/menus.js
+++ b/src/reducers/menus.js
@@ -7,6 +7,7 @@ const MENU_FILE = 'fileMenu';
const MENU_EDIT = 'editMenu';
const MENU_LANGUAGE = 'languageMenu';
const MENU_LOGIN = 'loginMenu';
+const MENU_MODE = 'modeMenu';
const initialState = {
@@ -15,7 +16,8 @@ const initialState = {
[MENU_FILE]: false,
[MENU_EDIT]: false,
[MENU_LANGUAGE]: false,
- [MENU_LOGIN]: false
+ [MENU_LOGIN]: false,
+ [MENU_MODE]: false
};
const reducer = function (state, action) {
@@ -59,6 +61,10 @@ const languageMenuOpen = state => state.scratchGui.menus[MENU_LANGUAGE];
const openLoginMenu = () => openMenu(MENU_LOGIN);
const closeLoginMenu = () => closeMenu(MENU_LOGIN);
const loginMenuOpen = state => state.scratchGui.menus[MENU_LOGIN];
+const openModeMenu = () => openMenu(MENU_MODE);
+const closeModeMenu = () => closeMenu(MENU_MODE);
+const modeMenuOpen = state => state.scratchGui.menus[MENU_MODE];
+
export {
reducer as default,
@@ -80,5 +86,8 @@ export {
languageMenuOpen,
openLoginMenu,
closeLoginMenu,
- loginMenuOpen
+ loginMenuOpen,
+ openModeMenu,
+ closeModeMenu,
+ modeMenuOpen
};
diff --git a/src/reducers/time-travel.js b/src/reducers/time-travel.js
new file mode 100644
index 000000000..23cfe6cbe
--- /dev/null
+++ b/src/reducers/time-travel.js
@@ -0,0 +1,96 @@
+const SET_TIME_TRAVEL = 'scratch-gui/time-travel/SET_TIME_TRAVEL';
+const initialState = {
+ year: 'NOW'
+};
+
+const NOW = 'NOW';
+const YEAR_2020 = '2020';
+const YEAR_1990 = '1990';
+const YEAR_1920 = '1920';
+const YEAR_220022BC = '220022BC';
+
+const reducer = function (state, action) {
+ if (typeof state === 'undefined') state = initialState;
+ switch (action.type) {
+ case SET_TIME_TRAVEL:
+ return Object.assign({}, state, {
+ year: action.year
+ });
+ default:
+ return state;
+ }
+};
+
+const isTimeTravel220022BC = function (state) {
+ return state.scratchGui.timeTravel.year === YEAR_220022BC;
+};
+const setTimeTravel220022BC = function () {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: YEAR_220022BC
+ };
+};
+
+const isTimeTravel1920 = function (state) {
+ return state.scratchGui.timeTravel.year === YEAR_1920;
+};
+const setTimeTravel1920 = function () {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: YEAR_1920
+ };
+};
+
+const isTimeTravel1990 = function (state) {
+ return state.scratchGui.timeTravel.year === YEAR_1990;
+};
+const setTimeTravel1990 = function () {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: YEAR_1990
+ };
+};
+
+
+const isTimeTravel2020 = function (state) {
+ return state.scratchGui.timeTravel.year === YEAR_2020;
+};
+const setTimeTravel2020 = function () {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: YEAR_2020
+ };
+};
+
+const isTimeTravelNow = function (state) {
+ return state.scratchGui.timeTravel.year === NOW;
+};
+const setTimeTravelNow = function () {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: NOW
+ };
+};
+
+const setTimeTravel = function (mode) {
+ return {
+ type: SET_TIME_TRAVEL,
+ year: mode
+ };
+};
+
+export {
+ reducer as default,
+ initialState as timeTravelInitialState,
+ isTimeTravel220022BC,
+ isTimeTravel1920,
+ isTimeTravel1990,
+ isTimeTravel2020,
+ isTimeTravelNow,
+ setTimeTravel220022BC,
+ setTimeTravel1920,
+ setTimeTravel1990,
+ setTimeTravel2020,
+ setTimeTravelNow,
+ setTimeTravel
+};
diff --git a/test/unit/components/menu-bar.test.jsx b/test/unit/components/menu-bar.test.jsx
index 3aaadb9de..6e3f1bd09 100644
--- a/test/unit/components/menu-bar.test.jsx
+++ b/test/unit/components/menu-bar.test.jsx
@@ -20,6 +20,9 @@ describe('MenuBar Component', () => {
projectState: {
loadingState: LoadingState.NOT_LOADED
},
+ timeTravel: {
+ year: 'NOW'
+ },
vm: new VM()
}
});
diff --git a/webpack.config.js b/webpack.config.js
index 168faa396..815333b96 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -112,7 +112,7 @@ module.exports = [
module: {
rules: base.module.rules.concat([
{
- test: /\.(svg|png|wav|gif|jpg)$/,
+ test: /\.(svg|png|wav|mp3|gif|jpg)$/,
loader: 'file-loader',
options: {
outputPath: 'static/assets/'
@@ -187,7 +187,8 @@ module.exports = [
patterns: [
{
from: 'extension-worker.{js,js.map}',
- context: 'node_modules/scratch-vm/dist/web'
+ context: 'node_modules/scratch-vm/dist/web',
+ noErrorOnMissing: true
}
]
})
@@ -213,7 +214,7 @@ module.exports = [
module: {
rules: base.module.rules.concat([
{
- test: /\.(svg|png|wav|gif|jpg)$/,
+ test: /\.(svg|png|wav|mp3|gif|jpg)$/,
loader: 'file-loader',
options: {
outputPath: 'static/assets/',
@@ -235,7 +236,8 @@ module.exports = [
patterns: [
{
from: 'extension-worker.{js,js.map}',
- context: 'node_modules/scratch-vm/dist/web'
+ context: 'node_modules/scratch-vm/dist/web',
+ noErrorOnMissing: true
}
]
}),