Merge pull request #402 from LLK/release/2.2.7

[master] Release 2.2.7
This commit is contained in:
Ray Schamp 2016-04-05 15:26:27 -04:00
commit 341e09acff
124 changed files with 4875 additions and 638 deletions

View file

@ -1,6 +1,8 @@
rules:
class-name-format: 0
color-literals: 2
final-newline: 2
force-element-nesting: 0
hex-notation: 2
indentation:
- 2
@ -13,6 +15,12 @@ rules:
max-depth: 4
no-css-comments: 0
no-ids: 0
no-mergeable-selectors: 0
no-qualifying-elements:
- 1
-
- allow-element-with-attribute
no-transition-all: 0
property-sort-order:
- 2
-
@ -21,4 +29,5 @@ rules:
- 2
-
style: double
shorthand-values: 0
zero-unit: 2

View file

@ -1,5 +1,6 @@
language: node_js
node_js: '0.12'
node_js:
- '4.2'
sudo: false
cache:
directories:
@ -11,12 +12,19 @@ env:
global:
- secure: A138rYuXDsOmpEwYxZ31WyXEeq5fgr9qyqsQh1nTFsjBKpFtNM+CN9e0QJQFT3PLs4wH/lWTRSyHxakxKQS1sxq828f9gHed+f15REKk/fRUplcCYIexT9xKVtU3D8CRNn/KBFWk75fZyZt20eyOVIv4h3pInKQz7y84J6PWzB1BCrAFvADrzS1X68Z3NJJLyxnz0YEurzz8mC2v4D0s/XifKTWvRtefD4QM6pE0C2iYyk+ThrLwg7i9FDHVfo0MrkgcdX7mz37SnTr7p7mHWnGXrGngi/NiDRQ+Uwwq/sr2UIww0rCwS1xsOcS//dC4NNqrrt1kUTsoC1Yt87Ny+gI0nUplsfEpdKajAkOYdANC5bJUGqPdSlOds1v9aJs9Hx48uGamWkm/3cFmoJ5uA2ZzUwbSGjTkWbnhwzT0YRvcLGhP1WE/EswaIyK5qMp522E79mP1yH6M750iUvi4N39+QW1BNX3ADkOwyAI67ArX5on5gWP83RXcJ15im7XsBpsmVn/KXi6AouWPb8jmSmKCj0QZCzfLY7ivM42IugYpK2NV7kFB38DpXQamJ5eskgwYa3elRmednIFUuwb1QDnONvJogVjk4CLmoSxssC2mJnnrUItM7l8G6As81GMI+6lTtl86hAuXBjUk60FMbgTAQDX9ll26LgpBy8jHSx8=
- secure: EX1fyov+f6ytWN2ZSL4dLslwrVkp6Ho/uoSLO38/qNG3XdGmBN4VprxddcQiWfo+Mrg3GdWcfcM/VazhhStBi1uLfZiw3RHZaSGuWbiuD2EtzqtlC+OVvoajgy91QFajh9Zzuwa0rYbEPd/sw01R53NoWJYl0GSteWk7C8Wv6anl4FUJCqgvvTV2ZEcyTtGcVJgUhKi1MfNpTSM6JWBy0DWszcyxj7C8LSs1+l9ZjAtnlUBWY13HsrNu8G5d+FwqGHZLUAjdu2O602wxV897/xLARLduZ+01ALpVefNEEGMB1Wd+xMw4dm2B0Uk86a4TBRCeOgJZ1yoJoPpGPOHTo+dgNXcU8ReszGVoy7uOjFWwu82FQq8gzfcf75yzaRJgG8/BJ6BkJfa0EmFg3iO5CwixQyHR5+CqsedtoLAWVT8zlOfQ/Z6yx4Pm7jXQSOkyvo09YJ2QIn4IFGPvwOVS7Firzi+fLl8GYApeSV9G10e1IzA4pPrKdJMRA4qRMPt9zJGq7ZO1J/d9aW/5KIsJUDnodnl7yXJyDMOyNeljT9I82ciHZcURxRRY080vrW6dgNJE1V9jxBhWEvr2iCeWMMedWaGuC41I7K9L79eW8lmaE+cQ+OZrzpOJP4GbfmIiXrh+0M4ChL/xBpjtiFwpNdkCXXhzWMnjJ4wCrii4yuc=
- CXX=g++-4.8
- EB_REGION=us-east-1
- EB_APP=scratch-www
- EB_AWS_BUCKET_NAME=elasticbeanstalk-us-east-1-307680192167
- SKIP_CLEANUP=true
- ELASTIC_BEANSTALK_LABEL=${TRAVIS_COMMIT:0:5}
- BUILD_ARCHIVE=$ELASTIC_BEANSTALK_LABEL.zip
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_deploy:
- zip -qr $BUILD_ARCHIVE .
deploy:
@ -29,7 +37,7 @@ deploy:
skip_cleanup: $SKIP_CLEANUP
region: $EB_REGION
app: $EB_APP
env: scratch-www-staging
env: scratch-www-stage-v208
on:
repo: LLK/scratch-www
branch:

View file

@ -59,6 +59,8 @@ test:
@echo ""
@make functional
@echo ""
@make localization
@echo ""
lint:
$(ESLINT) ./*.js
@ -80,6 +82,9 @@ functional:
integration:
$(TAP) ./test/integration/*.js
localization:
$(TAP) ./test/localization/*.js
# ------------------------------------
.PHONY: build clean deploy static tag translations webpack watch stop start test lint

View file

@ -39,9 +39,10 @@ var fs = require('fs');
var glob = require('glob');
var merge = require('lodash.merge');
var path = require('path');
var po2icu = require('po2icu');
var languages = require('../languages.json');
var localeCompare = require('./lib/locale-compare');
var localizedUrls = require('./lib/localized-urls');
// -----------------------------------------------------------------------------
// Main script
@ -54,12 +55,7 @@ if (!args.length) {
process.stdout.write('A destination directory must be specified.');
process.exit(1);
}
var verbose = false;
if (args.length > 1) {
verbose = (args[1] === '-v') ? true : false;
}
var poUiDir = path.resolve(__dirname, '../node_modules/scratchr2_translations/ui');
var outputDir = path.resolve(__dirname, '../', args[0]);
try {
fs.accessSync(outputDir, fs.F_OK);
@ -68,27 +64,19 @@ try {
fs.mkdirSync(outputDir);
}
// get global locale strings first.
var globalTemplateFile = path.resolve(__dirname, '../src/l10n.json');
// message key with english string values (i.e. default values)
var generalIds = JSON.parse(fs.readFileSync(globalTemplateFile, 'utf8'));
var viewLocales = {};
var generalLocales = {
en: generalIds
};
// FormattedMessage id with english string as value. Use for default values in translations
// Sample structure: { 'general-general.blah': 'blah', 'about-about.blah': 'blahblah' }
var idsWithICU = {};
// reverse (i.e. english string with message key as the value) object for searching po files.
// Sample structure: { 'blah': 'general-general.blah', 'blahblah': 'about-about.blah' }
var icuWithIds = {};
for (var id in generalIds) {
idsWithICU['general-' + id] = generalIds[id];
icuWithIds[generalIds[id]] = 'general-' + id;
}
// get global locale strings first.
var globalTemplateFile = path.resolve(__dirname, '../src/l10n.json');
localeCompare.getIdsForView('general', globalTemplateFile, viewLocales, idsWithICU, icuWithIds);
// start with all views, and remove localized ones as they are iterated over
var views = glob.sync(path.resolve(__dirname, '../src/views/*'));
@ -102,14 +90,28 @@ files.forEach(function (file) {
var dirPath = file.split('/');
dirPath.pop();
var view = dirPath.pop();
localeCompare.getIdsForView(view, file, viewLocales, idsWithICU, icuWithIds);
});
var viewIds = JSON.parse(fs.readFileSync(file, 'utf8'));
viewLocales[view] = {
en: viewIds
};
for (var id in viewIds) {
idsWithICU[view + '-' + id] = viewIds[id];
icuWithIds[viewIds[id]] = view + '-' + id; // add viewName to identifier for later
// get asset url translations
var localizedAssetUrls = {};
files = glob.sync(path.resolve(__dirname, '../src/views/**/l10n-static.json'));
files.forEach(function (file) {
var dirPath = file.split('/');
dirPath.pop();
var view = dirPath.pop();
localizedAssetUrls[view] = {};
var assetUrls = JSON.parse(fs.readFileSync(file, 'utf8'));
for (var lang in localizedUrls) {
localizedAssetUrls[view][lang] = {};
for (var key in assetUrls) {
if (localizedUrls[lang].hasOwnProperty(key)) {
localizedAssetUrls[view][lang][key] = localizedUrls[lang][key];
} else {
localizedAssetUrls[view][lang][key] = assetUrls[key];
}
}
}
});
@ -118,53 +120,21 @@ files.forEach(function (file) {
var md5WithIds = localeCompare.getMD5Map(icuWithIds);
// Get ui localization strings first
glob(poUiDir + '/*', function (err, files) {
if (err) throw new Error(err);
files.forEach(function (file) {
var lang = file.split('/').pop();
var jsFile = path.resolve(file, 'LC_MESSAGES/djangojs.po');
var pyFile = path.resolve(file, 'LC_MESSAGES/django.po');
var translations = {};
try {
var jsTranslations = po2icu.poFileToICUSync(lang, jsFile);
translations = localeCompare.mergeNewTranslations(translations, jsTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (verbose) process.stdout.write(lang + ': ' + err + '\n');
}
try {
var pyTranslations = po2icu.poFileToICUSync(lang, pyFile);
translations = localeCompare.mergeNewTranslations(translations, pyTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (verbose) process.stdout.write(lang + ': ' + err + '\n');
}
// add new translations to locale object
for (var id in translations) {
var ids = id.split('-'); // [viewName, stringId]
var viewName = ids[0];
var stringId = ids[1];
if (viewLocales.hasOwnProperty(viewName)) {
if (!viewLocales[viewName].hasOwnProperty(lang)) viewLocales[viewName][lang] = {};
viewLocales[viewName][lang][stringId] = translations[id];
} else {
// default to general
if (!generalLocales.hasOwnProperty(lang)) generalLocales[lang] = {};
generalLocales[lang][stringId] = translations[id];
var isoCodes = Object.keys(languages);
for (i in isoCodes) {
var translations = localeCompare.getTranslationsForLanguage(isoCodes[i], idsWithICU, md5WithIds);
for (var key in translations) {
viewLocales[key] = merge(viewLocales[key], translations[key]);
}
}
});
for (var i in views) {
var viewTranslations = generalLocales;
for (i in views) {
var viewTranslations = viewLocales['general'];
if (views[i] in viewLocales) {
viewTranslations = merge(viewLocales[views[i]], viewTranslations);
}
var objectString = JSON.stringify(viewTranslations);
var fileString = 'window._messages = ' + objectString + ';';
fs.writeFileSync(outputDir + '/' + views[i] + '.intl.js', fileString);
if (views[i] in localizedAssetUrls) {
viewTranslations = merge(viewTranslations, localizedAssetUrls[[views[i]]]);
}
localeCompare.writeTranslationsToJS(outputDir, views[i], viewTranslations);
}
});

View file

@ -3,6 +3,9 @@
// -----------------------------------------------------------------------------
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var po2icu = require('po2icu');
var Helpers = {};
@ -62,4 +65,75 @@ Helpers.getMD5Map = function (ICUIdMap) {
return md5Map;
};
/**
* Grabs the translated strings from the po files for the given language and strings
* @param {str} lang iso code of the language to use
* @param {object} idsWithICU key: '<viewName>-<react-intl string id>'.
* value: english strings for translation
* @param {object} md5WithIds key: md5 hash of the english strings for translation.
* value: '<viewName>-<react-intl string id>'
* @return {object} translations sub-objects by view containing:
* key: '<react-intl string id>'
* value: translated version of string
*/
Helpers.getTranslationsForLanguage = function (lang, idsWithICU, md5WithIds) {
var poUiDir = path.resolve(__dirname, '../../node_modules/scratchr2_translations/ui');
var jsFile = path.resolve(poUiDir, lang + '/LC_MESSAGES/djangojs.po');
var pyFile = path.resolve(poUiDir, lang + '/LC_MESSAGES/django.po');
var translations = {};
try {
fs.accessSync(jsFile, fs.R_OK);
var jsTranslations = po2icu.poFileToICUSync(lang, jsFile);
translations = Helpers.mergeNewTranslations(translations, jsTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
try {
fs.accessSync(pyFile, fs.R_OK);
var pyTranslations = po2icu.poFileToICUSync(lang, pyFile);
translations = Helpers.mergeNewTranslations(translations, pyTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
var translationsByView = {};
for (var id in translations) {
var ids = id.split('-'); // [viewName, stringId]
var viewName = ids[0];
var stringId = ids[1];
if (!translationsByView.hasOwnProperty(viewName)) {
translationsByView[viewName] = {};
}
if (!translationsByView[viewName].hasOwnProperty(lang)) {
translationsByView[viewName][lang] = {};
}
translationsByView[viewName][lang][stringId] = translations[id];
}
return translationsByView;
};
Helpers.writeTranslationsToJS = function (outputDir, viewName, translationObject) {
var objectString = JSON.stringify(translationObject);
var fileString = 'window._messages = ' + objectString + ';';
fs.writeFileSync(outputDir + '/' + viewName + '.intl.js', fileString);
};
Helpers.getIdsForView = function (viewName, viewFile, localeObject, idsWithICU, icuWithIds) {
var ids = JSON.parse(fs.readFileSync(viewFile, 'utf8'));
localeObject[viewName] = {
en: ids
};
for (var id in ids) {
idsWithICU[viewName + '-' + id] = ids[id];
icuWithIds[ids[id]] = viewName + '-' + id; // add viewName to identifier for later
}
};
module.exports = Helpers;

View file

@ -0,0 +1,52 @@
{
"en": {
"cards.starterLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/Scratch2Cards.pdf",
"cards.nameLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/AnimateYourNameCards.pdf",
"cards.pongLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/PongCards.pdf",
"cards.storyLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/StoryCards.pdf",
"cards.danceLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/DanceCards.pdf",
"cards.hideLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/Hide-and-Seek-Cards.pdf"
},
"ar": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ar/Scratch2Cards.pdf"
},
"ca": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ca/Scratch2Cards.pdf"
},
"cs": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/cs/Scratch2Cards.pdf"
},
"de": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/de/Scratch2Cards.pdf"
},
"es": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/es/Scratch2Cards.pdf"
},
"fr": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/fr/Scratch2Cards.pdf"
},
"hr": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/hr/Scratch2Cards.pdf"
},
"it": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/it/Scratch2Cards.pdf"
},
"ja": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ja/Scratch2Cards.pdf"
},
"ja-hr": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ja-hr/Scratch2Cards.pdf"
},
"ko": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ko/Scratch2Cards.pdf"
},
"nl": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/nl/Scratch2Cards.pdf"
},
"pt-br": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/pt-br/Scratch2Cards.pdf"
},
"sl": {
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/sl/Scratch2Cards.pdf"
}
}

