|
@ -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
|
||||
|
|
12
.travis.yml
|
@ -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:
|
||||
|
|
5
Makefile
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (var i in views) {
|
||||
var viewTranslations = generalLocales;
|
||||
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);
|
||||
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 (i in views) {
|
||||
var viewTranslations = viewLocales['general'];
|
||||
if (views[i] in viewLocales) {
|
||||
viewTranslations = merge(viewLocales[views[i]], viewTranslations);
|
||||
}
|
||||
if (views[i] in localizedAssetUrls) {
|
||||
viewTranslations = merge(viewTranslations, localizedAssetUrls[[views[i]]]);
|
||||
}
|
||||
localeCompare.writeTranslationsToJS(outputDir, views[i], viewTranslations);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
52
bin/lib/localized-urls.json
Normal 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"
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
"cat": "Meow",
|
||||
"nl": "Nederlands",
|
||||
"nb": "Norsk Bokmål",
|
||||
"nn": "Norsk Nynorsk",
|
||||
"uz": "Oʻzbekcha",
|
||||
"pl": "Polski",
|
||||
"pt": "Português",
|
||||
|
|
30
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) -->
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -51,21 +51,22 @@
|
|||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
font-size: small;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
.button-row {
|
||||
display: flex;
|
||||
font-size: small;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&.inprogress {
|
||||
background-color: $ui-dark-gray;
|
||||
color: $type-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
|
||||
&.inprogress {
|
||||
background-color: $ui-dark-gray;
|
||||
color: $type-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ $navigation-height: 50px;
|
|||
text-align: center;
|
||||
line-height: $navigation-height;
|
||||
|
||||
&, a {
|
||||
&,
|
||||
a {
|
||||
color: $ui-white;
|
||||
}
|
||||
|
||||
|
@ -23,14 +24,14 @@ $navigation-height: 50px;
|
|||
|
||||
.close {
|
||||
float: right;
|
||||
margin-top: $navigation-height/4;
|
||||
border-radius: $navigation-height/4;
|
||||
margin-top: $navigation-height / 4;
|
||||
border-radius: $navigation-height / 4;
|
||||
background-color: $box-shadow-gray;
|
||||
width: $navigation-height/2;
|
||||
height: $navigation-height/2;
|
||||
width: $navigation-height / 2;
|
||||
height: $navigation-height / 2;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
line-height: $navigation-height/2;
|
||||
line-height: $navigation-height / 2;
|
||||
color: $ui-white;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.slick-next,
|
||||
.slick-prev {
|
||||
margin-top: -$icon-size/2;
|
||||
margin-top: -$icon-size / 2;
|
||||
width: $icon-size;
|
||||
height: $icon-size;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -57,14 +57,6 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&:hover .costume-1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .costume-2 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.circle {
|
||||
display: block;
|
||||
top: 15px;
|
||||
|
@ -98,52 +90,74 @@
|
|||
}
|
||||
|
||||
|
||||
&.sprite-1 .circle {
|
||||
background-color: $splash-green;
|
||||
&.sprite-1 {
|
||||
.circle {
|
||||
background-color: $splash-green;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 60px;
|
||||
left: 50px;
|
||||
color: $splash-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-2 .circle {
|
||||
background-color: $splash-pink;
|
||||
&.sprite-2 {
|
||||
.circle {
|
||||
background-color: $splash-pink;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 77px;
|
||||
left: 50px;
|
||||
color: $splash-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-3 .circle {
|
||||
background-color: $splash-blue;
|
||||
&.sprite-3 {
|
||||
.circle {
|
||||
background-color: $splash-blue;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 37px;
|
||||
left: 45px;
|
||||
color: $splash-blue;
|
||||
}
|
||||
|
||||
.subtext {
|
||||
top: 63px;
|
||||
left: 60px;
|
||||
color: $ui-white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover.sprite-1 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-green;
|
||||
}
|
||||
&:hover {
|
||||
.costume-1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover.sprite-2 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-pink;
|
||||
}
|
||||
.costume-2 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover.sprite-3 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-blue;
|
||||
}
|
||||
&.sprite-1 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-1 .text {
|
||||
top: 60px;
|
||||
left: 50px;
|
||||
color: $splash-green;
|
||||
}
|
||||
&.sprite-2 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-2 .text {
|
||||
top: 77px;
|
||||
left: 50px;
|
||||
color: $splash-pink;
|
||||
}
|
||||
|
||||
&.sprite-3 .text {
|
||||
top: 37px;
|
||||
left: 45px;
|
||||
color: $splash-blue;
|
||||
}
|
||||
|
||||
&.sprite-3 .subtext {
|
||||
top: 63px;
|
||||
left: 60px;
|
||||
color: $ui-white;
|
||||
&.sprite-3 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.language-chooser {
|
||||
|
||||
}
|
|
@ -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?'} />
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
a {
|
||||
margin-top: 15px;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.error {
|
||||
border: 1px solid $active-dark-gray;
|
||||
|
|
246
src/components/microworld/microworld.jsx
Normal 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;
|
185
src/components/microworld/microworld.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
@import "../../colors";
|
||||
|
||||
&.ReactModal__Content {
|
||||
.ReactModal__Content {
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
@ -11,10 +11,10 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-top: -$modal-close-size/2;
|
||||
margin-right: -$modal-close-size/2;
|
||||
margin-top: -$modal-close-size / 2;
|
||||
margin-right: -$modal-close-size / 2;
|
||||
border: 2px solid $ui-border;
|
||||
border-radius: $modal-close-size/2;
|
||||
border-radius: $modal-close-size / 2;
|
||||
background-color: $active-dark-gray;
|
||||
cursor: pointer;
|
||||
width: $modal-close-size;
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -$arrow-border-width/2;
|
||||
top: -$arrow-border-width / 2;
|
||||
right: 10%;
|
||||
|
||||
transform: rotate(45deg);
|
||||
|
|
|
@ -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'} />
|
||||
</a>
|
||||
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);
|
||||
|
|
|
@ -51,72 +51,73 @@
|
|||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 10px;
|
||||
.logo {
|
||||
margin-right: 10px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
a {
|
||||
display: block;
|
||||
transition: .15s ease all;
|
||||
margin: 0 6px 0 0;
|
||||
border: 0;
|
||||
|
||||
background-image: url("/images/logo_sm.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 95%;
|
||||
width: 81px;
|
||||
height: 50px;
|
||||
|
||||
&:hover {
|
||||
transition: .15s ease all;
|
||||
margin: 0 6px 0 0;
|
||||
border: 0;
|
||||
|
||||
background-image: url("/images/logo_sm.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 95%;
|
||||
width: 81px;
|
||||
height: 50px;
|
||||
|
||||
&:hover {
|
||||
transition: .15s ease all;
|
||||
background-size: 100%;
|
||||
}
|
||||
background-size: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 17px 15px 0 15px;
|
||||
height: 33px;
|
||||
.link {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 17px 15px 0 15px;
|
||||
height: 33px;
|
||||
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
color: $type-white;
|
||||
font-size: .85rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> a:hover {
|
||||
background-color: $active-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 20px;
|
||||
border-right: 0;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
color: $type-white;
|
||||
flex-grow: 3;
|
||||
font-size: .85rem;
|
||||
font-weight: bold;
|
||||
|
||||
.ie9 & {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
&:hover {
|
||||
background-color: $active-gray;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 20px;
|
||||
border-right: 0;
|
||||
color: $type-white;
|
||||
flex-grow: 3;
|
||||
|
||||
.ie9 & {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background-color: $active-gray;
|
||||
height: 14px;
|
||||
|
||||
&[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,122 +149,125 @@
|
|||
transition: .15s ease background-color;
|
||||
background-color: $active-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.ie9 input[type=text] {
|
||||
width: 70px;
|
||||
.ie9 & {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
align-self: flex-end;
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
align-self: flex-end;
|
||||
|
||||
.ie9 & {
|
||||
float: none;
|
||||
}
|
||||
.ie9 & {
|
||||
float: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
a {
|
||||
&:hover {
|
||||
background-color: $active-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.messages,
|
||||
.mystuff {
|
||||
> a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 45%;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
width: 30px;
|
||||
overflow: hidden;
|
||||
text-indent: 50px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.messages,
|
||||
.mystuff {
|
||||
> a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 45%;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
width: 30px;
|
||||
overflow: hidden;
|
||||
text-indent: 50px;
|
||||
white-space: nowrap;
|
||||
|
||||
> a:hover {
|
||||
&:hover {
|
||||
background-size: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.messages {
|
||||
> a {
|
||||
background-image: url("/images/nav-notifications.png");
|
||||
}
|
||||
}
|
||||
|
||||
.messageCount {
|
||||
display: none;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: .25rem;
|
||||
border-radius: 1rem;
|
||||
background-color: $ui-orange;
|
||||
padding: 0 .25rem;
|
||||
text-indent: 0;
|
||||
line-height: 1rem;
|
||||
color: $type-white;
|
||||
font-size: .7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.messages {
|
||||
> a {
|
||||
background-image: url("/images/nav-notifications.png");
|
||||
}
|
||||
|
||||
.mystuff {
|
||||
> a {
|
||||
background-image: url("/images/mystuff.png");
|
||||
}
|
||||
}
|
||||
.message-count {
|
||||
display: none;
|
||||
|
||||
.login-dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.account-nav {
|
||||
.userInfo {
|
||||
padding-top: 14px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .8125rem;
|
||||
font-weight: normal;
|
||||
|
||||
.avatar {
|
||||
margin-right: 10px;
|
||||
border-radius: 3px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
|
||||
background-image: url("/images/dropdown.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
content: " ";
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
&.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: .25rem;
|
||||
border-radius: 1rem;
|
||||
background-color: $ui-orange;
|
||||
padding: 0 .25rem;
|
||||
text-indent: 0;
|
||||
line-height: 1rem;
|
||||
color: $type-white;
|
||||
font-size: .7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mystuff {
|
||||
> a {
|
||||
background-image: url("/images/mystuff.png");
|
||||
}
|
||||
}
|
||||
|
||||
.login-dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.account-nav {
|
||||
.user-info {
|
||||
padding-top: 14px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .8125rem;
|
||||
font-weight: normal;
|
||||
|
||||
.avatar {
|
||||
margin-right: 10px;
|
||||
border-radius: 3px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
|
||||
background-image: url("/images/dropdown.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
content: " ";
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
src/components/nestedcarousel/nestedcarousel.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
75
src/components/nestedcarousel/nestedcarousel.jsx
Normal 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;
|
20
src/components/nestedcarousel/nestedcarousel.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,10 +46,10 @@
|
|||
color: $type-gray;
|
||||
font-size: .85rem;
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(even) {
|
||||
border-top: 1px solid $ui-border;
|
||||
border-bottom: 1px solid $ui-border;
|
||||
&:nth-child(even) {
|
||||
border-top: 1px solid $ui-border;
|
||||
border-bottom: 1px solid $ui-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
25
src/components/page/page.jsx
Normal 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;
|
|
@ -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 {
|
||||
animation-delay: $delay;
|
||||
-webkit-animation-delay: $delay;
|
||||
|
||||
&:before {
|
||||
animation-delay: $delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@keyframes circleFadeDelay {
|
||||
0%, 39%, 100% { opacity: 0; }
|
||||
40% { opacity: 1; }
|
||||
0%,
|
||||
39%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
.thumbnail {
|
||||
.thumbnail-image {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumbnail-image img {
|
||||
margin-bottom: 2px;
|
||||
border: 1px solid $ui-border;
|
||||
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,12 +58,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thumbnail-loves:before {
|
||||
background-image: url("/svgs/love/love_type-gray.svg");
|
||||
.thumbnail-loves {
|
||||
&:before {
|
||||
background-image: url("/svgs/love/love_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-remixes:before {
|
||||
background-image: url("/svgs/remix/remix_type-gray.svg");
|
||||
.thumbnail-remixes {
|
||||
&:before {
|
||||
background-image: url("/svgs/remix/remix_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
&.project {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
50
src/init.js
|
@ -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();
|
||||
})();
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var cookie = require('cookie');
|
||||
var xhr = require('xhr');
|
||||
var pako = require('pako');
|
||||
|
||||
/**
|
||||
* Module that handles coookie interactions.
|
||||
|
@ -9,41 +10,70 @@ 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) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
// 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);
|
||||
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
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 obj[name];
|
||||
};
|
||||
return callback(null, strData);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
},
|
||||
get: function (name, callback) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
|
||||
Jar.use = function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
return obj[name];
|
||||
},
|
||||
use: function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Jar.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;
|
||||
},
|
||||
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;
|
||||
|
|
|
@ -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(
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
{jsx}
|
||||
</IntlProvider>,
|
||||
ReactDOM.render(
|
||||
<StoreProvider store={store}>
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
{jsx}
|
||||
</IntlProvider>
|
||||
</StoreProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
window._renderedComponents.push(component);
|
||||
// Get initial session
|
||||
store.dispatch(actions.refreshSession());
|
||||
};
|
||||
|
||||
module.exports = render;
|
||||
|
|
|
@ -32,29 +32,31 @@ h1 {
|
|||
|
||||
h4 {
|
||||
line-height: 1.1rem;
|
||||
font-size: 1.0rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p.legal {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
p {
|
||||
&.legal {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: $link-blue;
|
||||
}
|
||||
/* Links */
|
||||
a {
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
color: $link-blue;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Classes */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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;
|
|
@ -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'));
|
||||
|
|
|
@ -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
|
@ -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'));
|
43
src/views/cards/cards.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/views/cards/l10n-static.json
Normal 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
|
@ -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"
|
||||
}
|
|
@ -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>
|
||||
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
|
||||
<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'));
|
|
@ -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'));
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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
|
@ -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":"We’re 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."
|
||||
}
|
829
src/views/microworld_art/microworld_art.json
Normal 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
|
||||
}
|
6
src/views/microworld_art/microworld_art.jsx
Normal 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'));
|
487
src/views/microworld_fashion/microworld_fashion.json
Normal 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&mh=583&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
|
||||
}
|
6
src/views/microworld_fashion/microworld_fashion.jsx
Normal 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'));
|
544
src/views/microworld_hiphop/microworld_hiphop.json
Normal 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
|
||||
}
|
6
src/views/microworld_hiphop/microworld_hiphop.jsx
Normal 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'));
|
|
@ -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",
|
||||
|
||||
|
|
|
@ -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'));
|
||||
|
|
BIN
static/images/cards/card-use-overview.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
static/images/cards/cards-dance.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
static/images/cards/cards-hide.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
static/images/cards/cards-name.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
static/images/cards/cards-pong.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
static/images/cards/cards-starter.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/cards/cards-story.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
static/images/forum-image.png
Executable file
After Width: | Height: | Size: 94 KiB |
BIN
static/images/jobs.png
Normal file
After Width: | Height: | Size: 678 KiB |
BIN
static/images/microworlds/fashion/accessories-sprite.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
static/images/microworlds/fashion/click-block-color.gif
Executable file
After Width: | Height: | Size: 137 KiB |
BIN
static/images/microworlds/fashion/click-block-costume.gif
Executable file
After Width: | Height: | Size: 119 KiB |
BIN
static/images/microworlds/fashion/click-hat.gif
Executable file
After Width: | Height: | Size: 13 KiB |
BIN
static/images/microworlds/fashion/click-shirt.gif
Executable file
After Width: | Height: | Size: 11 KiB |
BIN
static/images/microworlds/fashion/green-flag.gif
Executable file
After Width: | Height: | Size: 83 KiB |
BIN
static/images/microworlds/fashion/hats-sprite.png
Executable file
After Width: | Height: | Size: 3.2 KiB |
BIN
static/images/microworlds/fashion/next-costume.png
Executable file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/images/microworlds/fashion/person-sprite.png
Executable file
After Width: | Height: | Size: 3.6 KiB |
BIN
static/images/microworlds/fashion/say-something.gif
Executable file
After Width: | Height: | Size: 195 KiB |
BIN
static/images/microworlds/fashion/shirts-sprite.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
static/images/microworlds/fashion/switch-wait.gif
Executable file
After Width: | Height: | Size: 585 KiB |
BIN
static/images/microworlds/fashion/when-clicked-color.gif
Executable file
After Width: | Height: | Size: 38 KiB |
BIN
static/images/microworlds/fashion/when-clicked-costume.gif
Executable file
After Width: | Height: | Size: 25 KiB |
BIN
static/images/microworlds/fashion/when-clicked-goto.gif
Executable file
After Width: | Height: | Size: 30 KiB |
BIN
static/images/microworlds/fashion/when-clicked-say.gif
Executable file
After Width: | Height: | Size: 38 KiB |
BIN
static/images/microworlds/hiphop/add-play-sound.gif
Executable file
After Width: | Height: | Size: 260 KiB |
BIN
static/images/microworlds/hiphop/add-repeat.gif
Executable file
After Width: | Height: | Size: 622 KiB |
BIN
static/images/microworlds/hiphop/add-wait.gif
Executable file
After Width: | Height: | Size: 193 KiB |
BIN
static/images/microworlds/hiphop/block-stack.png
Executable file
After Width: | Height: | Size: 7.5 KiB |
BIN
static/images/microworlds/hiphop/change-dance-moves.gif
Executable file
After Width: | Height: | Size: 471 KiB |
BIN
static/images/microworlds/hiphop/change-dance-timing.png
Executable file
After Width: | Height: | Size: 777 KiB |