View file

@ -33,6 +33,7 @@
"cat": "Meow",
"nl": "Nederlands",
"nb": "Norsk Bokmål",
"nn": "Norsk Nynorsk",
"uz": "Oʻzbekcha",
"pl": "Polski",
"pt": "Português",

View file

@ -21,14 +21,14 @@
},
"homepage": "https://github.com/llk/scratch-www#readme",
"dependencies": {
"bunyan": "1.5.1",
"compression": "1.5.2",
"express": "4.13.3",
"bunyan": "1.7.1",
"compression": "1.6.1",
"express": "4.13.4",
"express-http-proxy": "0.6.0",
"lodash.defaults": "3.1.2",
"mustache": "2.1.3",
"newrelic": "1.22.1",
"raven": "0.8.1"
"lodash.defaults": "4.0.1",
"mustache": "2.2.1",
"newrelic": "1.25.4",
"raven": "0.10.0"
},
"devDependencies": {
"autoprefixer-loader": "2.1.0",
@ -44,6 +44,7 @@
"json-loader": "0.5.2",
"json2po-stream": "1.0.3",
"jsx-loader": "0.13.2",
"keymirror": "0.1.1",
"lodash.clone": "3.0.3",
"lodash.defaultsdeep": "3.10.0",
"lodash.merge": "3.3.2",
@ -51,27 +52,26 @@
"lodash.range": "3.0.1",
"minilog": "2.0.8",
"node-sass": "3.3.3",
"pako": "0.2.8",
"po2icu": "git://github.com/LLK/po2icu.git#develop",
"react": "0.14.0",
"react-addons-test-utils": "0.14.0",
"react-dom": "0.14.0",
"react-intl": "2.0.0-beta-1",
"react-addons-test-utils": "0.14.7",
"react-modal": "0.6.1",
"react-onclickoutside": "4.1.1",
"react-redux": "4.4.0",
"react-slick": "0.9.2",
"redux-thunk": "2.0.1",
"routes-to-nginx-conf": "0.0.4",
"sass-lint": "1.3.2",
"sass-lint": "1.5.1",
"sass-loader": "2.0.1",
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
"slick-carousel": "1.5.8",
"source-map-support": "0.3.2",
"style-loader": "0.12.3",
"tap": "2.0.0",
"tape": "4.2.0",
"url-loader": "0.5.6",
"watch": "0.16.0",
"webpack": "1.12.2",
"webpack": "1.12.14",
"webpack-dev-middleware": "1.2.0",
"xhr": "2.0.4"
"xhr": "2.2.0"
}
}

View file

@ -22,5 +22,8 @@ module.exports = {
sentry_dsn: process.env.CLIENT_SENTRY_DSN || '',
// Use minified JS libraries
min: (process.env.NODE_ENV === 'production') ? '.min' : ''
min: (process.env.NODE_ENV === 'production') ? '.min' : '',
// Redux likes to have this
NODE_ENV: process.env.NODE_ENV
};

View file

@ -24,6 +24,21 @@
"view": "credits",
"title": "Credits"
},
{
"pattern": "/info/cards",
"view": "cards",
"title": "Cards"
},
{
"pattern": "/info/communityblocks-interviews",
"view": "communityblocks-interviews",
"title": "Community Blocks Beta Tester Interviews"
},
{
"pattern": "/jobs",
"view": "jobs",
"title": "Jobs"
},
{
"pattern": "/wedo",
"view": "wedo2",

View file

@ -41,13 +41,12 @@
</head>
<body>
<div id="navigation"></div>
<div id="view"></div>
<div id="footer"></div>
<div id="app"></div>
<!-- Scripts -->
<script src="/js/lib/react{{min}}.js"></script>
<script src="/js/lib/react-dom{{min}}.js"></script>
<script src="/js/lib/redux{{min}}.js"></script>
<script src="/js/lib/react-intl-with-locales{{min}}.js"></script>
<script src="/js/lib/raven.min.js"></script>
@ -56,7 +55,9 @@
<!-- Error logging (Sentry) -->
<script>
Raven.config('{{&sentry_dsn}}').install()
if ('{{&sentry_dsn}}' !== '') {
Raven.config('{{&sentry_dsn}}').install();
}
</script>
<!-- Analytics (GA) -->

View file

@ -1,15 +1,12 @@
var React = require('react');
var connect = require('react-redux').connect;
var Button = require('../../components/forms/button.jsx');
var Session = require('../../mixins/session.jsx');
require('./adminpanel.scss');
var AdminPanel = React.createClass({
type: 'AdminPanel',
mixins: [
Session
],
getInitialState: function () {
return {
showPanel: false
@ -22,8 +19,8 @@ var AdminPanel = React.createClass({
render: function () {
// make sure user is present before checking if they're an admin. Don't show anything if user not an admin.
var showAdmin = false;
if (this.state.session.user) {
showAdmin = this.state.session.permissions.admin;
if (this.props.session.user) {
showAdmin = this.props.session.permissions.admin;
}
if (!showAdmin) return false;
@ -78,4 +75,12 @@ var AdminPanel = React.createClass({
}
});
module.exports = AdminPanel;
var mapStateToProps = function (state) {
return {
session: state.session
};
};
var ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel);
module.exports = ConnectedAdminPanel;

View file

@ -51,6 +51,10 @@
margin: 0;
list-style: none;
}
}
}
.button-row {
display: flex;
font-size: small;
@ -67,6 +71,3 @@
}
}
}
}
}
}

View file

@ -13,7 +13,8 @@ $navigation-height: 50px;
text-align: center;
line-height: $navigation-height;
&, a {
&,
a {
color: $ui-white;
}

View file

@ -11,6 +11,7 @@ $base-bg: $ui-white;
//4 columns
@media only screen and (max-width: $mobile - 1) {
width: $cols4;
.box-header {
h4 {
font-size: .9rem;
@ -21,9 +22,10 @@ $base-bg: $ui-white;
//6 columns
@media only screen and (min-width: $mobile) and (max-width: $tablet - 1) {
width: $cols6;
.box-header {
h4 {
font-size: 1.0rem;
font-size: 1rem;
}
}
}
@ -31,6 +33,7 @@ $base-bg: $ui-white;
//8 columns
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
width: $cols8;
.box-header {
h4 {
font-size: 1.1rem;
@ -41,6 +44,7 @@ $base-bg: $ui-white;
//12 columns
@media only screen and (min-width: $desktop) {
width: $cols12;
.box-header {
h4 {
font-size: 1.1rem;

View file

@ -1,6 +1,9 @@
var connect = require('react-redux').connect;
var omit = require('lodash.omit');
var React = require('react');
var actions = require('../../redux/actions.js');
var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
@ -10,12 +13,8 @@ Modal.setAppElement(document.getElementById('view'));
var Intro = React.createClass({
type: 'Intro',
propTypes: {
projectCount: React.PropTypes.number
},
getDefaultProps: function () {
return {
projectCount: 10569070,
messages: {
'intro.aboutScratch': 'ABOUT SCRATCH',
'intro.forEducators': 'FOR EDUCATORS',
@ -23,8 +22,11 @@ var Intro = React.createClass({
'intro.joinScratch': 'JOIN SCRATCH',
'intro.seeExamples': 'SEE EXAMPLES',
'intro.tagLine': 'Create stories, games, and animations<br /> Share with others around the world',
'intro.tryItOut': 'TRY IT OUT'
}
'intro.tryItOut': 'TRY IT OUT',
'intro.description': 'A creative learning community with <span class="project-count"> ' +
'over 13 million </span>projects shared'
},
session: {}
};
},
getInitialState: function () {
@ -46,7 +48,7 @@ var Intro = React.createClass({
this.setState({'registrationOpen': false});
},
completeRegistration: function () {
window.refreshSession();
this.props.dispatch(actions.refreshSession());
this.closeRegistration();
},
render: function () {
@ -109,11 +111,8 @@ var Intro = React.createClass({
onRequestClose={this.closeRegistration}
onRegistrationDone={this.completeRegistration} />
</div>
<div className="description">
A creative learning community with
<span className="project-count"> {this.props.projectCount.toLocaleString()} </span>
projects shared
</div>
<div className="description"
dangerouslySetInnerHTML={{__html: this.props.messages['intro.description']}}></div>
<div className="links">
<a href="/about/">
{this.props.messages['intro.aboutScratch']}
@ -145,4 +144,12 @@ var Intro = React.createClass({
}
});
module.exports = Intro;
var mapStateToProps = function (state) {
return {
session: state.session
};
};
var ConnectedIntro = connect(mapStateToProps)(Intro);
module.exports = ConnectedIntro;

View file

@ -57,14 +57,6 @@
display: none;
}
&:hover .costume-1 {
display: none;
}
&:hover .costume-2 {
display: block;
}
.circle {
display: block;
top: 15px;
@ -98,55 +90,77 @@
}
&.sprite-1 .circle {
&.sprite-1 {
.circle {
background-color: $splash-green;
}
&.sprite-2 .circle {
background-color: $splash-pink;
}
&.sprite-3 .circle {
background-color: $splash-blue;
}
&:hover.sprite-1 .circle {
box-shadow: 0 0 10px 2px $splash-green;
}
&:hover.sprite-2 .circle {
box-shadow: 0 0 10px 2px $splash-pink;
}
&:hover.sprite-3 .circle {
box-shadow: 0 0 10px 2px $splash-blue;
}
&.sprite-1 .text {
.text {
top: 60px;
left: 50px;
color: $splash-green;
}
}
&.sprite-2 .text {
&.sprite-2 {
.circle {
background-color: $splash-pink;
}
.text {
top: 77px;
left: 50px;
color: $splash-pink;
}
}
&.sprite-3 .text {
&.sprite-3 {
.circle {
background-color: $splash-blue;
}
.text {
top: 37px;
left: 45px;
color: $splash-blue;
}
&.sprite-3 .subtext {
.subtext {
top: 63px;
left: 60px;
color: $ui-white;
}
}
&:hover {
.costume-1 {
display: none;
}
.costume-2 {
display: block;
}
&.sprite-1 {
.circle {
box-shadow: 0 0 10px 2px $splash-green;
}
}
&.sprite-2 {
.circle {
box-shadow: 0 0 10px 2px $splash-pink;
}
}
&.sprite-3 {
.circle {
box-shadow: 0 0 10px 2px $splash-blue;
}
}
}
}
.description {
margin-top: 10px;
font-size: 17px;

View file

@ -6,8 +6,6 @@ var jar = require('../../lib/jar.js');
var languages = require('../../../languages.json');
var Select = require('../forms/select.jsx');
require('./languagechooser.scss');
/**
* Footer dropdown menu that allows one to change their language.
*/

View file

@ -1,3 +0,0 @@
.language-chooser {
}

View file

@ -40,30 +40,30 @@ var Login = React.createClass({
return (
<div className="login">
<form onSubmit={this.handleSubmit}>
<label htmlFor="username">
<label htmlFor="username" key="usernameLabel">
<FormattedMessage
id='general.username'
defaultMessage={'Username'} />
</label>
<Input type="text" ref="username" name="username" maxLength="30" />
<label htmlFor="password">
<Input type="text" ref="username" name="username" maxLength="30" key="usernameInput" />
<label htmlFor="password" key="passwordLabel">
<FormattedMessage
id='general.password'
defaultMessage={'Password'} />
</label>
<Input type="password" ref="password" name="password" />
<Input type="password" ref="password" name="password" key="passwordInput" />
{this.state.waiting ? [
<Button className="submit-button white" type="submit" disabled="disabled">
<Button className="submit-button white" type="submit" disabled="disabled" key="submitButton">
<Spinner />
</Button>
] : [
<Button className="submit-button white" type="submit">
<Button className="submit-button white" type="submit" key="submitButton">
<FormattedMessage
id='general.signIn'
defaultMessage={'Sign in'} />
</Button>
]}
<a className="right" href="/accounts/password_reset/">
<a className="right" href="/accounts/password_reset/" key="passwordResetLink">
<FormattedMessage
id='login.forgotPassword'
defaultMessage={'Forgot Password?'} />

View file

@ -20,11 +20,12 @@
a {
margin-top: 15px;
}
a:hover {
&:hover {
background-color: transparent;
}
}
.error {
border: 1px solid $active-dark-gray;

View file

@ -0,0 +1,246 @@
var React = require('react');
require('./microworld.scss');
var Box = require('../../components/box/box.jsx');
var Carousel = require('../../components/carousel/carousel.jsx');
var Modal = require('../../components/modal/modal.jsx');
var NestedCarousel = require('../../components/nestedcarousel/nestedcarousel.jsx');
var Microworld = React.createClass({
type: 'Microworld',
propTypes: {
microworldData: React.PropTypes.node.isRequired
},
markVideoOpen: function (key) {
{/* When a video is clicked, mark it as an open video, so the video Modal will open.
Key is the number of the video, so distinguish between different videos on the page */}
var videoOpenArr = this.state.videoOpen;
videoOpenArr[key] = true;
this.setState({videoOpen: videoOpenArr});
},
markVideoClosed: function (key) {
{/* When a video's x is clicked, mark it as closed, so the video Modal will disappear.
Key is the number of the video, so distinguish between different videos on the page */}
var videoOpenArr = this.state.videoOpen;
videoOpenArr[key] = false;
this.setState({videoOpen: videoOpenArr});
},
getInitialState: function () {
return {
videoOpen: {}
};
},
renderVideos: function () {
var videos = this.props.microworldData.videos;
if (!videos || videos.length <= 0) {
return null;
}
return (
<div className="videos-section section">
<h1>Get Inspired...</h1>
<div className="videos-container">
<div className="videos">
{videos.map(this.renderVideo)}
</div>
</div>
</div>
);
},
renderVideo: function (video, key) {
var frameProps = {
width: 570,
height: 357,
padding: 15
};
return (
<div>
<div className="video">
<div className="play-button" onClick={this.markVideoOpen.bind(this, key)}>
</div>
<img src={video.image} />
</div>
<Modal
className="video-modal"
isOpen={this.state.videoOpen[key]}
onRequestClose={this.markVideoClosed.bind(this, key)}
style={{content:frameProps}}>
<iframe src={video.link} width="560" height="315" frameBorder="0"
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</Modal>
</div>
);
},
renderEditorWindow: function () {
var projectId = this.props.microworldData.microworld_project_id;
if (!projectId) {
return null;
}
return (
<div className="editor section">
<h1>Start Creating!</h1>
<a href={'//scratch.mit.edu/projects/'+ projectId +'/#editor'}>
<img className="scratch-link" src="/images/scratch-og.png"></img>
</a>
<iframe src={'//scratch.mit.edu/projects/embed-editor/' + projectId + '/?isMicroworld=true'}
frameBorder="0"> </iframe>
{this.renderTips()}
</div>
);
},
renderTips: function () {
var tips = this.props.microworldData.tips;
if (!tips || tips.length <= 0) {
return null;
}
return (
<div className="box nestedcarousel">
<div className="box-header">
</div>
<div className="box-content">
<NestedCarousel items={tips} settings={{slidesToShow:1,slidesToScroll:1}}/>
</div>
</div>
);
},
renderStarterProject: function () {
var starterProjects = this.props.microworldData.starter_projects;
if (!starterProjects || starterProjects.length <= 0){
return null;
}
return (
<div className="project-ideas">
<h1>Check out ideas for more projects</h1>
<Box
title="More Starter Projects"
key="starter_projects">
<Carousel items={starterProjects} />
</Box>
</div>
);
},
renderProjectIdeasBox: function () {
var communityProjects = this.props.microworldData.community_projects;
if (!communityProjects || communityProjects.size <= 0) {
return null;
}
var featured = communityProjects.featured_projects;
var all = communityProjects.newest_projects;
var rows = [];
if (featured && featured.length > 0){
rows.push(
<Box
title="Featured Community Projects"
key="community_featured_projects">
<Carousel items={featured} />
</Box>
);
}
if (all && all.length > 0) {
rows.push(
<Box
title="All Community Projects"
key="community_all_projects">
<Carousel items={all} />
</Box>
);
}
if (rows.length <= 0) {
return null;
}
return (
<div className="project-ideas">
<h1>Get inspiration from other projects</h1>
{rows}
</div>
);
},
renderForum: function () {
if (!this.props.microworldData.show_forum) {
return null;
}
return (
<div className="forum">
<h1>Chat with others!</h1>
<img src="/images/forum-image.png"/>
</div>
);
},
renderDesignStudio: function () {
var designChallenge = this.props.microworldData.design_challenge;
if (!designChallenge) {
return null;
}
if (designChallenge.studio_id) {
var studioHref = 'https://scratch.mit.edu//studios/' + designChallenge.studio_id + '/';
}
if (designChallenge.project_id) {
return (
<div className="side-by-side section">
<h1>Join our Design Challenge!</h1>
<div className="design-studio">
<iframe src={'https://scratch.mit.edu/projects/' + designChallenge.project_id +
'/#fullscreen'} frameBorder="0"> </iframe>
</div>
<div className="design-studio-projects">
<Box title="Examples"
key="scratch_design_studio"
moreTitle={studioHref ? 'Visit the studio' : null}
moreHref={studioHref ? studioHref : null}>
{/* The two carousels are used to show two rows of projects, one above the
other. This should be probably be changed, to allow better scrolling. */}
<Carousel settings={{slidesToShow:2,slidesToScroll:2}}
items={this.props.microworldData.design_challenge.studio1} />
<Carousel settings={{slidesToShow:2,slidesToScroll:2}}
items={this.props.microworldData.design_challenge.studio2} />
</Box>
</div>
</div>
);
} else {
return (
<div className="section">
<h1>Join our Design Challenge!</h1>
<Box
title="design Challenge Projects"
key="scratch_design_studio"
moreTitle={studioHref ? 'Visit the studio' : null}
moreHref={studioHref ? studioHref : null}>
<Carousel items={this.props.microworldData.design_challenge.studio1.concat(
this.props.microworldData.design_challenge.studio2)} />
</Box>
</div>
);
}
},
render: function () {
return (
<div className="inner">
<div className="top-banner section">
<h1>{this.props.microworldData.title}</h1>
<p>{this.props.microworldData.description.join(' ')}</p>
</div>
{this.renderVideos()}
<div className="content">
{this.renderEditorWindow()}
{this.renderStarterProject()}
{this.renderDesignStudio()}
{this.renderProjectIdeasBox()}
{this.renderForum()}
</div>
</div>
);
}
});
module.exports = Microworld;

View file

@ -0,0 +1,185 @@
@import "../../colors";
@import "../../frameless";
$base-bg: $ui-white;
#view {
background-color: $base-bg;
padding: 0;
// To be integrated into the Global Typography standards
h3,
p {
font-weight: 300;
}
p {
line-height: 2em;
}
h1 {
margin: 0 auto;
padding: 5px 10%;
text-align: center;
color: $type-gray;
}
.top-banner,
.videos-section,
.section {
padding: 30px 0;
width: 100%;
h1,
p {
margin: 0 auto;
padding: 5px 10%;
text-align: center;
color: $type-gray;
}
}
.videos-container {
display: flex;
margin: 0 auto;
justify-content: center;
flex-wrap: wrap;
align-items: center;
.videos {
display: inline-flex;
justify-content: center;
flex-wrap: wrap;
}
.video {
position: relative;
margin: 10px;
border-radius: 7px;
background-color: $active-gray;
padding: 2px;
max-width: 290px;
}
img {
margin: 10px 10px 5px;
border-radius: 5px;
width: calc(100% - 20px);
height: 179px;
}
.play-button {
display: block;
top: calc(50% - 25px);
left: calc(50% - 35px);
opacity: .8;
border: 5px solid $ui-border;
border-radius: 20px;
background-color: $type-gray;
width: 70px;
height: 50px;
&,
&:after {
position: absolute;
margin: 0;
cursor: pointer;
padding: 0;
}
&:after {
$play-arrow: rgba(255, 255, 255, 0);
top: 37px;
left: 28px;
margin-top: -30px;
border: solid transparent;
border-width: 18px;
border-color: $play-arrow;
border-left-color: $ui-white;
width: 0;
height: 0;
content: " ";
pointer-events: none;
}
}
}
.content {
img {
display: block;
margin-right: auto;
margin-left: auto;
}
.box,
iframe {
display: block;
margin-right: auto;
margin-left: auto;
border: 0;
padding-top: 25px;
padding-bottom: 15px;
}
iframe {
height: 600px;
}
.editor {
position: relative;
iframe {
width: 100%;
}
.scratch-link {
position: absolute;
right: 3%;
width: 10%;
}
}
.side-by-side {
margin-right: auto;
margin-left: auto;
height: 520px;
.design-studio-projects,
.design-studio {
display: inline-block;
width: 45%;
height: 500px;
}
.design-studio-projects {
float: right;
}
.design-studio {
float: left;
iframe {
margin-top: 60px;
width: 200%;
-webkit-transform: scale(.5);
-webkit-transform-origin: top left;
-moz-transform: scale(.5);
}
}
}
}
.box-content {
.nestedcarousel {
text-align: center;
.thumbnail {
display: inline-block;
margin: 0 50px;
}
}
}
}

View file

@ -1,6 +1,6 @@
@import "../../colors";
&.ReactModal__Content {
.ReactModal__Content {
iframe {
border: 0;
}

View file

@ -1,10 +1,12 @@
var classNames = require('classnames');
var connect = require('react-redux').connect;
var React = require('react');
var ReactIntl = require('react-intl');
var defineMessages = ReactIntl.defineMessages;
var FormattedMessage = ReactIntl.FormattedMessage;
var injectIntl = ReactIntl.injectIntl;
var actions = require('../../redux/actions.js');
var Api = require('../../mixins/api.jsx');
var Avatar = require('../avatar/avatar.jsx');
var Dropdown = require('./dropdown.jsx');
@ -13,32 +15,15 @@ var log = require('../../lib/log.js');
var Login = require('../login/login.jsx');
var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
var Session = require('../../mixins/session.jsx');
require('./navigation.scss');
Modal.setAppElement(document.getElementById('view'));
var defaultMessages = defineMessages({
messages: {
id: 'general.messages',
defaultMessage: 'Messages'
},
myStuff: {
id: 'general.myStuff',
defaultMessage: 'My Stuff'
},
search: {
id: 'general.search',
defaultMessage: 'Search'
}
});
var Navigation = React.createClass({
type: 'Navigation',
mixins: [
Api,
Session
Api
],
getInitialState: function () {
return {
@ -51,20 +36,25 @@ var Navigation = React.createClass({
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
};
},
getDefaultProps: function () {
return {
session: {}
};
},
componentDidMount: function () {
if (this.state.session.user) {
if (this.props.session.user) {
this.getMessageCount();
var intervalId = setInterval(this.getMessageCount, 120000); // check for new messages every 2 mins.
this.setState({'messageCountIntervalId': intervalId});
}
},
componentDidUpdate: function (prevProps, prevState) {
if (prevState.session.user != this.state.session.user) {
componentDidUpdate: function (prevProps) {
if (prevProps.session.user != this.props.session.user) {
this.setState({
'loginOpen': false,
'accountNavOpen': false
});
if (this.state.session.user) {
if (this.props.session.user) {
this.getMessageCount();
var intervalId = setInterval(this.getMessageCount, 120000);
this.setState({'messageCountIntervalId': intervalId});
@ -89,13 +79,13 @@ var Navigation = React.createClass({
}
},
getProfileUrl: function () {
if (!this.state.session.user) return;
return '/users/' + this.state.session.user.username + '/';
if (!this.props.session.user) return;
return '/users/' + this.props.session.user.username + '/';
},
getMessageCount: function () {
this.api({
method: 'get',
uri: '/users/' + this.state.session.user.username + '/messages/count'
uri: '/users/' + this.props.session.user.username + '/messages/count'
}, function (err, body) {
if (err) return this.setState({'unreadMessageCount': 0});
if (body) {
@ -141,7 +131,7 @@ var Navigation = React.createClass({
this.showCanceledDeletion();
}
}.bind(this));
window.refreshSession();
this.props.dispatch(actions.refreshSession());
}
}
// JS error already logged by api mixin
@ -158,7 +148,7 @@ var Navigation = React.createClass({
}, function (err) {
if (err) log.error(err);
this.closeLogin();
window.refreshSession();
this.props.dispatch(actions.refreshSession());
}.bind(this));
},
handleAccountNavClick: function (e) {
@ -178,57 +168,48 @@ var Navigation = React.createClass({
this.setState({'registrationOpen': false});
},
completeRegistration: function () {
window.refreshSession();
this.props.dispatch(actions.refreshSession());
this.closeRegistration();
},
render: function () {
var classes = classNames({
'inner': true,
'logged-in': this.state.session.user
'logged-in': this.props.session.user
});
var messageClasses = classNames({
'messageCount': true,
'message-count': true,
'show': this.state.unreadMessageCount > 0
});
var formatMessage = this.props.intl.formatMessage;
var createLink = this.props.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
return (
<div className={classes}>
<ul>
<li className="logo"><a href="/" aria-label="Scratch"></a></li>
<li className="link create">
<a href="/projects/editor">
<FormattedMessage
id="general.create"
defaultMessage={'Create'} />
<a href={createLink}>
<FormattedMessage id="general.create" />
</a>
</li>
<li className="link explore">
<a href="/explore?date=this_month">
<FormattedMessage
id="general.explore"
defaultMessage={'Explore'} />
<FormattedMessage id="general.explore" />
</a>
</li>
<li className="link discuss">
<a href="/discuss">
<FormattedMessage
id="general.discuss"
defaultMessage={'Discuss'} />
<FormattedMessage id="general.discuss" />
</a>
</li>
<li className="link about">
<a href="/about">
<FormattedMessage
id="general.about"
defaultMessage={'About'} />
<FormattedMessage id="general.about" />
</a>
</li>
<li className="link help">
<a href="/help">
<FormattedMessage
id="general.help"
defaultMessage={'Help'} />
<FormattedMessage id="general.help" />
</a>
</li>
@ -236,35 +217,35 @@ var Navigation = React.createClass({
<form action="/search/google_results" method="get">
<Input type="submit" value="" />
<Input type="text"
aria-label={formatMessage(defaultMessages.search)}
placeholder={formatMessage(defaultMessages.search)}
aria-label={formatMessage({id: 'general.search'})}
placeholder={formatMessage({id: 'general.search'})}
name="q" />
<Input type="hidden" name="date" value="anytime" />
<Input type="hidden" name="sort_by" value="datetime_shared" />
</form>
</li>
{this.state.session.user ? [
{this.props.session.user ? [
<li className="link right messages" key="messages">
<a
href="/messages/"
title={formatMessage(defaultMessages.messages)}>
title={formatMessage({id: 'general.messages'})}>
<span className={messageClasses}>{this.state.unreadMessageCount}</span>
<FormattedMessage {...defaultMessages.messages} />
<FormattedMessage id="general.messages" />
</a>
</li>,
<li className="link right mystuff" key="mystuff">
<a
href="/mystuff/"
title={formatMessage(defaultMessages.myStuff)}>
title={formatMessage({id: 'general.myStuff'})}>
<FormattedMessage {...defaultMessages.myStuff} />
<FormattedMessage id="general.myStuff" />
</a>
</li>,
<li className="link right account-nav" key="account-nav">
<a className="userInfo" href="#" onClick={this.handleAccountNavClick}>
<Avatar src={this.state.session.user.thumbnailUrl} alt="" />
{this.state.session.user.username}
<a className="user-info" href="#" onClick={this.handleAccountNavClick}>
<Avatar src={this.props.session.user.thumbnailUrl} alt="" />
{this.props.session.user.username}
</a>
<Dropdown
as="ul"
@ -272,37 +253,36 @@ var Navigation = React.createClass({
onRequestClose={this.closeAccountNav}>
<li>
<a href={this.getProfileUrl()}>
<FormattedMessage
id='general.profile'
defaultMessage={'Profile'} />
<FormattedMessage id="general.profile" />
</a>
</li>
<li>
<a href="/mystuff/">
<FormattedMessage {...defaultMessages.myStuff} />
<FormattedMessage id="general.myStuff" />
</a>
</li>
{this.state.session.permissions.educator ? [
{this.props.session.permissions.educator ? [
<li>
<a href="/educators/classes/">
<FormattedMessage
id='general.myClasses'
defaultMessage={'My Classes'} />
<FormattedMessage id="general.myClasses" />
</a>
</li>
] : []}
{this.props.session.permissions.student ? [
<li>
<a href={'/classes/' + this.props.session.user.classroomId + '/'}>
<FormattedMessage id="general.myClass" />
</a>
</li>
] : []}
<li>
<a href="/accounts/settings/">
<FormattedMessage
id='general.accountSettings'
defaultMessage={'Account settings'} />
<FormattedMessage id="general.accountSettings" />
</a>
</li>
<li className="divider">
<a href="#" onClick={this.handleLogOut}>
<FormattedMessage
id='navigation.signOut'
defaultMessage={'Sign out'} />
<FormattedMessage id="navigation.signOut" />
</a>
</li>
</Dropdown>
@ -310,9 +290,7 @@ var Navigation = React.createClass({
] : [
<li className="link right join" key="join">
<a href="#" onClick={this.handleJoinClick}>
<FormattedMessage
id='general.joinScratch'
defaultMessage={'Join Scratch'} />
<FormattedMessage id="general.joinScratch" />
</a>
</li>,
<Registration
@ -324,15 +302,15 @@ var Navigation = React.createClass({
<a
href="#"
onClick={this.handleLoginClick}
className="ignore-react-onclickoutside">
<FormattedMessage
id='general.signIn'
defaultMessage={'Sign In'} />
className="ignore-react-onclickoutside"
key="login-link">
<FormattedMessage id="general.signIn" />
</a>
<Dropdown
className="login-dropdown with-arrow"
isOpen={this.state.loginOpen}
onRequestClose={this.closeLogin}>
onRequestClose={this.closeLogin}
key="login-dropdown">
<Login
onLogIn={this.handleLogIn}
error={this.state.loginError} />
@ -356,4 +334,12 @@ var Navigation = React.createClass({
}
});
module.exports = injectIntl(Navigation);
var mapStateToProps = function (state) {
return {
session: state.session
};
};
var ConnectedNavigation = connect(mapStateToProps)(Navigation);
module.exports = injectIntl(ConnectedNavigation);

View file

@ -51,6 +51,7 @@
vertical-align: bottom;
}
}
}
.logo {
margin-right: 10px;
@ -86,13 +87,14 @@
color: $type-white;
font-size: .85rem;
font-weight: bold;
}
> a:hover {
&:hover {
background-color: $active-gray;
}
}
}
.search {
margin: 0 20px;
border-right: 0;
@ -114,9 +116,8 @@
border: 0;
background-color: $active-gray;
height: 14px;
}
input[type=submit] {
&[type=submit] {
position: absolute;
background-color: transparent;
@ -129,7 +130,7 @@
height: 40px;
}
input[type=text] {
&[type=text] {
transition: .15s ease background-color;
padding: 0;
padding-right: 10px;
@ -148,12 +149,13 @@
transition: .15s ease background-color;
background-color: $active-dark-gray;
}
}
.ie9 input[type=text] {
.ie9 & {
width: 70px;
}
}
}
}
.right {
float: right;
@ -164,10 +166,12 @@
float: none;
}
a:hover {
a {
&:hover {
background-color: $active-gray;
}
}
}
.messages,
.mystuff {
@ -181,19 +185,20 @@
overflow: hidden;
text-indent: 50px;
white-space: nowrap;
}
> a:hover {
&:hover {
background-size: 50%;
}
}
}
.messages {
> a {
background-image: url("/images/nav-notifications.png");
}
.messageCount {
.message-count {
display: none;
&.show {
@ -224,7 +229,7 @@
}
.account-nav {
.userInfo {
.user-info {
padding-top: 14px;
max-width: 260px;
}
@ -266,4 +271,3 @@
}
}
}
}

View file

@ -0,0 +1,42 @@
[
{
"title":"Start Dancing",
"thumbnails":[
{
"type":"tip",
"title":"First, select a sprite",
"thumbnailUrl":"/images/microworlds/hiphop/dancer-sprite.png"
},
{
"type":"tip",
"title":"Then, try this script",
"thumbnailUrl":"/images/microworlds/hiphop/switch-wait.gif"
},
{
"type":"tip",
"title":"Start it!",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Repeat the Dance",
"thumbnails":[
{
"type":"tip",
"title":"Add another \"wait\"",
"thumbnailUrl":"/images/microworlds/hiphop/add-wait.gif"
},
{
"type":"tip",
"title":"Drag this block over",
"thumbnailUrl":"/images/microworlds/hiphop/add-repeat.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
}
]

View file

@ -0,0 +1,75 @@
var classNames = require('classnames');
var defaults = require('lodash.defaults');
var React = require('react');
var Slider = require('react-slick');
var Thumbnail = require('../thumbnail/thumbnail.jsx');
require('slick-carousel/slick/slick.scss');
require('slick-carousel/slick/slick-theme.scss');
require('./nestedcarousel.scss');
{/*
NestedCarousel is used to show a carousel, where each slide is composed of a few
thumbnails (for example, to show step-by-syep tips, where each stage has a few steps).
It creates the thumbnails without links.
Each slide has a title, and then a list of thumbnails, that will be shown together.
*/}
var NestedCarousel = React.createClass({
type: 'NestedCarousel',
propTypes: {
items: React.PropTypes.array
},
getDefaultProps: function () {
return {
items: require('./nestedcarousel.json')
};
},
render: function () {
var settings = this.props.settings || {};
defaults(settings, {
dots: true,
infinite: false,
lazyLoad: true,
slidesToShow: 1,
slidesToScroll: 1,
variableWidth: false
});
var arrows = this.props.items.length > settings.slidesToShow;
var classes = classNames(
'nestedcarousel',
'carousel',
this.props.className
);
var stages = [];
for (var i=0; i < this.props.items.length; i++) {
var items = this.props.items[i].thumbnails;
var thumbnails = [];
for (var j=0; j < items.length; j++) {
var item = items[j];
thumbnails.push(
<Thumbnail key={'inner_' + i + '_' + j}
title={item.title}
src={item.thumbnailUrl}
linkTitle = {false} />);
}
stages.push(
<div key={'outer_' + i}>
<h3>{this.props.items[i].title}</h3>
{thumbnails}
</div>);
}
return (
<Slider className={classes} arrows={arrows} {... settings}>
{stages}
</Slider>
);
}
});
module.exports = NestedCarousel;

View file

@ -0,0 +1,20 @@
@import "../../colors";
@import "../carousel/carousel.scss";
.nestedcarousel {
/* Overrides carousel's settings for extra padding */
.slick-slide {
padding: 0;
/* Add some padding under the title for each slide */
h3 {
padding-bottom: 10px;
}
/* Align to top. Important when one of the slides have
more than one line of text */
.thumbnail.project {
vertical-align: top;
}
}
}

View file

@ -46,10 +46,10 @@
color: $type-gray;
font-size: .85rem;
}
}
li:nth-child(even) {
&:nth-child(even) {
border-top: 1px solid $ui-border;
border-bottom: 1px solid $ui-border;
}
}
}

View file

@ -0,0 +1,25 @@
var React = require('react');
var Navigation = require('../navigation/navigation.jsx');
var Footer = require('../footer/footer.jsx');
var Page = React.createClass({
type: 'Page',
render: function () {
return (
<div className="page">
<div id="navigation">
<Navigation />
</div>
<div id="view">
{this.props.children}
</div>
<div id="footer">
<Footer />
</div>
</div>
);
}
});
module.exports = Page;

View file

@ -21,27 +21,33 @@
width: 15%;
height: 15%;
content: "";
-webkit-animation: circleFadeDelay 1.2s infinite ease-in-out both;
}
}
@for $i from 1 through 12 {
$rotation: 30deg * ($i - 1);
$delay: -1.3s + $i * .1;
.circle#{$i} {
transform: rotate($rotation);
-ms-transform: rotate($rotation);
-webkit-transform: rotate($rotation);
}
.circle#{$i}:before {
&:before {
animation-delay: $delay;
-webkit-animation-delay: $delay;
}
}
}
}
@keyframes circleFadeDelay {
0%, 39%, 100% { opacity: 0; }
40% { opacity: 1; }
0%,
39%,
100% {
opacity: 0;
}
40% {
opacity: 1;
}
}

View file

@ -37,7 +37,7 @@
&.description {
/* clear styling for info element */
border: none;
border: 0;
border-radius: none;
text-decoration: none;
@ -47,7 +47,7 @@
}
&:active {
border: none;
border: 0;
box-shadow: none;
background-color: transparent;
}

View file

@ -16,6 +16,7 @@ var Thumbnail = React.createClass({
type: 'project',
showLoves: false,
showRemixes: false,
linkTitle: true,
alt: ''
};
},
@ -55,13 +56,23 @@ var Thumbnail = React.createClass({
</div>
);
}
var imgElement,titleElement;
if (this.props.linkTitle) {
imgElement = <a className="thumbnail-image" href={this.props.href}>
<img src={this.props.src} alt={this.props.alt} />
</a>;
titleElement = <a href={this.props.href}>{this.props.title}</a>;
} else {
imgElement = <img src={this.props.src} />;
titleElement = this.props.title;
}
return (
<div className={classes} >
<a className="thumbnail-image" href={this.props.href}>
<img src={this.props.src} alt={this.props.alt} />
</a>
{imgElement}
<div className="thumbnail-title">
<a href={this.props.href}>{this.props.title}</a>
{titleElement}
</div>
{extra}
</div>

View file

@ -3,12 +3,12 @@
.thumbnail {
.thumbnail-image {
display: block;
}
.thumbnail-image img {
img {
margin-bottom: 2px;
border: 1px solid $ui-border;
}
}
$extras: ".thumbnail-creator, .thumbnail-loves, .thumbnail-remixes";
@ -26,7 +26,7 @@
.thumbnail-title {
margin-bottom: 1px;
font-size: .9230em;
font-size: .923em;
font-weight: 800;
a {
@ -58,13 +58,17 @@
}
}
.thumbnail-loves:before {
.thumbnail-loves {
&:before {
background-image: url("/svgs/love/love_type-gray.svg");
}
}
.thumbnail-remixes:before {
.thumbnail-remixes {
&:before {
background-image: url("/svgs/remix/remix_type-gray.svg");
}
}
&.project {
$project-width: 144px;

View file

@ -4,6 +4,7 @@
.box-content {
padding: 0;
}
.welcome-col {
display: inline-block;
margin: 10px 15px;
@ -33,26 +34,32 @@
height: 10px;
content: "";
}
&.blue {
#{$color-bars} {
background-color: $splash-blue;
}
a {
color: $splash-blue;
}
}
&.green {
#{$color-bars} {
background-color: $splash-green;
}
a {
color: $splash-green;
}
}
&.pink {
#{$color-bars} {
background-color: $splash-pink;
}
a {
color: $splash-pink;
}

View file

@ -1,55 +1,5 @@
var api = require('./mixins/api.jsx').api;
var jar = require('./lib/jar');
/**
* -----------------------------------------------------------------------------
* Session
* -----------------------------------------------------------------------------
*/
(function () {
window._session = {};
/**
* Binds the object to private session variable and dispatches a global
* "session" event.
*
* @param {object} Session object
*
* @return {void}
*/
window.updateSession = function (session) {
window._session = session;
var sessionEvent = new CustomEvent('session', session);
window.dispatchEvent(sessionEvent);
};
/**
* Gets a session object from the local proxy method. Calls "updateSession"
* once session has been returned from the proxy.
*
* @return {void}
*/
window.refreshSession = function () {
api({
host: '',
uri: '/session/'
}, function (err, body) {
if (err) return;
if (typeof body !== 'undefined') {
if (body.banned) {
return window.location = body.redirectUrl;
} else {
window.updateSession(body);
}
}
});
};
// Fetch session
window.refreshSession();
})();
/**
* -----------------------------------------------------------------------------

View file

@ -22,6 +22,7 @@
"general.legal": "Legal",
"general.learnMore": "Learn More",
"general.messages": "Messages",
"general.myClass": "My Class",
"general.myClasses": "My Classes",
"general.myStuff": "My Stuff",
"general.offlineEditor": "Offline Editor",

View file

@ -1,5 +1,6 @@
var cookie = require('cookie');
var xhr = require('xhr');
var pako = require('pako');
/**
* Module that handles coookie interactions.
@ -9,9 +10,39 @@ var xhr = require('xhr');
* set(name, value) synchronously sets the cookie
* use(name, uri, callback) can by sync or async, gets cookie from the uri if not there.
*/
var Jar = {};
var Jar = {
unsign: function (value, callback) {
// Return the usable content portion of a signed, compressed cookie generated by
// Django's signing module
// https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
if (!value) return callback('No value to unsign');
try {
var b64Data = value.split(':')[0];
var decompress = false;
if (b64Data[0] === '.') {
decompress = true;
b64Data = b64Data.substring(1);
}
Jar.get = function (name, callback) {
// Django makes its base64 strings url safe by replacing + and / with - and _ respectively
// using base64.urlsafe_b64encode
// https://docs.python.org/2/library/base64.html#base64.b64encode
b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; });
var strData = atob(b64Data);
if (decompress) {
var charData = strData.split('').map(function (c) { return c.charCodeAt(0); });
var binData = new Uint8Array(charData);
var data = pako.inflate(binData);
strData = String.fromCharCode.apply(null, new Uint16Array(data));
}
return callback(null, strData);
} catch (e) {
return callback(e);
}
},
get: function (name, callback) {
// Get cookie by name
var obj = cookie.parse(document.cookie) || {};
@ -22,9 +53,8 @@ Jar.get = function (name, callback) {
}
return obj[name];
};
Jar.use = function (name, uri, callback) {
},
use: function (name, uri, callback) {
// Attempt to get cookie
Jar.get(name, function (err, obj) {
if (typeof obj !== 'undefined') return callback(null, obj);
@ -37,13 +67,13 @@ Jar.use = function (name, uri, callback) {
Jar.get(name, callback);
});
});
};
Jar.set = function (name, value) {
},
set: function (name, value) {
var obj = cookie.serialize(name, value);
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
var path = '; path=/';
document.cookie = obj + expires + path;
}
};
module.exports = Jar;

View file

@ -1,12 +1,18 @@
var redux = require('redux');
var thunk = require('redux-thunk').default;
var ReactDOM = require('react-dom');
var StoreProvider = require('react-redux').Provider;
var ReactIntl = require('./intl.jsx');
var IntlProvider = ReactIntl.IntlProvider;
var IntlProvider = require('./intl.jsx').IntlProvider;
var actions = require('../redux/actions.js');
var reducer = require('../redux/reducer.js');
require('../main.scss');
var Navigation = require('../components/navigation/navigation.jsx');
var Footer = require('../components/footer/footer.jsx');
var store = redux.createStore(
reducer,
redux.applyMiddleware(thunk)
);
var render = function (jsx, element) {
// Get locale and messages from global namespace (see "init.js")
@ -21,37 +27,18 @@ var render = function (jsx, element) {
}
var messages = window._messages[locale];
// Render nav and footer for page.
var nav = ReactDOM.render(
<IntlProvider locale={locale} messages={messages}>
<Navigation />
</IntlProvider>,
document.getElementById('navigation')
);
var footer = ReactDOM.render(
<IntlProvider locale={locale} messages={messages}>
<Footer />
</IntlProvider>,
document.getElementById('footer')
);
// Provide list of rendered components
window._renderedComponents = window._renderedComponents || [];
window._renderedComponents.push(nav);
window._renderedComponents.push(footer);
// Render view component
var component = ReactDOM.render(
ReactDOM.render(
<StoreProvider store={store}>
<IntlProvider locale={locale} messages={messages}>
{jsx}
</IntlProvider>,
</IntlProvider>
</StoreProvider>,
element
);
window._renderedComponents.push(component);
// Get initial session
store.dispatch(actions.refreshSession());
};
module.exports = render;

View file

@ -32,30 +32,32 @@ h1 {
h4 {
line-height: 1.1rem;
font-size: 1.0rem;
font-size: 1rem;
}
p.legal {
p {
&.legal {
font-size: .8rem;
}
/* Links */
p {
a {
white-space: nowrap;
}
}
a:link,
a:visited,
a:active {
/* Links */
a {
&:link,
&:visited,
&:active {
text-decoration: none;
color: $link-blue;
}
a:hover {
&:hover {
text-decoration: underline;
}
}
/* Classes */
.empty {

View file

@ -4,8 +4,6 @@ var xhr = require('xhr');
var jar = require('../lib/jar.js');
var log = require('../lib/log.js');
var CookieMixinFactory = require('./cookieMixinFactory.jsx');
/**
* Component mixin that constructs requests to the scratch api.
* Custom arguments:
@ -15,10 +13,6 @@ var CookieMixinFactory = require('./cookieMixinFactory.jsx');
* It also takes in other arguments specified in the xhr library spec.
*/
var Api = {
mixins: [
// Provides useScratchcsrftoken
CookieMixinFactory('scratchcsrftoken', '/csrf_token/')
],
api: function (opts, callback) {
defaults(opts, {
host: window.env.API_HOST,
@ -39,6 +33,13 @@ var Api = {
// custom headers.
defaults(opts, {useXDR: true});
delete opts.headers;
if (opts.authentication) {
var authenticationParams = ['x-token=' + opts.authentication];
var parts = opts.uri.split('?');
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
opts.uri = parts[0] + '?' + qs;
}
}
xhr(opts, function (err, res, body) {
if (err) log.error(err);
@ -56,8 +57,11 @@ var Api = {
if (typeof jar.get('scratchlanguage') !== 'undefined') {
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
}
if (opts.authentication) {
opts.headers['X-Token'] = opts.authentication;
}
if (opts.useCsrf) {
this.useScratchcsrftoken(function (err, csrftoken) {
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
if (err) return log.error('Error while retrieving CSRF token', err);
opts.json.csrftoken = csrftoken;
opts.headers['X-CSRFToken'] = csrftoken;

View file

@ -1,18 +0,0 @@
var Session = {
getInitialState: function () {
return {
session: window._session
};
},
updateSession: function () {
this.setState({'session': window._session});
},
componentWillMount: function () {
window.addEventListener('session', this.updateSession);
},
componentWillUnmount: function () {
window.removeEventListener('session', this.updateSession);
}
};
module.exports = Session;

83
src/redux/actions.js Normal file
View file

@ -0,0 +1,83 @@
var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var jar = require('../lib/jar.js');
var Types = keyMirror({
SET_SESSION: null,
SET_SESSION_ERROR: null,
SET_TOKEN: null,
SET_TOKEN_ERROR: null,
USE_TOKEN: null
});
var Actions = {
types: Types,
setSessionError: function (error) {
return {
type: Types.SET_SESSION_ERROR,
error: error
}
},
setSession: function (session) {
return {
type: Types.SET_SESSION,
session: session
}
},
refreshSession: function () {
return function (dispatch) {
api({
host: '',
uri: '/session/'
}, function (err, body) {
if (err) return dispatch(Actions.setSessionError(err));
if (typeof body !== 'undefined') {
if (body.banned) {
return window.location = url;
} else {
dispatch(Actions.getToken());
dispatch(Actions.setSession(body));
return;
}
}
});
};
},
getToken: function () {
return function (dispatch) {
jar.get('scratchsessionsid', function (err, value) {
if (err) return dispatch(Actions.setTokenError(err));
jar.unsign(value, function (err, contents) {
if (err) return dispatch(Actions.setTokenError(err));
try {
var sessionData = JSON.parse(contents);
} catch (err) {
return dispatch(Actions.setTokenError(err));
}
return dispatch(Actions.setToken(sessionData.token));
});
});
}
},
setToken: function (token) {
return {
type: Types.SET_TOKEN,
token: token
};
},
setTokenError: function (error) {
return {
type: Types.SET_SESSION_ERROR,
error: error
};
}
};
module.exports = Actions;

43
src/redux/reducer.js Normal file
View file

@ -0,0 +1,43 @@
var combineReducers = require('redux').combineReducers;
var actionTypes = require('./actions.js').types;
var sessionReducer = function (state, action) {
// Reducer for handling changes to session state
if (typeof state === 'undefined') {
state = {};
}
switch (action.type) {
case actionTypes.SET_SESSION:
return action.session;
case actionTypes.SET_SESSION_ERROR:
// TODO: do something with action.error
return state;
default:
return state;
}
};
var tokenReducer = function (state, action) {
// Reducer for updating the api token
if (typeof state === 'undefined') {
state = '';
}
switch (action.type) {
case actionTypes.SET_TOKEN:
return action.token;
case actionTypes.SET_TOKEN_ERROR:
// TODO: do something with the error
return state;
default:
return state;
}
};
var appReducer = combineReducers({
session: sessionReducer,
token: tokenReducer
});
module.exports = appReducer;

View file

@ -3,6 +3,8 @@ var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
var FormattedMessage = require('react-intl').FormattedMessage;
var render = require('../../lib/render.jsx');
var Page = require('../../components/page/page.jsx');
require('./about.scss');
var About = React.createClass({
@ -109,4 +111,4 @@ var About = React.createClass({
}
});
render(<About />, document.getElementById('view'));
render(<Page><About /></Page>, document.getElementById('app'));

View file

@ -60,7 +60,8 @@
width: calc(384px + 5px + 5px);
}
img, iframe {
img,
iframe {
display: block;
border: 1px solid $ui-gray;
padding: 5px;

150
src/views/cards/cards.jsx Normal file
View file

@ -0,0 +1,150 @@
var React = require('react');
var injectIntl = require('react-intl').injectIntl;
var FormattedMessage = require('react-intl').FormattedMessage;
var render = require('../../lib/render.jsx');
var Box = require('../../components/box/box.jsx');
var Page = require('../../components/page/page.jsx');
require('./cards.scss');
var Cards = injectIntl(React.createClass({
type: 'Cards',
render: function () {
var locale = window._locale || 'en';
var formatMessage = this.props.intl.formatMessage;
var englishLinks = {
'cards.starterLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/Scratch2Cards.pdf',
'cards.nameLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/AnimateYourNameCards.pdf',
'cards.pongLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/PongCards.pdf',
'cards.storyLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/StoryCards.pdf',
'cards.danceLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/DanceCards.pdf',
'cards.hideLink': '//scratch.mit.edu/scratchr2/static/pdfs/help/Hide-and-Seek-Cards.pdf'
};
var formattedLinks = {
'cards.starterLink': formatMessage({id: 'cards.starterLink'}),
'cards.nameLink': formatMessage({id: 'cards.nameLink'}),
'cards.pongLink': formatMessage({id: 'cards.pongLink'}),
'cards.storyLink': formatMessage({id: 'cards.storyLink'}),
'cards.danceLink': formatMessage({id: 'cards.danceLink'}),
'cards.hideLink': formatMessage({id: 'cards.hideLink'})
};
return (
<div className="inner">
<div className="intro cards">
<div className="intro-content">
<h1><FormattedMessage id='cards.introHeader' /></h1>
<p><FormattedMessage id='cards.introContent' /></p>
</div>
<img src='/images/cards/card-use-overview.png' alt="Card Use Explanation" />
</div>
<div className='cards-container'>
<Box title={''}>
<div className='card-row'>
<div>
<h4><FormattedMessage id='cards.starter' /></h4>
<a href={formattedLinks['cards.starterLink']}>
<img src="/images/cards/cards-starter.png" alt="" />
</a>
<a href={formattedLinks['cards.starterLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.starterLink'] === englishLinks['cards.starterLink'] &&
locale !== 'en'
) ? [
<span> <FormattedMessage id='cards.english' /></span>
] : []}
</a>
</div>
<div>
<h4><FormattedMessage id='cards.name' /></h4>
<a href={formattedLinks['cards.nameLink']}>
<img src="/images/cards/cards-name.png" alt="" />
</a>
<a href={formattedLinks['cards.nameLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.nameLink'] === englishLinks['cards.nameLink'] &&
locale !== 'en'
) ? [
<span> (<FormattedMessage id='cards.english' />)</span>
] : []}
</a>
</div>
<div>
<h4><FormattedMessage id='cards.pong' /></h4>
<a href={formattedLinks['cards.pongLink']}>
<img src="/images/cards/cards-pong.png" alt="" />
</a>
<a href={formattedLinks['cards.pongLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.pongLink'] === englishLinks['cards.pongLink'] &&
locale !== 'en'
) ? [
<span> (<FormattedMessage id='cards.english' />)</span>
] : []}
</a>
</div>
</div>
<div className='card-row'>
<div>
<h4><FormattedMessage id='cards.story' /></h4>
<a href={formattedLinks['cards.storyLink']}>
<img src="/images/cards/cards-story.png" alt="" />
</a>
<a href={formattedLinks['cards.storyLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.storyLink'] === englishLinks['cards.storyLink'] &&
locale !== 'en'
) ? [
<span> (<FormattedMessage id='cards.english' />)</span>
] : []}
</a>
</div>
<div>
<h4><FormattedMessage id='cards.dance' /></h4>
<a href={formattedLinks['cards.danceLink']}>
<img src="/images/cards/cards-dance.png" alt="" />
</a>
<a href={formattedLinks['cards.danceLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.danceLink'] === englishLinks['cards.danceLink'] &&
locale !== 'en'
) ? [
<span> (<FormattedMessage id='cards.english' />)</span>
] : []}
</a>
</div>
<div>
<h4><FormattedMessage id='cards.hide' /></h4>
<a href={formattedLinks['cards.hideLink']}>
<img src="/images/cards/cards-hide.png" alt="" />
</a>
<a href={formattedLinks['cards.hideLink']}>
<img src="/svgs/pdf-icon-ui-blue.svg" alt="" className='pdf-icon' />
<FormattedMessage id='cards.viewCard' />
{(
formattedLinks['cards.hideLink'] === englishLinks['cards.hideLink'] &&
locale !== 'en'
) ? [
<span> (<FormattedMessage id='cards.english' />)</span>
] : []}
</a>
</div>
</div>
</Box>
</div>
</div>
);
}
}));
render(<Page><Cards /></Page>, document.getElementById('app'));

View file

@ -0,0 +1,43 @@
@import "../../colors";
@import "../../typography";
.cards {
display: flex;
margin: 1em 0;
align-items: center;
justify-content: center;
.intro-content {
float: left;
width: 45%;
}
img {
width: 45%;
}
}
.cards-container {
text-align: center;
.card-row {
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
div {
display: inline-block;
padding: .5em;
a {
display: block;
.pdf-icon {
margin-right: .2em;
width: 1em;
}
}
}
}
}

View file

@ -0,0 +1,8 @@
{
"cards.starterLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/Scratch2Cards.pdf",
"cards.nameLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/AnimateYourNameCards.pdf",
"cards.pongLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/PongCards.pdf",
"cards.storyLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/StoryCards.pdf",
"cards.danceLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/DanceCards.pdf",
"cards.hideLink": "//scratch.mit.edu/scratchr2/static/pdfs/help/Hide-and-Seek-Cards.pdf"
}

13
src/views/cards/l10n.json Normal file
View file

@ -0,0 +1,13 @@
{
"cards.introHeader": "Scratch Cards",
"cards.introContent": "Scratch cards provide a quick way to learn new Scratch code.",
"cards.english": "English",
"cards.introWikiSupport": "Looking for Scratch Cards in your language? Check <a href=\"http://wiki.scratch.mit.edu/wiki/Scratch_Support_Materials\">Scratch Wiki</a>.",
"cards.viewCard": "View Cards",
"cards.starter": "Starter Cards",
"cards.name": "Animate Your Name",
"cards.pong": "Create a Pong Game",
"cards.story": "Animate a Story",
"cards.dance": "Dance, Dance, Dance",
"cards.hide": "Hide and Seek"
}

View file

@ -0,0 +1,62 @@
var React = require('react');
var render = require('../../lib/render.jsx');
var Page = require('../../components/page/page.jsx');
var CommunityBlocksInterviews = React.createClass({
type: 'communityblocks-interviews',
render: function () {
return (
<div className="inner">
<h2>Community Blocks Beta Tester Interviews</h2>
<br/>
<p>
Hello Scratchers!
</p>
<p>
I am Sayamindu Dasgupta (<a href="/users/sdg1/" target="_blank">sdg1</a> on Scratch) and I am a member of
the <a href="/info/credits" target="_blank">MIT Scratch Team</a>.
</p>
<p>
One of our projects on the MIT Scratch Team is to understand how people use Scratch, the Scratch Community
Blocks, and participate in the Scratch community. To do this, we are talking to Scratchers who have been
particapting the Community Blocks beta testing program directly through interviews. In the interview, we
would talk for an hour, asking about your Scratch experience (by phone or a service like Skype).
</p>
<p>
Thank you for indicating in the beta invitation survey that you are willing to be interviewed.
If you are still interested, please do the following steps:
</p>
<ul>
<li><b>Complete the consent forms:</b> If you are under 18 years old, please download and complete these
two forms (<a href="/pdfs/interviews/communityblocks/assent_form.pdf">one for you to sign</a>&nbsp;
and <a href="/pdfs/interviews/communityblocks/consent_for_parent.pdf">another for your parent to sign</a>).
If you are 18 years old and over, please complete&nbsp;
<a href="/pdfs/interviews/communityblocks/consent_for_over_18.pdf">this form</a>.</li>
<li><b>Send the forms:</b> You can send me the forms in two ways: (1) by email
(<a href="mailto:sayamindu@media.mit.edu" target="_blank">sayamindu@media.mit.edu</a>) by taking a picture
or scanning the forms, or (2) send it through snail mail to Sayamindu Dasgupta, 77 Massachusetts Ave
E14-464A, Cambridge, MA 02139</li>
<li><b>Schedule a time to talk:</b> Send me an email
(<a href="mailto:sayamindu@media.mit.edu" target="_blank">sayamindu@media.mit.edu</a>) with a possible time
where we can talk for about an hour.</li>
<li>If you have any questions, please do not hesitate to contact me by sending me an email
at <a href="mailto:sayamindu@media.mit.edu">sayamindu@media.mit.edu</a>.</li>
</ul>
<p>
Thank you and I look forward to speaking with you. Scratch on!
</p>
Sayamindu
</div>
);
}
});
render(<Page><CommunityBlocksInterviews /></Page>, document.getElementById('app'));

View file

@ -2,6 +2,7 @@ var React = require('react');
var render = require('../../lib/render.jsx');
var Activity = require('../../components/activity/activity.jsx');
var Page = require('../../components/page/page.jsx');
var Box = require('../../components/box/box.jsx');
var Button = require('../../components/forms/button.jsx');
var Carousel = require('../../components/carousel/carousel.jsx');
@ -44,4 +45,4 @@ var Components = React.createClass({
}
});
render(<Components />, document.getElementById('view'));
render(<Page><Components /></Page>, document.getElementById('app'));

View file

@ -1,6 +1,8 @@
var React = require('react');
var render = require('../../lib/render.jsx');
var Page = require('../../components/page/page.jsx');
require('./credits.scss');
var Credits = React.createClass({
@ -299,4 +301,4 @@ var Credits = React.createClass({
}
});
render(<Credits />, document.getElementById('view'));
render(<Page><Credits /></Page>, document.getElementById('app'));

View file

@ -3,6 +3,7 @@
#view {
p {
line-height: 1.5rem;
a {
word-wrap: break-word; /* Overrides: https://github.com/LLK/scratch-www/blob/develop/src/main.scss#L43-L47 */
}

View file

@ -4,6 +4,7 @@ var FormattedMessage = require('react-intl').FormattedMessage;
var React = require('react');
var render = require('../../lib/render.jsx');
var Page = require('../../components/page/page.jsx');
var Button = require('../../components/forms/button.jsx');
var Box = require('../../components/box/box.jsx');
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
@ -405,4 +406,4 @@ var Hoc = React.createClass({
}
});
render(<Hoc />, document.getElementById('view'));
render(<Page><Hoc /></Page>, document.getElementById('app'));

65
src/views/jobs/jobs.jsx Normal file
View file

@ -0,0 +1,65 @@
var React = require('react');
var render = require('../../lib/render.jsx');
var FormattedMessage = require('react-intl').FormattedMessage;
var Page = require('../../components/page/page.jsx');
require('./jobs.scss');
var Jobs = React.createClass({
type: 'Jobs',
render: function () {
return (
<div>
<div className="top">
<div className="inner">
<img src="/images/jobs.png" />
<h1><FormattedMessage id='jobs.titleQuestion' /></h1>
</div>
</div>
<div className="middle">
<div className="inner">
<h2><FormattedMessage id='jobs.joinScratchTeam' /></h2>
<p><FormattedMessage id='jobs.info' /></p>
<p><FormattedMessage id='jobs.workEnvironment' /></p>
</div>
</div>
<div className="bottom">
<div className="inner">
<h2><FormattedMessage id='jobs.openings' /></h2>
<ul>
<li>
<a href="http://bit.ly/24B9aEz">
Community Manager
</a>
<span>
(MIT Media Lab, Cambridge, MA)
</span>
</li>
<li>
<a href="http://bit.ly/21CTTE6">
Front-end Engineer
</a>
<span>
(MIT Media Lab, Cambridge, MA)
</span>
</li>
<li>
<a href="/jobs/community-counselor/">
Community Counselor
</a>
<span>
(MIT Media Lab, Cambridge, MA or Remote)
</span>
</li>
</ul>
</div>
</div>
</div>
);
}
});
render(<Page><Jobs /></Page>, document.getElementById('app'));

56
src/views/jobs/jobs.scss Normal file
View file

@ -0,0 +1,56 @@
@import "../../colors";
#view {
h1 {
line-height: 2.6rem;
font-size: 2.3rem;
font-weight: 300;
}
h2 {
font-size: 1.8rem;
font-weight: 300;
}
.top {
img {
margin-bottom: 10px;
}
}
.middle {
margin: 20px 0;
background-color: $ui-gray;
padding: 40px 0;
width: 100%;
}
.bottom {
width: 100%;
line-height: 200%;
.thin-heading {
padding: 20px 0;
}
p {
margin: auto;
width: 70%;
}
ul {
padding: 0;
list-style: none;
li {
margin: 0;
padding: 0;
span {
margin-left: 10px;
}
}
}
}
}

7
src/views/jobs/l10n.json Normal file
View file

@ -0,0 +1,7 @@
{
"jobs.info": "With Scratch, young people from all backgrounds are learning to program their own interactive stories, games, and animations. Children and teens from around the world have created and shared more than 10 million projects in the rapidly-growing Scratch online community.",
"jobs.joinScratchTeam": "Join the Scratch Team!",
"jobs.openings": "Current Job Openings",
"jobs.titleQuestion": "Want to work on an innovative project that is transforming the ways young people create, share, and learn?",
"jobs.workEnvironment":"Were seeking curious and motivated people to join our Scratch Team at the MIT Media Lab. We're a diverse group of educators, designers, and engineers, who work together in a playful, creative environment full of LEGO bricks, craft materials, and maker tools. We strongly value diversity, collaboration, and respect in the workplace."
}

View file

@ -0,0 +1,829 @@
{
"title":"Make Some Art",
"description":[
"Watch videos about how to create with technology.",
"Then, create your own art project.",
"Check out projects by others for inspiration,",
"communicate in the forum and join the challenges!"
],
"videos":[
{
"key":"1",
"image":"http://farm8.staticflickr.com/7245/7120445933_7de87c2bd9_z.jpg",
"link":"https://player.vimeo.com/video/40904471"
},
{
"key":"2",
"image":"http://blogs.adobe.com/conversations/files/2015/04/project-para2-1024x572.jpg",
"link":"https://www.youtube.com/embed/Tdvj8XMrqXc?autoplay=1"
},
{
"key":"3",
"image":"http://iluminate.com/wp-content/uploads/2015/07/iluminate-news-residency-at-resorts-world-genting-malaysia-1200x798.jpg",
"link":"https://www.youtube.com/embed/Xg1dUhVI9i0?autoplay=1"
}
],
"microworld_project_id":"88148127",
"tips":[
{
"title":"Start Dancing",
"thumbnails":[
{
"type":"tip",
"title":"First, select a sprite",
"thumbnailUrl":"/images/microworlds/hiphop/dancer-sprite.png"
},
{
"type":"tip",
"title":"Then, try this script",
"thumbnailUrl":"/images/microworlds/hiphop/switch-wait.gif"
},
{
"type":"tip",
"title":"Start it!",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Repeat the Dance",
"thumbnails":[
{
"type":"tip",
"title":"Add another \"wait\"",
"thumbnailUrl":"/images/microworlds/hiphop/add-wait.gif"
},
{
"type":"tip",
"title":"Drag this block over",
"thumbnailUrl":"/images/microworlds/hiphop/add-repeat.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Play Music",
"thumbnails":[
{
"type":"tip",
"title":"Add music that repeats",
"thumbnailUrl":"/images/microworlds/hiphop/add-play-sound.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Shadow Dance",
"thumbnails":[
{
"type":"tip",
"title":"Add this block",
"thumbnailUrl":"/images/microworlds/hiphop/shadow-dance.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
},
{
"type":"tip",
"title":"Click the stop sign to reset",
"thumbnailUrl":"/images/microworlds/hiphop/stop.gif"
}
]
},
{
"title":"More things to try",
"thumbnails":[
{
"type":"tip",
"title":"Try different dance moves",
"thumbnailUrl":"/images/microworlds/hiphop/change-dance-moves.gif"
},
{
"type":"tip",
"title":"Add more moves",
"thumbnailUrl":"/images/microworlds/hiphop/long-dance-script.png"
},
{
"type":"tip",
"title":"Change the timing",
"thumbnailUrl":"/images/microworlds/hiphop/change-dance-timing.png"
}
]
}
],
"starter_projects":[
{
"title":"Architecture Stamps Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2445/6318.png",
"creator":"CSFirst",
"id":24456318
},
{
"title":"Digital Art Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2513/9098.png",
"creator":"CSFirst",
"id":25139098
},
{
"title":"Graffiti Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2546/8311.png",
"creator":"CSFirst",
"id":25468311
},
{
"title":"Paint with Tera Starter ",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2743/2322.png",
"creator":"CSFirst",
"id":27432322
},
{
"title":"Interactive Art Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2501/4570.png",
"creator":"CSFirst",
"id":25014570
},
{
"title":"Interactive Art Starter Project - Kuniyoshi Start",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2482/0113.png",
"creator":"CSFirst",
"id":24820113
},
{
"title":"Interactive Art Starter Project - American Gothic",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2445/4633.png",
"creator":"CSFirst",
"id":24454633
},
{
"title":"Interactive Art Starter Project - The Scream",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2481/8944.png",
"creator":"CSFirst",
"id":24818944
},
{
"title":"Animation Starter Project - Penguin",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2395/6722.png",
"creator":"CSFirst",
"id":23956722
},
{
"title":"Animation Starter Project - Gobo",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2395/7767.png",
"creator":"CSFirst",
"id":23957767
},
{
"title":"Animation Starter Project - Ghost",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2395/7153.png",
"creator":"CSFirst",
"id":23957153
},
{
"title":"Animation Starter Project - Crab",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2395/6837.png",
"creator":"CSFirst",
"id":23956837
},
{
"title":"Intro to Art Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2420/9090.png",
"creator":"CSFirst",
"id":24209090
}
],
"community_projects":{
"featured_projects":[
{
"title":"Change color effect by-function",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/141/3159.png",
"creator":"dapontes",
"id":1413159
},
{
"title":"CobwebMaker",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/133/0100.png",
"creator":"-Scratcher-",
"id":1330100
},
{
"title":"QuadraticMap3_Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/136/2772.png",
"creator":"mathjp",
"id":1362772
},
{
"title":"Plants_Jp1",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/131/8150.png",
"creator":"mathjp",
"id":1318150
},
{
"title":"TreeMaker",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/131/5337.png",
"creator":"-Scratcher-",
"id":1315337
},
{
"title":"3D Curve art",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/128/7436.png",
"creator":"nasami",
"id":1287436
},
{
"title":"pen art maker",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/115/3338.png",
"creator":"cooljj100",
"id":1153338
},
{
"title":"e-lisa-avo",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/64/1173.png",
"creator":"gerhardmb",
"id":641173
},
{
"title":"Pyramid_IFS_Fractal",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/129/4615.png",
"creator":"mathjp",
"id":1294615
},
{
"title":"3D Snail",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/127/0157.png",
"creator":"mathjp",
"id":1270157
},
{
"title":"Fractile Shape Maker 1S 1S",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/115/5121.png",
"creator":"recycle49",
"id":1155121
},
{
"title":"Mosaik",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/110/3305.png",
"creator":"frcroth",
"id":1103305
},
{
"title":"QuadraticMap2_Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/109/9424.png",
"creator":"mathjp",
"id":1099424
},
{
"title":"3D Cube",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/105/2958.png",
"creator":"VilceGT",
"id":1052958
},
{
"title":"Fractal_CarpetsV1",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/105/9322.png",
"creator":"mathjp",
"id":1059322
},
{
"title":"QuadraticMap1_Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/100/1796.png",
"creator":"mathjp",
"id":1001796
},
{
"title":"Pattern/Shape Generator 1S 1S",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/94/6911.png",
"creator":"recycle49",
"id":946911
},
{
"title":"Sweet Rainbow Kaliedoscope Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/94/0283.png",
"creator":"Venazard",
"id":940283
},
{
"title":"Scratch3_Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/94/6973.png",
"creator":"mathjp",
"id":946973
},
{
"title":"Peter_de_Jong_Attractor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/85/3644.png",
"creator":"mathjp",
"id":853644
},
{
"title":"Invertable",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/53/6764.png",
"creator":"itsEMILagain",
"id":536764
},
{
"title":"Chalkboard designs",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/66/7803.png",
"creator":"Targethero",
"id":667803
},
{
"title":"Blades of grass",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/82/2114.png",
"creator":"AddZero",
"id":822114
},
{
"title":"Flexible Line",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/72/8858.png",
"creator":"Paddle2See",
"id":728858
}
],
"newest_projects":[
{
"title":"Pixel Art - Emerald ore",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8681/8843.png",
"creator":"SebastianLavers",
"id":86818843
},
{
"title":"Untitled-10",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8720/4697.png",
"creator":"SebastianLavers",
"id":87204697
},
{
"title":"pixel art - trayne",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8720/5631.png",
"creator":"SebastianLavers",
"id":87205631
},
{
"title":"MY STAR WARS PIXEL ICONS",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8294/4668.png",
"creator":"DevG2857",
"id":82944668
},
{
"title":"[Pixel Art] Bomb",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7663/8750.png",
"creator":"-PixelArt-",
"id":76638750
},
{
"title":"[Pixel Art] Pizza",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7652/8188.png",
"creator":"-PixelArt-",
"id":76528188
},
{
"title":"[Pixel Art] Ancient Book",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7652/9284.png",
"creator":"-PixelArt-",
"id":76529284
},
{
"title":"[Pixel Art] Grass Tile #2",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7567/6352.png",
"creator":"-PixelArt-",
"id":75676352
},
{
"title":"Millennium Falcon",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4829/2520.png",
"creator":"HD_Pixel_Squid",
"id":48292520
},
{
"title":"Pixel Art Scene",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/5076/2664.png",
"creator":"AgentCNF",
"id":50762664
},
{
"title":"Sunset Pixel Art - By JK102",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7332/8110.png",
"creator":"jk102",
"id":73328110
},
{
"title":"Dragon Ball Z Pixel Art",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7225/3242.png",
"creator":"agentmcguffin",
"id":72253242
},
{
"title":"Pixel Art - Award",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7260/3768.png",
"creator":"Pixels-",
"id":72603768
},
{
"title":"PixelMoltenArmor",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6927/7136.png",
"creator":"pixelart5",
"id":69277136
},
{
"title":"Fishing (Art)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1901/6045.png",
"creator":"IsoPixel",
"id":19016045
},
{
"title":"Placid Pool Simulation",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4696/9638.png",
"creator":"RememberNovember",
"id":46969638
},
{
"title":"[Pixel Art] Mountain Background",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7558/6228.png",
"creator":"-PixelArt-",
"id":75586228
},
{
"title":"[Pixel Art] Log",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7558/0582.png",
"creator":"-PixelArt-",
"id":75580582
},
{
"title":"[Pixel Art] Healthbar",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7468/7698.png",
"creator":"-PixelArt-",
"id":74687698
},
{
"title":"[Pixel Art] Food",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7450/4518.png",
"creator":"-PixelArt-",
"id":74504518
},
{
"title":"[Pixel Art] Starfield",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7450/3672.png",
"creator":"-PixelArt-",
"id":74503672
},
{
"title":"[Pixel Art] Nature Scene",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7449/9686.png",
"creator":"-PixelArt-",
"id":74499686
},
{
"title":"[Pixel Art] Clouds",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7450/1794.png",
"creator":"-PixelArt-",
"id":74501794
},
{
"title":"[Pixel Art] Medieval Door",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7333/1288.png",
"creator":"-PixelArt-",
"id":73331288
},
{
"title":"[Pixel Art] Sun and Moon",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7443/2594.png",
"creator":"-PixelArt-",
"id":74432594
},
{
"title":"[Pixel Art] Soda Can",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7333/0734.png",
"creator":"-PixelArt-",
"id":73330734
},
{
"title":"[Pixel Art] Rock Pack",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7324/3096.png",
"creator":"-PixelArt-",
"id":73243096
},
{
"title":"[Pixel Art] Grass Tile",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7321/3098.png",
"creator":"-PixelArt-",
"id":73213098
},
{
"title":"[Pixel Art] Animated Coin",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7321/2898.png",
"creator":"-PixelArt-",
"id":73212898
},
{
"title":"[Pixel Art] Sword",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7294/4390.png",
"creator":"-PixelArt-",
"id":72944390
}
]
},
"design_challenge":{
"project_id":"89144801",
"studio_id":"1728540",
"studio1":[
{
"gallery_id":1728540,
"creator":"mathjp",
"remixers_count":8,
"gallery_title":"Arts Design Challenge",
"love_count":28,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/326/6979.png",
"title":"Flowers_Creator",
"type":"project",
"id":3266979
},
{
"gallery_id":1728540,
"creator":"-Scratcher-",
"remixers_count":2,
"gallery_title":"Arts Design Challenge",
"love_count":9,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/131/5337.png",
"title":"TreeMaker",
"type":"project",
"id":1315337
},
{
"title":"a beautiful day with a flower",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/326/7716.png",
"creator":"Sakura102",
"id":3267716
},
{
"title":"A Walk in the Dream Park",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2032/9589.png",
"creator":"JoeTheMan18",
"id":20329589
},
{
"title":"Drawing with Shapes",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7700/9574.png",
"creator":"morant",
"id":77009574
},
{
"title":"pretty flower",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/271/1834.png",
"creator":"leszpio",
"id":2711834
},
{
"title":"Garden Secret",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1045/1607.png",
"creator":"Eddie101101",
"id":10451607
}
],
"studio2":[
{
"title":"Garden Secret",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1045/1607.png",
"creator":"Eddie101101",
"id":10451607
},
{
"title":"The Park",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2658/0310.png",
"creator":"taliapanda01",
"id":26580310
}
]
},
"show_forum":true
}

View file

@ -0,0 +1,6 @@
var render = require('../../lib/render.jsx');
var Microworld = require('../../components/microworld/microworld.jsx');
var microworldData = require('./microworld_art.json');
render(<Microworld microworldData={microworldData} />, document.getElementById('view'));

View file

@ -0,0 +1,487 @@
{
"title":"Fashion",
"description":[
"Do you like fashion?",
"Watch this video about making fashion projects.",
"Then see what you can do with the dress-up game below.",
"Explore other fashion projects and make your own!"
],
"videos":[
{
"image":"https://i.vimeocdn.com/video/528845372.webp?mw=900&amp;mh=583&amp;q=70",
"link":"//player.vimeo.com/video/528845372?title=0&byline=0&portrait=0"
}
],
"microworld_project_id":"68494068",
"tips":[
{
"title":"Click on blocks to see what they do",
"thumbnails":[
{
"type":"tip",
"title":" Try clicking this block ",
"thumbnailUrl":"/images/microworlds/fashion/click-block-color.gif"
},
{
"type":"tip",
"title":" Or this block",
"thumbnailUrl":"/images/microworlds/fashion/click-block-costume.gif"
}
]
},
{
"title":"Try changing colors",
"thumbnails":[
{
"type":"tip",
"title":" First, select a clothing item ",
"thumbnailUrl":"/images/microworlds/fashion/shirts-sprite.png"
},
{
"type":"tip",
"title":" Then, try this script",
"thumbnailUrl":"/images/microworlds/fashion/when-clicked-color.gif"
},
{
"type":"tip",
"title":" Test it out!",
"thumbnailUrl":"/images/microworlds/fashion/click-shirt.gif"
}
]
},
{
"title":"Try changing costumes",
"thumbnails":[
{
"type":"tip",
"title":"Select a piece of clothing ",
"thumbnailUrl":"/images/microworlds/fashion/hats-sprite.png"
},
{
"type":"tip",
"title":"Then, switch costumes",
"thumbnailUrl":"/images/microworlds/fashion/when-clicked-costume.gif"
},
{
"type":"tip",
"title":" Now, click it!",
"thumbnailUrl":"/images/microworlds/fashion/click-hat.gif"
}
]
},
{
"title":"Say hello",
"thumbnails":[
{
"type":"tip",
"title":"Select the person",
"thumbnailUrl":"/images/microworlds/fashion/person-sprite.png"
},
{
"type":"tip",
"title":"Type in a phrase",
"thumbnailUrl":"/images/microworlds/fashion/say-something.gif"
},
{
"type":"tip",
"title":"Then, try this script",
"thumbnailUrl":"/images/microworlds/fashion/when-clicked-say.gif"
}
]
},
{
"title":"Move the sprites",
"thumbnails":[
{
"type":"tip",
"title":"Select an accessory",
"thumbnailUrl":"/images/microworlds/fashion/accessories-sprite.png"
},
{
"type":"tip",
"title":"Tell it where to go",
"thumbnailUrl":"/images/microworlds/fashion/when-clicked-goto.gif"
}
]
},
{
"title":"Other things to try",
"thumbnails":[
{
"type":"tip",
"title":"Add a sound ",
"thumbnailUrl":"/images/microworlds/fashion/play-drum.png"
},
{
"type":"tip",
"title":" Explore other costumes ",
"thumbnailUrl":"/images/microworlds/fashion/next-costume.png"
},
{
"type":"tip",
"title":"Make longer scripts ",
"thumbnailUrl":"/images/microworlds/fashion/block-stack.png"
}
]
}
],
"_commentedout_starter_projects":[
{
"title":"Architecture Stamps Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2445/6318.png",
"creator":"CSFirst",
"id":24456318
}
],
"community_projects":{
"featured_projects":[
{
"title":"☆Mystica Dress Up Game☆",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7135/4508.png",
"creator":"xVanyx",
"id":71354508
},
{
"title":"Design a bag Contest! Round two! remix",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6762/5700.png",
"creator":"walterwolf",
"id":67625700
},
{
"title":"Paint Nails! ~ [Pinta las uñas]",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2465/4259.png",
"creator":"aguchita",
"id":24654259
},
{
"title":"★1990s Inspired Dress Up★",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6464/1106.png",
"creator":"amee-",
"id":64641106
},
{
"title":"Minion Dress up Game",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1205/3016.png",
"creator":"Dobbby",
"id":12053016
},
{
"title":"Mabel's Dress Up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2579/6804.png",
"creator":"MagicianCat",
"id":25796804
},
{
"title":"Jeremy's Dressup",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4110/6638.png",
"creator":"ipzy",
"id":41106638
},
{
"title":"Elsa Dress-Up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2130/5521.png",
"creator":"elsaunikitty",
"id":21305521
}
],
"newest_projects":[
{
"title":"Purse Land",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9349/4126.png",
"creator":"punkbass",
"id":93494126
},
{
"title":"Lilly's Fashion Salon! ",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9363/9916.png",
"creator":"cs54016",
"id":93639916
},
{
"title":"Lilly's Fashion Salon! ",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9363/9916.png",
"creator":"cs54016",
"id":93639916
},
{
"title":"dress up game (my characters/ bff characters)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9363/5245.png",
"creator":"lockeythekey",
"id":93635245
},
{
"title":"Anime Dress Up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9361/1094.png",
"creator":"Alyanda",
"id":93611094
},
{
"title":"Stick Man/Woman Dress Up!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9357/7986.png",
"creator":"loopey7890",
"id":93577986
},
{
"title":"2-D Dress Up!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9361/3207.png",
"creator":"Arkoarsenic",
"id":93613207
},
{
"title":"Stickbro Dress Up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9356/4550.png",
"creator":"Discord21",
"id":93564550
},
{
"title":"Bear Dress Up remix",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9361/2027.png",
"creator":"cssuperfudge",
"id":93612027
},
{
"title":"dress up Lucy",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9360/8879.png",
"creator":"123showbiz",
"id":93608879
},
{
"title":"Hair Play",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9181/0572.png",
"creator":"Aphrodite369",
"id":91810572
},
{
"title":"Warrior Cat Maker",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9357/0484.png",
"creator":"Nikinoo42",
"id":93570484
},
{
"title":"Bearie Dress Up remix",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9358/0802.png",
"creator":"SRajput",
"id":93580802
},
{
"title":"Chibi Cute Bear Dress Up :D",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9355/9967.png",
"creator":"UnicornAnna",
"id":93559967
},
{
"title":"Laura beach dress up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9356/5520.png",
"creator":"rozyczka2122004",
"id":93565520
},
{
"title":"Dress Up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9354/8643.png",
"creator":"rwd05",
"id":93548643
},
{
"title":"dress up scratch cat!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9356/1200.png",
"creator":"CONFIDENTDRAGON",
"id":93561200
},
{
"title":"Dress Up Olive",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9276/7275.png",
"creator":"rozyczka2122004",
"id":92767275
},
{
"title":"Bear Dress Up remix",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9354/5576.png",
"creator":"pixiepants14",
"id":93545576
},
{
"title":"Dress up Nano and Giga",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8681/5299.png",
"creator":"awesomesauce1243",
"id":86815299
}
]
},
"design_challenge":{
"_commentedout_project_id":"89144801",
"studio_id":"1424746",
"studio1":[
{
"title":" BIG cool dress up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/5027/9238.png",
"creator":"Ratties123",
"id":50279238
},
{
"title":"~*Dressup*~ (40+ clothes and acessories)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4966/3352.png",
"creator":"NinniV",
"id":49663352
},
{
"title":"Dress up Bun-bun",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7047/3464.png",
"creator":"LillyMoon15",
"id":70473464
},
{
"title":"Dress Up Pikachu",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1189/5141.png",
"creator":"poke10",
"id":11895141
},
{
"title":"dress up",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6261/8658.png",
"creator":"peachycream1",
"id":62618658
}
],
"studio2":[
{
"title":"Dress Up Fun!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6289/6096.png",
"creator":"sapphire-grace",
"id":62896096
},
{
"title":"Finicky Fashionista",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7210/8732.png",
"creator":"Bex36",
"id":72108732
},
{
"title":"Dress Up! :D",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6/2256.png",
"creator":"angelical",
"id":62256
},
{
"title":"Dress Up Tera",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/1165/6680.png",
"creator":"Scratchteam",
"id":11656680
}
]
},
"show_forum":false
}

View file

@ -0,0 +1,6 @@
var render = require('../../lib/render.jsx');
var Microworld = require('../../components/microworld/microworld.jsx');
var microworldData = require('./microworld_fashion.json');
render(<Microworld microworldData={microworldData} />, document.getElementById('view'));

View file

@ -0,0 +1,544 @@
{
"title":"Hip Hop",
"description":[
"Do you like hip-hop?",
"Watch a video about making hip-hop projects.",
"Then, create your own dance scene!",
"Check out projects by others for inspiration."
],
"videos":[
{
"image":"https://i.vimeocdn.com/video/521248373.webp?mw=900&mh=583&q=70",
"link":"//player.vimeo.com/video/124055657?title=0&byline=0&portrait=0"
}
],
"microworld_project_id":"68260156",
"tips":[
{
"title":"Start Dancing",
"thumbnails":[
{
"type":"tip",
"title":"First, select a sprite",
"thumbnailUrl":"/images/microworlds/hiphop/dancer-sprite.png"
},
{
"type":"tip",
"title":"Then, try this script",
"thumbnailUrl":"/images/microworlds/hiphop/switch-wait.gif"
},
{
"type":"tip",
"title":"Start it!",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Repeat the Dance",
"thumbnails":[
{
"type":"tip",
"title":"Add another \"wait\"",
"thumbnailUrl":"/images/microworlds/hiphop/add-wait.gif"
},
{
"type":"tip",
"title":"Drag this block over",
"thumbnailUrl":"/images/microworlds/hiphop/add-repeat.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Play Music",
"thumbnails":[
{
"type":"tip",
"title":"Add music that repeats",
"thumbnailUrl":"/images/microworlds/hiphop/add-play-sound.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
}
]
},
{
"title":"Shadow Dance",
"thumbnails":[
{
"type":"tip",
"title":"Add this block",
"thumbnailUrl":"/images/microworlds/hiphop/shadow-dance.gif"
},
{
"type":"tip",
"title":"Start it",
"thumbnailUrl":"/images/microworlds/hiphop/green-flag.gif"
},
{
"type":"tip",
"title":"Click the stop sign to reset",
"thumbnailUrl":"/images/microworlds/hiphop/stop.gif"
}
]
},
{
"title":"More things to try",
"thumbnails":[
{
"type":"tip",
"title":"Try different dance moves",
"thumbnailUrl":"/images/microworlds/hiphop/change-dance-moves.gif"
},
{
"type":"tip",
"title":"Add more moves",
"thumbnailUrl":"/images/microworlds/hiphop/long-dance-script.png"
},
{
"type":"tip",
"title":"Change the timing",
"thumbnailUrl":"/images/microworlds/hiphop/change-dance-timing.png"
}
]
}
],
"_commentedout_starter_projects":[
{
"title":"Architecture Stamps Starter Project",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2445/6318.png",
"creator":"CSFirst",
"id":24456318
}
],
"community_projects":{
"featured_projects":[
{
"title":"tuting spins",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4827/0148.png",
"creator":"LHB97",
"id":48270148
},
{
"title":"Top Rock Giga",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4048/8688.png",
"creator":"designerd",
"id":40488688
},
{
"title":"EpicDance",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/4819/7838.png",
"creator":"codamax",
"id":48197838
},
{
"title":"Top Rockin in LA",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/3848/8304.png",
"creator":"sleggss",
"id":38488304
},
{
"title":"Silhouette Dance",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/2758/2882.png",
"creator":"natalie",
"id":27582882
}
],
"newest_projects":[
{
"title":"Crash Team Racing - Oxide Station Music (musica)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9331/8865.png",
"creator":"golden_freddy_fnaf5",
"id":93318865
},
{
"title":"DANCE OFF!!!!!!!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9287/4306.png",
"creator":"fjoyce11",
"id":92874306
},
{
"title":"hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9301/0585.png",
"creator":"Graystripe436",
"id":93010585
},
{
"title":"Hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9100/9512.png",
"creator":"krivas57",
"id":91009512
},
{
"title":"ElectroKill",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9039/0194.png",
"creator":"Snipeshot621",
"id":90390194
},
{
"title":"Breakbeat Mayhem",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9033/9020.png",
"creator":"Snipeshot621",
"id":90339020
},
{
"title":"(GetUpMusic)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8848/1759.png",
"creator":"XavierTheBrave",
"id":88481759
},
{
"title":"Hip Hop Dance",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8864/4804.png",
"creator":"kawaiigroovycat15",
"id":88644804
},
{
"title":"lezione hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8840/9021.png",
"creator":"MartinaStrazzer",
"id":88409021
},
{
"title":"Hip Hop Peeps",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8834/4657.png",
"creator":"dhajacob",
"id":88344657
},
{
"title":"Hiphop (voorbeeld signalen)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8812/7565.png",
"creator":"Juf-Pinky",
"id":88127565
},
{
"title":"Flashback: 1990",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8645/1526.png",
"creator":"Splo",
"id":86451526
},
{
"title":"Focus by Ariana Grande",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8687/1578.png",
"creator":"musical11",
"id":86871578
},
{
"title":"Clock",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8640/1798.png",
"creator":"IlikeMario5",
"id":86401798
},
{
"title":"Nice Hip Hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6195/5882.png",
"creator":"FireyDeath4",
"id":61955882
},
{
"title":"SUPRISE!!!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8629/4705.png",
"creator":"Jungster31",
"id":86294705
},
{
"title":"Harold Hip-Hop's Big Adventure",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8536/8362.png",
"creator":"Will64",
"id":85368362
},
{
"title":"epic dance(add your own dancers)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8308/2812.png",
"creator":"Zstrap10",
"id":83082812
},
{
"title":"Hip-Hop VS. Bad photoshop! [Hallowen update!]",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7579/4280.png",
"creator":"konradel",
"id":75794280
},
{
"title":"hip hop is in the air",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7653/2008.png",
"creator":"millamarwick",
"id":76532008
}
]
},
"design_challenge":{
"_commentedout_project_id":"89144801",
"studio_id":"1065372",
"studio1":[
{
"title":"Crash Team Racing - Oxide Station Music (musica)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9331/8865.png",
"creator":"golden_freddy_fnaf5",
"id":93318865
},
{
"title":"DANCE OFF!!!!!!!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9287/4306.png",
"creator":"fjoyce11",
"id":92874306
},
{
"title":"hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9301/0585.png",
"creator":"Graystripe436",
"id":93010585
},
{
"title":"Hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9100/9512.png",
"creator":"krivas57",
"id":91009512
},
{
"title":"ElectroKill",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9039/0194.png",
"creator":"Snipeshot621",
"id":90390194
},
{
"title":"Breakbeat Mayhem",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/9033/9020.png",
"creator":"Snipeshot621",
"id":90339020
},
{
"title":"(GetUpMusic)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8848/1759.png",
"creator":"XavierTheBrave",
"id":88481759
},
{
"title":"Hip Hop Dance",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8864/4804.png",
"creator":"kawaiigroovycat15",
"id":88644804
},
{
"title":"lezione hip hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8840/9021.png",
"creator":"MartinaStrazzer",
"id":88409021
},
{
"title":"Hip Hop Peeps",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8834/4657.png",
"creator":"dhajacob",
"id":88344657
},
{
"title":"Hiphop (voorbeeld signalen)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8812/7565.png",
"creator":"Juf-Pinky",
"id":88127565
},
{
"title":"Flashback: 1990",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8645/1526.png",
"creator":"Splo",
"id":86451526
},
{
"title":"Focus by Ariana Grande",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8687/1578.png",
"creator":"musical11",
"id":86871578
},
{
"title":"Clock",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8640/1798.png",
"creator":"IlikeMario5",
"id":86401798
},
{
"title":"Nice Hip Hop",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/6195/5882.png",
"creator":"FireyDeath4",
"id":61955882
},
{
"title":"SUPRISE!!!",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8629/4705.png",
"creator":"Jungster31",
"id":86294705
}
],
"studio2":[
{
"title":"Harold Hip-Hop's Big Adventure",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8536/8362.png",
"creator":"Will64",
"id":85368362
},
{
"title":"epic dance(add your own dancers)",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/8308/2812.png",
"creator":"Zstrap10",
"id":83082812
},
{
"title":"Hip-Hop VS. Bad photoshop! [Hallowen update!]",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7579/4280.png",
"creator":"konradel",
"id":75794280
},
{
"title":"hip hop is in the air",
"type":"project",
"remixers_count":168,
"love_count":3062,
"thumbnail_url":"//cdn.scratch.mit.edu/static/site/projects/thumbnails/7653/2008.png",
"creator":"millamarwick",
"id":76532008
}
]
},
"show_forum":false
}

View file

@ -0,0 +1,6 @@
var render = require('../../lib/render.jsx');
var Microworld = require('../../components/microworld/microworld.jsx');
var microworldData = require('./microworld_hiphop.json');
render(<Microworld microworldData={microworldData} />, document.getElementById('view'));

View file

@ -18,6 +18,8 @@
"intro.seeExamples": "SEE EXAMPLES",
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
"intro.tryItOut": "TRY IT OUT",
"intro.description": "A creative learning community with <span class=\"project-count\"> {value} </span>projects shared",
"intro.defaultDescription": "A creative learning community with <span class=\"project-count\"> over 13 million </span>projects shared",
"news.scratchNews": "Scratch News",

View file

@ -1,10 +1,12 @@
var connect = require('react-redux').connect;
var injectIntl = require('react-intl').injectIntl;
var omit = require('lodash.omit');
var React = require('react');
var render = require('../../lib/render.jsx');
var actions = require('../../redux/actions.js');
var Api = require('../../mixins/api.jsx');
var Session = require('../../mixins/session.jsx');
var Activity = require('../../components/activity/activity.jsx');
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
@ -15,6 +17,7 @@ var Carousel = require('../../components/carousel/carousel.jsx');
var Intro = require('../../components/intro/intro.jsx');
var Modal = require('../../components/modal/modal.jsx');
var News = require('../../components/news/news.jsx');
var Page = require('../../components/page/page.jsx');
var Welcome = require('../../components/welcome/welcome.jsx');
require('./splash.scss');
@ -22,12 +25,11 @@ require('./splash.scss');
var Splash = injectIntl(React.createClass({
type: 'Splash',
mixins: [
Api,
Session
Api
],
getInitialState: function () {
return {
projectCount: 10569070,
projectCount: 13000000, // gets the shared project count
activity: [], // recent social actions taken by users someone is following
news: [], // gets news posts from the scratch Tumblr
featuredCustom: {}, // custom homepage rows, such as "Projects by Scratchers I'm Following"
@ -36,9 +38,14 @@ var Splash = injectIntl(React.createClass({
refreshCacheStatus: 'notrequested'
};
},
componentDidUpdate: function (prevProps, prevState) {
if (this.state.session.user != prevState.session.user) {
if (this.state.session.user) {
getDefaultProps: function () {
return {
session: {}
};
},
componentDidUpdate: function (prevProps) {
if (this.props.session.user != prevProps.session.user) {
if (this.props.session.user) {
this.getActivity();
this.getFeaturedCustom();
this.getNews();
@ -57,7 +64,7 @@ var Splash = injectIntl(React.createClass({
},
componentDidMount: function () {
this.getFeaturedGlobal();
if (this.state.session.user) {
if (this.props.session.user) {
this.getActivity();
this.getFeaturedCustom();
this.getNews();
@ -83,7 +90,7 @@ var Splash = injectIntl(React.createClass({
},
getActivity: function () {
this.api({
uri: '/proxy/users/' + this.state.session.user.username + '/activity?limit=5'
uri: '/proxy/users/' + this.props.session.user.username + '/activity?limit=5'
}, function (err, body) {
if (!err) this.setState({activity: body});
}.bind(this));
@ -97,7 +104,7 @@ var Splash = injectIntl(React.createClass({
},
getFeaturedCustom: function () {
this.api({
uri: '/proxy/users/' + this.state.session.user.id + '/featured'
uri: '/proxy/users/' + this.props.session.user.id + '/featured'
}, function (err, body) {
if (!err) this.setState({featuredCustom: body});
}.bind(this));
@ -160,20 +167,20 @@ var Splash = injectIntl(React.createClass({
useCsrf: true,
json: {cue: cue, value: false}
}, function (err) {
if (!err) window.refreshSession();
if (!err) this.props.dispatch(actions.refreshSession());
});
},
shouldShowWelcome: function () {
if (!this.state.session.user || !this.state.session.flags.show_welcome) return false;
if (!this.props.session.user || !this.props.session.flags.show_welcome) return false;
return (
new Date(this.state.session.user.dateJoined) >
new Date(this.props.session.user.dateJoined) >
new Date(new Date - 2*7*24*60*60*1000) // Two weeks ago
);
},
shouldShowEmailConfirmation: function () {
return (
this.state.session.user && this.state.session.flags.has_outstanding_email_confirmation &&
this.state.session.flags.confirm_email_banner);
this.props.session.user && this.props.session.flags.has_outstanding_email_confirmation &&
this.props.session.flags.confirm_email_banner);
},
renderHomepageRows: function () {
var formatMessage = this.props.intl.formatMessage;
@ -232,7 +239,7 @@ var Splash = injectIntl(React.createClass({
);
}
if (this.state.session.user &&
if (this.props.session.user &&
this.state.featuredGlobal.community_newest_projects &&
this.state.featuredGlobal.community_newest_projects.length > 0) {
@ -319,8 +326,9 @@ var Splash = injectIntl(React.createClass({
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
var homepageCacheState = this.getHomepageRefreshStatus();
var formatMessage = this.props.intl.formatMessage;
var formatHTMLMessage = this.props.intl.formatHTMLMessage;
var formatNumber = this.props.intl.formatNumber;
var formatMessage = this.props.intl.formatMessage;
var messages = {
'general.viewAll': formatMessage({id: 'general.viewAll'}),
'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
@ -336,6 +344,12 @@ var Splash = injectIntl(React.createClass({
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
'intro.tryItOut': formatMessage({id: 'intro.tryItOut'})
};
if (this.state.projectCount === this.getInitialState().projectCount) {
messages['intro.description'] = formatHTMLMessage({id: 'intro.defaultDescription'});
} else {
var count = formatNumber(this.state.projectCount);
messages['intro.description'] = formatHTMLMessage({id: 'intro.description'}, {value: count});
}
return (
<div className="splash">
@ -357,7 +371,7 @@ var Splash = injectIntl(React.createClass({
</Modal>
] : []}
<div key="inner" className="inner">
{this.state.session.user ? [
{this.props.session.user ? [
<div key="header" className="splash-header">
{this.shouldShowWelcome() ? [
<Welcome key="welcome"
@ -411,4 +425,12 @@ var Splash = injectIntl(React.createClass({
}
}));
render(<Splash />, document.getElementById('view'));
var mapStateToProps = function (state) {
return {
session: state.session
};
};
var ConnectedSplash = connect(mapStateToProps)(Splash);
render(<Page><ConnectedSplash /></Page>, document.getElementById('app'));

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
static/images/forum-image.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
static/images/jobs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Some files were not shown because too many files have changed in this diff Show more