mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
Merge pull request #1789 from LLK/hotfix/es6-upgrade
[Master] Upgrade to ES6, Take II
This commit is contained in:
commit
51533bbf39
188 changed files with 13697 additions and 11511 deletions
3
.babelrc
Normal file
3
.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"presets": ["es2015", "react"],
|
||||||
|
}
|
29
.eslintrc
29
.eslintrc
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
"rules": {
|
|
||||||
"curly": [2, "multi-line"],
|
|
||||||
"eol-last": [2],
|
|
||||||
"indent": [2, 4],
|
|
||||||
"linebreak-style": [2, "unix"],
|
|
||||||
"max-len": [2, 120, 4, {"ignoreUrls": true}],
|
|
||||||
"no-trailing-spaces": [2, { "skipBlankLines": true }],
|
|
||||||
"no-unused-vars": [2, {"args": "after-used", "varsIgnorePattern": "^_"}],
|
|
||||||
"quotes": [2, "single"],
|
|
||||||
"semi": [2, "always"],
|
|
||||||
"space-before-function-paren": [2, "always"],
|
|
||||||
"strict": [2, "never"]
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"formatMessage": true
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"react",
|
|
||||||
"json"
|
|
||||||
],
|
|
||||||
"extends": "eslint:recommended"
|
|
||||||
}
|
|
4
.eslintrc.js
Normal file
4
.eslintrc.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: ['scratch', 'scratch/node'],
|
||||||
|
plugins: ['json']
|
||||||
|
};
|
|
@ -133,3 +133,9 @@ file_filter = localizations/conference-index/<lang>.json
|
||||||
source_file = src/views/conference/2018/index/l10n.json
|
source_file = src/views/conference/2018/index/l10n.json
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
|
|
||||||
|
[scratch-website.preview-faq-l10njson]
|
||||||
|
file_filter = localizations/preview-faq/<lang>.json
|
||||||
|
source_file = src/views/preview-faq/l10n.json
|
||||||
|
source_lang = en
|
||||||
|
type = KEYVALUEJSON
|
|
@ -3,7 +3,7 @@ var defaults = require('lodash.defaults');
|
||||||
var fastlyConfig = require('./lib/fastly-config-methods');
|
var fastlyConfig = require('./lib/fastly-config-methods');
|
||||||
const languages = require('../languages.json');
|
const languages = require('../languages.json');
|
||||||
|
|
||||||
var route_json = require('../src/routes.json');
|
var routeJson = require('../src/routes.json');
|
||||||
|
|
||||||
const FASTLY_SERVICE_ID = process.env.FASTLY_SERVICE_ID || '';
|
const FASTLY_SERVICE_ID = process.env.FASTLY_SERVICE_ID || '';
|
||||||
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME || '';
|
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME || '';
|
||||||
|
@ -15,10 +15,10 @@ var extraAppRoutes = [
|
||||||
// TODO: Should this be added for every route?
|
// TODO: Should this be added for every route?
|
||||||
'/\\?',
|
'/\\?',
|
||||||
// View html
|
// View html
|
||||||
'/[^\/]*\.html$'
|
'/[^/]*.html$'
|
||||||
];
|
];
|
||||||
|
|
||||||
var routes = route_json.map(function (route) {
|
var routes = routeJson.map(function (route) {
|
||||||
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ async.auto({
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
// Validate latest version before continuing
|
// Validate latest version before continuing
|
||||||
if (response.active || response.locked) {
|
if (response.active || response.locked) {
|
||||||
fastly.cloneVersion(response.number, function (err, response) {
|
fastly.cloneVersion(response.number, function (e, resp) {
|
||||||
if (err) return cb('Failed to clone latest version: ' + err);
|
if (e) return cb('Failed to clone latest version: ' + e);
|
||||||
cb(null, response.number);
|
cb(null, resp.number);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cb(null, response.number);
|
cb(null, response.number);
|
||||||
|
@ -46,11 +46,11 @@ async.auto({
|
||||||
var recvCondition = '' +
|
var recvCondition = '' +
|
||||||
'if (' + notPassStatement + ') {\n' +
|
'if (' + notPassStatement + ') {\n' +
|
||||||
' set req.backend = F_s3;\n' +
|
' set req.backend = F_s3;\n' +
|
||||||
' set req.http.host = \"' + S3_BUCKET_NAME + '\";\n' +
|
' set req.http.host = "' + S3_BUCKET_NAME + '";\n' +
|
||||||
'} else {\n' +
|
'} else {\n' +
|
||||||
' if (!req.http.Fastly-FF) {\n' +
|
' if (!req.http.Fastly-FF) {\n' +
|
||||||
' if (req.http.X-Forwarded-For) {\n' +
|
' if (req.http.X-Forwarded-For) {\n' +
|
||||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
|
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;\n' +
|
||||||
' } else {\n' +
|
' } else {\n' +
|
||||||
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
||||||
' }\n' +
|
' }\n' +
|
||||||
|
@ -171,20 +171,19 @@ async.auto({
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
cb(null, headers);
|
cb(null, headers);
|
||||||
});
|
});
|
||||||
}]},
|
}]
|
||||||
function (err, results) {
|
}, function (err, results) {
|
||||||
if (err) throw new Error(err);
|
if (err) throw new Error(err);
|
||||||
if (process.env.FASTLY_ACTIVATE_CHANGES) {
|
if (process.env.FASTLY_ACTIVATE_CHANGES) {
|
||||||
fastly.activateVersion(results.version, function (err, response) {
|
fastly.activateVersion(results.version, function (e, resp) {
|
||||||
if (err) throw new Error(err);
|
if (err) throw new Error(e);
|
||||||
process.stdout.write('Successfully configured and activated version ' + response.number + '\n');
|
process.stdout.write('Successfully configured and activated version ' + resp.number + '\n');
|
||||||
if (process.env.FASTLY_PURGE_ALL) {
|
if (process.env.FASTLY_PURGE_ALL) {
|
||||||
fastly.purgeAll(FASTLY_SERVICE_ID, function (err) {
|
fastly.purgeAll(FASTLY_SERVICE_ID, function (error) {
|
||||||
if (err) throw new Error(err);
|
if (error) throw new Error(error);
|
||||||
process.stdout.write('Purged all.\n');
|
process.stdout.write('Purged all.\n');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
|
@ -24,9 +24,9 @@ var FastlyConfigMethods = {
|
||||||
*/
|
*/
|
||||||
getViewPaths: function (routes) {
|
getViewPaths: function (routes) {
|
||||||
return routes.reduce(function (paths, route) {
|
return routes.reduce(function (paths, route) {
|
||||||
var path = route.routeAlias || route.pattern;
|
var p = route.routeAlias || route.pattern;
|
||||||
if (paths.indexOf(path) === -1) {
|
if (paths.indexOf(p) === -1) {
|
||||||
paths.push(path);
|
paths.push(p);
|
||||||
}
|
}
|
||||||
return paths;
|
return paths;
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -39,7 +39,7 @@ var FastlyConfigMethods = {
|
||||||
* 2. /path/:arg([regex]) – :arg is removed, leaving just /path/([regex])
|
* 2. /path/:arg([regex]) – :arg is removed, leaving just /path/([regex])
|
||||||
*/
|
*/
|
||||||
expressPatternToRegex: function (pattern) {
|
expressPatternToRegex: function (pattern) {
|
||||||
pattern = pattern.replace(/(:\w+)(\([^\)]+\))/gi, '$2');
|
pattern = pattern.replace(/(:\w+)(\([^)]+\))/gi, '$2');
|
||||||
return pattern.replace(/(:\w+)/gi, '.+?');
|
return pattern.replace(/(:\w+)/gi, '.+?');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ var FastlyConfigMethods = {
|
||||||
return 'redirects/' + route.pattern;
|
return 'redirects/' + route.pattern;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns custom vcl configuration as a string that sets the varnish
|
* Returns custom vcl configuration as a string that sets the varnish
|
||||||
* Time to Live (TTL) for responses that come from s3.
|
* Time to Live (TTL) for responses that come from s3.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,8 +19,8 @@ module.exports = function (apiKey, serviceId) {
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
fastly.getFastlyAPIPrefix = function (serviceId, version) {
|
fastly.getFastlyAPIPrefix = function (servId, version) {
|
||||||
return '/service/' + encodeURIComponent(serviceId) + '/version/' + version;
|
return '/service/' + encodeURIComponent(servId) + '/version/' + version;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -32,15 +32,15 @@ module.exports = function (apiKey, serviceId) {
|
||||||
if (!this.serviceId) {
|
if (!this.serviceId) {
|
||||||
return cb('Failed to get latest version. No serviceId configured');
|
return cb('Failed to get latest version. No serviceId configured');
|
||||||
}
|
}
|
||||||
var url = '/service/'+ encodeURIComponent(this.serviceId) +'/version';
|
var url = '/service/' + encodeURIComponent(this.serviceId) + '/version';
|
||||||
this.request('GET', url, function (err, versions) {
|
this.request('GET', url, function (err, versions) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb('Failed to fetch versions: ' + err);
|
return cb('Failed to fetch versions: ' + err);
|
||||||
}
|
}
|
||||||
var latestVersion = versions.reduce(function (latestVersion, version) {
|
var latestVersion = versions.reduce(function (lateVersion, version) {
|
||||||
if (!latestVersion) return version;
|
if (!lateVersion) return version;
|
||||||
if (version.number > latestVersion.number) return version;
|
if (version.number > lateVersion.number) return version;
|
||||||
return latestVersion;
|
return lateVersion;
|
||||||
});
|
});
|
||||||
return cb(null, latestVersion);
|
return cb(null, latestVersion);
|
||||||
});
|
});
|
||||||
|
@ -63,16 +63,16 @@ module.exports = function (apiKey, serviceId) {
|
||||||
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/condition';
|
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/condition';
|
||||||
return this.request('PUT', putUrl, condition, function (err, response) {
|
return this.request('PUT', putUrl, condition, function (err, response) {
|
||||||
if (err && err.statusCode === 404) {
|
if (err && err.statusCode === 404) {
|
||||||
this.request('POST', postUrl, condition, function (err, response) {
|
this.request('POST', postUrl, condition, function (e, resp) {
|
||||||
if (err) {
|
if (e) {
|
||||||
return cb('Failed while inserting condition \"' + condition.statement + '\": ' + err);
|
return cb('Failed while inserting condition "' + condition.statement + '": ' + e);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, resp);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb('Failed to update condition \"' + condition.statement + '\": ' + err);
|
return cb('Failed to update condition "' + condition.statement + '": ' + err);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, response);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
@ -95,11 +95,11 @@ module.exports = function (apiKey, serviceId) {
|
||||||
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/header';
|
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/header';
|
||||||
return this.request('PUT', putUrl, header, function (err, response) {
|
return this.request('PUT', putUrl, header, function (err, response) {
|
||||||
if (err && err.statusCode === 404) {
|
if (err && err.statusCode === 404) {
|
||||||
this.request('POST', postUrl, header, function (err, response) {
|
this.request('POST', postUrl, header, function (e, resp) {
|
||||||
if (err) {
|
if (e) {
|
||||||
return cb('Failed to insert header: ' + err);
|
return cb('Failed to insert header: ' + e);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, resp);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,11 +127,11 @@ module.exports = function (apiKey, serviceId) {
|
||||||
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/response_object';
|
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/response_object';
|
||||||
return this.request('PUT', putUrl, responseObj, function (err, response) {
|
return this.request('PUT', putUrl, responseObj, function (err, response) {
|
||||||
if (err && err.statusCode === 404) {
|
if (err && err.statusCode === 404) {
|
||||||
this.request('POST', postUrl, responseObj, function (err, response) {
|
this.request('POST', postUrl, responseObj, function (e, resp) {
|
||||||
if (err) {
|
if (e) {
|
||||||
return cb('Failed to insert response object: ' + err);
|
return cb('Failed to insert response object: ' + e);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, resp);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ module.exports = function (apiKey, serviceId) {
|
||||||
this.request('PUT', url, cb);
|
this.request('PUT', url, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Upsert a custom vcl file. Attempts a PUT, and falls back
|
* Upsert a custom vcl file. Attempts a PUT, and falls back
|
||||||
* to POST if not there already.
|
* to POST if not there already.
|
||||||
*
|
*
|
||||||
|
@ -186,16 +186,16 @@ module.exports = function (apiKey, serviceId) {
|
||||||
return this.request('PUT', url, content, function (err, response) {
|
return this.request('PUT', url, content, function (err, response) {
|
||||||
if (err && err.statusCode === 404) {
|
if (err && err.statusCode === 404) {
|
||||||
content.name = name;
|
content.name = name;
|
||||||
this.request('POST', postUrl, content, function (err, response) {
|
this.request('POST', postUrl, content, function (e, resp) {
|
||||||
if (err) {
|
if (e) {
|
||||||
return cb('Failed while adding custom vcl \"' + name + '\": ' + err);
|
return cb('Failed while adding custom vcl "' + name + '": ' + e);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, resp);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb('Failed to update custom vcl \"' + name + '\": ' + err);
|
return cb('Failed to update custom vcl "' + name + '": ' + err);
|
||||||
}
|
}
|
||||||
return cb(null, response);
|
return cb(null, response);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
/**
|
/*
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
function Handler (route) {
|
const Handler = function (route) {
|
||||||
// Handle redirects
|
// Handle redirects
|
||||||
if (route.redirect) {
|
if (route.redirect) {
|
||||||
return (req, res) => { res.redirect(route.redirect); };
|
return (req, res) => {
|
||||||
|
res.redirect(route.redirect);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = '/' + route.name + '.html';
|
var url = '/' + route.name + '.html';
|
||||||
|
@ -12,9 +14,9 @@ function Handler (route) {
|
||||||
req.url = url;
|
req.url = url;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Export a new instance
|
* Export a new instance
|
||||||
*/
|
*/
|
||||||
module.exports = function (route) {
|
module.exports = function (route) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"ab": "Аҧсшәа",
|
||||||
"ar": "العربية",
|
"ar": "العربية",
|
||||||
"an": "Aragonés",
|
"an": "Aragonés",
|
||||||
"ast": "Asturianu",
|
"ast": "Asturianu",
|
||||||
|
|
45
package.json
45
package.json
|
@ -33,24 +33,27 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "1.5.2",
|
"async": "1.5.2",
|
||||||
"autoprefixer": "6.3.6",
|
"autoprefixer": "6.3.6",
|
||||||
"babel-core": "6.10.4",
|
"babel-cli": "6.26.0",
|
||||||
"babel-eslint": "5.0.4",
|
"babel-core": "6.23.1",
|
||||||
"babel-loader": "6.2.4",
|
"babel-eslint": "8.0.2",
|
||||||
"babel-preset-es2015": "6.9.0",
|
"babel-loader": "7.1.0",
|
||||||
"babel-preset-react": "6.11.1",
|
"babel-preset-es2015": "6.22.0",
|
||||||
|
"babel-preset-react": "6.22.0",
|
||||||
"cheerio": "1.0.0-rc.2",
|
"cheerio": "1.0.0-rc.2",
|
||||||
"classnames": "2.1.3",
|
"classnames": "2.2.5",
|
||||||
"cookie": "0.2.2",
|
"cookie": "0.2.2",
|
||||||
"copy-webpack-plugin": "0.2.0",
|
"copy-webpack-plugin": "0.2.0",
|
||||||
|
"create-react-class": "15.6.2",
|
||||||
"css-loader": "0.23.1",
|
"css-loader": "0.23.1",
|
||||||
"eslint": "1.3.1",
|
"eslint": "4.7.1",
|
||||||
|
"eslint-config-scratch": "5.0.0",
|
||||||
"eslint-plugin-json": "1.2.0",
|
"eslint-plugin-json": "1.2.0",
|
||||||
"eslint-plugin-react": "3.3.1",
|
"eslint-plugin-react": "7.4.0",
|
||||||
"exenv": "1.2.0",
|
"exenv": "1.2.0",
|
||||||
"fastly": "1.2.1",
|
"fastly": "1.2.1",
|
||||||
"file-loader": "0.8.4",
|
"file-loader": "0.8.4",
|
||||||
"formsy-react": "0.18.0",
|
"formsy-react": "0.19.5",
|
||||||
"formsy-react-components": "0.7.1",
|
"formsy-react-components": "0.11.1",
|
||||||
"git-bundle-sha": "0.0.2",
|
"git-bundle-sha": "0.0.2",
|
||||||
"glob": "5.0.15",
|
"glob": "5.0.15",
|
||||||
"google-libphonenumber": "1.0.21",
|
"google-libphonenumber": "1.0.21",
|
||||||
|
@ -59,6 +62,7 @@
|
||||||
"json-loader": "0.5.2",
|
"json-loader": "0.5.2",
|
||||||
"json2po-stream": "1.0.3",
|
"json2po-stream": "1.0.3",
|
||||||
"keymirror": "0.1.1",
|
"keymirror": "0.1.1",
|
||||||
|
"lodash.bindall": "4.4.0",
|
||||||
"lodash.clone": "3.0.3",
|
"lodash.clone": "3.0.3",
|
||||||
"lodash.defaultsdeep": "3.10.0",
|
"lodash.defaultsdeep": "3.10.0",
|
||||||
"lodash.isarray": "3.0.4",
|
"lodash.isarray": "3.0.4",
|
||||||
|
@ -70,21 +74,22 @@
|
||||||
"node-sass": "4.6.1",
|
"node-sass": "4.6.1",
|
||||||
"pako": "0.2.8",
|
"pako": "0.2.8",
|
||||||
"po2icu": "0.0.2",
|
"po2icu": "0.0.2",
|
||||||
"postcss-loader": "0.8.2",
|
"postcss-loader": "2.0.10",
|
||||||
|
"prop-types": "15.6.0",
|
||||||
"raven-js": "3.0.4",
|
"raven-js": "3.0.4",
|
||||||
"react": "15.1.0",
|
"react": "15.5.4",
|
||||||
"react-dom": "15.0.1",
|
"react-dom": "15.5.4",
|
||||||
"react-intl": "2.1.2",
|
"react-intl": "2.1.2",
|
||||||
"react-modal": "1.5.2",
|
"react-modal": "3.1.11",
|
||||||
"react-onclickoutside": "4.1.1",
|
"react-onclickoutside": "6.7.1",
|
||||||
"react-redux": "4.4.5",
|
"react-redux": "4.4.5",
|
||||||
"react-responsive": "1.1.4",
|
"react-responsive": "3.0.0",
|
||||||
"react-slick": "0.12.2",
|
"react-slick": "0.12.2",
|
||||||
"react-telephone-input": "3.4.5",
|
"react-telephone-input": "3.8.6",
|
||||||
"redux": "3.5.2",
|
"redux": "3.5.2",
|
||||||
"redux-thunk": "2.0.1",
|
"redux-thunk": "2.0.1",
|
||||||
"sass-lint": "1.5.1",
|
"sass-lint": "1.5.1",
|
||||||
"sass-loader": "2.0.1",
|
"sass-loader": "6.0.6",
|
||||||
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
|
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
|
||||||
"slick-carousel": "1.5.8",
|
"slick-carousel": "1.5.8",
|
||||||
"source-map-support": "0.3.2",
|
"source-map-support": "0.3.2",
|
||||||
|
@ -92,8 +97,8 @@
|
||||||
"tap": "7.1.2",
|
"tap": "7.1.2",
|
||||||
"url-loader": "0.5.6",
|
"url-loader": "0.5.6",
|
||||||
"watch": "0.16.0",
|
"watch": "0.16.0",
|
||||||
"webpack": "1.12.14",
|
"webpack": "2.7.0",
|
||||||
"webpack-dev-middleware": "1.2.0",
|
"webpack-dev-middleware": "2.0.4",
|
||||||
"xhr": "2.2.0"
|
"xhr": "2.2.0"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
|
|
11
src/.eslintrc.js
Normal file
11
src/.eslintrc.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: ['scratch', 'scratch/es6', 'scratch/react'],
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
process: true
|
||||||
|
},
|
||||||
|
plugins: ['json']
|
||||||
|
};
|
|
@ -1,33 +1,35 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./accordion.scss');
|
require('./accordion.scss');
|
||||||
|
|
||||||
var Accordion = React.createClass({
|
class Accordion extends React.Component {
|
||||||
type: 'Accordion',
|
constructor (props) {
|
||||||
getDefaultProps: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
titleAs: 'div',
|
'handleClick'
|
||||||
contentAs: 'div'
|
]);
|
||||||
};
|
this.state = {
|
||||||
},
|
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
isOpen: false
|
isOpen: false
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
toggleContent: function () {
|
handleClick (e) {
|
||||||
|
e.preventDefault();
|
||||||
this.setState({isOpen: !this.state.isOpen});
|
this.setState({isOpen: !this.state.isOpen});
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames({
|
const classes = classNames({
|
||||||
'content': true,
|
content: true,
|
||||||
'open': this.state.isOpen
|
open: this.state.isOpen
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div className="accordion">
|
<div className="accordion">
|
||||||
<this.props.titleAs className="title"
|
<this.props.titleAs
|
||||||
onClick={this.toggleContent}>
|
className="title"
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
{this.props.title}
|
{this.props.title}
|
||||||
</this.props.titleAs>
|
</this.props.titleAs>
|
||||||
<this.props.contentAs className={classes}>
|
<this.props.contentAs className={classes}>
|
||||||
|
@ -36,6 +38,16 @@ var Accordion = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Accordion.propTypes = {
|
||||||
|
content: PropTypes.node,
|
||||||
|
title: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
Accordion.defaultProps = {
|
||||||
|
contentAs: 'div',
|
||||||
|
titleAs: 'div'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Accordion;
|
module.exports = Accordion;
|
||||||
|
|
|
@ -1,37 +1,39 @@
|
||||||
var React = require('react');
|
const bindAll = require('lodash.bindall');
|
||||||
var connect = require('react-redux').connect;
|
const connect = require('react-redux').connect;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Button = require('../forms/button.jsx');
|
const Button = require('../forms/button.jsx');
|
||||||
|
|
||||||
require('./adminpanel.scss');
|
require('./adminpanel.scss');
|
||||||
|
|
||||||
var AdminPanel = React.createClass({
|
class AdminPanel extends React.Component {
|
||||||
type: 'AdminPanel',
|
constructor (props) {
|
||||||
getInitialState: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
|
'handleToggleVisibility'
|
||||||
|
]);
|
||||||
|
this.state = {
|
||||||
showPanel: false
|
showPanel: false
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
handleToggleVisibility: function (e) {
|
handleToggleVisibility (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({showPanel: !this.state.showPanel});
|
this.setState({showPanel: !this.state.showPanel});
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
// make sure user is present before checking if they're an admin. Don't show anything if user not an admin.
|
if (!this.props.isAdmin) return false;
|
||||||
var showAdmin = false;
|
|
||||||
if (this.props.session.session.user) {
|
|
||||||
showAdmin = this.props.session.session.permissions.admin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!showAdmin) return false;
|
|
||||||
|
|
||||||
if (this.state.showPanel) {
|
if (this.state.showPanel) {
|
||||||
return (
|
return (
|
||||||
<div id="admin-panel" className="visible">
|
<div
|
||||||
|
className="visible"
|
||||||
|
id="admin-panel"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
className="toggle"
|
className="toggle"
|
||||||
onClick={this.handleToggleVisibility}>
|
onClick={this.handleToggleVisibility}
|
||||||
|
>
|
||||||
x
|
x
|
||||||
</span>
|
</span>
|
||||||
<div className="admin-header">
|
<div className="admin-header">
|
||||||
|
@ -44,8 +46,15 @@ var AdminPanel = React.createClass({
|
||||||
<dd>
|
<dd>
|
||||||
<ul className="cache-list">
|
<ul className="cache-list">
|
||||||
<li>
|
<li>
|
||||||
<form method="post" action="/scratch_admin/page/clear-anon-cache/">
|
<form
|
||||||
<input type="hidden" name="path" value="/" />
|
action="/scratch_admin/page/clear-anon-cache/"
|
||||||
|
method="post"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
name="path"
|
||||||
|
type="hidden"
|
||||||
|
value="/"
|
||||||
|
/>
|
||||||
<div className="button-row">
|
<div className="button-row">
|
||||||
<span>For anonymous users:</span>
|
<span>For anonymous users:</span>
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
|
@ -53,34 +62,39 @@ var AdminPanel = React.createClass({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div id="admin-panel" className="hidden">
|
|
||||||
<span
|
|
||||||
className="toggle"
|
|
||||||
onClick={this.handleToggleVisibility}>
|
|
||||||
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="hidden"
|
||||||
|
id="admin-panel"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="toggle"
|
||||||
|
onClick={this.handleToggleVisibility}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
var mapStateToProps = function (state) {
|
AdminPanel.propTypes = {
|
||||||
return {
|
children: PropTypes.node,
|
||||||
session: state.session
|
isAdmin: PropTypes.bool
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel);
|
const mapStateToProps = state => ({
|
||||||
|
isAdmin: state.permissions.admin
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel);
|
||||||
|
|
||||||
module.exports = ConnectedAdminPanel;
|
module.exports = ConnectedAdminPanel;
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
var classNames = require('classnames');
|
const omit = require('lodash.omit');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Avatar = React.createClass({
|
const Avatar = props => (
|
||||||
type: 'Avatar',
|
<img
|
||||||
propTypes: {
|
className={classNames('avatar', props.className)}
|
||||||
src: React.PropTypes.string
|
{...omit(props, ['className'])}
|
||||||
},
|
/>
|
||||||
getDefaultProps: function () {
|
);
|
||||||
return {
|
|
||||||
src: '//cdn2.scratch.mit.edu/get_image/user/2584924_24x24.png?v=1438702210.96'
|
Avatar.propTypes = {
|
||||||
};
|
className: PropTypes.string,
|
||||||
},
|
src: PropTypes.string
|
||||||
render: function () {
|
};
|
||||||
var classes = classNames(
|
|
||||||
'avatar',
|
Avatar.defaultProps = {
|
||||||
this.props.className
|
src: '//cdn2.scratch.mit.edu/get_image/user/2584924_24x24.png?v=1438702210.96'
|
||||||
);
|
};
|
||||||
return <img {... this.props} className={classes} />;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Avatar;
|
module.exports = Avatar;
|
||||||
|
|
|
@ -1,40 +1,38 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./box.scss');
|
require('./box.scss');
|
||||||
|
|
||||||
var Box = React.createClass({
|
const Box = props => (
|
||||||
type: 'Box',
|
<div className={classNames('box', props.className)}>
|
||||||
propTypes: {
|
<div className="box-header">
|
||||||
title: React.PropTypes.string.isRequired,
|
<h4>{props.title}</h4>
|
||||||
subtitle: React.PropTypes.string,
|
<h5>{props.subtitle}</h5>
|
||||||
moreTitle: React.PropTypes.string,
|
<p>
|
||||||
moreHref: React.PropTypes.string,
|
<a
|
||||||
moreProps: React.PropTypes.object
|
href={props.moreHref}
|
||||||
},
|
{...props.moreProps}
|
||||||
render: function () {
|
>
|
||||||
var classes = classNames(
|
{props.moreTitle}
|
||||||
'box',
|
</a>
|
||||||
this.props.className
|
</p>
|
||||||
);
|
</div>
|
||||||
return (
|
|
||||||
<div className={classes}>
|
|
||||||
<div className="box-header">
|
|
||||||
<h4>{this.props.title}</h4>
|
|
||||||
<h5>{this.props.subtitle}</h5>
|
|
||||||
<p>
|
|
||||||
<a href={this.props.moreHref} {...this.props.moreProps}>
|
|
||||||
{this.props.moreTitle}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="box-content">
|
<div className="box-content">
|
||||||
{this.props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
Box.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
|
moreHref: PropTypes.string,
|
||||||
|
moreProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
|
moreTitle: PropTypes.string,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Box;
|
module.exports = Box;
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./card.scss');
|
require('./card.scss');
|
||||||
|
|
||||||
var Card = React.createClass({
|
const Card = props => (
|
||||||
displayName: 'Card',
|
<div className={classNames(['card', props.className])}>
|
||||||
render: function () {
|
{props.children}
|
||||||
return (
|
</div>
|
||||||
<div className={classNames(['card', this.props.className])}>
|
);
|
||||||
{this.props.children}
|
|
||||||
</div>
|
Card.propTypes = {
|
||||||
);
|
children: PropTypes.node,
|
||||||
}
|
className: PropTypes.string
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = Card;
|
module.exports = Card;
|
||||||
|
|
|
@ -1,95 +1,115 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var Slider = require('react-slick');
|
const React = require('react');
|
||||||
|
const Slider = require('react-slick');
|
||||||
|
|
||||||
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
const Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||||
|
|
||||||
var frameless = require('../../lib/frameless.js');
|
const frameless = require('../../lib/frameless.js');
|
||||||
|
|
||||||
require('slick-carousel/slick/slick.scss');
|
require('slick-carousel/slick/slick.scss');
|
||||||
require('slick-carousel/slick/slick-theme.scss');
|
require('slick-carousel/slick/slick-theme.scss');
|
||||||
require('./carousel.scss');
|
require('./carousel.scss');
|
||||||
|
|
||||||
/**
|
const Carousel = props => {
|
||||||
* Displays content in horizontal scrolling box. Example usage: splash page rows.
|
defaults(props.settings, {
|
||||||
*/
|
centerMode: false,
|
||||||
var Carousel = React.createClass({
|
dots: false,
|
||||||
type: 'Carousel',
|
infinite: false,
|
||||||
propTypes: {
|
lazyLoad: true,
|
||||||
items: React.PropTypes.array
|
slidesToShow: 5,
|
||||||
},
|
slidesToScroll: 5,
|
||||||
getDefaultProps: function () {
|
variableWidth: true,
|
||||||
return {
|
responsive: [
|
||||||
items: require('./carousel.json'),
|
{
|
||||||
showRemixes: false,
|
breakpoint: frameless.mobile,
|
||||||
showLoves: false,
|
settings: {
|
||||||
type: 'project'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var settings = this.props.settings || {};
|
|
||||||
defaults(settings, {
|
|
||||||
centerMode: false,
|
|
||||||
dots: false,
|
|
||||||
infinite: false,
|
|
||||||
lazyLoad: true,
|
|
||||||
slidesToShow: 5,
|
|
||||||
slidesToScroll: 5,
|
|
||||||
variableWidth: true,
|
|
||||||
responsive: [
|
|
||||||
{breakpoint: frameless.mobile, settings: {
|
|
||||||
arrows: true,
|
arrows: true,
|
||||||
slidesToScroll: 1,
|
slidesToScroll: 1,
|
||||||
slidesToShow: 1,
|
slidesToShow: 1,
|
||||||
centerMode: true
|
centerMode: true
|
||||||
}},
|
}
|
||||||
{breakpoint: frameless.tablet, settings: {
|
},
|
||||||
|
{
|
||||||
|
breakpoint: frameless.tablet,
|
||||||
|
settings: {
|
||||||
slidesToScroll: 2,
|
slidesToScroll: 2,
|
||||||
slidesToShow: 2
|
slidesToShow: 2
|
||||||
}},
|
}
|
||||||
{breakpoint: frameless.desktop, settings: {
|
},
|
||||||
|
{
|
||||||
|
breakpoint: frameless.desktop,
|
||||||
|
settings: {
|
||||||
slidesToScroll: 4,
|
slidesToScroll: 4,
|
||||||
slidesToShow: 4
|
slidesToShow: 4
|
||||||
}}
|
}
|
||||||
]
|
}
|
||||||
});
|
]
|
||||||
var arrows = this.props.items.length > settings.slidesToShow;
|
});
|
||||||
var classes = classNames(
|
const arrows = props.items.length > props.settings.slidesToShow;
|
||||||
'carousel',
|
return (
|
||||||
this.props.className
|
<Slider
|
||||||
);
|
arrows={arrows}
|
||||||
return (
|
className={classNames('carousel', props.className)}
|
||||||
<Slider className={classes} arrows={arrows} {... settings}>
|
{... props.settings}
|
||||||
{this.props.items.map(function (item) {
|
>
|
||||||
var href = '';
|
{props.items.map(item => {
|
||||||
switch (this.props.type) {
|
let href = '';
|
||||||
case 'gallery':
|
switch (props.type) {
|
||||||
href = '/studios/' + item.id + '/';
|
case 'gallery':
|
||||||
break;
|
href = `/studios/${item.id}/`;
|
||||||
case 'project':
|
break;
|
||||||
href = '/projects/' + item.id + '/';
|
case 'project':
|
||||||
break;
|
href = `/projects/${item.id}/`;
|
||||||
default:
|
break;
|
||||||
href = '/' + item.type + '/' + item.id + '/';
|
default:
|
||||||
}
|
href = `/${item.type}/${item.id}/`;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Thumbnail key={[this.key, item.id].join('.')}
|
<Thumbnail
|
||||||
showLoves={this.props.showLoves}
|
creator={item.author.username}
|
||||||
showRemixes={this.props.showRemixes}
|
href={href}
|
||||||
type={this.props.type}
|
key={[props.type, item.id].join('.')}
|
||||||
href={href}
|
loves={item.stats.loves}
|
||||||
title={item.title}
|
remixes={item.stats.remixes}
|
||||||
src={item.image}
|
showLoves={props.showLoves}
|
||||||
creator={item.author.username}
|
showRemixes={props.showRemixes}
|
||||||
remixes={item.stats.remixes}
|
src={item.image}
|
||||||
loves={item.stats.loves} />
|
title={item.title}
|
||||||
);
|
type={props.type}
|
||||||
}.bind(this))}
|
/>
|
||||||
</Slider>
|
);
|
||||||
);
|
})}
|
||||||
}
|
</Slider>
|
||||||
});
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Carousel.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
settings: PropTypes.shape({
|
||||||
|
centerMode: PropTypes.bool,
|
||||||
|
dots: PropTypes.bool,
|
||||||
|
infinite: PropTypes.bool,
|
||||||
|
lazyLoad: PropTypes.bool,
|
||||||
|
slidesToShow: PropTypes.number,
|
||||||
|
slidesToScroll: PropTypes.number,
|
||||||
|
variableWidth: PropTypes.bool,
|
||||||
|
responsive: PropTypes.array
|
||||||
|
}),
|
||||||
|
showLoves: PropTypes.bool,
|
||||||
|
showRemixes: PropTypes.bool,
|
||||||
|
type: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
Carousel.defaultProps = {
|
||||||
|
items: require('./carousel.json'),
|
||||||
|
settings: {},
|
||||||
|
showRemixes: false,
|
||||||
|
showLoves: false,
|
||||||
|
type: 'project'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Carousel;
|
module.exports = Carousel;
|
||||||
|
|
|
@ -1,97 +1,118 @@
|
||||||
// This component handles json returned via proxy from a django server,
|
// This component handles json returned via proxy from a django server,
|
||||||
// or directly from a django server, and the model structure that system
|
// or directly from a django server, and the model structure that system
|
||||||
// has.
|
// has.
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var Slider = require('react-slick');
|
const React = require('react');
|
||||||
|
const Slider = require('react-slick');
|
||||||
|
|
||||||
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
const Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||||
|
|
||||||
var frameless = require('../../lib/frameless.js');
|
const frameless = require('../../lib/frameless.js');
|
||||||
|
|
||||||
require('slick-carousel/slick/slick.scss');
|
require('slick-carousel/slick/slick.scss');
|
||||||
require('slick-carousel/slick/slick-theme.scss');
|
require('slick-carousel/slick/slick-theme.scss');
|
||||||
require('./carousel.scss');
|
require('./carousel.scss');
|
||||||
|
|
||||||
/**
|
const Carousel = props => {
|
||||||
* Displays content in horizontal scrolling box. Example usage: splash page rows.
|
defaults(props.settings, {
|
||||||
*/
|
centerMode: false,
|
||||||
var LegacyCarousel = React.createClass({
|
dots: false,
|
||||||
type: 'LegacyCarousel',
|
infinite: false,
|
||||||
propTypes: {
|
lazyLoad: true,
|
||||||
items: React.PropTypes.array
|
slidesToShow: 5,
|
||||||
},
|
slidesToScroll: 5,
|
||||||
getDefaultProps: function () {
|
variableWidth: true,
|
||||||
return {
|
responsive: [
|
||||||
items: require('./carousel.json'),
|
{
|
||||||
showRemixes: false,
|
breakpoint: frameless.mobile,
|
||||||
showLoves: false
|
settings: {
|
||||||
};
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var settings = this.props.settings || {};
|
|
||||||
defaults(settings, {
|
|
||||||
centerMode: false,
|
|
||||||
dots: false,
|
|
||||||
infinite: false,
|
|
||||||
lazyLoad: true,
|
|
||||||
slidesToShow: 5,
|
|
||||||
slidesToScroll: 5,
|
|
||||||
variableWidth: true,
|
|
||||||
responsive: [
|
|
||||||
{breakpoint: frameless.mobile, settings: {
|
|
||||||
arrows: true,
|
arrows: true,
|
||||||
slidesToScroll: 1,
|
slidesToScroll: 1,
|
||||||
slidesToShow: 1,
|
slidesToShow: 1,
|
||||||
centerMode: true
|
centerMode: true
|
||||||
}},
|
}
|
||||||
{breakpoint: frameless.tablet, settings: {
|
},
|
||||||
|
{
|
||||||
|
breakpoint: frameless.tablet,
|
||||||
|
settings: {
|
||||||
slidesToScroll: 2,
|
slidesToScroll: 2,
|
||||||
slidesToShow: 2
|
slidesToShow: 2
|
||||||
}},
|
}
|
||||||
{breakpoint: frameless.desktop, settings: {
|
},
|
||||||
|
{
|
||||||
|
breakpoint: frameless.desktop,
|
||||||
|
settings: {
|
||||||
slidesToScroll: 4,
|
slidesToScroll: 4,
|
||||||
slidesToShow: 4
|
slidesToShow: 4
|
||||||
}}
|
}
|
||||||
]
|
}
|
||||||
});
|
]
|
||||||
var arrows = this.props.items.length > settings.slidesToShow;
|
});
|
||||||
var classes = classNames(
|
const arrows = props.items.length > props.settings.slidesToShow;
|
||||||
'carousel',
|
return (
|
||||||
this.props.className
|
<Slider
|
||||||
);
|
arrows={arrows}
|
||||||
return (
|
className={classNames('carousel', props.className)}
|
||||||
<Slider className={classes} arrows={arrows} {... settings}>
|
{... props.settings}
|
||||||
{this.props.items.map(function (item) {
|
>
|
||||||
var href = '';
|
{props.items.map(item => {
|
||||||
switch (item.type) {
|
let href = '';
|
||||||
case 'gallery':
|
switch (item.type) {
|
||||||
href = '/studios/' + item.id + '/';
|
case 'gallery':
|
||||||
break;
|
href = `/studios/${item.id}/`;
|
||||||
case 'project':
|
break;
|
||||||
href = '/projects/' + item.id + '/';
|
case 'project':
|
||||||
break;
|
href = `/projects/${item.id}/`;
|
||||||
default:
|
break;
|
||||||
href = '/' + item.type + '/' + item.id + '/';
|
default:
|
||||||
}
|
href = `/${item.type}/${item.id}/`;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Thumbnail key={[this.key, item.id].join('.')}
|
<Thumbnail
|
||||||
showLoves={this.props.showLoves}
|
creator={item.creator}
|
||||||
showRemixes={this.props.showRemixes}
|
href={href}
|
||||||
type={item.type}
|
key={[props.type, item.id].join('.')}
|
||||||
href={href}
|
loves={item.love_count}
|
||||||
title={item.title}
|
remixes={item.remixers_count}
|
||||||
src={item.thumbnail_url}
|
showLoves={props.showLoves}
|
||||||
creator={item.creator}
|
showRemixes={props.showRemixes}
|
||||||
remixes={item.remixers_count}
|
src={item.thumbnail_url}
|
||||||
loves={item.love_count} />
|
title={item.title}
|
||||||
);
|
type={item.type}
|
||||||
}.bind(this))}
|
/>
|
||||||
</Slider>
|
);
|
||||||
);
|
})}
|
||||||
}
|
</Slider>
|
||||||
});
|
);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = LegacyCarousel;
|
Carousel.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
settings: PropTypes.shape({
|
||||||
|
centerMode: PropTypes.bool,
|
||||||
|
dots: PropTypes.bool,
|
||||||
|
infinite: PropTypes.bool,
|
||||||
|
lazyLoad: PropTypes.bool,
|
||||||
|
slidesToShow: PropTypes.number,
|
||||||
|
slidesToScroll: PropTypes.number,
|
||||||
|
variableWidth: PropTypes.bool,
|
||||||
|
responsive: PropTypes.array
|
||||||
|
}),
|
||||||
|
showLoves: PropTypes.bool,
|
||||||
|
showRemixes: PropTypes.bool,
|
||||||
|
type: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
Carousel.defaultProps = {
|
||||||
|
items: require('./carousel.json'),
|
||||||
|
settings: {},
|
||||||
|
showRemixes: false,
|
||||||
|
showLoves: false,
|
||||||
|
type: 'project'
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Carousel;
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FormattedRelative = require('react-intl').FormattedRelative;
|
const FormattedRelative = require('react-intl').FormattedRelative;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var EmojiText = require('../emoji-text/emoji-text.jsx');
|
const EmojiText = require('../emoji-text/emoji-text.jsx');
|
||||||
|
|
||||||
require('./comment.scss');
|
require('./comment.scss');
|
||||||
|
|
||||||
var CommentText = React.createClass({
|
const CommentText = props => (
|
||||||
type: 'CommentText',
|
<div className={classNames('comment-text', props.className)}>
|
||||||
propTypes: {
|
<EmojiText
|
||||||
comment: React.PropTypes.string.isRequired,
|
className="mod-comment"
|
||||||
datetimeCreated: React.PropTypes.string,
|
text={props.comment}
|
||||||
className: React.PropTypes.string
|
/>
|
||||||
},
|
{typeof props.datetimeCreated === 'undefined' ? [] : [
|
||||||
render: function () {
|
<p
|
||||||
var classes = classNames(
|
className="comment-text-timestamp"
|
||||||
'comment-text',
|
key="comment-text-timestamp"
|
||||||
this.props.class
|
>
|
||||||
);
|
<FormattedRelative value={new Date(props.datetimeCreated)} />
|
||||||
return (
|
</p>
|
||||||
<div className={classes}>
|
]}
|
||||||
<EmojiText className="mod-comment" text={this.props.comment} />
|
</div>
|
||||||
{typeof this.props.datetimeCreated !== 'undefined' ? [
|
);
|
||||||
<p className="comment-text-timestamp">
|
|
||||||
<FormattedRelative value={new Date(this.props.datetimeCreated)} />
|
CommentText.propTypes = {
|
||||||
</p>
|
className: PropTypes.string,
|
||||||
] : []}
|
comment: PropTypes.string.isRequired,
|
||||||
</div>
|
datetimeCreated: PropTypes.string
|
||||||
);
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = CommentText;
|
module.exports = CommentText;
|
||||||
|
|
|
@ -1,22 +1,29 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./deck.scss');
|
require('./deck.scss');
|
||||||
|
|
||||||
var Deck = React.createClass({
|
const Deck = props => (
|
||||||
displayName: 'Deck',
|
<div className={classNames(['deck', props.className])}>
|
||||||
render: function () {
|
<div className="inner">
|
||||||
return (
|
<a
|
||||||
<div className={classNames(['deck', this.props.className])}>
|
aria-label="Scratch"
|
||||||
<div className="inner">
|
href="/"
|
||||||
<a href="/" aria-label="Scratch">
|
>
|
||||||
<img className="logo" src="/images/logo_sm.png" />
|
<img
|
||||||
</a>
|
className="logo"
|
||||||
{this.props.children}
|
src="/images/logo_sm.png"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</a>
|
||||||
);
|
{props.children}
|
||||||
}
|
</div>
|
||||||
});
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
Deck.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Deck;
|
module.exports = Deck;
|
||||||
|
|
|
@ -1,33 +1,29 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./banner.scss');
|
require('./banner.scss');
|
||||||
|
|
||||||
/**
|
const Banner = props => (
|
||||||
* Container for messages displayed below the nav bar that can be dismissed
|
<div className={classNames('banner', props.className)}>
|
||||||
* (See: email not confirmed banner)
|
<div className="inner">
|
||||||
*/
|
{props.children}
|
||||||
var Banner = React.createClass({
|
{props.onRequestDismiss ? [
|
||||||
type: 'Banner',
|
<a
|
||||||
propTypes: {
|
className="close"
|
||||||
onRequestDismiss: React.PropTypes.func
|
href="#"
|
||||||
},
|
key="close"
|
||||||
render: function () {
|
onClick={props.onRequestDismiss}
|
||||||
var classes = classNames(
|
>x</a>
|
||||||
'banner',
|
] : []}
|
||||||
this.props.className
|
</div>
|
||||||
);
|
</div>
|
||||||
return (
|
);
|
||||||
<div className={classes}>
|
|
||||||
<div className="inner">
|
Banner.propTypes = {
|
||||||
{this.props.children}
|
children: PropTypes.node,
|
||||||
{this.props.onRequestDismiss ? [
|
className: PropTypes.string,
|
||||||
<a className="close" key="close" href="#" onClick={this.props.onRequestDismiss}>x</a>
|
onRequestDismiss: PropTypes.func
|
||||||
] : []}
|
};
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Banner;
|
module.exports = Banner;
|
||||||
|
|
|
@ -1,40 +1,46 @@
|
||||||
var React = require('react');
|
const bindAll = require('lodash.bindall');
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
|
const onClickOutside = require('react-onclickoutside').default;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./dropdown.scss');
|
require('./dropdown.scss');
|
||||||
|
|
||||||
var Dropdown = React.createClass({
|
class Dropdown extends React.Component {
|
||||||
type: 'Dropdown',
|
constructor (props) {
|
||||||
mixins: [
|
super(props);
|
||||||
require('react-onclickoutside')
|
bindAll(this, [
|
||||||
],
|
'handleClickOutside'
|
||||||
propTypes: {
|
]);
|
||||||
onRequestClose: React.PropTypes.func,
|
}
|
||||||
isOpen: React.PropTypes.bool
|
handleClickOutside () {
|
||||||
},
|
|
||||||
getDefaultProps: function () {
|
|
||||||
return {
|
|
||||||
as: 'div',
|
|
||||||
isOpen: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleClickOutside: function () {
|
|
||||||
if (this.props.isOpen) {
|
if (this.props.isOpen) {
|
||||||
this.props.onRequestClose();
|
this.props.onRequestClose();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames(
|
|
||||||
'dropdown',
|
|
||||||
this.props.className,
|
|
||||||
{open: this.props.isOpen}
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<this.props.as className={classes}>
|
<this.props.as
|
||||||
|
className={classNames('dropdown', this.props.className, {
|
||||||
|
open: this.props.isOpen
|
||||||
|
})}
|
||||||
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</this.props.as>
|
</this.props.as>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = Dropdown;
|
Dropdown.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
onRequestClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
Dropdown.defaultProps = {
|
||||||
|
as: 'div',
|
||||||
|
isOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = onClickOutside(Dropdown);
|
||||||
|
|
|
@ -24,8 +24,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
background-color: transparent;
|
&:link,
|
||||||
color: $type-white;
|
&:visited,
|
||||||
|
&:active {
|
||||||
|
background-color: transparent;
|
||||||
|
color: $type-white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./emoji-text.scss');
|
require('./emoji-text.scss');
|
||||||
|
|
||||||
var EmojiText = React.createClass({
|
const EmojiText = props => (
|
||||||
type: 'EmojiText',
|
<props.as
|
||||||
propTyes: {
|
className={classNames('emoji-text', props.className)}
|
||||||
text: React.PropTypes.string.isRequired,
|
dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
|
||||||
className: React.PropTypes.string
|
__html: props.text
|
||||||
},
|
}}
|
||||||
getDefaultProps: function () {
|
/>
|
||||||
return {
|
);
|
||||||
as: 'p'
|
|
||||||
};
|
EmojiText.propTypes = {
|
||||||
},
|
className: PropTypes.string,
|
||||||
render: function () {
|
text: PropTypes.string.isRequired
|
||||||
var classes = classNames(
|
};
|
||||||
'emoji-text',
|
|
||||||
this.props.className
|
EmojiText.defaultProps = {
|
||||||
);
|
as: 'p'
|
||||||
return (
|
};
|
||||||
<this.props.as
|
|
||||||
className={classes}
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: this.props.text
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = EmojiText;
|
module.exports = EmojiText;
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./flex-row.scss');
|
require('./flex-row.scss');
|
||||||
|
|
||||||
var FlexRow = React.createClass({
|
const FlexRow = props => (
|
||||||
type: 'FlexRow',
|
<props.as className={classNames('flex-row', props.className)}>
|
||||||
getDefaultProps: function () {
|
{props.children}
|
||||||
return {
|
</props.as>
|
||||||
as: 'div'
|
);
|
||||||
};
|
|
||||||
},
|
FlexRow.propTypes = {
|
||||||
render: function () {
|
children: PropTypes.node,
|
||||||
var classes = classNames(
|
className: PropTypes.string
|
||||||
'flex-row',
|
};
|
||||||
this.props.className
|
|
||||||
);
|
FlexRow.defaultProps = {
|
||||||
return (
|
as: 'div'
|
||||||
<this.props.as className={classes}>
|
};
|
||||||
{this.props.children}
|
|
||||||
</this.props.as>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = FlexRow;
|
module.exports = FlexRow;
|
||||||
|
|
|
@ -1,125 +1,152 @@
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
var FlexRow = require('../../../flex-row/flex-row.jsx');
|
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||||
var FooterBox = require('../../container/footer.jsx');
|
const FooterBox = require('../../container/footer.jsx');
|
||||||
|
|
||||||
require('../footer.scss');
|
require('../footer.scss');
|
||||||
|
|
||||||
var ConferenceFooter = React.createClass({
|
const ConferenceFooter = () => (
|
||||||
type: 'ConferenceFooter',
|
<FooterBox>
|
||||||
render: function () {
|
<div className="collaborators">
|
||||||
return (
|
<h4>Sponsors</h4>
|
||||||
<FooterBox>
|
<FlexRow as="ul">
|
||||||
<div className="collaborators">
|
<li className="odl">
|
||||||
<h4>Sponsors</h4>
|
<a href="https://odl.mit.edu/">
|
||||||
|
<img
|
||||||
|
alt="MIT Office of Digital Learning"
|
||||||
|
src="/images/conference/footer/mit-odl.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="intel">
|
||||||
|
<a href="http://www.intel.com/content/www/us/en/homepage.html">
|
||||||
|
<img
|
||||||
|
alt="Intel"
|
||||||
|
src="/images/conference/footer/intel.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="lego">
|
||||||
|
<a href="http://www.legofoundation.com/">
|
||||||
|
<img
|
||||||
|
alt="LEGO Foundation"
|
||||||
|
src="/images/conference/footer/lego-foundation.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="google">
|
||||||
|
<a href="http://www.google.com/">
|
||||||
|
<img
|
||||||
|
alt="Google"
|
||||||
|
src="/images/conference/footer/google.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="siegel">
|
||||||
|
<a href="http://www.siegelendowment.org/">
|
||||||
|
<img
|
||||||
|
alt="Siegel Family Endowment"
|
||||||
|
src="/images/conference/footer/siegel-endowment.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="nostarch">
|
||||||
|
<a href="https://www.nostarch.com/">
|
||||||
|
<img
|
||||||
|
alt="No Starch Press"
|
||||||
|
src="/images/conference/footer/no-starch.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="scratchfoundation">
|
||||||
|
<a href="http://www.scratchfoundation.org/">
|
||||||
|
<img
|
||||||
|
alt="Scratch Foundation"
|
||||||
|
src="/images/conference/footer/scratch-foundation.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
<FlexRow className="scratch-links">
|
||||||
|
<div className="family">
|
||||||
|
<h4>Scratch Family</h4>
|
||||||
|
<FlexRow>
|
||||||
|
<FlexRow
|
||||||
|
as="ul"
|
||||||
|
className="column"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a href="https://scratch.mit.edu">Scratch</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://www.scratchjr.org/">ScratchJr</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
<FlexRow
|
||||||
|
as="ul"
|
||||||
|
className="column"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a href="http://www.scratchfoundation.org/">Scratch Foundation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://scratched.gse.harvard.edu/">ScratchEd</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
<FlexRow
|
||||||
|
as="ul"
|
||||||
|
className="column"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a href="http://day.scratch.mit.edu">Scratch Day</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
</FlexRow>
|
||||||
|
<p className="legal">
|
||||||
|
Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="media">
|
||||||
|
<div className="contact-us">
|
||||||
|
<h4>Contact</h4>
|
||||||
|
<p>
|
||||||
|
<a href="mailto:help@scratch.mit.edu">
|
||||||
|
Email Us
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="social">
|
||||||
<FlexRow as="ul">
|
<FlexRow as="ul">
|
||||||
<li className="odl">
|
<li>
|
||||||
<a href="https://odl.mit.edu/">
|
<a href="//www.twitter.com/scratch">
|
||||||
<img src="/images/conference/footer/mit-odl.png"
|
<img
|
||||||
alt="MIT Office of Digital Learning" />
|
alt="scratch twitter"
|
||||||
|
src="/images/conference/footer/twitter.png"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="intel">
|
<li>
|
||||||
<a href="http://www.intel.com/content/www/us/en/homepage.html">
|
<a href="//www.facebook.com/scratchteam">
|
||||||
<img src="/images/conference/footer/intel.png"
|
<img
|
||||||
alt="Intel" />
|
alt="scratch facebook"
|
||||||
|
src="/images/conference/footer/facebook.png"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="lego">
|
<li>
|
||||||
<a href="http://www.legofoundation.com/">
|
<a href="http://medium.com/scratchfoundation-blog">
|
||||||
<img src="/images/conference/footer/lego-foundation.png"
|
<img
|
||||||
alt="LEGO Foundation" />
|
alt="scratch foundation blog"
|
||||||
</a>
|
src="/images/conference/footer/medium.png"
|
||||||
</li>
|
/>
|
||||||
<li className="google">
|
|
||||||
<a href="http://www.google.com/">
|
|
||||||
<img src="/images/conference/footer/google.png"
|
|
||||||
alt="Google" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className="siegel">
|
|
||||||
<a href="http://www.siegelendowment.org/">
|
|
||||||
<img src="/images/conference/footer/siegel-endowment.png"
|
|
||||||
alt="Siegel Family Endowment" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className="nostarch">
|
|
||||||
<a href="https://www.nostarch.com/">
|
|
||||||
<img src="/images/conference/footer/no-starch.png"
|
|
||||||
alt="No Starch Press" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className="scratchfoundation">
|
|
||||||
<a href="http://www.scratchfoundation.org/">
|
|
||||||
<img src="/images/conference/footer/scratch-foundation.png"
|
|
||||||
alt="Scratch Foundation" />
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</div>
|
</div>
|
||||||
<FlexRow className="scratch-links">
|
</div>
|
||||||
<div className="family">
|
</FlexRow>
|
||||||
<h4>Scratch Family</h4>
|
</FooterBox>
|
||||||
<FlexRow>
|
);
|
||||||
<FlexRow as="ul" className="column">
|
|
||||||
<li>
|
|
||||||
<a href="https://scratch.mit.edu" target="_blank">Scratch</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
|
|
||||||
</li>
|
|
||||||
</FlexRow>
|
|
||||||
<FlexRow as="ul" className="column">
|
|
||||||
<li>
|
|
||||||
<a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
|
|
||||||
</li>
|
|
||||||
</FlexRow>
|
|
||||||
<FlexRow as="ul" className="column">
|
|
||||||
<li>
|
|
||||||
<a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
|
|
||||||
</li>
|
|
||||||
</FlexRow>
|
|
||||||
</FlexRow>
|
|
||||||
<p className="legal">
|
|
||||||
Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="media">
|
|
||||||
<div className="contact-us">
|
|
||||||
<h4>Contact</h4>
|
|
||||||
<p>
|
|
||||||
<a href="mailto:help@scratch.mit.edu" target="_blank">
|
|
||||||
Email Us
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="social">
|
|
||||||
<FlexRow as="ul">
|
|
||||||
<li>
|
|
||||||
<a href="//www.twitter.com/scratch" target="_blank">
|
|
||||||
<img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="//www.facebook.com/scratchteam" target="_blank">
|
|
||||||
<img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="http://medium.com/scratchfoundation-blog" target="_blank">
|
|
||||||
<img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</FlexRow>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</FlexRow>
|
|
||||||
</FooterBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = ConferenceFooter;
|
module.exports = ConferenceFooter;
|
||||||
|
|
|
@ -1,84 +1,100 @@
|
||||||
var React = require('react');
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
var ReactIntl = require('react-intl');
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
const FooterBox = require('../../container/footer.jsx');
|
||||||
|
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||||
var FlexRow = require('../../../flex-row/flex-row.jsx');
|
|
||||||
var FooterBox = require('../../container/footer.jsx');
|
|
||||||
var LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
|
||||||
|
|
||||||
require('../footer.scss');
|
require('../footer.scss');
|
||||||
|
|
||||||
var ConferenceFooter = React.createClass({
|
const ConferenceFooter = props => (
|
||||||
type: 'ConferenceFooter',
|
<FooterBox>
|
||||||
render: function () {
|
<FlexRow className="scratch-links">
|
||||||
return (
|
<div className="family">
|
||||||
<FooterBox>
|
<h4><FormattedMessage id="footer.scratchFamily" /></h4>
|
||||||
<FlexRow className="scratch-links">
|
<FlexRow>
|
||||||
<div className="family">
|
<FlexRow
|
||||||
<h4><FormattedMessage id='footer.scratchFamily' /></h4>
|
as="ul"
|
||||||
<FlexRow>
|
className="column"
|
||||||
<FlexRow as="ul" className="column">
|
>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://scratch.mit.edu" target="_blank">Scratch</a>
|
<a href="https://scratch.mit.edu">Scratch</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
|
<a href="http://www.scratchjr.org/">ScratchJr</a>
|
||||||
</li>
|
</li>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
<FlexRow as="ul" className="column">
|
<FlexRow
|
||||||
<li>
|
as="ul"
|
||||||
<a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
|
className="column"
|
||||||
</li>
|
>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
|
<a href="http://www.scratchfoundation.org/">Scratch Foundation</a>
|
||||||
</li>
|
</li>
|
||||||
</FlexRow>
|
<li>
|
||||||
<FlexRow as="ul" className="column">
|
<a href="http://scratched.gse.harvard.edu/">ScratchEd</a>
|
||||||
<li>
|
</li>
|
||||||
<a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
|
</FlexRow>
|
||||||
</li>
|
<FlexRow
|
||||||
</FlexRow>
|
as="ul"
|
||||||
</FlexRow>
|
className="column"
|
||||||
<p className="legal">
|
>
|
||||||
<FormattedMessage id='general.copyright' />
|
<li>
|
||||||
</p>
|
<a href="http://day.scratch.mit.edu">Scratch Day</a>
|
||||||
</div>
|
</li>
|
||||||
<div className="media">
|
</FlexRow>
|
||||||
<div className="contact-us">
|
|
||||||
<h4>Contact</h4>
|
|
||||||
<p>
|
|
||||||
<a href="mailto:help@scratch.mit.edu" target="_blank">
|
|
||||||
Email Us
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="social">
|
|
||||||
<FlexRow as="ul">
|
|
||||||
<li>
|
|
||||||
<a href="//www.twitter.com/scratch" target="_blank">
|
|
||||||
<img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="//www.facebook.com/scratchteam" target="_blank">
|
|
||||||
<img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="http://medium.com/scratchfoundation-blog" target="_blank">
|
|
||||||
<img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</FlexRow>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
<LanguageChooser locale={this.props.intl.locale} />
|
<p className="legal">
|
||||||
</FooterBox>
|
<FormattedMessage id="general.copyright" />
|
||||||
);
|
</p>
|
||||||
}
|
</div>
|
||||||
});
|
<div className="media">
|
||||||
|
<div className="contact-us">
|
||||||
|
<h4>Contact</h4>
|
||||||
|
<p>
|
||||||
|
<a href="mailto:help@scratch.mit.edu">
|
||||||
|
Email Us
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="social">
|
||||||
|
<FlexRow as="ul">
|
||||||
|
<li>
|
||||||
|
<a href="//www.twitter.com/scratch">
|
||||||
|
<img
|
||||||
|
alt="scratch twitter"
|
||||||
|
src="/images/conference/footer/twitter.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="//www.facebook.com/scratchteam">
|
||||||
|
<img
|
||||||
|
alt="scratch facebook"
|
||||||
|
src="/images/conference/footer/facebook.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://medium.com/scratchfoundation-blog">
|
||||||
|
<img
|
||||||
|
alt="scratch foundation blog"
|
||||||
|
src="/images/conference/footer/medium.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FlexRow>
|
||||||
|
<LanguageChooser locale={props.intl.locale} />
|
||||||
|
</FooterBox>
|
||||||
|
);
|
||||||
|
|
||||||
|
ConferenceFooter.propTypes = {
|
||||||
|
intl: intlShape
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = injectIntl(ConferenceFooter);
|
module.exports = injectIntl(ConferenceFooter);
|
||||||
|
|
|
@ -1,84 +1,146 @@
|
||||||
var React = require('react');
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var ReactIntl = require('react-intl');
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
const FooterBox = require('../../container/footer.jsx');
|
||||||
|
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||||
var FlexRow = require('../../../flex-row/flex-row.jsx');
|
|
||||||
var FooterBox = require('../../container/footer.jsx');
|
|
||||||
var LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
|
||||||
|
|
||||||
require('../footer.scss');
|
require('../footer.scss');
|
||||||
|
|
||||||
var ConferenceFooter = React.createClass({
|
const ConferenceFooter = props => (
|
||||||
type: 'ConferenceFooter',
|
<FooterBox>
|
||||||
render: function () {
|
<FlexRow className="scratch-links">
|
||||||
return (
|
<div className="family">
|
||||||
<FooterBox>
|
<h4><FormattedMessage id="footer.scratchFamily" /></h4>
|
||||||
<FlexRow className="scratch-links">
|
<FlexRow>
|
||||||
<div className="family">
|
<FlexRow
|
||||||
<h4><FormattedMessage id='footer.scratchFamily' /></h4>
|
as="ul"
|
||||||
<FlexRow>
|
className="column"
|
||||||
<FlexRow as="ul" className="column">
|
>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://scratch.mit.edu" target="_blank">Scratch</a>
|
<a
|
||||||
</li>
|
href="https://scratch.mit.edu"
|
||||||
<li>
|
rel="noopener noreferrer"
|
||||||
<a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
|
target="_blank"
|
||||||
</li>
|
>
|
||||||
</FlexRow>
|
Scratch
|
||||||
<FlexRow as="ul" className="column">
|
</a>
|
||||||
<li>
|
</li>
|
||||||
<a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
|
<li>
|
||||||
</li>
|
<a
|
||||||
<li>
|
href="http://www.scratchjr.org/"
|
||||||
<a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
|
rel="noopener noreferrer"
|
||||||
</li>
|
target="_blank"
|
||||||
</FlexRow>
|
>
|
||||||
<FlexRow as="ul" className="column">
|
ScratchJr
|
||||||
<li>
|
</a>
|
||||||
<a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
|
</li>
|
||||||
</li>
|
</FlexRow>
|
||||||
</FlexRow>
|
<FlexRow
|
||||||
</FlexRow>
|
as="ul"
|
||||||
<p className="legal">
|
className="column"
|
||||||
<FormattedMessage id='general.copyright' />
|
>
|
||||||
</p>
|
<li>
|
||||||
</div>
|
<a
|
||||||
<div className="media">
|
href="http://www.scratchfoundation.org/"
|
||||||
<div className="contact-us">
|
rel="noopener noreferrer"
|
||||||
<h4>Contact</h4>
|
target="_blank"
|
||||||
<p>
|
>
|
||||||
<a href="mailto:conference@scratch.mit.edu" target="_blank">
|
Scratch Foundation
|
||||||
Email Us
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</p>
|
<li>
|
||||||
</div>
|
<a
|
||||||
<div className="social">
|
href="http://scratched.gse.harvard.edu/"
|
||||||
<FlexRow as="ul">
|
rel="noopener noreferrer"
|
||||||
<li>
|
target="_blank"
|
||||||
<a href="//www.twitter.com/scratch" target="_blank">
|
>
|
||||||
<img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
|
ScratchEd
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
</FlexRow>
|
||||||
<a href="//www.facebook.com/scratchteam" target="_blank">
|
<FlexRow
|
||||||
<img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
|
as="ul"
|
||||||
</a>
|
className="column"
|
||||||
</li>
|
>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://medium.com/scratchfoundation-blog" target="_blank">
|
<a
|
||||||
<img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
|
href="http://day.scratch.mit.edu"
|
||||||
</a>
|
rel="noopener noreferrer"
|
||||||
</li>
|
target="_blank"
|
||||||
</FlexRow>
|
>
|
||||||
</div>
|
Scratch Day
|
||||||
</div>
|
</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
<LanguageChooser locale={this.props.intl.locale} />
|
<p className="legal">
|
||||||
</FooterBox>
|
<FormattedMessage id="general.copyright" />
|
||||||
);
|
</p>
|
||||||
}
|
</div>
|
||||||
});
|
<div className="media">
|
||||||
|
<div className="contact-us">
|
||||||
|
<h4>Contact</h4>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="mailto:conference@scratch.mit.edu"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Email Us
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="social">
|
||||||
|
<FlexRow as="ul">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="//www.twitter.com/scratch"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="scratch twitter"
|
||||||
|
src="/images/conference/footer/twitter.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="//www.facebook.com/scratchteam"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="scratch facebook"
|
||||||
|
src="/images/conference/footer/facebook.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="http://medium.com/scratchfoundation-blog"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="scratch foundation blog"
|
||||||
|
src="/images/conference/footer/medium.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FlexRow>
|
||||||
|
<LanguageChooser locale={props.intl.locale} />
|
||||||
|
</FooterBox>
|
||||||
|
);
|
||||||
|
|
||||||
|
ConferenceFooter.propTypes = {
|
||||||
|
intl: intlShape
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = injectIntl(ConferenceFooter);
|
module.exports = injectIntl(ConferenceFooter);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./footer.scss');
|
require('./footer.scss');
|
||||||
|
|
||||||
var FooterBox = React.createClass({
|
const FooterBox = props => (
|
||||||
type: 'FooterBox',
|
<div className="inner">
|
||||||
render: function () {
|
{props.children}
|
||||||
return (
|
</div>
|
||||||
<div className="inner">
|
);
|
||||||
{this.props.children}
|
|
||||||
</div>
|
FooterBox.propTypes = {
|
||||||
);
|
children: PropTypes.node
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = FooterBox;
|
module.exports = FooterBox;
|
||||||
|
|
|
@ -1,226 +1,225 @@
|
||||||
var React = require('react');
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var ReactIntl = require('react-intl');
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
const intlShape = require('react-intl').intlShape;
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
const MediaQuery = require('react-responsive').default;
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var FooterBox = require('../container/footer.jsx');
|
const FooterBox = require('../container/footer.jsx');
|
||||||
var LanguageChooser = require('../../languagechooser/languagechooser.jsx');
|
const LanguageChooser = require('../../languagechooser/languagechooser.jsx');
|
||||||
|
|
||||||
var MediaQuery = require('react-responsive');
|
const frameless = require('../../../lib/frameless');
|
||||||
var frameless = require('../../../lib/frameless');
|
|
||||||
|
|
||||||
require('./footer.scss');
|
require('./footer.scss');
|
||||||
|
|
||||||
var Footer = React.createClass({
|
const Footer = props => (
|
||||||
type: 'Footer',
|
<FooterBox>
|
||||||
render: function () {
|
<MediaQuery maxWidth={frameless.tablet - 1}>
|
||||||
return (
|
<div className="lists">
|
||||||
<FooterBox>
|
<dl>
|
||||||
<MediaQuery maxWidth={frameless.tablet - 1}>
|
<dd>
|
||||||
<div className="lists">
|
<a href="/about">
|
||||||
<dl>
|
<FormattedMessage id="general.aboutScratch" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/about">
|
</dd>
|
||||||
<FormattedMessage id='general.aboutScratch' />
|
<dd>
|
||||||
</a>
|
<a href="/jobs">
|
||||||
</dd>
|
<FormattedMessage id="general.jobs" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/jobs">
|
</dd>
|
||||||
<FormattedMessage id='general.jobs' />
|
<dd>
|
||||||
</a>
|
<a href="/contact-us/">
|
||||||
</dd>
|
<FormattedMessage id="general.contactUs" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/contact-us/">
|
</dd>
|
||||||
<FormattedMessage id='general.contactUs' />
|
</dl>
|
||||||
</a>
|
<dl>
|
||||||
</dd>
|
<dd>
|
||||||
</dl>
|
<a href="/terms_of_use">
|
||||||
<dl>
|
<FormattedMessage id="general.termsOfUse" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/terms_of_use">
|
</dd>
|
||||||
<FormattedMessage id='general.termsOfUse' />
|
<dd>
|
||||||
</a>
|
<a href="/privacy_policy">
|
||||||
</dd>
|
<FormattedMessage id="general.privacyPolicy" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/privacy_policy">
|
</dd>
|
||||||
<FormattedMessage id='general.privacyPolicy' />
|
<dd>
|
||||||
</a>
|
<a href="/community_guidelines">
|
||||||
</dd>
|
<FormattedMessage id="general.guidelines" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/community_guidelines">
|
</dd>
|
||||||
<FormattedMessage id='general.guidelines' />
|
</dl>
|
||||||
</a>
|
</div>
|
||||||
</dd>
|
</MediaQuery>
|
||||||
</dl>
|
<MediaQuery minWidth={frameless.tablet}>
|
||||||
</div>
|
<div className="lists">
|
||||||
</MediaQuery>
|
<dl>
|
||||||
<MediaQuery minWidth={frameless.tablet}>
|
<dt>
|
||||||
<div className="lists">
|
<FormattedMessage id="general.about" />
|
||||||
<dl>
|
</dt>
|
||||||
<dt>
|
<dd>
|
||||||
<FormattedMessage id='general.about' />
|
<a href="/about">
|
||||||
</dt>
|
<FormattedMessage id="general.aboutScratch" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/about">
|
</dd>
|
||||||
<FormattedMessage id='general.aboutScratch' />
|
<dd>
|
||||||
</a>
|
<a href="/parents/">
|
||||||
</dd>
|
<FormattedMessage id="general.forParents" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/parents/">
|
</dd>
|
||||||
<FormattedMessage id='general.forParents' />
|
<dd>
|
||||||
</a>
|
<a href="/educators">
|
||||||
</dd>
|
<FormattedMessage id="general.forEducators" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/educators">
|
</dd>
|
||||||
<FormattedMessage id='general.forEducators' />
|
<dd>
|
||||||
</a>
|
<a href="/developers">
|
||||||
</dd>
|
<FormattedMessage id="general.forDevelopers" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/developers">
|
</dd>
|
||||||
<FormattedMessage id='general.forDevelopers' />
|
<dd>
|
||||||
</a>
|
<a href="/info/credits">
|
||||||
</dd>
|
<FormattedMessage id="general.credits" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/info/credits">
|
</dd>
|
||||||
<FormattedMessage id='general.credits' />
|
<dd>
|
||||||
</a>
|
<a href="/jobs">
|
||||||
</dd>
|
<FormattedMessage id="general.jobs" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/jobs">
|
</dd>
|
||||||
<FormattedMessage id='general.jobs' />
|
<dd>
|
||||||
</a>
|
<a href="http://wiki.scratch.mit.edu/wiki/Scratch_Press">
|
||||||
</dd>
|
<FormattedMessage id="general.press" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="http://wiki.scratch.mit.edu/wiki/Scratch_Press">
|
</dd>
|
||||||
<FormattedMessage id='general.press' />
|
</dl>
|
||||||
</a>
|
<dl>
|
||||||
</dd>
|
<dt>
|
||||||
</dl>
|
<FormattedMessage id="general.community" />
|
||||||
<dl>
|
</dt>
|
||||||
<dt>
|
<dd>
|
||||||
<FormattedMessage id='general.community' />
|
<a href="/community_guidelines">
|
||||||
</dt>
|
<FormattedMessage id="general.guidelines" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/community_guidelines">
|
</dd>
|
||||||
<FormattedMessage id='general.guidelines' />
|
<dd>
|
||||||
</a>
|
<a href="/discuss/">
|
||||||
</dd>
|
<FormattedMessage id="footer.discuss" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/discuss/">
|
</dd>
|
||||||
<FormattedMessage id='footer.discuss' />
|
<dd>
|
||||||
</a>
|
<a href="https://wiki.scratch.mit.edu/">
|
||||||
</dd>
|
<FormattedMessage id="general.wiki" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="https://wiki.scratch.mit.edu/">
|
</dd>
|
||||||
<FormattedMessage id='general.wiki' />
|
<dd>
|
||||||
</a>
|
<a href="/statistics/">
|
||||||
</dd>
|
<FormattedMessage id="general.statistics" />
|
||||||
<dd>
|
</a>
|
||||||
<a href="/statistics/">
|
</dd>
|
||||||
<FormattedMessage id='general.statistics' />
|
</dl>
|
||||||
</a>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>
|
<dt>
|
||||||
<FormattedMessage id='general.support' />
|
<FormattedMessage id="general.support" />
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/tips">
|
<a href="/tips">
|
||||||
<FormattedMessage id='general.tips' />
|
<FormattedMessage id="general.tips" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/info/faq">
|
<a href="/info/faq">
|
||||||
<FormattedMessage id='general.faq' />
|
<FormattedMessage id="general.faq" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/download">
|
<a href="/download">
|
||||||
<FormattedMessage id='general.offlineEditor' />
|
<FormattedMessage id="general.offlineEditor" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/contact-us/">
|
<a href="/contact-us/">
|
||||||
<FormattedMessage id='general.contactUs' />
|
<FormattedMessage id="general.contactUs" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/store">
|
<a href="/store">
|
||||||
<FormattedMessage id='general.scratchStore' />
|
<FormattedMessage id="general.scratchStore" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="https://secure.donationpay.org/scratchfoundation/">
|
<a href="https://secure.donationpay.org/scratchfoundation/">
|
||||||
<FormattedMessage id='general.donate'/>
|
<FormattedMessage id="general.donate" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>
|
<dt>
|
||||||
<FormattedMessage id='general.legal'/>
|
<FormattedMessage id="general.legal" />
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/terms_of_use">
|
<a href="/terms_of_use">
|
||||||
<FormattedMessage id='general.termsOfUse' />
|
<FormattedMessage id="general.termsOfUse" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/privacy_policy">
|
<a href="/privacy_policy">
|
||||||
<FormattedMessage id='general.privacyPolicy' />
|
<FormattedMessage id="general.privacyPolicy" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/DMCA">
|
<a href="/DMCA">
|
||||||
<FormattedMessage id='general.dmca' />
|
<FormattedMessage id="general.dmca" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>
|
<dt>
|
||||||
<FormattedMessage id='footer.scratchFamily' />
|
<FormattedMessage id="footer.scratchFamily" />
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="http://scratched.gse.harvard.edu/">
|
<a href="http://scratched.gse.harvard.edu/">
|
||||||
<FormattedMessage id='general.scratchEd' />
|
<FormattedMessage id="general.scratchEd" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="http://www.scratchjr.org/">
|
<a href="http://www.scratchjr.org/">
|
||||||
<FormattedMessage id='general.scratchJr' />
|
<FormattedMessage id="general.scratchJr" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="http://day.scratch.mit.edu/">
|
<a href="http://day.scratch.mit.edu/">
|
||||||
Scratch Day
|
Scratch Day
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/conference">
|
<a href="/conference">
|
||||||
<FormattedMessage id='general.scratchConference' />
|
<FormattedMessage id="general.scratchConference" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="http://www.scratchfoundation.org/">
|
<a href="http://www.scratchfoundation.org/">
|
||||||
<FormattedMessage id='general.scratchFoundation' />
|
<FormattedMessage id="general.scratchFoundation" />
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<LanguageChooser locale={this.props.intl.locale} />
|
<LanguageChooser locale={props.intl.locale} />
|
||||||
|
|
||||||
<div className="copyright">
|
<div className="copyright">
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage id='general.copyright' />
|
<FormattedMessage id="general.copyright" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</FooterBox>
|
</FooterBox>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
Footer.propTypes = {
|
||||||
|
intl: intlShape.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = injectIntl(Footer);
|
module.exports = injectIntl(Footer);
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
var classNames = require('classnames');
|
const omit = require('lodash.omit');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./button.scss');
|
require('./button.scss');
|
||||||
|
|
||||||
var Button = React.createClass({
|
const Button = props => {
|
||||||
type: 'Button',
|
const classes = classNames('button', props.className);
|
||||||
propTypes: {
|
|
||||||
|
return (
|
||||||
},
|
<button
|
||||||
render: function () {
|
className={classes}
|
||||||
var classes = classNames(
|
{...omit(props, ['className', 'children'])}
|
||||||
'button',
|
>
|
||||||
this.props.className
|
{props.children}
|
||||||
);
|
</button>
|
||||||
return (
|
);
|
||||||
<button {... this.props} className={classes} >{this.props.children}</button>
|
};
|
||||||
);
|
|
||||||
}
|
Button.propTypes = {
|
||||||
});
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Button;
|
module.exports = Button;
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./charcount.scss');
|
require('./charcount.scss');
|
||||||
|
|
||||||
var CharCount = React.createClass({
|
const CharCount = props => (
|
||||||
type: 'CharCount',
|
<p
|
||||||
getDefaultProps: function () {
|
className={classNames('char-count', props.className, {
|
||||||
return {
|
overmax: (props.currentCharacters > props.maxCharacters)
|
||||||
maxCharacters: 0,
|
})}
|
||||||
currentCharacters: 0
|
>
|
||||||
};
|
{props.currentCharacters}/{props.maxCharacters}
|
||||||
},
|
</p>
|
||||||
render: function () {
|
);
|
||||||
var classes = classNames(
|
|
||||||
'char-count',
|
CharCount.propTypes = {
|
||||||
this.props.className,
|
className: PropTypes.string,
|
||||||
{overmax: (this.props.currentCharacters > this.props.maxCharacters)}
|
currentCharacters: PropTypes.number,
|
||||||
);
|
maxCharacters: PropTypes.number
|
||||||
return (
|
};
|
||||||
<p className={classes}>
|
|
||||||
{this.props.currentCharacters}/{this.props.maxCharacters}
|
CharCount.defaultProps = {
|
||||||
</p>
|
currentCharacters: 0,
|
||||||
);
|
maxCharacters: 0
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = CharCount;
|
module.exports = CharCount;
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup;
|
const FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const React = require('react');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./checkbox-group.scss');
|
require('./checkbox-group.scss');
|
||||||
|
|
||||||
var CheckboxGroup = React.createClass({
|
const CheckboxGroup = props => (
|
||||||
type: 'CheckboxGroup',
|
<div className={classNames('checkbox-group', props.className)}>
|
||||||
render: function () {
|
<FRCCheckboxGroup
|
||||||
var classes = classNames(
|
className={classNames('checkbox-group', props.className)}
|
||||||
'checkbox-group',
|
{... props}
|
||||||
this.props.className
|
/>
|
||||||
);
|
</div>
|
||||||
return (
|
);
|
||||||
<div className={classes}>
|
|
||||||
<FRCCheckboxGroup {... this.props} className={classes} />
|
CheckboxGroup.propTypes = {
|
||||||
</div>
|
className: PropTypes.string
|
||||||
);
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(CheckboxGroup));
|
module.exports = inputHOC(defaultValidationHOC(CheckboxGroup));
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FRCCheckbox = require('formsy-react-components').Checkbox;
|
const FRCCheckbox = require('formsy-react-components').Checkbox;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const React = require('react');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./checkbox.scss');
|
require('./checkbox.scss');
|
||||||
|
|
||||||
var Checkbox = React.createClass({
|
const Checkbox = props => (
|
||||||
type: 'Checkbox',
|
<FRCCheckbox
|
||||||
render: function () {
|
rowClassName={classNames('checkbox-row', props.className)}
|
||||||
var classes = classNames(
|
{...props}
|
||||||
'checkbox-row',
|
/>
|
||||||
this.props.className
|
);
|
||||||
);
|
|
||||||
return (
|
Checkbox.propTypes = {
|
||||||
<FRCCheckbox rowClassName={classes} {... this.props} />
|
className: PropTypes.string
|
||||||
);
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(Checkbox));
|
module.exports = inputHOC(defaultValidationHOC(Checkbox));
|
||||||
|
|
|
@ -1,47 +1,61 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var Formsy = require('formsy-react');
|
const classNames = require('classnames');
|
||||||
var omit = require('lodash.omit');
|
const Formsy = require('formsy-react');
|
||||||
var React = require('react');
|
const omit = require('lodash.omit');
|
||||||
var validations = require('./validations.jsx').validations;
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
for (var validation in validations) {
|
const validations = require('./validations.jsx').validations;
|
||||||
|
|
||||||
|
for (const validation in validations) {
|
||||||
Formsy.addValidationRule(validation, validations[validation]);
|
Formsy.addValidationRule(validation, validations[validation]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var Form = React.createClass({
|
class Form extends React.Component {
|
||||||
getDefaultProps: function () {
|
constructor (props) {
|
||||||
return {
|
super(props);
|
||||||
noValidate: true,
|
bindAll(this, [
|
||||||
onChange: function () {}
|
'handleChange'
|
||||||
};
|
]);
|
||||||
},
|
this.state = {
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
allValues: {}
|
allValues: {}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
onChange: function (currentValues, isChanged) {
|
handleChange (currentValues, isChanged) {
|
||||||
this.setState({allValues: omit(currentValues, 'all')});
|
this.setState({allValues: omit(currentValues, 'all')});
|
||||||
this.props.onChange(currentValues, isChanged);
|
this.props.onChange(currentValues, isChanged);
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames(
|
|
||||||
'form',
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Formsy.Form {... this.props} className={classes} ref="formsy" onChange={this.onChange}>
|
<Formsy.Form
|
||||||
{React.Children.map(this.props.children, function (child) {
|
className={classNames('form', this.props.className)}
|
||||||
|
ref={form => {
|
||||||
|
this.formsy = form;
|
||||||
|
}}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
{...this.props}
|
||||||
|
>
|
||||||
|
{React.Children.map(this.props.children, child => {
|
||||||
if (!child) return child;
|
if (!child) return child;
|
||||||
if (child.props.name === 'all') {
|
if (child.props.name === 'all') {
|
||||||
return React.cloneElement(child, {value: this.state.allValues});
|
return React.cloneElement(child, {value: this.state.allValues});
|
||||||
} else {
|
|
||||||
return child;
|
|
||||||
}
|
}
|
||||||
}.bind(this))}
|
return child;
|
||||||
|
})}
|
||||||
</Formsy.Form>
|
</Formsy.Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.defaultProps = {
|
||||||
|
noValidate: true,
|
||||||
|
onChange: function () {}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Form;
|
module.exports = Form;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var Formsy = require('formsy-react');
|
const Formsy = require('formsy-react');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./general-error.scss');
|
require('./general-error.scss');
|
||||||
|
|
||||||
|
@ -10,13 +11,18 @@ require('./general-error.scss');
|
||||||
* give it a name, and apply your validation error to
|
* give it a name, and apply your validation error to
|
||||||
* the name of the GeneralError component.
|
* the name of the GeneralError component.
|
||||||
*/
|
*/
|
||||||
module.exports = Formsy.HOC(React.createClass({
|
const GeneralError = props => {
|
||||||
render: function () {
|
if (!props.showError()) return null;
|
||||||
if (!this.props.showError()) return null;
|
return (
|
||||||
return (
|
<p className="general-error">
|
||||||
<p className="general-error">
|
{props.getErrorMessage()}
|
||||||
{this.props.getErrorMessage()}
|
</p>
|
||||||
</p>
|
);
|
||||||
);
|
};
|
||||||
}
|
|
||||||
}));
|
GeneralError.propTypes = {
|
||||||
|
getErrorMessage: PropTypes.func,
|
||||||
|
showError: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Formsy.HOC(GeneralError);
|
||||||
|
|
|
@ -1,20 +1,32 @@
|
||||||
var React = require('react');
|
const omit = require('lodash.omit');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
module.exports = function InputComponentMixin (Component) {
|
/**
|
||||||
var InputComponent = React.createClass({
|
* Higher-order component for building an input field
|
||||||
getDefaultProps: function () {
|
* @param {React.Component} Component an input component
|
||||||
return {
|
* @return {React.Component} a wrapped input component
|
||||||
messages: {
|
*/
|
||||||
'general.notRequired': 'Not Required'
|
module.exports = Component => {
|
||||||
}
|
const InputComponent = props => (
|
||||||
};
|
<Component
|
||||||
},
|
help={props.required ? null : props.messages['general.notRequired']}
|
||||||
render: function () {
|
{...omit(props, ['messages'])}
|
||||||
return (
|
/>
|
||||||
<Component help={this.props.required ? null : this.props.messages['general.notRequired']}
|
);
|
||||||
{...this.props} />
|
|
||||||
);
|
InputComponent.propTypes = {
|
||||||
|
messages: PropTypes.shape({
|
||||||
|
'general.notRequired': PropTypes.string
|
||||||
|
}),
|
||||||
|
required: PropTypes.oneOfType([PropTypes.bool, PropTypes.string])
|
||||||
|
};
|
||||||
|
|
||||||
|
InputComponent.defaultProps = {
|
||||||
|
messages: {
|
||||||
|
'general.notRequired': 'Not Required'
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
return InputComponent;
|
return InputComponent;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,46 +1,57 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var FRCInput = require('formsy-react-components').Input;
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const FRCInput = require('formsy-react-components').Input;
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const omit = require('lodash.omit');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./input.scss');
|
require('./input.scss');
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
|
|
||||||
var Input = React.createClass({
|
class Input extends React.Component {
|
||||||
type: 'Input',
|
constructor (props) {
|
||||||
getDefaultProps: function () {
|
super(props);
|
||||||
return {};
|
bindAll(this, [
|
||||||
},
|
'handleInvalid',
|
||||||
getInitialState: function () {
|
'handleValid'
|
||||||
return {
|
]);
|
||||||
|
this.state = {
|
||||||
status: ''
|
status: ''
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
onValid: function () {
|
handleValid () {
|
||||||
this.setState({
|
this.setState({
|
||||||
status: 'pass'
|
status: 'pass'
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
onInvalid: function () {
|
handleInvalid () {
|
||||||
this.setState({
|
this.setState({
|
||||||
status: 'fail'
|
status: 'fail'
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames(
|
|
||||||
this.state.status,
|
|
||||||
this.props.className,
|
|
||||||
{'no-label': (typeof this.props.label === 'undefined')}
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<FRCInput {... this.props}
|
<FRCInput
|
||||||
className="input"
|
className="input"
|
||||||
rowClassName={classes}
|
rowClassName={classNames(
|
||||||
onValid={this.onValid}
|
this.state.status,
|
||||||
onInvalid={this.onInvalid} />
|
this.props.className,
|
||||||
|
{'no-label': (typeof this.props.label === 'undefined')}
|
||||||
|
)}
|
||||||
|
onInvalid={this.handleInvalid}
|
||||||
|
onValid={this.handleValid}
|
||||||
|
{...omit(this.props, ['className'])}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Input.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
label: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(Input));
|
module.exports = inputHOC(defaultValidationHOC(Input));
|
||||||
|
|
|
@ -1,23 +1,33 @@
|
||||||
var allCountries = require('react-telephone-input/lib/country_data').allCountries;
|
const allCountries = require('react-telephone-input/lib/country_data').allCountries;
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var ComponentMixin = require('formsy-react-components').ComponentMixin;
|
const ComponentMixin = require('formsy-react-components').ComponentMixin;
|
||||||
var FormsyMixin = require('formsy-react').Mixin;
|
const createReactClass = require('create-react-class');
|
||||||
var React = require('react');
|
const FormsyMixin = require('formsy-react').Mixin;
|
||||||
var ReactPhoneInput = require('react-telephone-input/lib/withStyles');
|
const omit = require('lodash.omit');
|
||||||
var Row = require('formsy-react-components').Row;
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const ReactPhoneInput = require('react-telephone-input/lib/withStyles').default;
|
||||||
|
const Row = require('formsy-react-components').Row;
|
||||||
|
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
var intl = require('../../lib/intl.jsx');
|
const intl = require('../../lib/intl.jsx');
|
||||||
var validationHOCFactory = require('./validations.jsx').validationHOCFactory;
|
const validationHOCFactory = require('./validations.jsx').validationHOCFactory;
|
||||||
|
|
||||||
var allIso2 = allCountries.map(function (country) {return country.iso2;});
|
const allIso2 = allCountries.map(country => (country.iso2));
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./phone-input.scss');
|
require('./phone-input.scss');
|
||||||
|
|
||||||
var PhoneInput = React.createClass({
|
const PhoneInput = createReactClass({ // eslint-disable-line react/prefer-es6-class
|
||||||
displayName: 'PhoneInput',
|
displayName: 'PhoneInput',
|
||||||
|
propTypes: {
|
||||||
|
className: PropTypes.string,
|
||||||
|
defaultCountry: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
name: PropTypes.string,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
FormsyMixin,
|
FormsyMixin,
|
||||||
ComponentMixin
|
ComponentMixin
|
||||||
|
@ -31,29 +41,34 @@ var PhoneInput = React.createClass({
|
||||||
defaultCountry: 'us'
|
defaultCountry: 'us'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onChangeInput: function (number, country) {
|
handleChangeInput: function (number, country) {
|
||||||
var value = {national_number: number, country_code: country};
|
const value = {
|
||||||
|
national_number: number,
|
||||||
|
country_code: country
|
||||||
|
};
|
||||||
this.setValue(value);
|
this.setValue(value);
|
||||||
this.props.onChange(this.props.name, value);
|
this.props.onChange(this.props.name, value);
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var defaultCountry = PhoneInput.getDefaultProps().defaultCountry;
|
let defaultCountry = PhoneInput.getDefaultProps().defaultCountry;
|
||||||
if (allIso2.indexOf(this.props.defaultCountry.toLowerCase()) !== -1) {
|
if (allIso2.indexOf(this.props.defaultCountry.toLowerCase()) !== -1) {
|
||||||
defaultCountry = this.props.defaultCountry.toLowerCase();
|
defaultCountry = this.props.defaultCountry.toLowerCase();
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Row {... this.getRowProperties()}
|
<Row
|
||||||
htmlFor={this.getId()}
|
htmlFor={this.getId()}
|
||||||
rowClassName={classNames('phone-input', this.props.className)}
|
rowClassName={classNames('phone-input', this.props.className)}
|
||||||
|
{...this.getRowProperties()}
|
||||||
>
|
>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<ReactPhoneInput className="form-control"
|
<ReactPhoneInput
|
||||||
{... this.props}
|
className="form-control"
|
||||||
defaultCountry={defaultCountry}
|
defaultCountry={defaultCountry}
|
||||||
onChange={this.onChangeInput}
|
disabled={this.isFormDisabled() || this.props.disabled}
|
||||||
id={this.getId()}
|
id={this.getId()}
|
||||||
label={null}
|
label={null}
|
||||||
disabled={this.isFormDisabled() || this.props.disabled}
|
onChange={this.handleChangeInput}
|
||||||
|
{...omit(this.props, ['className', 'disabled', 'onChange'])}
|
||||||
/>
|
/>
|
||||||
{this.renderHelp()}
|
{this.renderHelp()}
|
||||||
{this.renderErrorMessage()}
|
{this.renderErrorMessage()}
|
||||||
|
@ -63,7 +78,7 @@ var PhoneInput = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var phoneValidationHOC = validationHOCFactory({
|
const phoneValidationHOC = validationHOCFactory({
|
||||||
isPhone: <intl.FormattedMessage id="teacherRegistration.validationPhoneNumber" />
|
isPhone: <intl.FormattedMessage id="teacherRegistration.validationPhoneNumber" />
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FRCRadioGroup = require('formsy-react-components').RadioGroup;
|
const FRCRadioGroup = require('formsy-react-components').RadioGroup;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const React = require('react');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./radio-group.scss');
|
require('./radio-group.scss');
|
||||||
|
|
||||||
var RadioGroup = React.createClass({
|
const RadioGroup = props => (
|
||||||
type: 'RadioGroup',
|
<FRCRadioGroup
|
||||||
render: function () {
|
className={classNames('radio-group', props.className)}
|
||||||
var classes = classNames(
|
{... props}
|
||||||
'radio-group',
|
/>
|
||||||
this.props.className
|
);
|
||||||
);
|
|
||||||
return (
|
RadioGroup.propTypes = {
|
||||||
<FRCRadioGroup {... this.props} className={classes} />
|
className: PropTypes.string
|
||||||
);
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(RadioGroup));
|
module.exports = inputHOC(defaultValidationHOC(RadioGroup));
|
||||||
|
|
|
@ -1,33 +1,31 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var defaults = require('lodash.defaultsdeep');
|
const defaults = require('lodash.defaultsdeep');
|
||||||
var FRCSelect = require('formsy-react-components').Select;
|
const FRCSelect = require('formsy-react-components').Select;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const React = require('react');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./select.scss');
|
require('./select.scss');
|
||||||
|
|
||||||
var Select = React.createClass({
|
const Select = props => {
|
||||||
type: 'Select',
|
if (props.required && !props.value) {
|
||||||
propTypes: {
|
props = defaults({}, props, {value: props.options[0].value});
|
||||||
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var classes = classNames(
|
|
||||||
'select',
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
var props = this.props;
|
|
||||||
if (this.props.required && !this.props.value) {
|
|
||||||
props = defaults({}, this.props, {value: this.props.options[0].value});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes}>
|
|
||||||
<FRCSelect {... props} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
return (
|
||||||
|
<div className={classNames('select', props.className)}>
|
||||||
|
<FRCSelect {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Select.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
options: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
required: PropTypes.bool,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(Select));
|
module.exports = inputHOC(defaultValidationHOC(Select));
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FRCTextarea = require('formsy-react-components').Textarea;
|
const FRCTextarea = require('formsy-react-components').Textarea;
|
||||||
var React = require('react');
|
const omit = require('lodash.omit');
|
||||||
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
const PropTypes = require('prop-types');
|
||||||
var inputHOC = require('./input-hoc.jsx');
|
const React = require('react');
|
||||||
|
|
||||||
|
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||||
|
const inputHOC = require('./input-hoc.jsx');
|
||||||
|
|
||||||
require('./row.scss');
|
require('./row.scss');
|
||||||
require('./textarea.scss');
|
require('./textarea.scss');
|
||||||
|
|
||||||
var TextArea = React.createClass({
|
const TextArea = props => (
|
||||||
type: 'TextArea',
|
<FRCTextarea
|
||||||
render: function () {
|
className="textarea"
|
||||||
var classes = classNames(
|
rowClassName={classNames('textarea-row', props.className)}
|
||||||
'textarea-row',
|
{...omit(props, ['className'])}
|
||||||
this.props.className
|
/>
|
||||||
);
|
);
|
||||||
return (
|
|
||||||
<FRCTextarea {... this.props}
|
TextArea.propTypes = {
|
||||||
className="textarea"
|
className: PropTypes.string
|
||||||
rowClassName={classes} />
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = inputHOC(defaultValidationHOC(TextArea));
|
module.exports = inputHOC(defaultValidationHOC(TextArea));
|
||||||
|
|
|
@ -1,48 +1,46 @@
|
||||||
var defaults = require('lodash.defaultsdeep');
|
const defaults = require('lodash.defaultsdeep');
|
||||||
var intl = require('../../lib/intl.jsx');
|
const intl = require('../../lib/intl.jsx');
|
||||||
var libphonenumber = require('google-libphonenumber');
|
const libphonenumber = require('google-libphonenumber');
|
||||||
var phoneNumberUtil = libphonenumber.PhoneNumberUtil.getInstance();
|
const omit = require('lodash.omit');
|
||||||
var React = require('react');
|
const phoneNumberUtil = libphonenumber.PhoneNumberUtil.getInstance();
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
module.exports = {};
|
const React = require('react');
|
||||||
|
|
||||||
module.exports.validations = {
|
module.exports.validations = {
|
||||||
notEquals: function (values, value, neq) {
|
notEquals: (values, value, neq) => (value !== neq),
|
||||||
return value !== neq;
|
notEqualsField: (values, value, field) => (value !== values[field]),
|
||||||
},
|
isPhone: (values, value) => {
|
||||||
notEqualsField: function (values, value, field) {
|
|
||||||
return value !== values[field];
|
|
||||||
},
|
|
||||||
isPhone: function (values, value) {
|
|
||||||
if (typeof value === 'undefined') return true;
|
if (typeof value === 'undefined') return true;
|
||||||
if (value && value.national_number === '+') return true;
|
if (value && value.national_number === '+') return true;
|
||||||
try {
|
try {
|
||||||
var parsed = phoneNumberUtil.parse(value.national_number, value.country_code.iso2);
|
const parsed = phoneNumberUtil.parse(value.national_number, value.country_code.iso2);
|
||||||
|
return phoneNumberUtil.isValidNumber(parsed);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return phoneNumberUtil.isValidNumber(parsed);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.validations.notEqualsUsername = module.exports.validations.notEquals;
|
module.exports.validations.notEqualsUsername = module.exports.validations.notEquals;
|
||||||
|
|
||||||
module.exports.validationHOCFactory = function (defaultValidationErrors) {
|
module.exports.validationHOCFactory = defaultValidationErrors => (Component => {
|
||||||
return function (Component) {
|
const ValidatedComponent = props => (
|
||||||
var ValidatedComponent = React.createClass({
|
<Component
|
||||||
render: function () {
|
validationErrors={defaults(
|
||||||
var validationErrors = defaults(
|
{},
|
||||||
{},
|
defaultValidationErrors,
|
||||||
defaultValidationErrors,
|
props.validationErrors
|
||||||
this.props.validationErrors
|
)}
|
||||||
);
|
{...omit(props, ['validationErrors'])}
|
||||||
return (
|
/>
|
||||||
<Component {...this.props} validationErrors={validationErrors} />
|
);
|
||||||
);
|
|
||||||
}
|
ValidatedComponent.propTypes = {
|
||||||
});
|
validationErrors: PropTypes.object // eslint-disable-line react/forbid-prop-types
|
||||||
return ValidatedComponent;
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
return ValidatedComponent;
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.defaultValidationHOC = module.exports.validationHOCFactory({
|
module.exports.defaultValidationHOC = module.exports.validationHOCFactory({
|
||||||
isDefaultRequiredValue: <intl.FormattedMessage id="form.validationRequired" />
|
isDefaultRequiredValue: <intl.FormattedMessage id="form.validationRequired" />
|
||||||
|
|
|
@ -1,79 +1,68 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
const Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||||
var FlexRow = require('../flex-row/flex-row.jsx');
|
const FlexRow = require('../flex-row/flex-row.jsx');
|
||||||
|
|
||||||
require('./grid.scss');
|
require('./grid.scss');
|
||||||
|
|
||||||
var Grid = React.createClass({
|
const Grid = props => (
|
||||||
type: 'Grid',
|
<div className={classNames('grid', props.className)}>
|
||||||
getDefaultProps: function () {
|
<FlexRow>
|
||||||
return {
|
{props.items.map((item, key) => {
|
||||||
items: require('./grid.json'),
|
const href = `/${props.itemType}/${item.id}/`;
|
||||||
itemType: 'projects',
|
if (props.itemType === 'projects') {
|
||||||
showLoves: false,
|
return (
|
||||||
showFavorites: false,
|
<Thumbnail
|
||||||
showRemixes: false,
|
avatar={`https://cdn2.scratch.mit.edu/get_image/user/${item.author.id}_32x32.png`}
|
||||||
showViews: false,
|
creator={item.author.username}
|
||||||
showAvatar: false
|
favorites={item.stats.favorites}
|
||||||
};
|
href={href}
|
||||||
},
|
key={key}
|
||||||
render: function () {
|
loves={item.stats.loves}
|
||||||
var classes = classNames(
|
remixes={item.stats.remixes}
|
||||||
'grid',
|
showAvatar={props.showAvatar}
|
||||||
this.props.className
|
showFavorites={props.showFavorites}
|
||||||
);
|
showLoves={props.showLoves}
|
||||||
return (
|
showRemixes={props.showRemixes}
|
||||||
<div className={classes}>
|
showViews={props.showViews}
|
||||||
<FlexRow>
|
src={item.image}
|
||||||
{this.props.items.map(function (item, key) {
|
title={item.title}
|
||||||
var href = '/' + this.props.itemType + '/' + item.id + '/';
|
type={'project'}
|
||||||
|
views={item.stats.views}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Thumbnail
|
||||||
|
href={href}
|
||||||
|
key={key}
|
||||||
|
owner={item.owner}
|
||||||
|
src={item.image}
|
||||||
|
title={item.title}
|
||||||
|
type={'gallery'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
if (this.props.itemType == 'projects') {
|
Grid.propTypes = {
|
||||||
return (
|
className: PropTypes.string,
|
||||||
<Thumbnail
|
itemType: PropTypes.string,
|
||||||
key={key}
|
items: PropTypes.arrayOf(PropTypes.object)
|
||||||
showLoves={this.props.showLoves}
|
};
|
||||||
showFavorites={this.props.showFavorites}
|
|
||||||
showRemixes={this.props.showRemixes}
|
Grid.defaultProps = {
|
||||||
showViews={this.props.showViews}
|
items: require('./grid.json'),
|
||||||
showAvatar={this.props.showAvatar}
|
itemType: 'projects',
|
||||||
type={'project'}
|
showLoves: false,
|
||||||
href={href}
|
showFavorites: false,
|
||||||
title={item.title}
|
showRemixes: false,
|
||||||
src={item.image}
|
showViews: false,
|
||||||
avatar={
|
showAvatar: false
|
||||||
'https://uploads.scratch.mit.edu/users/avatars/' +
|
};
|
||||||
item.author.id +
|
|
||||||
'.png'
|
|
||||||
}
|
|
||||||
creator={item.author.username}
|
|
||||||
loves={item.stats.loves}
|
|
||||||
favorites={item.stats.favorites}
|
|
||||||
remixes={item.stats.remixes}
|
|
||||||
views={item.stats.views}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<Thumbnail
|
|
||||||
key={key}
|
|
||||||
type={'gallery'}
|
|
||||||
href={href}
|
|
||||||
title={item.title}
|
|
||||||
src={item.image}
|
|
||||||
srcDefault={'https://uploads.scratch.mit.edu/galleries/thumbnails/default.png'}
|
|
||||||
owner={item.owner}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}.bind(this))}
|
|
||||||
</FlexRow>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Grid;
|
module.exports = Grid;
|
||||||
|
|
|
@ -1,39 +1,34 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var TitleBanner = require('../../components/title-banner/title-banner.jsx');
|
const React = require('react');
|
||||||
|
|
||||||
|
const TitleBanner = require('../../components/title-banner/title-banner.jsx');
|
||||||
|
|
||||||
require('./informationpage.scss');
|
require('./informationpage.scss');
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Container for a table of contents
|
* Container for a table of contents
|
||||||
* alongside a long body of text
|
* alongside a long body of text
|
||||||
*/
|
*/
|
||||||
var InformationPage = React.createClass({
|
const InformationPage = props => (
|
||||||
type: 'InformationPage',
|
<div className="information-page">
|
||||||
propTypes: {
|
<TitleBanner className="masthead">
|
||||||
title: React.PropTypes.string.isRequired
|
<div className="inner">
|
||||||
},
|
<h1 className="title-banner-h1">
|
||||||
render: function () {
|
{props.title}
|
||||||
var classes = classNames(
|
</h1>
|
||||||
'info-outer',
|
|
||||||
'inner',
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className="information-page">
|
|
||||||
<TitleBanner className="masthead">
|
|
||||||
<div className="inner">
|
|
||||||
<h1 className="title-banner-h1">
|
|
||||||
{this.props.title}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</TitleBanner>
|
|
||||||
<div className={classes}>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</TitleBanner>
|
||||||
}
|
<div className={classNames('info-outer', 'inner', props.className)}>
|
||||||
});
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
InformationPage.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = InformationPage;
|
module.exports = InformationPage;
|
||||||
|
|
|
@ -1,111 +1,128 @@
|
||||||
var connect = require('react-redux').connect;
|
const bindAll = require('lodash.bindall');
|
||||||
var React = require('react');
|
const connect = require('react-redux').connect;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var sessionActions = require('../../redux/session.js');
|
const sessionActions = require('../../redux/session.js');
|
||||||
|
|
||||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
const IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
var Registration = require('../registration/registration.jsx');
|
const Registration = require('../registration/registration.jsx');
|
||||||
|
|
||||||
require('./intro.scss');
|
require('./intro.scss');
|
||||||
|
|
||||||
var Intro = React.createClass({
|
class Intro extends React.Component {
|
||||||
type: 'Intro',
|
constructor (props) {
|
||||||
getDefaultProps: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
messages: {
|
'handleShowVideo',
|
||||||
'intro.aboutScratch': 'ABOUT SCRATCH',
|
'handleCloseVideo',
|
||||||
'intro.forEducators': 'FOR EDUCATORS',
|
'handleJoinClick',
|
||||||
'intro.forParents': 'FOR PARENTS',
|
'handleCloseRegistration',
|
||||||
'intro.itsFree': 'it\'s free!',
|
'handleCompleteRegistration'
|
||||||
'intro.joinScratch': 'JOIN SCRATCH',
|
]);
|
||||||
'intro.seeExamples': 'SEE EXAMPLES',
|
this.state = {
|
||||||
'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"> ' +
|
|
||||||
'over 14 million </span>projects shared'
|
|
||||||
},
|
|
||||||
session: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
videoOpen: false
|
videoOpen: false
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
showVideo: function () {
|
handleShowVideo () {
|
||||||
this.setState({videoOpen: true});
|
this.setState({videoOpen: true});
|
||||||
},
|
}
|
||||||
closeVideo: function () {
|
handleCloseVideo () {
|
||||||
this.setState({videoOpen: false});
|
this.setState({videoOpen: false});
|
||||||
},
|
}
|
||||||
handleJoinClick: function (e) {
|
handleJoinClick (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({'registrationOpen': true});
|
this.setState({registrationOpen: true});
|
||||||
},
|
}
|
||||||
closeRegistration: function () {
|
handleCloseRegistration () {
|
||||||
this.setState({'registrationOpen': false});
|
this.setState({registrationOpen: false});
|
||||||
},
|
}
|
||||||
completeRegistration: function () {
|
handleCompleteRegistration () {
|
||||||
this.props.dispatch(sessionActions.refreshSession());
|
this.props.dispatch(sessionActions.refreshSession());
|
||||||
this.closeRegistration();
|
this.closeRegistration();
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="intro">
|
<div className="intro">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h1 dangerouslySetInnerHTML={{__html: this.props.messages['intro.tagLine']}}>
|
<h1
|
||||||
</h1>
|
dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
|
||||||
|
__html: this.props.messages['intro.tagLine']
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className="sprites">
|
<div className="sprites">
|
||||||
<a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
|
<a
|
||||||
|
className="sprite sprite-1"
|
||||||
|
href="/projects/editor/?tip_bar=getStarted"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
|
alt="Scratch Cat"
|
||||||
className="costume costume-1"
|
className="costume costume-1"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png"
|
||||||
alt="Scratch Cat" />
|
/>
|
||||||
<img
|
<img
|
||||||
|
alt="Scratch Cat"
|
||||||
className="costume costume-2"
|
className="costume costume-2"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png"
|
||||||
alt="Scratch Cat" />
|
/>
|
||||||
<div className="circle"></div>
|
<div className="circle" />
|
||||||
<div className="text">
|
<div className="text">
|
||||||
{this.props.messages['intro.tryItOut']}
|
{this.props.messages['intro.tryItOut']}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a className="sprite sprite-2" href="/starter_projects/">
|
<a
|
||||||
|
className="sprite sprite-2"
|
||||||
|
href="/starter_projects/"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
|
alt="Tera"
|
||||||
className="costume costume-1"
|
className="costume costume-1"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png"
|
||||||
alt="Tera" />
|
/>
|
||||||
<img
|
<img
|
||||||
|
alt="Tera"
|
||||||
className="costume costume-2"
|
className="costume costume-2"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png"
|
||||||
alt="Tera" />
|
/>
|
||||||
<div className="circle"></div>
|
<div className="circle" />
|
||||||
<div className="text">
|
<div className="text">
|
||||||
{this.props.messages['intro.seeExamples']}
|
{this.props.messages['intro.seeExamples']}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
|
<a
|
||||||
|
className="sprite sprite-3"
|
||||||
|
href="#"
|
||||||
|
onClick={this.handleJoinClick}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
|
alt="Gobo"
|
||||||
className="costume costume-1"
|
className="costume costume-1"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png"
|
||||||
alt="Gobo" />
|
/>
|
||||||
<img
|
<img
|
||||||
|
alt="Gobo"
|
||||||
className="costume costume-2"
|
className="costume costume-2"
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png"
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png"
|
||||||
alt="Gobo" />
|
/>
|
||||||
<div className="circle"></div>
|
<div className="circle" />
|
||||||
<div className="text">
|
<div className="text">
|
||||||
{this.props.messages['intro.joinScratch']}
|
{this.props.messages['intro.joinScratch']}
|
||||||
</div>
|
</div>
|
||||||
<div className="text subtext">{this.props.messages['intro.itsFree']}</div>
|
<div className="text subtext">{this.props.messages['intro.itsFree']}</div>
|
||||||
</a>
|
</a>
|
||||||
<Registration key="registration"
|
<Registration
|
||||||
isOpen={this.state.registrationOpen}
|
isOpen={this.state.registrationOpen}
|
||||||
onRequestClose={this.closeRegistration}
|
key="registration"
|
||||||
onRegistrationDone={this.completeRegistration} />
|
onRegistrationDone={this.handleCompleteRegistration}
|
||||||
|
onRequestClose={this.handleCloseRegistration}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="description"
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: this.props.messages['intro.description']}}></div>
|
className="description"
|
||||||
|
dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
|
||||||
|
__html: this.props.messages['intro.description']
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className="links">
|
<div className="links">
|
||||||
<a href="/about/">
|
<a href="/about/">
|
||||||
{this.props.messages['intro.aboutScratch']}
|
{this.props.messages['intro.aboutScratch']}
|
||||||
|
@ -113,33 +130,70 @@ var Intro = React.createClass({
|
||||||
<a href="/educators/">
|
<a href="/educators/">
|
||||||
{this.props.messages['intro.forEducators']}
|
{this.props.messages['intro.forEducators']}
|
||||||
</a>
|
</a>
|
||||||
<a className="last" href="/parents/">
|
<a
|
||||||
|
className="last"
|
||||||
|
href="/parents/"
|
||||||
|
>
|
||||||
{this.props.messages['intro.forParents']}
|
{this.props.messages['intro.forParents']}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="video">
|
<div className="video">
|
||||||
<div className="play-button" onClick={this.showVideo}></div>
|
<div
|
||||||
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
|
className="play-button"
|
||||||
alt="Intro Video" />
|
onClick={this.handleShowVideo}
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
alt="Intro Video"
|
||||||
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<IframeModal
|
<IframeModal
|
||||||
className="mod-intro-video"
|
className="mod-intro-video"
|
||||||
isOpen={this.state.videoOpen}
|
isOpen={this.state.videoOpen}
|
||||||
onRequestClose={this.closeVideo}
|
|
||||||
src="//player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
|
src="//player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
|
||||||
|
onRequestClose={this.handleCloseVideo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
var mapStateToProps = function (state) {
|
Intro.propTypes = {
|
||||||
return {
|
dispatch: PropTypes.func.isRequired,
|
||||||
session: state.session
|
messages: PropTypes.shape({
|
||||||
};
|
'intro.aboutScratch': PropTypes.string,
|
||||||
|
'intro.forEducators': PropTypes.string,
|
||||||
|
'intro.forParents': PropTypes.string,
|
||||||
|
'intro.itsFree': PropTypes.string,
|
||||||
|
'intro.joinScratch': PropTypes.string,
|
||||||
|
'intro.seeExamples': PropTypes.string,
|
||||||
|
'intro.tagLine': PropTypes.string,
|
||||||
|
'intro.tryItOut': PropTypes.string,
|
||||||
|
'intro.description': PropTypes.string
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
var ConnectedIntro = connect(mapStateToProps)(Intro);
|
Intro.defaultProps = {
|
||||||
|
messages: {
|
||||||
|
'intro.aboutScratch': 'ABOUT SCRATCH',
|
||||||
|
'intro.forEducators': 'FOR EDUCATORS',
|
||||||
|
'intro.forParents': 'FOR PARENTS',
|
||||||
|
'intro.itsFree': 'it\'s free!',
|
||||||
|
'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.description': 'A creative learning community with <span class="project-count"> ' +
|
||||||
|
'over 14 million </span>projects shared'
|
||||||
|
},
|
||||||
|
session: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
session: state.session
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedIntro = connect(mapStateToProps)(Intro);
|
||||||
|
|
||||||
module.exports = ConnectedIntro;
|
module.exports = ConnectedIntro;
|
||||||
|
|
|
@ -1,46 +1,57 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var jar = require('../../lib/jar.js');
|
const jar = require('../../lib/jar.js');
|
||||||
var languages = require('../../../languages.json');
|
const languages = require('../../../languages.json');
|
||||||
var Form = require('../forms/form.jsx');
|
const Form = require('../forms/form.jsx');
|
||||||
var Select = require('../forms/select.jsx');
|
const Select = require('../forms/select.jsx');
|
||||||
|
|
||||||
require('./languagechooser.scss');
|
require('./languagechooser.scss');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Footer dropdown menu that allows one to change their language.
|
* Footer dropdown menu that allows one to change their language.
|
||||||
*/
|
*/
|
||||||
var LanguageChooser = React.createClass({
|
class LanguageChooser extends React.Component {
|
||||||
type: 'LanguageChooser',
|
constructor (props) {
|
||||||
getDefaultProps: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
languages: languages,
|
'handleSetLanguage'
|
||||||
locale: 'en'
|
]);
|
||||||
};
|
}
|
||||||
},
|
handleSetLanguage (name, value) {
|
||||||
onSetLanguage: function (name, value) {
|
|
||||||
jar.set('scratchlanguage', value);
|
jar.set('scratchlanguage', value);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames(
|
const languageOptions = Object.keys(this.props.languages).map(value => ({
|
||||||
'language-chooser',
|
value: value,
|
||||||
this.props.className
|
label: this.props.languages[value]
|
||||||
);
|
}));
|
||||||
var languageOptions = Object.keys(this.props.languages).map(function (value) {
|
|
||||||
return {value: value, label: this.props.languages[value]};
|
|
||||||
}.bind(this));
|
|
||||||
return (
|
return (
|
||||||
<Form className={classes}>
|
<Form className={classNames('language-chooser', this.props.className)}>
|
||||||
<Select name="language"
|
<Select
|
||||||
options={languageOptions}
|
required
|
||||||
value={this.props.locale}
|
name="language"
|
||||||
onChange={this.onSetLanguage}
|
options={languageOptions}
|
||||||
required />
|
value={this.props.locale}
|
||||||
|
onChange={this.handleSetLanguage}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
LanguageChooser.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
languages: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
|
locale: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
LanguageChooser.defaultProps = {
|
||||||
|
languages: languages,
|
||||||
|
locale: 'en'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = LanguageChooser;
|
module.exports = LanguageChooser;
|
||||||
|
|
|
@ -1,66 +1,108 @@
|
||||||
var React = require('react');
|
const bindAll = require('lodash.bindall');
|
||||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var log = require('../../lib/log.js');
|
const log = require('../../lib/log.js');
|
||||||
|
|
||||||
var Form = require('../forms/form.jsx');
|
const Form = require('../forms/form.jsx');
|
||||||
var Input = require('../forms/input.jsx');
|
const Input = require('../forms/input.jsx');
|
||||||
var Button = require('../forms/button.jsx');
|
const Button = require('../forms/button.jsx');
|
||||||
var Spinner = require('../spinner/spinner.jsx');
|
const Spinner = require('../spinner/spinner.jsx');
|
||||||
|
|
||||||
require('./login.scss');
|
require('./login.scss');
|
||||||
|
|
||||||
var Login = React.createClass({
|
class Login extends React.Component {
|
||||||
type: 'Login',
|
constructor (props) {
|
||||||
propTypes: {
|
super(props);
|
||||||
onLogIn: React.PropTypes.func,
|
bindAll(this, [
|
||||||
error: React.PropTypes.string
|
'handleSubmit'
|
||||||
},
|
]);
|
||||||
getInitialState: function () {
|
this.state = {
|
||||||
return {
|
|
||||||
waiting: false
|
waiting: false
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
handleSubmit: function (formData) {
|
handleSubmit (formData) {
|
||||||
this.setState({waiting: true});
|
this.setState({waiting: true});
|
||||||
this.props.onLogIn(formData, function (err) {
|
this.props.onLogIn(formData, err => {
|
||||||
if (err) log.error(err);
|
if (err) log.error(err);
|
||||||
this.setState({waiting: false});
|
this.setState({waiting: false});
|
||||||
}.bind(this));
|
});
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var error;
|
let error;
|
||||||
if (this.props.error) {
|
if (this.props.error) {
|
||||||
error = <div className="error">{this.props.error}</div>;
|
error = <div className="error">{this.props.error}</div>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="login">
|
<div className="login">
|
||||||
<Form onSubmit={this.handleSubmit}>
|
<Form onSubmit={this.handleSubmit}>
|
||||||
<label htmlFor="username" key="usernameLabel">
|
<label
|
||||||
<FormattedMessage id='general.username' />
|
htmlFor="username"
|
||||||
|
key="usernameLabel"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="general.username" />
|
||||||
</label>
|
</label>
|
||||||
<Input type="text" ref="username" name="username" maxLength="30" key="usernameInput" required />
|
<Input
|
||||||
<label htmlFor="password" key="passwordLabel">
|
required
|
||||||
<FormattedMessage id='general.password' />
|
key="usernameInput"
|
||||||
|
maxLength="30"
|
||||||
|
name="username"
|
||||||
|
ref={input => {
|
||||||
|
this.username = input;
|
||||||
|
}}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="password"
|
||||||
|
key="passwordLabel"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="general.password" />
|
||||||
</label>
|
</label>
|
||||||
<Input type="password" ref="password" name="password" key="passwordInput" required />
|
<Input
|
||||||
|
required
|
||||||
|
key="passwordInput"
|
||||||
|
name="password"
|
||||||
|
ref={input => {
|
||||||
|
this.password = input;
|
||||||
|
}}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
{this.state.waiting ? [
|
{this.state.waiting ? [
|
||||||
<Button className="submit-button white" type="submit" disabled="disabled" key="submitButton">
|
<Button
|
||||||
|
className="submit-button white"
|
||||||
|
disabled="disabled"
|
||||||
|
key="submitButton"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</Button>
|
</Button>
|
||||||
] : [
|
] : [
|
||||||
<Button className="submit-button white" type="submit" key="submitButton">
|
<Button
|
||||||
<FormattedMessage id='general.signIn' />
|
className="submit-button white"
|
||||||
|
key="submitButton"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="general.signIn" />
|
||||||
</Button>
|
</Button>
|
||||||
]}
|
]}
|
||||||
<a className="right" href="/accounts/password_reset/" key="passwordResetLink">
|
<a
|
||||||
<FormattedMessage id='login.needHelp' />
|
className="right"
|
||||||
|
href="/accounts/password_reset/"
|
||||||
|
key="passwordResetLink"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="login.needHelp" />
|
||||||
</a>
|
</a>
|
||||||
{error}
|
{error}
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Login.propTypes = {
|
||||||
|
error: PropTypes.string,
|
||||||
|
onLogIn: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Login;
|
module.exports = Login;
|
||||||
|
|
|
@ -1,60 +1,68 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
var MediaQuery = require('react-responsive');
|
const MediaQuery = require('react-responsive').default;
|
||||||
var frameless = require('../../lib/frameless');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const frameless = require('../../lib/frameless');
|
||||||
|
|
||||||
require('./masonrygrid.scss');
|
require('./masonrygrid.scss');
|
||||||
|
|
||||||
var MasonryGrid = React.createClass({
|
class MasonryGrid extends React.Component {
|
||||||
type: 'MasonryGrid',
|
constructor (props) {
|
||||||
getDefaultProps: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
as: 'div'
|
'reorderColumns'
|
||||||
};
|
]);
|
||||||
},
|
}
|
||||||
reorderColumns: function (items, cols) {
|
reorderColumns (items, cols) {
|
||||||
var a1 = [];
|
const a1 = [];
|
||||||
var a2 = [];
|
const a2 = [];
|
||||||
var a3 = [];
|
const a3 = [];
|
||||||
var i = 0;
|
let i = 0;
|
||||||
//only implemented for 2 and 3 columns so far - easy to extend if needed
|
// only implemented for 2 and 3 columns so far - easy to extend if needed
|
||||||
if (cols > 1 && cols < 4) {
|
if (cols > 1 && cols < 4) {
|
||||||
for (i=0;i<items.length;i++){
|
for (i = 0; i < items.length; i++){
|
||||||
var col = (i+cols)%cols;
|
const col = (i + cols) % cols;
|
||||||
if (col === 0) {
|
if (col === 0) {
|
||||||
a1.push(items[i]);
|
a1.push(items[i]);
|
||||||
}
|
} else if (col === 1) {
|
||||||
else if (col === 1) {
|
|
||||||
a2.push(items[i]);
|
a2.push(items[i]);
|
||||||
}
|
} else if (col === 2) {
|
||||||
else if (col === 2) {
|
|
||||||
a3.push(items[i]);
|
a3.push(items[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a1.concat(a2,a3);
|
return a1.concat(a2, a3);
|
||||||
} else {
|
|
||||||
return items;
|
|
||||||
}
|
}
|
||||||
},
|
return items;
|
||||||
render: function () {
|
}
|
||||||
var classes = classNames(
|
render () {
|
||||||
'masonry',
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<this.props.as className={classes}>
|
<this.props.as className={classNames('masonry', this.props.className)}>
|
||||||
<MediaQuery maxWidth={frameless.tablet - 1} >
|
<MediaQuery maxWidth={frameless.tablet - 1}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<MediaQuery minWidth={frameless.tablet} maxWidth={frameless.desktop - 1} >
|
<MediaQuery
|
||||||
|
maxWidth={frameless.desktop - 1}
|
||||||
|
minWidth={frameless.tablet}
|
||||||
|
>
|
||||||
{this.reorderColumns(this.props.children, 2)}
|
{this.reorderColumns(this.props.children, 2)}
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
<MediaQuery minWidth={frameless.desktop} >
|
<MediaQuery minWidth={frameless.desktop}>
|
||||||
{this.reorderColumns(this.props.children, 3)}
|
{this.reorderColumns(this.props.children, 3)}
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
</this.props.as>
|
</this.props.as>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
MasonryGrid.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
MasonryGrid.defaultProps = {
|
||||||
|
as: 'div'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = MasonryGrid;
|
module.exports = MasonryGrid;
|
||||||
|
|
|
@ -1,39 +1,53 @@
|
||||||
var React = require('react');
|
const bindAll = require('lodash.bindall');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const Box = require('../box/box.jsx');
|
||||||
|
const LegacyCarousel = require('../carousel/legacy-carousel.jsx');
|
||||||
|
const IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
|
const NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
|
||||||
|
|
||||||
require('./microworld.scss');
|
require('./microworld.scss');
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
class Microworld extends React.Component {
|
||||||
var LegacyCarousel = require('../carousel/legacy-carousel.jsx');
|
constructor (props) {
|
||||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
super(props);
|
||||||
var NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
|
bindAll(this, [
|
||||||
|
'markVideoOpen',
|
||||||
var Microworld = React.createClass({
|
'markVideoClosed',
|
||||||
type: 'Microworld',
|
'renderVideos',
|
||||||
propTypes: {
|
'renderVideo',
|
||||||
microworldData: React.PropTypes.node.isRequired
|
'renderEditorWindow',
|
||||||
},
|
'renderTips',
|
||||||
markVideoOpen: function (key) {
|
'renderStarterProject',
|
||||||
{/* When a video is clicked, mark it as an open video, so the video Modal will open.
|
'renderProjectIdeasBox',
|
||||||
Key is the number of the video, so distinguish between different videos on the page */}
|
'renderForum',
|
||||||
|
'renderDesignStudio'
|
||||||
var videoOpenArr = this.state.videoOpen;
|
]);
|
||||||
videoOpenArr[key] = true;
|
this.state = {
|
||||||
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: {}
|
videoOpen: {}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
renderVideos: function () {
|
markVideoOpen (key) {
|
||||||
var videos = this.props.microworldData.videos;
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
const videoOpenArr = this.state.videoOpen;
|
||||||
|
videoOpenArr[key] = true;
|
||||||
|
this.setState({videoOpen: videoOpenArr});
|
||||||
|
}
|
||||||
|
markVideoClosed (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
|
||||||
|
*/
|
||||||
|
const videoOpenArr = this.state.videoOpen;
|
||||||
|
videoOpenArr[key] = false;
|
||||||
|
this.setState({videoOpen: videoOpenArr});
|
||||||
|
}
|
||||||
|
renderVideos () {
|
||||||
|
const videos = this.props.microworldData.videos;
|
||||||
if (!videos || videos.length <= 0) {
|
if (!videos || videos.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -48,26 +62,32 @@ var Microworld = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderVideo: function (video, key) {
|
renderVideo (video, key) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="video">
|
<div className="video">
|
||||||
<div className="play-button" onClick={this.markVideoOpen.bind(this, key)}>
|
<div
|
||||||
</div>
|
className="play-button"
|
||||||
|
onClick={() => { // eslint-disable-line react/jsx-no-bind
|
||||||
|
this.markVideoOpen(key);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<img src={video.image} />
|
<img src={video.image} />
|
||||||
</div>
|
</div>
|
||||||
<IframeModal
|
<IframeModal
|
||||||
className="mod-microworld-video"
|
className="mod-microworld-video"
|
||||||
isOpen={this.state.videoOpen[key]}
|
isOpen={this.state.videoOpen[key]}
|
||||||
onRequestClose={this.markVideoClosed.bind(this, key)}
|
|
||||||
src={video.link}
|
src={video.link}
|
||||||
|
onRequestClose={() => { // eslint-disable-line react/jsx-no-bind
|
||||||
|
this.markVideoClosed(key);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderEditorWindow: function () {
|
renderEditorWindow () {
|
||||||
var projectId = this.props.microworldData.microworld_project_id;
|
const projectId = this.props.microworldData.microworld_project_id;
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -75,30 +95,37 @@ var Microworld = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="editor section">
|
<div className="editor section">
|
||||||
<h1 className="sectionheader">Start Creating!</h1>
|
<h1 className="sectionheader">Start Creating!</h1>
|
||||||
<iframe src={'//scratch.mit.edu/projects/embed-editor/' + projectId + '/?isMicroworld=true'}
|
<iframe
|
||||||
frameBorder="0"> </iframe>
|
frameBorder="0"
|
||||||
|
src={`//scratch.mit.edu/projects/embed-editor/${projectId}/?isMicroworld=true`}
|
||||||
|
/>
|
||||||
{this.renderTips()}
|
{this.renderTips()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderTips: function () {
|
renderTips () {
|
||||||
var tips = this.props.microworldData.tips;
|
const tips = this.props.microworldData.tips;
|
||||||
if (!tips || tips.length <= 0) {
|
if (!tips || tips.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="box nestedcarousel">
|
<div className="box nestedcarousel">
|
||||||
<div className="box-header">
|
<div className="box-header" />
|
||||||
</div>
|
|
||||||
<div className="box-content">
|
<div className="box-content">
|
||||||
<NestedCarousel items={tips} settings={{slidesToShow:1,slidesToScroll:1}}/>
|
<NestedCarousel
|
||||||
|
items={tips}
|
||||||
|
settings={{
|
||||||
|
slidesToShow: 1,
|
||||||
|
slidesToScroll: 1
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderStarterProject: function () {
|
renderStarterProject () {
|
||||||
var starterProjects = this.props.microworldData.starter_projects;
|
const starterProjects = this.props.microworldData.starter_projects;
|
||||||
if (!starterProjects || starterProjects.length <= 0){
|
if (!starterProjects || starterProjects.length <= 0){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -107,39 +134,42 @@ var Microworld = React.createClass({
|
||||||
<div className="project-ideas">
|
<div className="project-ideas">
|
||||||
<h1 className="sectionheader">Check out ideas for more projects</h1>
|
<h1 className="sectionheader">Check out ideas for more projects</h1>
|
||||||
<Box
|
<Box
|
||||||
|
key="starter_projects"
|
||||||
title="More Starter Projects"
|
title="More Starter Projects"
|
||||||
key="starter_projects">
|
>
|
||||||
<LegacyCarousel items={starterProjects} />
|
<LegacyCarousel items={starterProjects} />
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderProjectIdeasBox: function () {
|
renderProjectIdeasBox () {
|
||||||
var communityProjects = this.props.microworldData.community_projects;
|
const communityProjects = this.props.microworldData.community_projects;
|
||||||
if (!communityProjects || communityProjects.size <= 0) {
|
if (!communityProjects || communityProjects.size <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var featured = communityProjects.featured_projects;
|
const featured = communityProjects.featured_projects;
|
||||||
var all = communityProjects.newest_projects;
|
const all = communityProjects.newest_projects;
|
||||||
|
|
||||||
var rows = [];
|
const rows = [];
|
||||||
if (featured && featured.length > 0){
|
if (featured && featured.length > 0){
|
||||||
rows.push(
|
rows.push(
|
||||||
<Box
|
<Box
|
||||||
|
key="community_featured_projects"
|
||||||
title="Featured Community Projects"
|
title="Featured Community Projects"
|
||||||
key="community_featured_projects">
|
>
|
||||||
<LegacyCarousel items={featured} />
|
<LegacyCarousel items={featured} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (all && all.length > 0) {
|
if (all && all.length > 0) {
|
||||||
rows.push(
|
rows.push(
|
||||||
<Box
|
<Box
|
||||||
title="All Community Projects"
|
key="community_all_projects"
|
||||||
key="community_all_projects">
|
title="All Community Projects"
|
||||||
<LegacyCarousel items={all} />
|
>
|
||||||
</Box>
|
<LegacyCarousel items={all} />
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (rows.length <= 0) {
|
if (rows.length <= 0) {
|
||||||
|
@ -151,67 +181,88 @@ var Microworld = React.createClass({
|
||||||
{rows}
|
{rows}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderForum: function () {
|
renderForum () {
|
||||||
if (!this.props.microworldData.show_forum) {
|
if (!this.props.microworldData.show_forum) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="forum">
|
<div className="forum">
|
||||||
<h1 className="sectionheader">Chat with others!</h1>
|
<h1 className="sectionheader">Chat with others!</h1>
|
||||||
<img src="/images/forum-image.png"/>
|
<img src="/images/forum-image.png" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
renderDesignStudio: function () {
|
renderDesignStudio () {
|
||||||
var designChallenge = this.props.microworldData.design_challenge;
|
const designChallenge = this.props.microworldData.design_challenge;
|
||||||
if (!designChallenge) {
|
if (!designChallenge) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let studioHref = '';
|
||||||
if (designChallenge.studio_id) {
|
if (designChallenge.studio_id) {
|
||||||
var studioHref = 'https://scratch.mit.edu//studios/' + designChallenge.studio_id + '/';
|
studioHref = `https://scratch.mit.edu//studios/${designChallenge.studio_id}/`;
|
||||||
}
|
}
|
||||||
if (designChallenge.project_id) {
|
if (designChallenge.project_id) {
|
||||||
return (
|
return (
|
||||||
<div className="side-by-side section">
|
<div className="side-by-side section">
|
||||||
<h1 className="sectionheader">Join our Design Challenge!</h1>
|
<h1 className="sectionheader">Join our Design Challenge!</h1>
|
||||||
<div className="design-studio">
|
<div className="design-studio">
|
||||||
<iframe src={'https://scratch.mit.edu/projects/' + designChallenge.project_id +
|
<iframe
|
||||||
'/#fullscreen'} frameBorder="0"> </iframe>
|
frameBorder="0"
|
||||||
|
src={`https://scratch.mit.edu/projects/${designChallenge.project_id}/#fullscreen`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="design-studio-projects">
|
<div className="design-studio-projects">
|
||||||
<Box title="Examples"
|
<Box
|
||||||
key="scratch_design_studio"
|
key="scratch_design_studio"
|
||||||
moreTitle={studioHref ? 'Visit the studio' : null}
|
moreHref={studioHref ? studioHref : null}
|
||||||
moreHref={studioHref ? studioHref : null}>
|
moreTitle={studioHref ? 'Visit the studio' : null}
|
||||||
|
title="Examples"
|
||||||
|
>
|
||||||
{/* The two carousels are used to show two rows of projects, one above the
|
{/* 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. */}
|
other. This should be probably be changed, to allow better scrolling. */}
|
||||||
<LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
|
<LegacyCarousel
|
||||||
items={this.props.microworldData.design_challenge.studio1} />
|
items={this.props.microworldData.design_challenge.studio1}
|
||||||
<LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
|
settings={{
|
||||||
items={this.props.microworldData.design_challenge.studio2} />
|
slidesToShow: 2,
|
||||||
|
slidesToScroll: 2
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<LegacyCarousel
|
||||||
|
items={this.props.microworldData.design_challenge.studio2}
|
||||||
|
settings={{
|
||||||
|
slidesToShow: 2,
|
||||||
|
slidesToScroll: 2
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className="section">
|
|
||||||
<h1 className="sectionheader">Join our Design Challenge!</h1>
|
|
||||||
<Box
|
|
||||||
title="design Challenge Projects"
|
|
||||||
key="scratch_design_studio"
|
|
||||||
moreTitle={studioHref ? 'Visit the studio' : null}
|
|
||||||
moreHref={studioHref ? studioHref : null}>
|
|
||||||
<LegacyCarousel items={this.props.microworldData.design_challenge.studio1.concat(
|
|
||||||
this.props.microworldData.design_challenge.studio2)} />
|
|
||||||
</Box>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
return (
|
||||||
render: function () {
|
<div className="section">
|
||||||
|
<h1 className="sectionheader">Join our Design Challenge!</h1>
|
||||||
|
<Box
|
||||||
|
key="scratch_design_studio"
|
||||||
|
moreHref={studioHref ? studioHref : null}
|
||||||
|
moreTitle={studioHref ? 'Visit the studio' : null}
|
||||||
|
title="design Challenge Projects"
|
||||||
|
>
|
||||||
|
<LegacyCarousel
|
||||||
|
items={
|
||||||
|
this.props.microworldData.design_challenge.studio1.concat(
|
||||||
|
this.props.microworldData.design_challenge.studio2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
`</Box>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="inner microworld">
|
<div className="inner microworld">
|
||||||
<div className="top-banner section">
|
<div className="top-banner section">
|
||||||
|
@ -231,6 +282,10 @@ var Microworld = React.createClass({
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Microworld.propTypes = {
|
||||||
|
microworldData: PropTypes.node.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Microworld;
|
module.exports = Microworld;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var omit = require('lodash.omit');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const omit = require('lodash.omit');
|
||||||
var ReactModal = require('react-modal');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const ReactModal = require('react-modal');
|
||||||
|
|
||||||
require('./modal.scss');
|
require('./modal.scss');
|
||||||
|
|
||||||
|
@ -10,47 +12,55 @@ ReactModal.setAppElement(document.getElementById('view'));
|
||||||
/**
|
/**
|
||||||
* Container for pop up windows (See: registration window)
|
* Container for pop up windows (See: registration window)
|
||||||
*/
|
*/
|
||||||
var Modal = React.createClass({
|
class Modal extends React.Component {
|
||||||
type: 'Modal',
|
constructor (props) {
|
||||||
propTypes: {
|
super(props);
|
||||||
className: React.PropTypes.string,
|
bindAll(this, [
|
||||||
overlayClassName: React.PropTypes.string
|
'handleRequestClose'
|
||||||
},
|
]);
|
||||||
requestClose: function () {
|
}
|
||||||
|
handleRequestClose () {
|
||||||
return this.modal.portal.requestClose();
|
return this.modal.portal.requestClose();
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var modalClasses = classNames(
|
|
||||||
'modal-content',
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
var overlayClasses = classNames(
|
|
||||||
'modal-overlay',
|
|
||||||
this.props.overlayClassName
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactModal
|
<ReactModal
|
||||||
ref={
|
appElement={document.getElementById('view')}
|
||||||
function (component) {
|
className={{
|
||||||
this.modal = component;
|
base: classNames('modal-content', this.props.className),
|
||||||
}.bind(this)
|
afterOpen: classNames('modal-content', this.props.className),
|
||||||
}
|
beforeClose: classNames('modal-content', this.props.className)
|
||||||
className={modalClasses}
|
}}
|
||||||
overlayClassName={overlayClasses}
|
overlayClassName={{
|
||||||
|
base: classNames('modal-overlay', this.props.overlayClassName),
|
||||||
|
afterOpen: classNames('modal-overlay', this.props.overlayClassName),
|
||||||
|
beforeClose: classNames('modal-overlay', this.props.overlayClassName)
|
||||||
|
}}
|
||||||
|
ref={component => {
|
||||||
|
this.modal = component;
|
||||||
|
}}
|
||||||
{...omit(this.props, ['className', 'overlayClassName'])}
|
{...omit(this.props, ['className', 'overlayClassName'])}
|
||||||
>
|
>
|
||||||
<div className="modal-content-close" onClick={this.requestClose}>
|
<div
|
||||||
|
className="modal-content-close"
|
||||||
|
onClick={this.handleRequestClose}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
|
alt="close-icon"
|
||||||
className="modal-content-close-img"
|
className="modal-content-close-img"
|
||||||
src="/svgs/modal/close-x.svg"
|
src="/svgs/modal/close-x.svg"
|
||||||
alt="close-icon"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</ReactModal>
|
</ReactModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Modal.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
|
overlayClassName: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Modal;
|
module.exports = Modal;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
background-color: transparentize($ui-blue, .3);
|
background-color: transparentize($ui-blue, .3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ReactModal__Content:focus {
|
.modal-content:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,28 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var omit = require('lodash.omit');
|
const omit = require('lodash.omit');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Modal = require('../base/modal.jsx');
|
const Modal = require('../base/modal.jsx');
|
||||||
|
|
||||||
require('./modal.scss');
|
require('./modal.scss');
|
||||||
|
|
||||||
var IframeModal = React.createClass({
|
const IframeModal = props => (
|
||||||
propTypes: {
|
<Modal {...omit(props, ['src'])}>
|
||||||
isOpen: React.PropTypes.bool,
|
<iframe
|
||||||
onRequestClose: React.PropTypes.func,
|
className={classNames('modal-content-iframe', props.className)}
|
||||||
className: React.PropTypes.string,
|
ref={props.componentRef}
|
||||||
componentRef: React.PropTypes.func,
|
src={props.src}
|
||||||
src: React.PropTypes.string
|
/>
|
||||||
},
|
</Modal>
|
||||||
render: function () {
|
);
|
||||||
var iframeClasses = classNames(
|
|
||||||
'modal-content-iframe',
|
IframeModal.propTypes = {
|
||||||
this.props.className
|
className: PropTypes.string,
|
||||||
);
|
componentRef: PropTypes.func,
|
||||||
return (
|
isOpen: PropTypes.bool,
|
||||||
<Modal {...omit(this.props, ['src'])}>
|
onRequestClose: PropTypes.func,
|
||||||
<iframe
|
src: PropTypes.string
|
||||||
ref={this.props.componentRef}
|
};
|
||||||
src={this.props.src}
|
|
||||||
className={iframeClasses}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = IframeModal;
|
module.exports = IframeModal;
|
||||||
|
|
|
@ -1,121 +1,138 @@
|
||||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var MediaQuery = require('react-responsive');
|
const MediaQuery = require('react-responsive').default;
|
||||||
var omit = require('lodash.omit');
|
const omit = require('lodash.omit');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var FlexRow = require('../../flex-row/flex-row.jsx');
|
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||||
var frameless = require('../../../lib/frameless');
|
const frameless = require('../../../lib/frameless');
|
||||||
var Modal = require('../base/modal.jsx');
|
const Modal = require('../base/modal.jsx');
|
||||||
var TitleBanner = require('../../title-banner/title-banner.jsx');
|
const TitleBanner = require('../../title-banner/title-banner.jsx');
|
||||||
|
|
||||||
require('../../forms/button.scss');
|
require('../../forms/button.scss');
|
||||||
require('./modal.scss');
|
require('./modal.scss');
|
||||||
|
|
||||||
var TTTModal = React.createClass({
|
const TTTModal = props => (
|
||||||
propTypes: {
|
<Modal
|
||||||
title: React.PropTypes.string.isRequired,
|
className="mod-ttt"
|
||||||
description: React.PropTypes.string.isRequired,
|
{...omit(
|
||||||
tutorialLoc: React.PropTypes.string.isRequired,
|
props,
|
||||||
activityLoc: React.PropTypes.string.isRequired,
|
[
|
||||||
guideLoc: React.PropTypes.string.isRequired,
|
'title',
|
||||||
thumbUrl: React.PropTypes.string.isRequired,
|
'description',
|
||||||
bannerUrl: React.PropTypes.string.isRequired
|
'tutorialLoc',
|
||||||
},
|
'activityLoc',
|
||||||
render: function () {
|
'guideLoc',
|
||||||
var modalOmit = [
|
'thumbUrl',
|
||||||
'title',
|
'bannerUrl'
|
||||||
'description',
|
]
|
||||||
'tutorialLoc',
|
)}
|
||||||
'activityLoc',
|
>
|
||||||
'guideLoc',
|
<TitleBanner className="mod-ttt">
|
||||||
'thumbUrl',
|
<MediaQuery minWidth={frameless.mobile}>
|
||||||
'bannerUrl'
|
<img
|
||||||
];
|
alt=""
|
||||||
return (
|
className="mod-ttt-img"
|
||||||
<Modal
|
src={props.bannerUrl}
|
||||||
className="mod-ttt"
|
/>
|
||||||
{...omit(this.props, modalOmit)}
|
</MediaQuery>
|
||||||
|
<MediaQuery maxWidth={frameless.mobile - 1}>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
className="mod-ttt-img"
|
||||||
|
src={props.thumbUrl}
|
||||||
|
/>
|
||||||
|
</MediaQuery>
|
||||||
|
</TitleBanner>
|
||||||
|
<div className="ttt-title">
|
||||||
|
<h2>{props.title}</h2>
|
||||||
|
<p className="ttt-description">{props.description}</p>
|
||||||
|
</div>
|
||||||
|
<ul className="modal-content-ttt">
|
||||||
|
<FlexRow
|
||||||
|
as="li"
|
||||||
|
className="mod-ttt-item"
|
||||||
>
|
>
|
||||||
<TitleBanner className="mod-ttt">
|
<div className="modal-content-ttt-text">
|
||||||
<MediaQuery minWidth={frameless.mobile}>
|
<div className="modal-content-ttt-title">
|
||||||
<img className="mod-ttt-img" src={this.props.bannerUrl} alt="" />
|
<img
|
||||||
</MediaQuery>
|
alt="tutorial-icon"
|
||||||
<MediaQuery maxWidth={frameless.mobile - 1}>
|
className="modal-content-ttt-title-img"
|
||||||
<img className="mod-ttt-img" src={this.props.thumbUrl} alt="" />
|
src="/svgs/ttt/tutorial.svg"
|
||||||
</MediaQuery>
|
/>
|
||||||
</TitleBanner>
|
<FormattedMessage id="ttt.tutorial" />
|
||||||
<div className="ttt-title">
|
</div>
|
||||||
<h2>{this.props.title}</h2>
|
<p className="modal-content-ttt-subtitle">
|
||||||
<p className="ttt-description">{this.props.description}</p>
|
<FormattedMessage id="ttt.tutorialSubtitle" />
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul className="modal-content-ttt">
|
<a
|
||||||
<FlexRow as="li" className="mod-ttt-item">
|
className="button white mod-ttt-item"
|
||||||
<div className="modal-content-ttt-text">
|
href={props.tutorialLoc}
|
||||||
<div className="modal-content-ttt-title">
|
>
|
||||||
<img
|
<FormattedMessage id="tile.tryIt" />
|
||||||
className="modal-content-ttt-title-img"
|
</a>
|
||||||
src="/svgs/ttt/tutorial.svg"
|
</FlexRow>
|
||||||
alt="tutorial-icon"
|
<FlexRow
|
||||||
/>
|
as="li"
|
||||||
<FormattedMessage id="ttt.tutorial" />
|
className="mod-ttt-item"
|
||||||
</div>
|
>
|
||||||
<p className="modal-content-ttt-subtitle">
|
<div className="modal-content-ttt-text">
|
||||||
<FormattedMessage id="ttt.tutorialSubtitle" />
|
<div className="modal-content-ttt-title">
|
||||||
</p>
|
<img
|
||||||
</div>
|
alt="activity-cards-icon"
|
||||||
<a
|
className="modal-content-ttt-title-img"
|
||||||
href={this.props.tutorialLoc}
|
src="/svgs/ttt/activity-cards.svg"
|
||||||
className="button white mod-ttt-item"
|
/>
|
||||||
>
|
<FormattedMessage id="ttt.activityTitle" />
|
||||||
<FormattedMessage id="tile.tryIt" />
|
</div>
|
||||||
</a>
|
<p className="modal-content-ttt-subtitle">
|
||||||
</FlexRow>
|
<FormattedMessage id="ttt.activitySubtitle" />
|
||||||
<FlexRow as="li" className="mod-ttt-item">
|
</p>
|
||||||
<div className="modal-content-ttt-text">
|
</div>
|
||||||
<div className="modal-content-ttt-title">
|
<a
|
||||||
<img
|
className="button white mod-ttt-item"
|
||||||
className="modal-content-ttt-title-img"
|
href={props.activityLoc}
|
||||||
src="/svgs/ttt/activity-cards.svg"
|
>
|
||||||
alt="activity-cards-icon"
|
<FormattedMessage id="ttt.open" />
|
||||||
/>
|
</a>
|
||||||
<FormattedMessage id="ttt.activityTitle" />
|
</FlexRow>
|
||||||
</div>
|
<FlexRow
|
||||||
<p className="modal-content-ttt-subtitle">
|
as="li"
|
||||||
<FormattedMessage id="ttt.activitySubtitle" />
|
className="mod-ttt-item"
|
||||||
</p>
|
>
|
||||||
</div>
|
<div className="modal-content-ttt-text">
|
||||||
<a
|
<div className="modal-content-ttt-title">
|
||||||
href={this.props.activityLoc}
|
<img
|
||||||
className="button white mod-ttt-item"
|
alt="educator-guide-icon"
|
||||||
>
|
className="modal-content-ttt-title-img"
|
||||||
<FormattedMessage id="ttt.open" />
|
src="/svgs/ttt/educator-guide.svg"
|
||||||
</a>
|
/>
|
||||||
</FlexRow>
|
<FormattedMessage id="ttt.educatorTitle" />
|
||||||
<FlexRow as="li" className="mod-ttt-item">
|
</div>
|
||||||
<div className="modal-content-ttt-text">
|
<p className="modal-content-ttt-subtitle">
|
||||||
<div className="modal-content-ttt-title">
|
<FormattedMessage id="ttt.educatorSubtitle" />
|
||||||
<img
|
</p>
|
||||||
className="modal-content-ttt-title-img"
|
</div>
|
||||||
src="/svgs/ttt/educator-guide.svg"
|
<a
|
||||||
alt="educator-guide-icon"
|
className="button white mod-ttt-item"
|
||||||
/>
|
href={props.guideLoc}
|
||||||
<FormattedMessage id="ttt.educatorTitle" />
|
>
|
||||||
</div>
|
<FormattedMessage id="ttt.open" />
|
||||||
<p className="modal-content-ttt-subtitle">
|
</a>
|
||||||
<FormattedMessage id="ttt.educatorSubtitle" />
|
</FlexRow>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</Modal>
|
||||||
<a
|
);
|
||||||
href={this.props.guideLoc}
|
|
||||||
className="button white mod-ttt-item"
|
TTTModal.propTypes = {
|
||||||
>
|
activityLoc: PropTypes.string.isRequired,
|
||||||
<FormattedMessage id="ttt.open" />
|
bannerUrl: PropTypes.string.isRequired,
|
||||||
</a>
|
description: PropTypes.string.isRequired,
|
||||||
</FlexRow>
|
guideLoc: PropTypes.string.isRequired,
|
||||||
</ul>
|
thumbUrl: PropTypes.string.isRequired,
|
||||||
</Modal>
|
title: PropTypes.string.isRequired,
|
||||||
);
|
tutorialLoc: PropTypes.string.isRequired
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = TTTModal;
|
module.exports = TTTModal;
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
var NavigationBox = React.createClass({
|
const NavigationBox = props => (
|
||||||
type: 'NavigationBox',
|
<div className={classNames('inner', props.className)}>
|
||||||
render: function () {
|
{props.children}
|
||||||
var classes = classNames(
|
</div>
|
||||||
'inner',
|
);
|
||||||
this.props.className
|
|
||||||
);
|
NavigationBox.propTypes = {
|
||||||
return (
|
children: PropTypes.node,
|
||||||
<div className={classes}>
|
className: PropTypes.string
|
||||||
{this.props.children}
|
};
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = NavigationBox;
|
module.exports = NavigationBox;
|
||||||
|
|
|
@ -1,42 +1,55 @@
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
var NavigationBox = require('../../base/navigation.jsx');
|
const NavigationBox = require('../../base/navigation.jsx');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
var Navigation = React.createClass({
|
const Navigation = () => (
|
||||||
type: 'Navigation',
|
<NavigationBox>
|
||||||
render: function () {
|
<ul className="ul mod-2016">
|
||||||
return (
|
<li className="li-left mod-logo mod-2016">
|
||||||
<NavigationBox>
|
<a
|
||||||
<ul className="ul mod-2016">
|
className="logo-a"
|
||||||
<li className="li-left mod-logo mod-2016">
|
href="/conference/2016"
|
||||||
<a href="/conference/2016" className="logo-a">
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/logo_sm.png"
|
alt="Scratch Logo"
|
||||||
alt="Scratch Logo"
|
className="logo-a-image"
|
||||||
className="logo-a-image"
|
src="/images/logo_sm.png"
|
||||||
/>
|
/>
|
||||||
<p className="logo-a-title">Conference</p>
|
<p className="logo-a-title">Conference</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="li-right mod-2016">
|
||||||
|
<ul className="li-right-ul mod-2016">
|
||||||
|
<li className="link expect">
|
||||||
|
<a
|
||||||
|
className="link-a"
|
||||||
|
href="/conference/2016/expect"
|
||||||
|
>
|
||||||
|
What to Expect
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="li-right mod-2016">
|
<li className="link plan">
|
||||||
<ul className="li-right-ul mod-2016">
|
<a
|
||||||
<li className="link expect">
|
className="link-a"
|
||||||
<a href="/conference/2016/expect" className="link-a">What to Expect</a>
|
href="/conference/2016/plan"
|
||||||
</li>
|
>
|
||||||
<li className="link plan">
|
Plan Your Visit
|
||||||
<a href="/conference/2016/plan" className="link-a">Plan Your Visit</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="link schedule">
|
<li className="link schedule">
|
||||||
<a href="/conference/2016/schedule" className="link-a">Schedule</a>
|
<a
|
||||||
</li>
|
className="link-a"
|
||||||
</ul>
|
href="/conference/2016/schedule"
|
||||||
|
>
|
||||||
|
Schedule
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</NavigationBox>
|
</li>
|
||||||
);
|
</ul>
|
||||||
}
|
</NavigationBox>
|
||||||
});
|
);
|
||||||
|
|
||||||
module.exports = Navigation;
|
module.exports = Navigation;
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
var NavigationBox = require('../../base/navigation.jsx');
|
const NavigationBox = require('../../base/navigation.jsx');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
var Navigation = React.createClass({
|
const Navigation = () => (
|
||||||
type: 'Navigation',
|
<NavigationBox>
|
||||||
render: function () {
|
<ul className="ul mod-2017">
|
||||||
return (
|
<li className="li-left mod-logo mod-2017">
|
||||||
<NavigationBox>
|
<a
|
||||||
<ul className="ul mod-2017">
|
className="logo-a"
|
||||||
<li className="li-left mod-logo mod-2017">
|
href="/conference"
|
||||||
<a href="/conference" className="logo-a">
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/logo_sm.png"
|
alt="Scratch Logo"
|
||||||
alt="Scratch Logo"
|
className="logo-a-image"
|
||||||
className="logo-a-image"
|
src="/images/logo_sm.png"
|
||||||
/>
|
/>
|
||||||
<p className="logo-a-title">Conferences</p>
|
<p className="logo-a-title">Conferences</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</NavigationBox>
|
</NavigationBox>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Navigation;
|
module.exports = Navigation;
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
var NavigationBox = require('../../base/navigation.jsx');
|
const NavigationBox = require('../../base/navigation.jsx');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
var Navigation = React.createClass({
|
const Navigation = () => (
|
||||||
type: 'Navigation',
|
<NavigationBox>
|
||||||
render: function () {
|
<ul className="ul mod-2018">
|
||||||
return (
|
<li className="li-left mod-logo mod-2018">
|
||||||
<NavigationBox>
|
<a
|
||||||
<ul className="ul mod-2018">
|
className="logo-a"
|
||||||
<li className="li-left mod-logo mod-2018">
|
href="/"
|
||||||
<a href="/" className="logo-a">
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/logo_sm.png"
|
alt="Scratch Logo"
|
||||||
alt="Scratch Logo"
|
className="logo-a-image"
|
||||||
className="logo-a-image"
|
src="/images/logo_sm.png"
|
||||||
/>
|
/>
|
||||||
<p className="logo-a-title">Conferences</p>
|
<p className="logo-a-title">Conferences</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</NavigationBox>
|
</NavigationBox>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Navigation;
|
module.exports = Navigation;
|
||||||
|
|
|
@ -1,31 +1,48 @@
|
||||||
var classNames = require('classnames');
|
const bindAll = require('lodash.bindall');
|
||||||
var connect = require('react-redux').connect;
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const connect = require('react-redux').connect;
|
||||||
var ReactIntl = require('react-intl');
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var messageCountActions = require('../../../redux/message-count.js');
|
const messageCountActions = require('../../../redux/message-count.js');
|
||||||
var sessionActions = require('../../../redux/session.js');
|
const sessionActions = require('../../../redux/session.js');
|
||||||
|
|
||||||
var api = require('../../../lib/api');
|
const api = require('../../../lib/api');
|
||||||
var Avatar = require('../../avatar/avatar.jsx');
|
const Avatar = require('../../avatar/avatar.jsx');
|
||||||
var Button = require('../../forms/button.jsx');
|
const Button = require('../../forms/button.jsx');
|
||||||
var Dropdown = require('../../dropdown/dropdown.jsx');
|
const Dropdown = require('../../dropdown/dropdown.jsx');
|
||||||
var Form = require('../../forms/form.jsx');
|
const Form = require('../../forms/form.jsx');
|
||||||
var Input = require('../../forms/input.jsx');
|
const Input = require('../../forms/input.jsx');
|
||||||
var log = require('../../../lib/log.js');
|
const log = require('../../../lib/log.js');
|
||||||
var Login = require('../../login/login.jsx');
|
const Login = require('../../login/login.jsx');
|
||||||
var Modal = require('../../modal/base/modal.jsx');
|
const Modal = require('../../modal/base/modal.jsx');
|
||||||
var NavigationBox = require('../base/navigation.jsx');
|
const NavigationBox = require('../base/navigation.jsx');
|
||||||
var Registration = require('../../registration/registration.jsx');
|
const Registration = require('../../registration/registration.jsx');
|
||||||
|
|
||||||
require('./navigation.scss');
|
require('./navigation.scss');
|
||||||
|
|
||||||
var Navigation = React.createClass({
|
class Navigation extends React.Component {
|
||||||
type: 'Navigation',
|
constructor (props) {
|
||||||
getInitialState: function () {
|
super(props);
|
||||||
return {
|
bindAll(this, [
|
||||||
|
'getProfileUrl',
|
||||||
|
'handleJoinClick',
|
||||||
|
'handleLoginClick',
|
||||||
|
'handleCloseLogin',
|
||||||
|
'handleLogIn',
|
||||||
|
'handleLogOut',
|
||||||
|
'handleAccountNavClick',
|
||||||
|
'handleCloseAccountNav',
|
||||||
|
'showCanceledDeletion',
|
||||||
|
'handleCloseCanceledDeletion',
|
||||||
|
'handleCloseRegistration',
|
||||||
|
'handleCompleteRegistration',
|
||||||
|
'handleSearchSubmit'
|
||||||
|
]);
|
||||||
|
this.state = {
|
||||||
accountNavOpen: false,
|
accountNavOpen: false,
|
||||||
canceledDeletionOpen: false,
|
canceledDeletionOpen: false,
|
||||||
loginOpen: false,
|
loginOpen: false,
|
||||||
|
@ -33,155 +50,153 @@ var Navigation = React.createClass({
|
||||||
registrationOpen: false,
|
registrationOpen: false,
|
||||||
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
|
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
getDefaultProps: function () {
|
componentDidMount () {
|
||||||
return {
|
|
||||||
session: {},
|
|
||||||
unreadMessageCount: 0, // bubble number to display how many notifications someone has.
|
|
||||||
searchTerm: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
componentDidMount: function () {
|
|
||||||
if (this.props.session.session.user) {
|
if (this.props.session.session.user) {
|
||||||
var intervalId = setInterval(function () {
|
const intervalId = setInterval(() => {
|
||||||
this.props.dispatch(messageCountActions.getCount(this.props.session.session.user.username));
|
this.props.dispatch(
|
||||||
}.bind(this), 120000); // check for new messages every 2 mins.
|
messageCountActions.getCount(this.props.session.session.user.username)
|
||||||
this.setState({'messageCountIntervalId': intervalId});
|
);
|
||||||
|
}, 120000); // check for new messages every 2 mins.
|
||||||
|
this.setState({ // eslint-disable-line react/no-did-mount-set-state
|
||||||
|
messageCountIntervalId: intervalId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
componentDidUpdate: function (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
if (prevProps.session.session.user != this.props.session.session.user) {
|
if (prevProps.session.session.user !== this.props.session.session.user) {
|
||||||
this.setState({
|
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
||||||
'loginOpen': false,
|
loginOpen: false,
|
||||||
'accountNavOpen': false
|
accountNavOpen: false
|
||||||
});
|
});
|
||||||
if (this.props.session.session.user) {
|
if (this.props.session.session.user) {
|
||||||
var intervalId = setInterval(function () {
|
const intervalId = setInterval(() => {
|
||||||
this.props.dispatch(messageCountActions.getCount(this.props.session.session.user.username));
|
this.props.dispatch(
|
||||||
}.bind(this), 120000); // check for new messages every 2 mins.
|
messageCountActions.getCount(this.props.session.session.user.username)
|
||||||
this.setState({'messageCountIntervalId': intervalId});
|
);
|
||||||
|
}, 120000); // check for new messages every 2 mins.
|
||||||
|
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
||||||
|
messageCountIntervalId: intervalId
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// clear message count check, and set to default id.
|
// clear message count check, and set to default id.
|
||||||
clearInterval(this.state.messageCountIntervalId);
|
clearInterval(this.state.messageCountIntervalId);
|
||||||
this.props.dispatch(messageCountActions.setCount(0));
|
this.props.dispatch(messageCountActions.setCount(0));
|
||||||
this.setState({
|
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
||||||
'messageCountIntervalId': -1
|
messageCountIntervalId: -1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
componentWillUnmount: function () {
|
componentWillUnmount () {
|
||||||
// clear message interval if it exists
|
// clear message interval if it exists
|
||||||
if (this.state.messageCountIntervalId != -1) {
|
if (this.state.messageCountIntervalId !== -1) {
|
||||||
clearInterval(this.state.messageCountIntervalId);
|
clearInterval(this.state.messageCountIntervalId);
|
||||||
this.props.dispatch(messageCountActions.setCount(0));
|
this.props.dispatch(messageCountActions.setCount(0));
|
||||||
this.setState({
|
this.setState({
|
||||||
'messageCountIntervalId': -1
|
messageCountIntervalId: -1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getProfileUrl: function () {
|
getProfileUrl () {
|
||||||
if (!this.props.session.session.user) return;
|
if (!this.props.session.session.user) return;
|
||||||
return '/users/' + this.props.session.session.user.username + '/';
|
return `/users/${this.props.session.session.user.username}/`;
|
||||||
},
|
}
|
||||||
handleJoinClick: function (e) {
|
handleJoinClick (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({'registrationOpen': true});
|
this.setState({registrationOpen: true});
|
||||||
},
|
}
|
||||||
handleLoginClick: function (e) {
|
handleLoginClick (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({'loginOpen': !this.state.loginOpen});
|
this.setState({loginOpen: !this.state.loginOpen});
|
||||||
},
|
}
|
||||||
closeLogin: function () {
|
handleCloseLogin () {
|
||||||
this.setState({'loginOpen': false});
|
this.setState({loginOpen: false});
|
||||||
},
|
}
|
||||||
handleLogIn: function (formData, callback) {
|
handleLogIn (formData, callback) {
|
||||||
this.setState({'loginError': null});
|
this.setState({loginError: null});
|
||||||
formData['useMessages'] = true;
|
formData.useMessages = true;
|
||||||
api({
|
api({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
host: '',
|
host: '',
|
||||||
uri: '/accounts/login/',
|
uri: '/accounts/login/',
|
||||||
json: formData,
|
json: formData,
|
||||||
useCsrf: true
|
useCsrf: true
|
||||||
}, function (err, body) {
|
}, (err, body) => {
|
||||||
if (err) this.setState({'loginError': err.message});
|
if (err) this.setState({loginError: err.message});
|
||||||
if (body) {
|
if (body) {
|
||||||
body = body[0];
|
body = body[0];
|
||||||
if (!body.success) {
|
if (body.success) {
|
||||||
|
this.handleCloseLogin();
|
||||||
|
body.messages.map(message => { // eslint-disable-line array-callback-return
|
||||||
|
if (message.message === 'canceled-deletion') {
|
||||||
|
this.showCanceledDeletion();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.dispatch(sessionActions.refreshSession());
|
||||||
|
} else {
|
||||||
if (body.redirect) {
|
if (body.redirect) {
|
||||||
window.location = body.redirect;
|
window.location = body.redirect;
|
||||||
}
|
}
|
||||||
// Update login error message to a friendlier one if it exists
|
// Update login error message to a friendlier one if it exists
|
||||||
this.setState({'loginError': body.msg});
|
this.setState({loginError: body.msg});
|
||||||
} else {
|
|
||||||
this.closeLogin();
|
|
||||||
body.messages.map(function (message) {
|
|
||||||
if (message.message == 'canceled-deletion') {
|
|
||||||
this.showCanceledDeletion();
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
this.props.dispatch(sessionActions.refreshSession());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// JS error already logged by api mixin
|
// JS error already logged by api mixin
|
||||||
callback();
|
callback();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
}
|
||||||
handleLogOut: function (e) {
|
handleLogOut (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
api({
|
api({
|
||||||
host: '',
|
host: '',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
uri: '/accounts/logout/',
|
uri: '/accounts/logout/',
|
||||||
useCsrf: true
|
useCsrf: true
|
||||||
}, function (err) {
|
}, err => {
|
||||||
if (err) log.error(err);
|
if (err) log.error(err);
|
||||||
this.closeLogin();
|
this.handleCloseLogin();
|
||||||
window.location = '/';
|
window.location = '/';
|
||||||
}.bind(this));
|
});
|
||||||
},
|
}
|
||||||
handleAccountNavClick: function (e) {
|
handleAccountNavClick (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({'accountNavOpen': true});
|
this.setState({accountNavOpen: true});
|
||||||
},
|
}
|
||||||
closeAccountNav: function () {
|
handleCloseAccountNav () {
|
||||||
this.setState({'accountNavOpen': false});
|
this.setState({accountNavOpen: false});
|
||||||
},
|
}
|
||||||
showCanceledDeletion: function () {
|
showCanceledDeletion () {
|
||||||
this.setState({'canceledDeletionOpen': true});
|
this.setState({canceledDeletionOpen: true});
|
||||||
},
|
}
|
||||||
closeCanceledDeletion: function () {
|
handleCloseCanceledDeletion () {
|
||||||
this.setState({'canceledDeletionOpen': false});
|
this.setState({canceledDeletionOpen: false});
|
||||||
},
|
}
|
||||||
closeRegistration: function () {
|
handleCloseRegistration () {
|
||||||
this.setState({'registrationOpen': false});
|
this.setState({registrationOpen: false});
|
||||||
},
|
}
|
||||||
completeRegistration: function () {
|
handleCompleteRegistration () {
|
||||||
this.props.dispatch(sessionActions.refreshSession());
|
this.props.dispatch(sessionActions.refreshSession());
|
||||||
this.closeRegistration();
|
this.handleCloseRegistration();
|
||||||
},
|
}
|
||||||
onSearchSubmit: function (formData) {
|
handleSearchSubmit (formData) {
|
||||||
window.location.href = '/search/projects?q=' + encodeURIComponent(formData.q);
|
window.location.href = `/search/projects?q=${encodeURIComponent(formData.q)}`;
|
||||||
},
|
}
|
||||||
render: function () {
|
render () {
|
||||||
var classes = classNames({
|
const createLink = this.props.session.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
|
||||||
'logged-in': this.props.session.session.user
|
|
||||||
});
|
|
||||||
var messageClasses = classNames({
|
|
||||||
'message-count': true,
|
|
||||||
'show': this.props.unreadMessageCount > 0
|
|
||||||
});
|
|
||||||
var dropdownClasses = classNames({
|
|
||||||
'user-info': true,
|
|
||||||
'open': this.state.accountNavOpen
|
|
||||||
});
|
|
||||||
var formatMessage = this.props.intl.formatMessage;
|
|
||||||
var createLink = this.props.session.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
|
|
||||||
return (
|
return (
|
||||||
<NavigationBox className={classes}>
|
<NavigationBox
|
||||||
|
className={classNames({
|
||||||
|
'logged-in': this.props.session.session.user
|
||||||
|
})}
|
||||||
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="logo"><a href="/" aria-label="Scratch"></a></li>
|
<li className="logo">
|
||||||
|
<a
|
||||||
|
aria-label="Scratch"
|
||||||
|
href="/"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li className="link create">
|
<li className="link create">
|
||||||
<a href={createLink}>
|
<a href={createLink}>
|
||||||
|
@ -205,47 +220,76 @@ var Navigation = React.createClass({
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="search">
|
<li className="search">
|
||||||
<Form onSubmit={this.onSearchSubmit}>
|
<Form onSubmit={this.handleSearchSubmit}>
|
||||||
<Button type="submit" className="btn-search" />
|
<Button
|
||||||
<Input type="text"
|
className="btn-search"
|
||||||
value={this.props.searchTerm}
|
type="submit"
|
||||||
aria-label={formatMessage({id: 'general.search'})}
|
/>
|
||||||
placeholder={formatMessage({id: 'general.search'})}
|
<Input
|
||||||
name="q" />
|
aria-label={this.props.intl.formatMessage({id: 'general.search'})}
|
||||||
|
name="q"
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'general.search'})}
|
||||||
|
type="text"
|
||||||
|
value={this.props.searchTerm}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</li>
|
</li>
|
||||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
||||||
this.props.session.session.user ? [
|
this.props.session.session.user ? [
|
||||||
<li className="link right messages" key="messages">
|
<li
|
||||||
|
className="link right messages"
|
||||||
|
key="messages"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href="/messages/"
|
href="/messages/"
|
||||||
title={formatMessage({id: 'general.messages'})}>
|
title={this.props.intl.formatMessage({id: 'general.messages'})}
|
||||||
|
>
|
||||||
<span className={messageClasses}>{this.props.unreadMessageCount}</span>
|
<span
|
||||||
|
className={classNames({
|
||||||
|
'message-count': true,
|
||||||
|
'show': this.props.unreadMessageCount > 0
|
||||||
|
})}
|
||||||
|
>{this.props.unreadMessageCount}</span>
|
||||||
<FormattedMessage id="general.messages" />
|
<FormattedMessage id="general.messages" />
|
||||||
</a>
|
</a>
|
||||||
</li>,
|
</li>,
|
||||||
<li className="link right mystuff" key="mystuff">
|
<li
|
||||||
|
className="link right mystuff"
|
||||||
|
key="mystuff"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href="/mystuff/"
|
href="/mystuff/"
|
||||||
title={formatMessage({id: 'general.myStuff'})}>
|
title={this.props.intl.formatMessage({id: 'general.myStuff'})}
|
||||||
|
>
|
||||||
<FormattedMessage id="general.myStuff" />
|
<FormattedMessage id="general.myStuff" />
|
||||||
</a>
|
</a>
|
||||||
</li>,
|
</li>,
|
||||||
<li className="link right account-nav" key="account-nav">
|
<li
|
||||||
<a className={dropdownClasses}
|
className="link right account-nav"
|
||||||
href="#" onClick={this.handleAccountNavClick}>
|
key="account-nav"
|
||||||
<Avatar src={this.props.session.session.user.thumbnailUrl} alt="" />
|
>
|
||||||
<span className='profile-name'>
|
<a
|
||||||
|
className={classNames({
|
||||||
|
'user-info': true,
|
||||||
|
'open': this.state.accountNavOpen
|
||||||
|
})}
|
||||||
|
href="#"
|
||||||
|
onClick={this.handleAccountNavClick}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
alt=""
|
||||||
|
src={this.props.session.session.user.thumbnailUrl}
|
||||||
|
/>
|
||||||
|
<span className="profile-name">
|
||||||
{this.props.session.session.user.username}
|
{this.props.session.session.user.username}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
as="ul"
|
as="ul"
|
||||||
isOpen={this.state.accountNavOpen}
|
className={process.env.SCRATCH_ENV}
|
||||||
onRequestClose={this.closeAccountNav}
|
isOpen={this.state.accountNavOpen}
|
||||||
className={process.env.SCRATCH_ENV}>
|
onRequestClose={this.handleCloseAccountNav}
|
||||||
|
>
|
||||||
<li>
|
<li>
|
||||||
<a href={this.getProfileUrl()}>
|
<a href={this.getProfileUrl()}>
|
||||||
<FormattedMessage id="general.profile" />
|
<FormattedMessage id="general.profile" />
|
||||||
|
@ -264,8 +308,8 @@ var Navigation = React.createClass({
|
||||||
</li>
|
</li>
|
||||||
] : []}
|
] : []}
|
||||||
{this.props.permissions.student ? [
|
{this.props.permissions.student ? [
|
||||||
<li>
|
<li key="my-class-li">
|
||||||
<a href={'/classes/' + this.props.session.session.user.classroomId + '/'}>
|
<a href={`/classes/${this.props.session.session.user.classroomId}/`}>
|
||||||
<FormattedMessage id="general.myClass" />
|
<FormattedMessage id="general.myClass" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -276,69 +320,123 @@ var Navigation = React.createClass({
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="divider">
|
<li className="divider">
|
||||||
<a href="#" onClick={this.handleLogOut}>
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={this.handleLogOut}
|
||||||
|
>
|
||||||
<FormattedMessage id="navigation.signOut" />
|
<FormattedMessage id="navigation.signOut" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</li>
|
</li>
|
||||||
] : [
|
] : [
|
||||||
<li className="link right join" key="join">
|
<li
|
||||||
<a href="#" onClick={this.handleJoinClick}>
|
className="link right join"
|
||||||
|
key="join"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={this.handleJoinClick}
|
||||||
|
>
|
||||||
<FormattedMessage id="general.joinScratch" />
|
<FormattedMessage id="general.joinScratch" />
|
||||||
</a>
|
</a>
|
||||||
</li>,
|
</li>,
|
||||||
<Registration
|
<Registration
|
||||||
key="registration"
|
isOpen={this.state.registrationOpen}
|
||||||
isOpen={this.state.registrationOpen}
|
key="registration"
|
||||||
onRequestClose={this.closeRegistration}
|
onRegistrationDone={this.handleCompleteRegistration}
|
||||||
onRegistrationDone={this.completeRegistration} />,
|
onRequestClose={this.handleCloseRegistration}
|
||||||
<li className="link right login-item" key="login">
|
/>,
|
||||||
|
<li
|
||||||
|
className="link right login-item"
|
||||||
|
key="login"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href="#"
|
|
||||||
onClick={this.handleLoginClick}
|
|
||||||
className="ignore-react-onclickoutside"
|
className="ignore-react-onclickoutside"
|
||||||
key="login-link">
|
href="#"
|
||||||
<FormattedMessage id="general.signIn" />
|
key="login-link"
|
||||||
</a>
|
onClick={this.handleLoginClick}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="general.signIn" />
|
||||||
|
</a>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="login-dropdown with-arrow"
|
className="login-dropdown with-arrow"
|
||||||
isOpen={this.state.loginOpen}
|
isOpen={this.state.loginOpen}
|
||||||
onRequestClose={this.closeLogin}
|
key="login-dropdown"
|
||||||
key="login-dropdown">
|
onRequestClose={this.handleCloseLogin}
|
||||||
|
>
|
||||||
<Login
|
<Login
|
||||||
|
error={this.state.loginError}
|
||||||
onLogIn={this.handleLogIn}
|
onLogIn={this.handleLogIn}
|
||||||
error={this.state.loginError} />
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</li>
|
</li>
|
||||||
]) : [
|
]) : []}
|
||||||
]}
|
|
||||||
</ul>
|
</ul>
|
||||||
<Modal isOpen={this.state.canceledDeletionOpen}
|
<Modal
|
||||||
onRequestClose={this.closeCanceledDeletion}
|
isOpen={this.state.canceledDeletionOpen}
|
||||||
style={{content:{padding: 15}}}>
|
style={{
|
||||||
|
content: {
|
||||||
|
padding: 15
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onRequestClose={this.handleCloseCanceledDeletion}
|
||||||
|
>
|
||||||
<h4>Your Account Will Not Be Deleted</h4>
|
<h4>Your Account Will Not Be Deleted</h4>
|
||||||
|
<h4><FormattedMessage id="general.noDeletionTitle" /></h4>
|
||||||
<p>
|
<p>
|
||||||
Your account was scheduled for deletion but you logged in. Your account has been reactivated.
|
<FormattedMessage
|
||||||
If you didn’t request for your account to be deleted, you should
|
id="general.noDeletionDescription"
|
||||||
{' '}<a href="/accounts/password_reset/">change your password</a>{' '}
|
values={{
|
||||||
to make sure your account is secure.
|
resetLink: <a href="/accounts/password_reset/">
|
||||||
|
{this.props.intl.formatMessage({id: 'general.noDeletionLink'})}
|
||||||
|
</a>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</Modal>
|
</Modal>
|
||||||
</NavigationBox>
|
</NavigationBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
var mapStateToProps = function (state) {
|
Navigation.propTypes = {
|
||||||
return {
|
dispatch: PropTypes.func,
|
||||||
session: state.session,
|
intl: intlShape,
|
||||||
permissions: state.permissions,
|
permissions: PropTypes.shape({
|
||||||
unreadMessageCount: state.messageCount.messageCount,
|
admin: PropTypes.bool,
|
||||||
searchTerm: state.navigation
|
social: PropTypes.bool,
|
||||||
};
|
educator: PropTypes.bool,
|
||||||
|
educator_invitee: PropTypes.bool,
|
||||||
|
student: PropTypes.bool
|
||||||
|
}),
|
||||||
|
searchTerm: PropTypes.string,
|
||||||
|
session: PropTypes.shape({
|
||||||
|
session: PropTypes.shape({
|
||||||
|
user: PropTypes.shape({
|
||||||
|
classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
thumbnailUrl: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
status: PropTypes.string
|
||||||
|
}),
|
||||||
|
unreadMessageCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||||
};
|
};
|
||||||
|
|
||||||
var ConnectedNavigation = connect(mapStateToProps)(Navigation);
|
Navigation.defaultProps = {
|
||||||
|
session: {},
|
||||||
|
unreadMessageCount: 0, // bubble number to display how many notifications someone has.
|
||||||
|
searchTerm: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
session: state.session,
|
||||||
|
permissions: state.permissions,
|
||||||
|
unreadMessageCount: state.messageCount.messageCount,
|
||||||
|
searchTerm: state.navigation
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedNavigation = connect(mapStateToProps)(Navigation);
|
||||||
|
|
||||||
module.exports = injectIntl(ConnectedNavigation);
|
module.exports = injectIntl(ConnectedNavigation);
|
||||||
|
|
|
@ -1,75 +1,84 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
var Slider = require('react-slick');
|
const React = require('react');
|
||||||
|
const Slider = require('react-slick');
|
||||||
|
|
||||||
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
const Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||||
|
|
||||||
require('slick-carousel/slick/slick.scss');
|
require('slick-carousel/slick/slick.scss');
|
||||||
require('slick-carousel/slick/slick-theme.scss');
|
require('slick-carousel/slick/slick-theme.scss');
|
||||||
require('./nestedcarousel.scss');
|
require('./nestedcarousel.scss');
|
||||||
|
|
||||||
|
|
||||||
{/*
|
/*
|
||||||
NestedCarousel is used to show a carousel, where each slide is composed of a few
|
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).
|
thumbnails (for example, to show step-by-syep tips, where each stage has a few steps).
|
||||||
It creates the thumbnails without links.
|
It creates the thumbnails without links.
|
||||||
|
|
||||||
Each slide has a title, and then a list of thumbnails, that will be shown together.
|
Each slide has a title, and then a list of thumbnails, that will be shown together.
|
||||||
*/}
|
*/
|
||||||
var NestedCarousel = React.createClass({
|
const NestedCarousel = props => {
|
||||||
type: 'NestedCarousel',
|
defaults(props.settings, {
|
||||||
propTypes: {
|
dots: true,
|
||||||
items: React.PropTypes.array
|
infinite: false,
|
||||||
},
|
lazyLoad: true,
|
||||||
getDefaultProps: function () {
|
slidesToShow: 1,
|
||||||
return {
|
slidesToScroll: 1,
|
||||||
items: require('./nestedcarousel.json')
|
variableWidth: false
|
||||||
};
|
});
|
||||||
},
|
|
||||||
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;
|
const arrows = props.items.length > props.settings.slidesToShow;
|
||||||
|
const stages = [];
|
||||||
var classes = classNames(
|
|
||||||
'nestedcarousel',
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
'carousel',
|
const items = props.items[i].thumbnails;
|
||||||
this.props.className
|
const thumbnails = [];
|
||||||
);
|
for (let j = 0; j < items.length; j++) {
|
||||||
|
const item = items[j];
|
||||||
var stages = [];
|
thumbnails.push(
|
||||||
for (var i=0; i < this.props.items.length; i++) {
|
<Thumbnail
|
||||||
var items = this.props.items[i].thumbnails;
|
key={`inner_${i}_${j}`}
|
||||||
var thumbnails = [];
|
linkTitle={false}
|
||||||
for (var j=0; j < items.length; j++) {
|
src={item.thumbnailUrl}
|
||||||
var item = items[j];
|
title={item.title}
|
||||||
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 (
|
stages.push(
|
||||||
<Slider className={classes} arrows={arrows} {... settings}>
|
<div key={`outer_${i}`}>
|
||||||
{stages}
|
<h3>{props.items[i].title}</h3>
|
||||||
</Slider>
|
{thumbnails}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
return (
|
||||||
|
<Slider
|
||||||
|
arrows={arrows}
|
||||||
|
className={classNames('nestedcarousel', 'carousel', props.className)}
|
||||||
|
{...props.settings}
|
||||||
|
>
|
||||||
|
{stages}
|
||||||
|
</Slider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
NestedCarousel.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
settings: PropTypes.shape({
|
||||||
|
dots: PropTypes.bool,
|
||||||
|
infinite: PropTypes.bool,
|
||||||
|
lazyLoad: PropTypes.bool,
|
||||||
|
slidesToShow: PropTypes.number,
|
||||||
|
slidesToScroll: PropTypes.number,
|
||||||
|
variableWidth: PropTypes.bool
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
NestedCarousel.defaultProps = {
|
||||||
|
settings: {},
|
||||||
|
items: require('./nestedcarousel.json')
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = NestedCarousel;
|
module.exports = NestedCarousel;
|
||||||
|
|
|
@ -1,54 +1,53 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
const Box = require('../box/box.jsx');
|
||||||
|
|
||||||
require('./news.scss');
|
require('./news.scss');
|
||||||
|
|
||||||
var News = React.createClass({
|
const News = props => (
|
||||||
type: 'News',
|
<Box
|
||||||
propTypes: {
|
className="news"
|
||||||
items: React.PropTypes.array
|
moreHref="/discuss/5/"
|
||||||
},
|
moreTitle={props.messages['general.viewAll']}
|
||||||
getDefaultProps: function () {
|
title={props.messages['news.scratchNews']}
|
||||||
return {
|
>
|
||||||
items: require('./news.json'),
|
<ul>
|
||||||
messages: {
|
{props.items.map(item => (
|
||||||
'general.viewAll': 'View All',
|
<li key={item.id}>
|
||||||
'news.scratchNews': 'Scratch News'
|
<a href={item.url}>
|
||||||
}
|
<img
|
||||||
};
|
alt=""
|
||||||
},
|
className="news-image"
|
||||||
render: function () {
|
height="53"
|
||||||
return (
|
src={item.image}
|
||||||
<Box
|
width="53"
|
||||||
className="news"
|
/>
|
||||||
title={this.props.messages['news.scratchNews']}
|
<div className="news-description">
|
||||||
moreTitle={this.props.messages['general.viewAll']}
|
<h4>{item.headline}</h4>
|
||||||
moreHref="/discuss/5/">
|
<p>{item.copy}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
<ul>
|
News.propTypes = {
|
||||||
{this.props.items.map(function (item) {
|
items: PropTypes.arrayOf(PropTypes.object),
|
||||||
return (
|
messages: PropTypes.shape({
|
||||||
<li key={item.id}>
|
'general.viewAll': PropTypes.string,
|
||||||
<a href={item.url}>
|
'news.scratchNews': PropTypes.string
|
||||||
<img src={item.image}
|
})
|
||||||
className="news-image"
|
};
|
||||||
width="53"
|
|
||||||
height="53"
|
News.defaultProps = {
|
||||||
alt=""
|
items: require('./news.json'),
|
||||||
/>
|
messages: {
|
||||||
<div className="news-description">
|
'general.viewAll': 'View All',
|
||||||
<h4>{item.headline}</h4>
|
'news.scratchNews': 'Scratch News'
|
||||||
<p>{item.copy}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = News;
|
module.exports = News;
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Navigation = require('../../../navigation/conference/2016/navigation.jsx');
|
const Navigation = require('../../../navigation/conference/2016/navigation.jsx');
|
||||||
var Footer = require('../../../footer/conference/2016/footer.jsx');
|
const Footer = require('../../../footer/conference/2016/footer.jsx');
|
||||||
|
|
||||||
require('../page.scss');
|
require('../page.scss');
|
||||||
|
|
||||||
var Page = React.createClass({
|
const Page = props => (
|
||||||
type: 'Page',
|
<div className="page mod-conference">
|
||||||
render: function () {
|
<div id="navigation">
|
||||||
return (
|
<Navigation />
|
||||||
<div className="page mod-conference">
|
</div>
|
||||||
<div id="navigation">
|
<div id="view">
|
||||||
<Navigation />
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div id="view">
|
<div id="footer">
|
||||||
{this.props.children}
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
</div>
|
||||||
<Footer />
|
);
|
||||||
</div>
|
|
||||||
</div>
|
Page.propTypes = {
|
||||||
);
|
children: PropTypes.node
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Page;
|
module.exports = Page;
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Navigation = require('../../../navigation/conference/2017/navigation.jsx');
|
const Navigation = require('../../../navigation/conference/2017/navigation.jsx');
|
||||||
var Footer = require('../../../footer/conference/2017/footer.jsx');
|
const Footer = require('../../../footer/conference/2017/footer.jsx');
|
||||||
|
|
||||||
require('../page.scss');
|
require('../page.scss');
|
||||||
|
|
||||||
var Page = React.createClass({
|
const Page = props => (
|
||||||
type: 'Page',
|
<div className="page mod-conference">
|
||||||
render: function () {
|
<div id="navigation">
|
||||||
return (
|
<Navigation />
|
||||||
<div className="page mod-conference">
|
</div>
|
||||||
<div id="navigation">
|
<div id="view">
|
||||||
<Navigation />
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div id="view">
|
<div id="footer">
|
||||||
{this.props.children}
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
</div>
|
||||||
<Footer />
|
);
|
||||||
</div>
|
|
||||||
</div>
|
Page.propTypes = {
|
||||||
);
|
children: PropTypes.node
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Page;
|
module.exports = Page;
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Navigation = require('../../../navigation/conference/2018/navigation.jsx');
|
const Navigation = require('../../../navigation/conference/2018/navigation.jsx');
|
||||||
var Footer = require('../../../footer/conference/2018/footer.jsx');
|
const Footer = require('../../../footer/conference/2018/footer.jsx');
|
||||||
|
|
||||||
require('../page.scss');
|
require('../page.scss');
|
||||||
|
|
||||||
var Page = React.createClass({
|
const Page = props => (
|
||||||
type: 'Page',
|
<div className="page mod-conference">
|
||||||
render: function () {
|
<div id="navigation">
|
||||||
return (
|
<Navigation />
|
||||||
<div className="page mod-conference">
|
</div>
|
||||||
<div id="navigation">
|
<div id="view">
|
||||||
<Navigation />
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div id="view">
|
<div id="footer">
|
||||||
{this.props.children}
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
</div>
|
||||||
<Footer />
|
);
|
||||||
</div>
|
|
||||||
</div>
|
Page.propTypes = {
|
||||||
);
|
children: PropTypes.node
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Page;
|
module.exports = Page;
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
var React = require('react');
|
const classNames = require('classnames');
|
||||||
var classNames = require('classnames');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Navigation = require('../../navigation/www/navigation.jsx');
|
const Navigation = require('../../navigation/www/navigation.jsx');
|
||||||
var Footer = require('../../footer/www/footer.jsx');
|
const Footer = require('../../footer/www/footer.jsx');
|
||||||
|
|
||||||
var Page = React.createClass({
|
const Page = props => (
|
||||||
type: 'Page',
|
<div className="page">
|
||||||
render: function () {
|
<div
|
||||||
var classes = classNames({
|
className={classNames({
|
||||||
'staging': process.env.SCRATCH_ENV == 'staging'
|
staging: process.env.SCRATCH_ENV === 'staging'
|
||||||
});
|
})}
|
||||||
return (
|
id="navigation"
|
||||||
<div className="page">
|
>
|
||||||
<div id="navigation" className={classes}>
|
<Navigation />
|
||||||
<Navigation />
|
</div>
|
||||||
</div>
|
<div id="view">
|
||||||
<div id="view">
|
{props.children}
|
||||||
{this.props.children}
|
</div>
|
||||||
</div>
|
<div id="footer">
|
||||||
<div id="footer">
|
<Footer />
|
||||||
<Footer />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
|
||||||
}
|
Page.propTypes = {
|
||||||
});
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Page;
|
module.exports = Page;
|
||||||
|
|
|
@ -1,42 +1,45 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
const Progression = props => {
|
||||||
displayName: 'Progression',
|
const childProps = {
|
||||||
propTypes: {
|
activeStep: props.step,
|
||||||
step: function (props, propName, componentName) {
|
totalSteps: React.Children.count(props.children)
|
||||||
var stepValidator = function (props, propName) {
|
};
|
||||||
if (props[propName] > -1 && props[propName] < props.children.length) {
|
return (
|
||||||
return null;
|
<div
|
||||||
} else {
|
className={classNames('progression', props.className)}
|
||||||
return new Error('Prop `step` out of range');
|
{...props}
|
||||||
|
>
|
||||||
|
{React.Children.map(props.children, (child, id) => {
|
||||||
|
if (id === props.step) {
|
||||||
|
return React.cloneElement(child, childProps);
|
||||||
}
|
}
|
||||||
};
|
})}
|
||||||
return (
|
</div>
|
||||||
React.PropTypes.number.isRequired(props, propName, componentName) ||
|
);
|
||||||
stepValidator(props, propName, componentName)
|
};
|
||||||
);
|
|
||||||
}
|
Progression.propTypes = {
|
||||||
},
|
children: PropTypes.node,
|
||||||
getDefaultProps: function () {
|
className: PropTypes.string,
|
||||||
return {
|
step: function (props, propName, componentName) {
|
||||||
step: 0
|
const stepValidator = (propz, name) => {
|
||||||
};
|
if (propz[name] > -1 && propz[name] < propz.children.length) {
|
||||||
},
|
return null;
|
||||||
render: function () {
|
}
|
||||||
var childProps = {
|
return new Error('Prop `step` out of range');
|
||||||
activeStep: this.props.step,
|
|
||||||
totalSteps: React.Children.count(this.props.children)
|
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div {... this.props}
|
(typeof props[propName] === 'number' ? null : new Error('Not a number')) ||
|
||||||
className={classNames('progression', this.props.className)}>
|
stepValidator(props, propName, componentName)
|
||||||
{React.Children.map(this.props.children, function (child, id) {
|
|
||||||
if (id === this.props.step) {
|
|
||||||
return React.cloneElement(child, childProps);
|
|
||||||
}
|
|
||||||
}, this)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Progression.defaultProps = {
|
||||||
|
step: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Progression;
|
||||||
|
|
|
@ -1,53 +1,62 @@
|
||||||
var React = require('react');
|
const bindAll = require('lodash.bindall');
|
||||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
|
|
||||||
require('./registration.scss');
|
require('./registration.scss');
|
||||||
|
|
||||||
var Registration = React.createClass({
|
class Registration extends React.Component {
|
||||||
propTypes: {
|
constructor (props) {
|
||||||
isOpen: React.PropTypes.bool,
|
super(props);
|
||||||
onRegistrationDone: React.PropTypes.func,
|
bindAll(this, [
|
||||||
onRequestClose: React.PropTypes.func
|
'handleMessage',
|
||||||
},
|
'toggleMessageListener'
|
||||||
onMessage: function (e) {
|
]);
|
||||||
if (e.origin != window.location.origin) return;
|
}
|
||||||
if (e.source != this.registrationIframe.contentWindow) return;
|
componentDidMount () {
|
||||||
if (e.data == 'registration-done') this.props.onRegistrationDone();
|
if (this.props.isOpen) this.toggleMessageListener(true);
|
||||||
if (e.data == 'registration-relaunch') {
|
}
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
this.toggleMessageListener(this.props.isOpen && !prevProps.isOpen);
|
||||||
|
}
|
||||||
|
componentWillUnmount () {
|
||||||
|
this.toggleMessageListener(false);
|
||||||
|
}
|
||||||
|
handleMessage (e) {
|
||||||
|
if (e.origin !== window.location.origin) return;
|
||||||
|
if (e.source !== this.registrationIframe.contentWindow) return;
|
||||||
|
if (e.data === 'registration-done') this.props.onRegistrationDone();
|
||||||
|
if (e.data === 'registration-relaunch') {
|
||||||
this.registrationIframe.contentWindow.location.reload();
|
this.registrationIframe.contentWindow.location.reload();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
toggleMessageListener: function (present) {
|
toggleMessageListener (present) {
|
||||||
if (present) {
|
if (present) {
|
||||||
window.addEventListener('message', this.onMessage);
|
window.addEventListener('message', this.handleMessage);
|
||||||
} else {
|
} else {
|
||||||
window.removeEventListener('message', this.onMessage);
|
window.removeEventListener('message', this.handleMessage);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
componentDidMount: function () {
|
render () {
|
||||||
if (this.props.isOpen) this.toggleMessageListener(true);
|
|
||||||
},
|
|
||||||
componentDidUpdate: function (prevProps) {
|
|
||||||
this.toggleMessageListener(this.props.isOpen && !prevProps.isOpen);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function () {
|
|
||||||
this.toggleMessageListener(false);
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
return (
|
return (
|
||||||
<IframeModal
|
<IframeModal
|
||||||
isOpen={this.props.isOpen}
|
|
||||||
onRequestClose={this.props.onRequestClose}
|
|
||||||
className="mod-registration"
|
className="mod-registration"
|
||||||
componentRef={
|
componentRef={iframe => { // eslint-disable-line react/jsx-no-bind
|
||||||
function (iframe) {
|
this.registrationIframe = iframe;
|
||||||
this.registrationIframe = iframe;
|
}}
|
||||||
}.bind(this)
|
isOpen={this.props.isOpen}
|
||||||
}
|
|
||||||
src="/accounts/standalone-registration/"
|
src="/accounts/standalone-registration/"
|
||||||
|
onRequestClose={this.props.onRequestClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Registration.propTypes = {
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
onRegistrationDone: PropTypes.func,
|
||||||
|
onRequestClose: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Registration;
|
module.exports = Registration;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,18 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./slide.scss');
|
require('./slide.scss');
|
||||||
|
|
||||||
var Slide = React.createClass({
|
const Slide = props => (
|
||||||
displayName: 'Slide',
|
<div className={classNames(['slide', props.className])}>
|
||||||
render: function () {
|
{props.children}
|
||||||
return (
|
</div>
|
||||||
<div className={classNames(['slide', this.props.className])}>
|
);
|
||||||
{this.props.children}
|
|
||||||
</div>
|
Slide.propTypes = {
|
||||||
);
|
children: PropTypes.node,
|
||||||
}
|
className: PropTypes.string
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = Slide;
|
module.exports = Slide;
|
||||||
|
|
|
@ -1,57 +1,47 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var FormattedRelative = require('react-intl').FormattedRelative;
|
const FormattedRelative = require('react-intl').FormattedRelative;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var FlexRow = require('../flex-row/flex-row.jsx');
|
const FlexRow = require('../flex-row/flex-row.jsx');
|
||||||
|
|
||||||
require('./social-message.scss');
|
require('./social-message.scss');
|
||||||
|
|
||||||
var SocialMessage = React.createClass({
|
const SocialMessage = props => (
|
||||||
type: 'SocialMessage',
|
<props.as className={classNames('social-message', props.className)}>
|
||||||
propTypes: {
|
<FlexRow className="mod-social-message">
|
||||||
as: React.PropTypes.string,
|
<div className="social-message-content">
|
||||||
datetime: React.PropTypes.string.isRequired,
|
{typeof props.iconSrc === 'undefined' ? [] : [
|
||||||
iconSrc: React.PropTypes.string,
|
<img
|
||||||
iconAlt: React.PropTypes.string,
|
alt={props.iconAlt}
|
||||||
imgClassName: React.PropTypes.string
|
className={classNames('social-message-icon', props.imgClassName)}
|
||||||
},
|
key="social-message-icon"
|
||||||
getDefaultProps: function () {
|
src={props.iconSrc}
|
||||||
return {
|
/>
|
||||||
as: 'li'
|
]}
|
||||||
};
|
<div>
|
||||||
},
|
{props.children}
|
||||||
render: function () {
|
</div>
|
||||||
var classes = classNames(
|
</div>
|
||||||
'social-message',
|
<span className="social-message-date">
|
||||||
this.props.className
|
<FormattedRelative value={new Date(props.datetime)} />
|
||||||
);
|
</span>
|
||||||
var imgClass = classNames(
|
</FlexRow>
|
||||||
'social-message-icon',
|
</props.as>
|
||||||
this.props.imgClassName
|
);
|
||||||
);
|
|
||||||
return (
|
SocialMessage.propTypes = {
|
||||||
<this.props.as className={classes}>
|
as: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
||||||
<FlexRow className="mod-social-message">
|
children: PropTypes.node,
|
||||||
<div className="social-message-content">
|
className: PropTypes.string,
|
||||||
{typeof this.props.iconSrc !== 'undefined' ? [
|
datetime: PropTypes.string.isRequired,
|
||||||
<img
|
iconAlt: PropTypes.string,
|
||||||
key="social-message-icon"
|
iconSrc: PropTypes.string,
|
||||||
className={imgClass}
|
imgClassName: PropTypes.string
|
||||||
src={this.props.iconSrc}
|
};
|
||||||
alt={this.props.iconAlt}
|
|
||||||
/>
|
SocialMessage.defaultProps = {
|
||||||
] : []}
|
as: 'li'
|
||||||
<div>
|
};
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span className="social-message-date">
|
|
||||||
<FormattedRelative value={new Date(this.props.datetime)} />
|
|
||||||
</span>
|
|
||||||
</FlexRow>
|
|
||||||
</this.props.as>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SocialMessage;
|
module.exports = SocialMessage;
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
var range = require('lodash.range');
|
const range = require('lodash.range');
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
require('./spinner.scss');
|
require('./spinner.scss');
|
||||||
|
|
||||||
var Spinner = React.createClass({
|
// Adapted from http://tobiasahlin.com/spinkit/
|
||||||
// Adapted from http://tobiasahlin.com/spinkit/
|
const Spinner = () => (
|
||||||
type: 'Spinner',
|
<div className="spinner">
|
||||||
render: function () {
|
{range(1, 13).map(id => (
|
||||||
return (
|
<div
|
||||||
<div className="spinner">
|
className={`circle${id} circle`}
|
||||||
{range(1,13).map(function (id) {
|
key={`circle${id}`}
|
||||||
return <div className={'circle' + id + ' circle'}></div>;
|
/>
|
||||||
})}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Spinner;
|
module.exports = Spinner;
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./stepnavigation.scss');
|
require('./stepnavigation.scss');
|
||||||
|
|
||||||
var StepNavigation = React.createClass({
|
const StepNavigation = props => (
|
||||||
type: 'Navigation',
|
<ul className={classNames('step-navigation', props.className)}>
|
||||||
render: function () {
|
{Array.apply(null, Array(props.steps)).map((v, step) => (
|
||||||
return (
|
<li
|
||||||
<ul className={classNames('step-navigation', this.props.className)}>
|
className={classNames({
|
||||||
{Array.apply(null, Array(this.props.steps)).map(function (v, step) {
|
active: step < props.active,
|
||||||
return (
|
selected: step === props.active
|
||||||
<li key={step}
|
})}
|
||||||
className={classNames({
|
key={step}
|
||||||
active: step < this.props.active,
|
>
|
||||||
selected: step === this.props.active
|
<div className="indicator" />
|
||||||
})}
|
</li>
|
||||||
>
|
))}
|
||||||
<div className="indicator" />
|
</ul>
|
||||||
</li>
|
);
|
||||||
);
|
|
||||||
}.bind(this))}
|
StepNavigation.propTypes = {
|
||||||
</ul>
|
active: PropTypes.number,
|
||||||
);
|
className: PropTypes.string,
|
||||||
}
|
steps: PropTypes.number
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = StepNavigation;
|
module.exports = StepNavigation;
|
||||||
|
|
|
@ -1,36 +1,38 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./subnavigation.scss');
|
require('./subnavigation.scss');
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Container for a custom, horizontal list of navigation elements
|
* Container for a custom, horizontal list of navigation elements
|
||||||
* that can be displayed within a view or component.
|
* that can be displayed within a view or component.
|
||||||
*/
|
*/
|
||||||
var SubNavigation = React.createClass({
|
const SubNavigation = props => (
|
||||||
type: 'SubNavigation',
|
<div
|
||||||
getDefaultProps: function () {
|
className={classNames(
|
||||||
return {
|
|
||||||
align: 'middle'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var classes = classNames(
|
|
||||||
[
|
[
|
||||||
'sub-nav',
|
'sub-nav',
|
||||||
this.props.className
|
props.className
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
'sub-nav-align-left': this.props.align === 'left',
|
'sub-nav-align-left': props.align === 'left',
|
||||||
'sub-nav-align-right': this.props.align === 'right'
|
'sub-nav-align-right': props.align === 'right'
|
||||||
}
|
}
|
||||||
);
|
)}
|
||||||
return (
|
>
|
||||||
<div className={classes}>
|
{props.children}
|
||||||
{this.props.children}
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
|
||||||
}
|
SubNavigation.propTypes = {
|
||||||
});
|
align: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
SubNavigation.defaultProps = {
|
||||||
|
align: 'middle'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = SubNavigation;
|
module.exports = SubNavigation;
|
||||||
|
|
|
@ -1,28 +1,26 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
const PropTypes = require('prop-types');
|
||||||
var React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
|
const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||||
|
|
||||||
require('./tabs.scss');
|
require('./tabs.scss');
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Container for a custom, horizontal list of navigation elements
|
* Container for a custom, horizontal list of navigation elements
|
||||||
* that can be displayed within a view or component.
|
* that can be displayed within a view or component.
|
||||||
*/
|
*/
|
||||||
var Tabs = React.createClass({
|
const Tabs = props => (
|
||||||
type: 'Tabs',
|
<div className="tab-background">
|
||||||
render: function () {
|
<SubNavigation className={classNames('tabs', props.className)}>
|
||||||
var classes = classNames(
|
{props.children}
|
||||||
'tabs',
|
</SubNavigation>
|
||||||
this.props.className
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
|
||||||
<div className='tab-background'>
|
Tabs.propTypes = {
|
||||||
<SubNavigation className={classes}>
|
children: PropTypes.node,
|
||||||
{this.props.children}
|
className: PropTypes.string
|
||||||
</SubNavigation>
|
};
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Tabs;
|
module.exports = Tabs;
|
||||||
|
|
|
@ -1,86 +1,100 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var connect = require('react-redux').connect;
|
const connect = require('react-redux').connect;
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var sessionActions = require('../../redux/session.js');
|
const sessionActions = require('../../redux/session.js');
|
||||||
|
|
||||||
var TitleBanner = require('../title-banner/title-banner.jsx');
|
const TitleBanner = require('../title-banner/title-banner.jsx');
|
||||||
var Button = require('../forms/button.jsx');
|
const Button = require('../forms/button.jsx');
|
||||||
var FlexRow = require('../flex-row/flex-row.jsx');
|
const FlexRow = require('../flex-row/flex-row.jsx');
|
||||||
|
|
||||||
require('./teacher-banner.scss');
|
require('./teacher-banner.scss');
|
||||||
|
|
||||||
var TeacherBanner = React.createClass({
|
const TeacherBanner = props => (
|
||||||
type: 'TeacherBanner',
|
<TitleBanner className={classNames('teacher-banner', props.className)}>
|
||||||
getDefaultProps: function () {
|
<FlexRow className="inner">
|
||||||
return {
|
<div className="welcome">
|
||||||
messages: {
|
{props.sessionStatus === sessionActions.Status.FETCHED ? (
|
||||||
'teacherbanner.greeting': 'Hi',
|
props.user ? [
|
||||||
'teacherbanner.subgreeting': 'Teacher Account',
|
<h3 key="greeting">
|
||||||
'teacherbanner.classesButton': 'My Classes',
|
{props.messages['teacherbanner.greeting']},{' '}
|
||||||
'teacherbanner.resourcesButton': 'Educator Resources',
|
{props.user.username}
|
||||||
'teacherbanner.faqButton': 'Teacher Account FAQ'
|
</h3>,
|
||||||
},
|
<p
|
||||||
session: {}
|
className="title-banner-p"
|
||||||
};
|
key="subgreeting"
|
||||||
},
|
>
|
||||||
render: function () {
|
{props.messages['teacherbanner.subgreeting']}
|
||||||
var classes = classNames(
|
</p>
|
||||||
'teacher-banner',
|
] : []
|
||||||
this.props.className
|
) : []}
|
||||||
);
|
</div>
|
||||||
return (
|
<FlexRow className="quick-links">
|
||||||
<TitleBanner className={classes}>
|
{props.sessionStatus === sessionActions.Status.FETCHED ? (
|
||||||
<FlexRow className="inner">
|
props.user ? [
|
||||||
<div className="welcome">
|
<a
|
||||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
href="/educators/classes"
|
||||||
this.props.session.session.user ? [
|
key="classes-button"
|
||||||
<h3 key="greeting">
|
>
|
||||||
{this.props.messages['teacherbanner.greeting']},{' '}
|
<Button>
|
||||||
{this.props.session.session.user.username}
|
{props.messages['teacherbanner.classesButton']}
|
||||||
</h3>,
|
</Button>
|
||||||
<p
|
</a>,
|
||||||
key="subgreeting"
|
<a
|
||||||
className="title-banner-p"
|
href="/info/educators"
|
||||||
>
|
key="resources-button"
|
||||||
{this.props.messages['teacherbanner.subgreeting']}
|
>
|
||||||
</p>
|
<Button>
|
||||||
] : []
|
{props.messages['teacherbanner.resourcesButton']}
|
||||||
): []}
|
</Button>
|
||||||
</div>
|
</a>,
|
||||||
<FlexRow className="quick-links">
|
<a
|
||||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
href="/educators/faq"
|
||||||
this.props.session.session.user ? [
|
key="faq-button"
|
||||||
<a href="/educators/classes" key="classes-button">
|
>
|
||||||
<Button>
|
<Button>
|
||||||
{this.props.messages['teacherbanner.classesButton']}
|
{props.messages['teacherbanner.faqButton']}
|
||||||
</Button>
|
</Button>
|
||||||
</a>,
|
</a>
|
||||||
<a href="/info/educators" key="resources-button">
|
] : []
|
||||||
<Button>
|
) : []}
|
||||||
{this.props.messages['teacherbanner.resourcesButton']}
|
</FlexRow>
|
||||||
</Button>
|
</FlexRow>
|
||||||
</a>,
|
</TitleBanner>
|
||||||
<a href="/educators/faq" key="faq-button">
|
);
|
||||||
<Button>
|
|
||||||
{this.props.messages['teacherbanner.faqButton']}
|
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
] : []
|
|
||||||
): []}
|
|
||||||
</FlexRow>
|
|
||||||
</FlexRow>
|
|
||||||
</TitleBanner>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var mapStateToProps = function (state) {
|
TeacherBanner.propTypes = {
|
||||||
return {
|
className: PropTypes.string,
|
||||||
session: state.session
|
messages: PropTypes.shape({
|
||||||
};
|
'teacherbanner.greeting': PropTypes.string,
|
||||||
|
'teacherbanner.subgreeting': PropTypes.string,
|
||||||
|
'teacherbanner.classesButton': PropTypes.string,
|
||||||
|
'teacherbanner.resourcesButton': PropTypes.string,
|
||||||
|
'teacherbanner.faqButton': PropTypes.string
|
||||||
|
}),
|
||||||
|
sessionStatus: PropTypes.string,
|
||||||
|
user: PropTypes.shape({
|
||||||
|
username: PropTypes.string
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
var ConnectedTeacherBanner = connect(mapStateToProps)(TeacherBanner);
|
TeacherBanner.defaultProps = {
|
||||||
|
messages: {
|
||||||
|
'teacherbanner.greeting': 'Hi',
|
||||||
|
'teacherbanner.subgreeting': 'Teacher Account',
|
||||||
|
'teacherbanner.classesButton': 'My Classes',
|
||||||
|
'teacherbanner.resourcesButton': 'Educator Resources',
|
||||||
|
'teacherbanner.faqButton': 'Teacher Account FAQ'
|
||||||
|
},
|
||||||
|
user: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
sessionStatus: state.session.status,
|
||||||
|
user: state.session.session.user
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedTeacherBanner = connect(mapStateToProps)(TeacherBanner);
|
||||||
|
|
||||||
module.exports = ConnectedTeacherBanner;
|
module.exports = ConnectedTeacherBanner;
|
||||||
|
|
|
@ -1,186 +1,168 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./thumbnail.scss');
|
require('./thumbnail.scss');
|
||||||
|
|
||||||
var Thumbnail = React.createClass({
|
const Thumbnail = props => {
|
||||||
type: 'Thumbnail',
|
const extra = [];
|
||||||
propTypes: {
|
const info = [];
|
||||||
src: React.PropTypes.string
|
|
||||||
},
|
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
srcFallback: false,
|
|
||||||
avatarFallback: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getDefaultProps: function () {
|
|
||||||
return {
|
|
||||||
href: '#',
|
|
||||||
title: 'Project',
|
|
||||||
src: '',
|
|
||||||
srcDefault: 'https://uploads.scratch.mit.edu/projects/thumbnails/default.png',
|
|
||||||
avatar: '',
|
|
||||||
avatarDefault: 'https://uploads.scratch.mit.edu/users/avatars/default.png',
|
|
||||||
type: 'project',
|
|
||||||
showLoves: false,
|
|
||||||
showFavorites: false,
|
|
||||||
showRemixes: false,
|
|
||||||
showViews: false,
|
|
||||||
showAvatar: false,
|
|
||||||
linkTitle: true,
|
|
||||||
alt: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleSrcError: function () {
|
|
||||||
this.setState({srcFallback: true});
|
|
||||||
},
|
|
||||||
handleAvatarError: function () {
|
|
||||||
this.setState({avatarFallback: true});
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var classes = classNames(
|
|
||||||
'thumbnail',
|
|
||||||
this.props.type,
|
|
||||||
this.props.className
|
|
||||||
);
|
|
||||||
var extra = [];
|
|
||||||
var info = [];
|
|
||||||
|
|
||||||
if (this.props.loves && this.props.showLoves) {
|
if (props.loves && props.showLoves) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<div
|
<div
|
||||||
key="loves"
|
className="thumbnail-loves"
|
||||||
className="thumbnail-loves"
|
key="loves"
|
||||||
title={this.props.loves + ' loves'}>
|
title={`${props.loves} loves`}
|
||||||
{this.props.loves}
|
>
|
||||||
</div>
|
{props.loves}
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.props.favorites && this.props.showFavorites) {
|
|
||||||
extra.push(
|
|
||||||
<div
|
|
||||||
key="favorites"
|
|
||||||
className="thumbnail-favorites"
|
|
||||||
title={this.favorites + ' favorites'}>
|
|
||||||
{this.props.favorites}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.props.remixes && this.props.showRemixes) {
|
|
||||||
extra.push(
|
|
||||||
<div
|
|
||||||
key="remixes"
|
|
||||||
className="thumbnail-remixes"
|
|
||||||
title={this.props.remixes + ' remixes'}
|
|
||||||
>
|
|
||||||
{this.props.remixes}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.props.views && this.props.showViews) {
|
|
||||||
extra.push(
|
|
||||||
<div
|
|
||||||
key="views"
|
|
||||||
className="thumbnail-views"
|
|
||||||
title={this.props.views + ' views'}
|
|
||||||
>
|
|
||||||
{this.props.views}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var imgElement, titleElement, avatarElement;
|
|
||||||
if (this.props.linkTitle) {
|
|
||||||
if (this.state.srcFallback) {
|
|
||||||
imgElement = (
|
|
||||||
<a
|
|
||||||
className="thumbnail-image"
|
|
||||||
href={this.props.href}
|
|
||||||
key="imgElement"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt={this.props.alt}
|
|
||||||
src={this.props.srcDefault}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
imgElement = (
|
|
||||||
<a
|
|
||||||
className="thumbnail-image"
|
|
||||||
href={this.props.href}
|
|
||||||
key="imgElement"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt={this.props.alt}
|
|
||||||
src={this.props.src}
|
|
||||||
onError={this.handleSrcError}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
titleElement = (
|
|
||||||
<a href={this.props.href} key="titleElement">
|
|
||||||
{this.props.title}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
imgElement = <img src={this.props.src} />;
|
|
||||||
titleElement = this.props.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.push(titleElement);
|
|
||||||
|
|
||||||
if (this.props.creator) {
|
|
||||||
info.push(
|
|
||||||
<div key="creator" className="thumbnail-creator">
|
|
||||||
<a href={'/users/' + this.props.creator + '/'}>{this.props.creator}</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.avatar && this.props.showAvatar) {
|
|
||||||
if (this.state.avatarFallback) {
|
|
||||||
avatarElement = (
|
|
||||||
<a
|
|
||||||
className="creator-image"
|
|
||||||
href={'/users/' + this.props.creator + '/'}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt={this.props.creator}
|
|
||||||
src={this.props.avatarDefault}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
avatarElement = (
|
|
||||||
<a
|
|
||||||
className="creator-image"
|
|
||||||
href={'/users/' + this.props.creator + '/'}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt={this.props.creator}
|
|
||||||
src={this.props.avatar}
|
|
||||||
onError={this.handleAvatarError}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes} >
|
|
||||||
{imgElement}
|
|
||||||
<div className="thumbnail-info">
|
|
||||||
{avatarElement}
|
|
||||||
<div className="thumbnail-title">
|
|
||||||
{info}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{extra}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
if (props.favorites && props.showFavorites) {
|
||||||
|
extra.push(
|
||||||
|
<div
|
||||||
|
className="thumbnail-favorites"
|
||||||
|
key="favorites"
|
||||||
|
title={`${props.favorites} favorites`}
|
||||||
|
>
|
||||||
|
{props.favorites}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (props.remixes && props.showRemixes) {
|
||||||
|
extra.push(
|
||||||
|
<div
|
||||||
|
className="thumbnail-remixes"
|
||||||
|
key="remixes"
|
||||||
|
title={`${props.remixes} remixes`}
|
||||||
|
>
|
||||||
|
{props.remixes}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (props.views && props.showViews) {
|
||||||
|
extra.push(
|
||||||
|
<div
|
||||||
|
className="thumbnail-views"
|
||||||
|
key="views"
|
||||||
|
title={`${props.views} views`}
|
||||||
|
>
|
||||||
|
{props.views}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let imgElement;
|
||||||
|
let titleElement;
|
||||||
|
let avatarElement;
|
||||||
|
|
||||||
|
if (props.linkTitle) {
|
||||||
|
imgElement = (
|
||||||
|
<a
|
||||||
|
className="thumbnail-image"
|
||||||
|
href={props.href}
|
||||||
|
key="imgElement"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={props.alt}
|
||||||
|
src={props.src}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
titleElement = (
|
||||||
|
<a
|
||||||
|
href={props.href}
|
||||||
|
key="titleElement"
|
||||||
|
>
|
||||||
|
{props.title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
imgElement = <img src={props.src} />;
|
||||||
|
titleElement = props.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.push(titleElement);
|
||||||
|
|
||||||
|
if (props.creator) {
|
||||||
|
info.push(
|
||||||
|
<div
|
||||||
|
className="thumbnail-creator"
|
||||||
|
key="creator"
|
||||||
|
>
|
||||||
|
<a href={`/users/${props.creator}/`}>{props.creator}</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.avatar && props.showAvatar) {
|
||||||
|
avatarElement = (
|
||||||
|
<a
|
||||||
|
className="creator-image"
|
||||||
|
href={`/users/${props.creator}/`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={props.creator}
|
||||||
|
src={props.avatar}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'thumbnail',
|
||||||
|
props.type,
|
||||||
|
props.className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{imgElement}
|
||||||
|
<div className="thumbnail-info">
|
||||||
|
{avatarElement}
|
||||||
|
<div className="thumbnail-title">
|
||||||
|
{info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{extra}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Thumbnail.propTypes = {
|
||||||
|
alt: PropTypes.string,
|
||||||
|
avatar: PropTypes.string,
|
||||||
|
className: PropTypes.string,
|
||||||
|
creator: PropTypes.string,
|
||||||
|
favorites: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
href: PropTypes.string,
|
||||||
|
linkTitle: PropTypes.bool,
|
||||||
|
loves: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
remixes: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
showAvatar: PropTypes.bool,
|
||||||
|
showFavorites: PropTypes.bool,
|
||||||
|
showLoves: PropTypes.bool,
|
||||||
|
showRemixes: PropTypes.bool,
|
||||||
|
showViews: PropTypes.bool,
|
||||||
|
src: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
type: PropTypes.string,
|
||||||
|
views: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||||
|
};
|
||||||
|
|
||||||
|
Thumbnail.defaultProps = {
|
||||||
|
alt: '',
|
||||||
|
avatar: '',
|
||||||
|
href: '#',
|
||||||
|
linkTitle: true,
|
||||||
|
showAvatar: false,
|
||||||
|
showFavorites: false,
|
||||||
|
showLoves: false,
|
||||||
|
showRemixes: false,
|
||||||
|
showViews: false,
|
||||||
|
src: '',
|
||||||
|
title: 'Project',
|
||||||
|
type: 'project'
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Thumbnail;
|
module.exports = Thumbnail;
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./title-banner.scss');
|
require('./title-banner.scss');
|
||||||
|
|
||||||
var TitleBanner = React.createClass({
|
const TitleBanner = props => (
|
||||||
type: 'TitleBanner',
|
<div className={classNames('title-banner', props.className)}>
|
||||||
render: function () {
|
{props.children}
|
||||||
var classes = classNames(
|
</div>
|
||||||
'title-banner',
|
);
|
||||||
this.props.className
|
|
||||||
);
|
TitleBanner.propTypes = {
|
||||||
return (
|
children: PropTypes.node,
|
||||||
<div className={classes}>
|
className: PropTypes.string
|
||||||
{this.props.children}
|
};
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = TitleBanner;
|
module.exports = TitleBanner;
|
||||||
|
|
|
@ -1,33 +1,39 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('./tooltip.scss');
|
require('./tooltip.scss');
|
||||||
|
|
||||||
var Tooltip = React.createClass({
|
const Tooltip = props => (
|
||||||
type: 'Tooltip',
|
<span
|
||||||
getDefaultProps: function () {
|
className={classNames(
|
||||||
return {
|
|
||||||
title: '',
|
|
||||||
tipContent: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var classes = classNames(
|
|
||||||
'tooltip',
|
'tooltip',
|
||||||
this.props.className,
|
props.className,
|
||||||
{overmax: (this.props.currentCharacters > this.props.maxCharacters)}
|
{overmax: (props.currentCharacters > props.maxCharacters)}
|
||||||
);
|
)}
|
||||||
return (
|
>
|
||||||
<span className={classes}>
|
<span className="tip">
|
||||||
<span className="tip">
|
<img
|
||||||
<img src="/svgs/tooltip/info.svg" alt="info icon" />
|
alt="info icon"
|
||||||
</span>
|
src="/svgs/tooltip/info.svg"
|
||||||
<span className="expand">
|
/>
|
||||||
{this.props.tipContent}
|
</span>
|
||||||
</span>
|
<span className="expand">
|
||||||
</span>
|
{props.tipContent}
|
||||||
);
|
</span>
|
||||||
}
|
</span>
|
||||||
});
|
);
|
||||||
|
|
||||||
|
Tooltip.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
currentCharacters: PropTypes.number,
|
||||||
|
maxCharacters: PropTypes.number,
|
||||||
|
tipContent: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
Tooltip.defaultProps = {
|
||||||
|
title: '',
|
||||||
|
tipContent: ''
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Tooltip;
|
module.exports = Tooltip;
|
||||||
|
|
|
@ -1,58 +1,68 @@
|
||||||
var classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
var React = require('react');
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
require('../forms/button.scss');
|
require('../forms/button.scss');
|
||||||
require('./ttt-tile.scss');
|
require('./ttt-tile.scss');
|
||||||
|
|
||||||
var TTTTile = React.createClass({
|
const TTTTile = props => (
|
||||||
type: 'TTTTile',
|
<div className={classNames('ttt-tile', props.className)}>
|
||||||
propTypes: {
|
<a href={props.tutorialLoc}>
|
||||||
title: React.PropTypes.string.isRequired,
|
<div className="ttt-tile-tutorial">
|
||||||
description: React.PropTypes.string,
|
<div className="ttt-tile-image">
|
||||||
thumbUrl: React.PropTypes.string.isRequired,
|
<img
|
||||||
tutorialLoc: React.PropTypes.string.isRequired,
|
alt=""
|
||||||
onGuideClick: React.PropTypes.func
|
className="ttt-tile-image-img"
|
||||||
},
|
src={props.thumbUrl}
|
||||||
render: function () {
|
/>
|
||||||
var classes = classNames(
|
<div className="ttt-tile-image-try">
|
||||||
'ttt-tile',
|
<div className="button mod-ttt-try-button">
|
||||||
this.props.className
|
<FormattedMessage id="tile.tryIt" />
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className={classes} >
|
|
||||||
<a href={this.props.tutorialLoc}>
|
|
||||||
<div className="ttt-tile-tutorial">
|
|
||||||
<div className="ttt-tile-image">
|
|
||||||
<img className="ttt-tile-image-img" src={this.props.thumbUrl} alt="" />
|
|
||||||
<div className="ttt-tile-image-try">
|
|
||||||
<div className="button mod-ttt-try-button">
|
|
||||||
<FormattedMessage id="tile.tryIt" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ttt-tile-info">
|
|
||||||
|
|
||||||
<div className="ttt-tile-tag">
|
|
||||||
<FormattedMessage id='ttt.tutorial' defaultMessage='Tutorial'/>
|
|
||||||
</div>
|
|
||||||
<h4 className="ttt-tile-title">{this.props.title}</h4>
|
|
||||||
<p className="ttt-tile-description">
|
|
||||||
{this.props.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="ttt-tile-info">
|
||||||
|
|
||||||
</a>
|
<div className="ttt-tile-tag">
|
||||||
{this.props.onGuideClick && (
|
<FormattedMessage
|
||||||
<div className="ttt-tile-guides" onClick={this.props.onGuideClick}>
|
defaultMessage="Tutorial"
|
||||||
<FormattedMessage id='tile.guides' defaultMessage='See Cards and Guides'/>
|
id="ttt.tutorial"
|
||||||
<img className="ttt-tile-open-modal" src="/svgs/modal/open-blue.svg" />
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<h4 className="ttt-tile-title">{props.title}</h4>
|
||||||
|
<p className="ttt-tile-description">
|
||||||
|
{props.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
}
|
</a>
|
||||||
});
|
{props.onGuideClick && (
|
||||||
|
<div
|
||||||
|
className="ttt-tile-guides"
|
||||||
|
onClick={props.onGuideClick}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="See Cards and Guides"
|
||||||
|
id="tile.guides"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
className="ttt-tile-open-modal"
|
||||||
|
src="/svgs/modal/open-blue.svg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
TTTTile.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
description: PropTypes.string,
|
||||||
|
onGuideClick: PropTypes.func,
|
||||||
|
thumbUrl: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
tutorialLoc: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = TTTTile;
|
module.exports = TTTTile;
|
||||||
|
|
|
@ -1,69 +1,81 @@
|
||||||
var React = require('react');
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
const Box = require('../box/box.jsx');
|
||||||
|
|
||||||
require('./welcome.scss');
|
require('./welcome.scss');
|
||||||
|
|
||||||
var Welcome = React.createClass({
|
const Welcome = props => (
|
||||||
type: 'Welcome',
|
<Box
|
||||||
propTypes: {
|
className="welcome"
|
||||||
onDismiss: React.PropTypes.func
|
moreHref="#"
|
||||||
},
|
moreProps={{
|
||||||
getDefaultProps: function () {
|
className: 'close',
|
||||||
return {
|
title: 'Dismiss',
|
||||||
messages: {
|
onClick: props.onDismiss
|
||||||
'welcome.welcomeToScratch': 'Welcome to Scratch!',
|
}}
|
||||||
'welcome.learn': 'Learn how to make a project in Scratch',
|
moreTitle="x"
|
||||||
'welcome.tryOut': 'Try out starter projects',
|
title={props.messages['welcome.welcomeToScratch']}
|
||||||
'welcome.connect': 'Connect with other Scratchers'
|
>
|
||||||
}
|
<div className="welcome-col blue">
|
||||||
};
|
<h4>
|
||||||
},
|
<a href="/projects/editor/?tip_bar=getStarted">
|
||||||
render: function () {
|
{props.messages['welcome.learn']}
|
||||||
return (
|
</a>
|
||||||
<Box title={this.props.messages['welcome.welcomeToScratch']}
|
</h4>
|
||||||
className="welcome"
|
<a href="/projects/editor/?tip_bar=getStarted">
|
||||||
moreTitle="x"
|
<img
|
||||||
moreHref="#"
|
alt="Get Started"
|
||||||
moreProps={{
|
src="/images/welcome-learn.png"
|
||||||
className: 'close',
|
/>
|
||||||
title: 'Dismiss',
|
</a>
|
||||||
onClick: this.props.onDismiss
|
</div>
|
||||||
}}>
|
<div className="welcome-col green">
|
||||||
|
<h4>
|
||||||
|
<a href="/starter_projects/">
|
||||||
|
{props.messages['welcome.tryOut']}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<a href="/starter_projects/">
|
||||||
|
<img
|
||||||
|
alt="Starter Projects"
|
||||||
|
src="/images/welcome-try.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="welcome-col pink">
|
||||||
|
<h4>
|
||||||
|
<a href="/studios/146521/">
|
||||||
|
{props.messages['welcome.connect']}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<a href="/studios/146521/">
|
||||||
|
<img
|
||||||
|
alt="Connect"
|
||||||
|
src="/images/welcome-connect.png"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
<div className="welcome-col blue">
|
Welcome.propTypes = {
|
||||||
<h4>
|
messages: PropTypes.shape({
|
||||||
<a href="/projects/editor/?tip_bar=getStarted">
|
'welcome.welcomeToScratch': PropTypes.string,
|
||||||
{this.props.messages['welcome.learn']}
|
'welcome.learn': PropTypes.string,
|
||||||
</a>
|
'welcome.tryOut': PropTypes.string,
|
||||||
</h4>
|
'welcome.connect': PropTypes.string
|
||||||
<a href="/projects/editor/?tip_bar=getStarted">
|
}),
|
||||||
<img src="/images/welcome-learn.png" alt="Get Started" />
|
onDismiss: PropTypes.func
|
||||||
</a>
|
};
|
||||||
</div>
|
|
||||||
<div className="welcome-col green">
|
Welcome.defaultProps = {
|
||||||
<h4>
|
messages: {
|
||||||
<a href="/starter_projects/">
|
'welcome.welcomeToScratch': 'Welcome to Scratch!',
|
||||||
{this.props.messages['welcome.tryOut']}
|
'welcome.learn': 'Learn how to make a project in Scratch',
|
||||||
</a>
|
'welcome.tryOut': 'Try out starter projects',
|
||||||
</h4>
|
'welcome.connect': 'Connect with other Scratchers'
|
||||||
<a href="/starter_projects/">
|
|
||||||
<img src="/images/welcome-try.png" alt="Starter Projects" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="welcome-col pink">
|
|
||||||
<h4>
|
|
||||||
<a href="/studios/146521/">
|
|
||||||
{this.props.messages['welcome.connect']}
|
|
||||||
</a>
|
|
||||||
</h4>
|
|
||||||
<a href="/studios/146521/">
|
|
||||||
<img src="/images/welcome-connect.png" alt="Connect" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = Welcome;
|
module.exports = Welcome;
|
||||||
|
|
18
src/init.js
18
src/init.js
|
@ -1,12 +1,12 @@
|
||||||
var jar = require('./lib/jar');
|
const jar = require('./lib/jar');
|
||||||
var Raven = require('raven-js');
|
const Raven = require('raven-js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
* Error handling
|
* Error handling
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
if (process.env.SENTRY_DSN !== '') {
|
if (process.env.SENTRY_DSN !== '') {
|
||||||
Raven.config(process.env.SENTRY_DSN).install();
|
Raven.config(process.env.SENTRY_DSN).install();
|
||||||
}
|
}
|
||||||
|
@ -17,22 +17,22 @@ var Raven = require('raven-js');
|
||||||
* L10N
|
* L10N
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
/**
|
/*
|
||||||
* Bind locale code from cookie if available. Uses navigator language API as a fallback.
|
* Bind locale code from cookie if available. Uses navigator language API as a fallback.
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function updateLocale () {
|
const updateLocale = () => {
|
||||||
var obj = jar.get('scratchlanguage');
|
let obj = jar.get('scratchlanguage');
|
||||||
if (typeof obj === 'undefined') {
|
if (typeof obj === 'undefined') {
|
||||||
obj = window.navigator.userLanguage || window.navigator.language;
|
obj = window.navigator.userLanguage || window.navigator.language;
|
||||||
if (['pt','pt-pt','PT','PT-PT'].indexOf(obj) !== -1) {
|
if (['pt', 'pt-pt', 'PT', 'PT-PT'].indexOf(obj) !== -1) {
|
||||||
obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
|
obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
};
|
||||||
|
|
||||||
window._locale = updateLocale();
|
window._locale = updateLocale();
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
"general.myClass": "My Class",
|
"general.myClass": "My Class",
|
||||||
"general.myClasses": "My Classes",
|
"general.myClasses": "My Classes",
|
||||||
"general.myStuff": "My Stuff",
|
"general.myStuff": "My Stuff",
|
||||||
|
"general.noDeletionTitle": "Your Account Will Not Be Deleted",
|
||||||
|
"general.noDeletionDescription": "Your account was scheduled for deletion but you logged in. Your account has been reactivated. If you didn’t request for your account to be deleted, you should {resetLink} to make sure your account is secure.",
|
||||||
|
"general.noDeletionLink": "change your password",
|
||||||
"general.notRequired": "Not Required",
|
"general.notRequired": "Not Required",
|
||||||
"general.other": "Other",
|
"general.other": "Other",
|
||||||
"general.offlineEditor": "Offline Editor",
|
"general.offlineEditor": "Offline Editor",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var xhr = require('xhr');
|
const xhr = require('xhr');
|
||||||
|
|
||||||
var jar = require('./jar');
|
const jar = require('./jar');
|
||||||
var log = require('./log');
|
const log = require('./log');
|
||||||
var urlParams = require('./url-params');
|
const urlParams = require('./url-params');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method that constructs requests to the scratch api.
|
* Helper method that constructs requests to the scratch api.
|
||||||
|
@ -12,9 +12,11 @@ var urlParams = require('./url-params');
|
||||||
* CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
|
* CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
|
||||||
*
|
*
|
||||||
* It also takes in other arguments specified in the xhr library spec.
|
* It also takes in other arguments specified in the xhr library spec.
|
||||||
|
*
|
||||||
|
* @param {object} opts optional xhr args (see above)
|
||||||
|
* @param {Function} callback [description]
|
||||||
*/
|
*/
|
||||||
|
module.exports = (opts, callback) => {
|
||||||
module.exports = function (opts, callback) {
|
|
||||||
defaults(opts, {
|
defaults(opts, {
|
||||||
host: process.env.API_HOST,
|
host: process.env.API_HOST,
|
||||||
headers: {},
|
headers: {},
|
||||||
|
@ -40,26 +42,29 @@ module.exports = function (opts, callback) {
|
||||||
opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiRequest = function (opts) {
|
const apiRequest = options => {
|
||||||
if (opts.host !== '') {
|
if (options.host !== '') {
|
||||||
if ('withCredentials' in new XMLHttpRequest()) {
|
if ('withCredentials' in new XMLHttpRequest()) {
|
||||||
opts.useXDR = false;
|
options.useXDR = false;
|
||||||
} else {
|
} else {
|
||||||
// For IE < 10, we must use XDR for cross-domain requests. XDR does not support
|
// For IE < 10, we must use XDR for cross-domain requests. XDR does not support
|
||||||
// custom headers.
|
// custom headers.
|
||||||
opts.useXDR = true;
|
options.useXDR = true;
|
||||||
delete opts.headers;
|
delete options.headers;
|
||||||
if (opts.authentication) {
|
if (options.authentication) {
|
||||||
var authenticationParams = ['x-token=' + opts.authentication];
|
const authenticationParams = [`x-token=${options.authentication}`];
|
||||||
var parts = opts.uri.split('?');
|
const parts = options.uri.split('?');
|
||||||
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
|
const qs = (parts[1] || '')
|
||||||
opts.uri = parts[0] + '?' + qs;
|
.split('&')
|
||||||
|
.concat(authenticationParams)
|
||||||
|
.join('&');
|
||||||
|
options.uri = `${parts[0]}?${qs}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhr(opts, function (err, res, body) {
|
xhr(options, (err, res, body) => {
|
||||||
if (err) log.error(err);
|
if (err) log.error(err);
|
||||||
if (opts.responseType === 'json' && typeof body === 'string') {
|
if (options.responseType === 'json' && typeof body === 'string') {
|
||||||
// IE doesn't parse responses as JSON without the json attribute,
|
// IE doesn't parse responses as JSON without the json attribute,
|
||||||
// even with responseType: 'json'.
|
// even with responseType: 'json'.
|
||||||
// See https://github.com/Raynos/xhr/issues/123
|
// See https://github.com/Raynos/xhr/issues/123
|
||||||
|
@ -73,25 +78,25 @@ module.exports = function (opts, callback) {
|
||||||
// [{success: true, redirect: "/location/to/redirect"}]
|
// [{success: true, redirect: "/location/to/redirect"}]
|
||||||
try {
|
try {
|
||||||
if ('redirect' in body[0]) window.location = body[0].redirect;
|
if ('redirect' in body[0]) window.location = body[0].redirect;
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
callback(err, body, res);
|
callback(err, body, res);
|
||||||
});
|
});
|
||||||
}.bind(this);
|
};
|
||||||
|
|
||||||
if (typeof jar.get('scratchlanguage') !== 'undefined') {
|
if (typeof jar.get('scratchlanguage') !== 'undefined') {
|
||||||
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
|
opts.headers['Accept-Language'] = `${jar.get('scratchlanguage')}, en;q=0.8`;
|
||||||
}
|
}
|
||||||
if (opts.authentication) {
|
if (opts.authentication) {
|
||||||
opts.headers['X-Token'] = opts.authentication;
|
opts.headers['X-Token'] = opts.authentication;
|
||||||
}
|
}
|
||||||
if (opts.useCsrf) {
|
if (opts.useCsrf) {
|
||||||
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
|
jar.use('scratchcsrftoken', '/csrf_token/', (err, csrftoken) => {
|
||||||
if (err) return log.error('Error while retrieving CSRF token', err);
|
if (err) return log.error('Error while retrieving CSRF token', err);
|
||||||
opts.headers['X-CSRFToken'] = csrftoken;
|
opts.headers['X-CSRFToken'] = csrftoken;
|
||||||
apiRequest(opts);
|
apiRequest(opts);
|
||||||
}.bind(this));
|
});
|
||||||
} else {
|
} else {
|
||||||
apiRequest(opts);
|
apiRequest(opts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,28 @@
|
||||||
module.exports = {};
|
module.exports = {};
|
||||||
var countries = module.exports.data = require('iso-3166-2').data;
|
const countries = module.exports.data = require('iso-3166-2').data;
|
||||||
|
|
||||||
module.exports.countryOptions = Object.keys(countries).map(function (code) {
|
module.exports.countryOptions = Object.keys(countries).map(code => ({
|
||||||
return {value: code.toLowerCase(), label: countries[code].name};
|
value: code.toLowerCase(),
|
||||||
}).sort(function (a, b) {
|
label: countries[code].name
|
||||||
return a.label < b.label ? -1 : 1;
|
}))
|
||||||
});
|
.sort((a, b) => {
|
||||||
|
if (a.label < b.label) {
|
||||||
module.exports.subdivisionOptions =
|
return -1;
|
||||||
Object.keys(countries).reduce(function (subByCountry, code) {
|
}
|
||||||
subByCountry[code.toLowerCase()] = Object.keys(countries[code].sub).map(function (subCode) {
|
return 1;
|
||||||
return {
|
|
||||||
value: subCode.toLowerCase(),
|
|
||||||
label: countries[code].sub[subCode].name,
|
|
||||||
type: countries[code].sub[subCode].type
|
|
||||||
};
|
|
||||||
}).sort(function (a, b) {
|
|
||||||
return a.label < b.label ? -1 : 1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.subdivisionOptions = Object.keys(countries).reduce((subByCountry, code) => {
|
||||||
|
subByCountry[code.toLowerCase()] = Object.keys(countries[code].sub).map(subCode => ({
|
||||||
|
value: subCode.toLowerCase(),
|
||||||
|
label: countries[code].sub[subCode].name,
|
||||||
|
type: countries[code].sub[subCode].type
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.label < b.label) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
return subByCountry;
|
return subByCountry;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Takes an ISO time and returns a string representing how long ago the date
|
|
||||||
* represents. For example, "2015-01-01T00:00:00" becomes "1 minute ago".
|
|
||||||
*
|
|
||||||
* Based on "JavaScript Pretty Date"
|
|
||||||
* Copyright (c) 2011 John Resig (ejohn.org)
|
|
||||||
* Licensed under the MIT and GPL licenses.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Format = {
|
|
||||||
date: function (stamp) {
|
|
||||||
stamp = (stamp || '').replace(/-/g,'/').replace(/[TZ]/g,' ');
|
|
||||||
|
|
||||||
var date = new Date(stamp);
|
|
||||||
var diff = (((new Date()).getTime() - date.getTime()) / 1000);
|
|
||||||
var day_diff = Math.floor(diff / 86400);
|
|
||||||
|
|
||||||
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
|
|
||||||
return 'A while ago';
|
|
||||||
}
|
|
||||||
|
|
||||||
return day_diff == 0 && (
|
|
||||||
diff < 60 && 'Just now' ||
|
|
||||||
diff < 120 && '1 minute ago' ||
|
|
||||||
diff < 3600 && Math.floor( diff / 60 ) + ' minutes ago' ||
|
|
||||||
diff < 7200 && '1 hour ago' ||
|
|
||||||
diff < 86400 && Math.floor( diff / 3600 ) + ' hours ago') ||
|
|
||||||
day_diff == 1 && 'Yesterday' ||
|
|
||||||
day_diff < 7 && day_diff + ' days ago' ||
|
|
||||||
day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Format;
|
|
|
@ -1,9 +1,10 @@
|
||||||
/* This file contains breakpoints from _frameless.scss, to be used in MediaQuery elements.
|
/* This file contains breakpoints from _frameless.scss, to be used in MediaQuery elements.
|
||||||
* All units are in px, as per _frameless.scss and the MediaQuery default arguments.
|
* All units are in px, as per _frameless.scss and the MediaQuery default arguments.
|
||||||
*/
|
*/
|
||||||
|
const frameless = {
|
||||||
module.exports = {
|
|
||||||
desktop: 942,
|
desktop: 942,
|
||||||
tablet: 640,
|
tablet: 640,
|
||||||
mobile: 480
|
mobile: 480
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = frameless;
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
var requireAll = require('./require-all');
|
const requireAll = require('./require-all');
|
||||||
var ReactIntl = require('react-intl');
|
const ReactIntl = require('react-intl');
|
||||||
|
|
||||||
var allLocaleData = requireAll(require.context('react-intl/locale-data', true, /^\.\/.*\.js$/));
|
const allLocaleData = requireAll(require.context('react-intl/locale-data', true, /^\.\/.*\.js$/));
|
||||||
var customLocaleData = require('../../custom-locales.json');
|
const customLocaleData = require('../../custom-locales.json');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add all locales
|
* Add all locales
|
||||||
*/
|
*/
|
||||||
for (var locale in allLocaleData) {
|
for (const locale in allLocaleData) {
|
||||||
ReactIntl.addLocaleData(allLocaleData[locale]);
|
ReactIntl.addLocaleData(allLocaleData[locale]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add custom locales to react-intl if it doesn't have them.
|
* Add custom locales to react-intl if it doesn't have them.
|
||||||
*/
|
*/
|
||||||
for (var customLocale in customLocaleData) {
|
for (const customLocale in customLocaleData) {
|
||||||
ReactIntl.addLocaleData(customLocaleData[customLocale]);
|
ReactIntl.addLocaleData(customLocaleData[customLocale]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
var cookie = require('cookie');
|
const cookie = require('cookie');
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var xhr = require('xhr');
|
const xhr = require('xhr');
|
||||||
var pako = require('pako');
|
const pako = require('pako');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module that handles coookie interactions.
|
* Module that handles coookie interactions.
|
||||||
|
@ -11,16 +11,16 @@ var pako = require('pako');
|
||||||
* set(name, value) – synchronously sets the cookie
|
* set(name, value) – synchronously sets the cookie
|
||||||
* use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there.
|
* use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there.
|
||||||
*/
|
*/
|
||||||
var Jar = {
|
const Jar = {
|
||||||
unsign: function (value, callback) {
|
unsign: (value, callback) => {
|
||||||
// Return the usable content portion of a signed, compressed cookie generated by
|
// Return the usable content portion of a signed, compressed cookie generated by
|
||||||
// Django's signing module
|
// Django's signing module
|
||||||
// https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
|
// https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
|
||||||
if (typeof value === 'undefined') return callback(null, value);
|
if (typeof value === 'undefined') return callback(null, value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var b64Data = value.split(':')[0];
|
let b64Data = value.split(':')[0];
|
||||||
var decompress = false;
|
let decompress = false;
|
||||||
if (b64Data[0] === '.') {
|
if (b64Data[0] === '.') {
|
||||||
decompress = true;
|
decompress = true;
|
||||||
b64Data = b64Data.substring(1);
|
b64Data = b64Data.substring(1);
|
||||||
|
@ -29,24 +29,29 @@ var Jar = {
|
||||||
// Django makes its base64 strings url safe by replacing + and / with - and _ respectively
|
// Django makes its base64 strings url safe by replacing + and / with - and _ respectively
|
||||||
// using base64.urlsafe_b64encode
|
// using base64.urlsafe_b64encode
|
||||||
// https://docs.python.org/2/library/base64.html#base64.b64encode
|
// https://docs.python.org/2/library/base64.html#base64.b64encode
|
||||||
b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; });
|
b64Data = b64Data.replace(
|
||||||
var strData = atob(b64Data);
|
/[-_]/g,
|
||||||
|
c => ({
|
||||||
|
'-': '+',
|
||||||
|
'_': '/'
|
||||||
|
}[c])
|
||||||
|
);
|
||||||
|
let strData = atob(b64Data);
|
||||||
|
|
||||||
if (decompress) {
|
if (decompress) {
|
||||||
var charData = strData.split('').map(function (c) { return c.charCodeAt(0); });
|
const charData = strData.split('').map(c => (c.charCodeAt(0)));
|
||||||
var binData = new Uint8Array(charData);
|
const binData = new Uint8Array(charData);
|
||||||
var data = pako.inflate(binData);
|
const data = pako.inflate(binData);
|
||||||
strData = String.fromCharCode.apply(null, new Uint16Array(data));
|
strData = String.fromCharCode.apply(null, new Uint16Array(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, strData);
|
return callback(null, strData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return callback(e);
|
return callback(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get: function (name, callback) {
|
get: (name, callback) => {
|
||||||
// Get cookie by name
|
// Get cookie by name
|
||||||
var obj = cookie.parse(document.cookie) || {};
|
const obj = cookie.parse(document.cookie) || {};
|
||||||
|
|
||||||
// Handle optional callback
|
// Handle optional callback
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
|
@ -56,44 +61,44 @@ var Jar = {
|
||||||
|
|
||||||
return obj[name];
|
return obj[name];
|
||||||
},
|
},
|
||||||
use: function (name, uri, callback) {
|
use: (name, uri, callback) => {
|
||||||
// Attempt to get cookie
|
// Attempt to get cookie
|
||||||
Jar.get(name, function (err, obj) {
|
Jar.get(name, (err, obj) => {
|
||||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||||
|
|
||||||
// Make XHR request to cookie setter uri
|
// Make XHR request to cookie setter uri
|
||||||
xhr({
|
xhr({
|
||||||
uri: uri
|
uri: uri
|
||||||
}, function (err) {
|
}, e => {
|
||||||
if (err) return callback(err);
|
if (e) return callback(e);
|
||||||
Jar.get(name, callback);
|
Jar.get(name, callback);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
set: function (name, value, opts) {
|
set: (name, value, opts) => {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
defaults(opts, {
|
defaults(opts, {
|
||||||
expires: new Date(new Date().setYear(new Date().getFullYear() + 1))
|
expires: new Date(new Date().setYear(new Date().getFullYear() + 1))
|
||||||
});
|
});
|
||||||
opts.path = '/';
|
opts.path = '/';
|
||||||
var obj = cookie.serialize(name, value, opts);
|
const obj = cookie.serialize(name, value, opts);
|
||||||
document.cookie = obj;
|
document.cookie = obj;
|
||||||
},
|
},
|
||||||
getUnsignedValue: function (cookieName, signedValue, callback) {
|
getUnsignedValue: (cookieName, signedValue, callback) => {
|
||||||
// Get a value from a signed object
|
// Get a value from a signed object
|
||||||
Jar.get(cookieName, function (err, value) {
|
Jar.get(cookieName, (err, value) => {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
if (typeof value === 'undefined') return callback(null, value);
|
if (typeof value === 'undefined') return callback(null, value);
|
||||||
|
|
||||||
Jar.unsign(value, function (err, contents) {
|
Jar.unsign(value, (e, contents) => {
|
||||||
if (err) return callback(err);
|
if (e) return callback(e);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var data = JSON.parse(contents);
|
const data = JSON.parse(contents);
|
||||||
} catch (err) {
|
return callback(null, data[signedValue]);
|
||||||
return callback(err);
|
} catch (error) {
|
||||||
|
return callback(error);
|
||||||
}
|
}
|
||||||
return callback(null, data[signedValue]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var minilog = require('minilog');
|
const minilog = require('minilog');
|
||||||
minilog.enable();
|
minilog.enable();
|
||||||
|
|
||||||
module.exports = minilog('www');
|
module.exports = minilog('www');
|
||||||
|
|
|
@ -1,21 +1,27 @@
|
||||||
var redux = require('redux');
|
const redux = require('redux');
|
||||||
var thunk = require('redux-thunk').default;
|
const thunk = require('redux-thunk').default;
|
||||||
// JSX syntax transforms to React.createElement
|
// JSX syntax transforms to React.createElement
|
||||||
var React = require('react'); // eslint-disable-line
|
const React = require('react'); // eslint-disable-line
|
||||||
var ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
var StoreProvider = require('react-redux').Provider;
|
const StoreProvider = require('react-redux').Provider;
|
||||||
|
|
||||||
var IntlProvider = require('./intl.jsx').IntlProvider;
|
const IntlProvider = require('./intl.jsx').IntlProvider;
|
||||||
var permissionsActions = require('../redux/permissions.js');
|
const permissionsActions = require('../redux/permissions.js');
|
||||||
var sessionActions = require('../redux/session.js');
|
const sessionActions = require('../redux/session.js');
|
||||||
var reducer = require('../redux/reducer.js');
|
const reducer = require('../redux/reducer.js');
|
||||||
|
|
||||||
require('../main.scss');
|
require('../main.scss');
|
||||||
|
|
||||||
var render = function (jsx, element, reducers) {
|
/**
|
||||||
|
* Function to render views into a full page
|
||||||
|
* @param {object} jsx jsx component of the view
|
||||||
|
* @param {object} element html element to render to on the template
|
||||||
|
* @param {array} reducers list of view-specific reducers
|
||||||
|
*/
|
||||||
|
const render = (jsx, element, reducers) => {
|
||||||
// Get locale and messages from global namespace (see "init.js")
|
// Get locale and messages from global namespace (see "init.js")
|
||||||
var locale = window._locale || 'en';
|
let locale = window._locale || 'en';
|
||||||
var messages = {};
|
let messages = {};
|
||||||
if (typeof window._messages !== 'undefined') {
|
if (typeof window._messages !== 'undefined') {
|
||||||
if (typeof window._messages[locale] === 'undefined') {
|
if (typeof window._messages[locale] === 'undefined') {
|
||||||
// Fall back on the split
|
// Fall back on the split
|
||||||
|
@ -28,8 +34,8 @@ var render = function (jsx, element, reducers) {
|
||||||
messages = window._messages[locale];
|
messages = window._messages[locale];
|
||||||
}
|
}
|
||||||
|
|
||||||
var allReducers = reducer(reducers);
|
const allReducers = reducer(reducers);
|
||||||
var store = redux.createStore(
|
const store = redux.createStore(
|
||||||
allReducers,
|
allReducers,
|
||||||
redux.applyMiddleware(thunk)
|
redux.applyMiddleware(thunk)
|
||||||
);
|
);
|
||||||
|
@ -37,7 +43,10 @@ var render = function (jsx, element, reducers) {
|
||||||
// Render view component
|
// Render view component
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<StoreProvider store={store}>
|
<StoreProvider store={store}>
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
<IntlProvider
|
||||||
|
locale={locale}
|
||||||
|
messages={messages}
|
||||||
|
>
|
||||||
{jsx}
|
{jsx}
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</StoreProvider>,
|
</StoreProvider>,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
var requireAll = function (requireContext) {
|
const requireAll = requireContext => (
|
||||||
return requireContext.keys().map(requireContext);
|
requireContext.keys().map(requireContext)
|
||||||
};
|
);
|
||||||
|
|
||||||
module.exports = requireAll;
|
module.exports = requireAll;
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
/*
|
/*
|
||||||
* Function that shuffles an array using a Fisher-Yates shuffle.
|
* Function that shuffles an array using a Fisher-Yates shuffle.
|
||||||
*/
|
*/
|
||||||
|
module.exports.shuffle = arr => {
|
||||||
module.exports.shuffle = function (arr) {
|
let i = 0;
|
||||||
var i, j = 0;
|
let j = 0;
|
||||||
var temp = null;
|
let temp = null;
|
||||||
if (arr) {
|
if (arr) {
|
||||||
var tempArray = arr.slice(0);
|
const tempArray = arr.slice(0);
|
||||||
} else {
|
for (i = arr.length - 1; i > 0; i -= 1) {
|
||||||
return arr;
|
j = Math.floor(Math.random() * (i + 1));
|
||||||
|
temp = tempArray[i];
|
||||||
|
tempArray[i] = tempArray[j];
|
||||||
|
tempArray[j] = temp;
|
||||||
|
}
|
||||||
|
return tempArray;
|
||||||
}
|
}
|
||||||
|
return arr;
|
||||||
for (i = arr.length - 1; i > 0; i -= 1) {
|
|
||||||
j = Math.floor(Math.random() * (i + 1));
|
|
||||||
temp = tempArray[i];
|
|
||||||
tempArray[i] = tempArray[j];
|
|
||||||
tempArray[j] = temp;
|
|
||||||
}
|
|
||||||
return tempArray;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,21 +2,18 @@
|
||||||
* urlParams({a: 1, b: 2, c: 3})
|
* urlParams({a: 1, b: 2, c: 3})
|
||||||
* // a=1&b=2&c=3
|
* // a=1&b=2&c=3
|
||||||
*/
|
*/
|
||||||
module.exports = function urlParams (values) {
|
const params = values => (
|
||||||
return Object
|
Object.keys(values).map(key => {
|
||||||
.keys(values)
|
const value = typeof values[key] === 'undefined' ? '' : values[key];
|
||||||
.map(function (key) {
|
const encodeKeyValuePair = val => (
|
||||||
var value = typeof values[key] === 'undefined' ? '' : values[key];
|
[key, val].map(encodeURIComponent).join('=')
|
||||||
function encodeKeyValuePair (value) {
|
);
|
||||||
return [key, value]
|
if (Array.isArray(value)) {
|
||||||
.map(encodeURIComponent)
|
return value.map(encodeKeyValuePair).join('&');
|
||||||
.join('=');
|
}
|
||||||
}
|
return encodeKeyValuePair(value);
|
||||||
if (Array.isArray(value)) {
|
})
|
||||||
return value.map(encodeKeyValuePair).join('&');
|
.join('&')
|
||||||
} else {
|
);
|
||||||
return encodeKeyValuePair(value);
|
|
||||||
}
|
module.exports = params;
|
||||||
})
|
|
||||||
.join('&');
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
var api = require('../lib/api');
|
const api = require('../lib/api');
|
||||||
|
|
||||||
var Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_DETAILS: null,
|
SET_DETAILS: null,
|
||||||
SET_DETAILS_FETCHING: null,
|
SET_DETAILS_FETCHING: null,
|
||||||
SET_DETAILS_ERROR: null
|
SET_DETAILS_ERROR: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.detailsReducer = function (state, action) {
|
module.exports.detailsReducer = (state, action) => {
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = {};
|
state = {};
|
||||||
}
|
}
|
||||||
|
@ -23,61 +23,50 @@ module.exports.detailsReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setDetailsError = function (error) {
|
module.exports.setDetailsError = error => ({
|
||||||
return {
|
type: Types.SET_DETAILS_ERROR,
|
||||||
type: Types.SET_DETAILS_ERROR,
|
error: error
|
||||||
error: error
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setDetails = function (details) {
|
module.exports.setDetails = details => ({
|
||||||
return {
|
type: Types.SET_DETAILS,
|
||||||
type: Types.SET_DETAILS,
|
details: details
|
||||||
details: details
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setDetailsFetching = function () {
|
module.exports.setDetailsFetching = () => ({
|
||||||
return {
|
type: Types.SET_DETAILS_FETCHING,
|
||||||
type: Types.SET_DETAILS_FETCHING,
|
fetching: true
|
||||||
fetching: true
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.startGetDetails = function (id) {
|
module.exports.getDetails = id => (dispatch => {
|
||||||
return function (dispatch) {
|
api({
|
||||||
dispatch(module.exports.setDetailsFetching());
|
uri: `/conference/${id}/details`
|
||||||
dispatch(module.exports.getDetails(id));
|
}, (err, body) => {
|
||||||
};
|
if (err) {
|
||||||
};
|
dispatch(module.exports.setDetailsError(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.getDetails = function (id) {
|
if (typeof body !== 'undefined') {
|
||||||
return function (dispatch) {
|
const columns = body.columns;
|
||||||
api({
|
if (body.rows) {
|
||||||
uri: '/conference/' + id + '/details'
|
const details = body.rows[0];
|
||||||
}, function (err, body) {
|
const detailsObject = details.reduce((prev, cur, index) => {
|
||||||
if (err) {
|
prev[columns[index]] = cur;
|
||||||
dispatch(module.exports.setDetailsError(err));
|
return prev;
|
||||||
return;
|
}, {});
|
||||||
}
|
dispatch(module.exports.setDetails(detailsObject));
|
||||||
|
|
||||||
if (typeof body !== 'undefined') {
|
|
||||||
var columns = body.columns;
|
|
||||||
if (body.rows) {
|
|
||||||
var details = body.rows[0];
|
|
||||||
var detailsObject = details.reduce(function (prev, cur, index) {
|
|
||||||
prev[columns[index]] = cur;
|
|
||||||
return prev;
|
|
||||||
}, {});
|
|
||||||
dispatch(module.exports.setDetails(detailsObject));
|
|
||||||
} else {
|
|
||||||
dispatch(module.exports.setDetailsError('Not Found'));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(module.exports.setDetailsError('An unexpected error occurred'));
|
dispatch(module.exports.setDetailsError('Not Found'));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
return;
|
||||||
};
|
}
|
||||||
};
|
dispatch(module.exports.setDetailsError('An unexpected error occurred'));
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.startGetDetails = id => (dispatch => {
|
||||||
|
dispatch(module.exports.setDetailsFetching());
|
||||||
|
dispatch(module.exports.getDetails(id));
|
||||||
|
});
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
var api = require('../lib/api');
|
const api = require('../lib/api');
|
||||||
|
|
||||||
var Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_SCHEDULE: null,
|
SET_SCHEDULE: null,
|
||||||
SET_SCHEDULE_FETCHING: null,
|
SET_SCHEDULE_FETCHING: null,
|
||||||
SET_SCHEDULE_ERROR: null
|
SET_SCHEDULE_ERROR: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.scheduleReducer = function (state, action) {
|
module.exports.scheduleReducer = (state, action) => {
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = {
|
state = {
|
||||||
timeSlots: [],
|
timeSlots: [],
|
||||||
|
@ -26,40 +26,27 @@ module.exports.scheduleReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setSchedule = function (schedule) {
|
module.exports.setSchedule = schedule => ({
|
||||||
return {
|
type: Types.SET_SCHEDULE,
|
||||||
type: Types.SET_SCHEDULE,
|
schedule: schedule
|
||||||
schedule: schedule
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setScheduleFetching = function () {
|
module.exports.setScheduleFetching = () => ({
|
||||||
return {
|
type: Types.SET_SCHEDULE_FETCHING,
|
||||||
type: Types.SET_SCHEDULE_FETCHING,
|
fetching: true
|
||||||
fetching: true
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setScheduleError = function (error) {
|
module.exports.setScheduleError = error => ({
|
||||||
return {
|
type: Types.SET_SCHEDULE_ERROR,
|
||||||
type: Types.SET_SCHEDULE_ERROR,
|
error: error
|
||||||
error: error
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.startGetSchedule = function (day) {
|
|
||||||
return function (dispatch) {
|
|
||||||
dispatch(module.exports.setScheduleFetching());
|
|
||||||
dispatch(module.exports.getDaySchedule(day));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// group periods of time by start time
|
// group periods of time by start time
|
||||||
module.exports.sortTimeSlots = function (timeSlot1, timeSlot2) {
|
module.exports.sortTimeSlots = (timeSlot1, timeSlot2) => {
|
||||||
var timeSlot1Am = (timeSlot1.time.substr(timeSlot1.time.length - 1, timeSlot1.time.length) === 'a') ? true : false;
|
const timeSlot1Am = (timeSlot1.time.substr(timeSlot1.time.length - 1, timeSlot1.time.length) === 'a');
|
||||||
var timeSlot2Am = (timeSlot2.time.substr(timeSlot2.time.length - 1, timeSlot2.time.length) === 'a') ? true : false;
|
const timeSlot2Am = (timeSlot2.time.substr(timeSlot2.time.length - 1, timeSlot2.time.length) === 'a');
|
||||||
var timeSlot1Time = parseInt(timeSlot1.time.substr(0, timeSlot1.time.length - 1));
|
let timeSlot1Time = parseInt(timeSlot1.time.substr(0, timeSlot1.time.length - 1), 10);
|
||||||
var timeSlot2Time = parseInt(timeSlot2.time.substr(0, timeSlot2.time.length - 1));
|
let timeSlot2Time = parseInt(timeSlot2.time.substr(0, timeSlot2.time.length - 1), 10);
|
||||||
|
|
||||||
// convert to 24-hour for sorting
|
// convert to 24-hour for sorting
|
||||||
if (timeSlot1Time !== 12 && !timeSlot1Am) {
|
if (timeSlot1Time !== 12 && !timeSlot1Am) {
|
||||||
|
@ -71,68 +58,67 @@ module.exports.sortTimeSlots = function (timeSlot1, timeSlot2) {
|
||||||
|
|
||||||
if (timeSlot1Time < timeSlot2Time) {
|
if (timeSlot1Time < timeSlot2Time) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the schedule for the given day from the api
|
* Gets the schedule for the given day from the api
|
||||||
* @param {String} day Day of the conference (Thursday, Friday or Satrurday)
|
* @param {string} day Day of the conference (Thursday, Friday or Satrurday)
|
||||||
*
|
*
|
||||||
* @return {Object} Schedule for the day, broken into chunks
|
* @return {object} Schedule for the day, broken into chunks
|
||||||
*/
|
*/
|
||||||
module.exports.getDaySchedule = function (day) {
|
module.exports.getDaySchedule = day => (dispatch => {
|
||||||
return function (dispatch) {
|
api({
|
||||||
api({
|
uri: `/conference/schedule/${day}`
|
||||||
uri: '/conference/schedule/' + day
|
}, (err, body) => {
|
||||||
}, function (err, body) {
|
if (err) {
|
||||||
if (err) {
|
dispatch(module.exports.setScheduleError(err));
|
||||||
dispatch(module.exports.setScheduleError(err));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof body !== 'undefined') {
|
if (typeof body !== 'undefined') {
|
||||||
var columns = body.columns;
|
const columns = body.columns;
|
||||||
var rows = body.rows || [];
|
const rows = body.rows || [];
|
||||||
// Group events by the time period in which they occur (for presentation)
|
// Group events by the time period in which they occur (for presentation)
|
||||||
var scheduleByTimeSlot = rows.reduce(function (prev, cur) {
|
const scheduleByTimeSlot = rows.reduce((prev, cur) => {
|
||||||
var cleanedRow = {};
|
const cleanedRow = {};
|
||||||
for (var i = 0; i < columns.length; i++) {
|
for (let i = 0; i < columns.length; i++) {
|
||||||
if (cur[i].length > 0) {
|
if (cur[i].length > 0) {
|
||||||
cleanedRow[columns[i]] = cur[i];
|
cleanedRow[columns[i]] = cur[i];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cleanedRow['uri'] = '/conference/2016/' + cleanedRow.rowid + '/details';
|
}
|
||||||
var timeSlot = cleanedRow.Chunk + cleanedRow.Start;
|
cleanedRow.uri = `/conference/2016/${cleanedRow.rowid}/details`;
|
||||||
if (typeof prev.timeSlots[timeSlot] === 'undefined') {
|
const timeSlot = cleanedRow.Chunk + cleanedRow.Start;
|
||||||
prev.timeSlots[timeSlot] = [cleanedRow];
|
if (typeof prev.timeSlots[timeSlot] === 'undefined') {
|
||||||
prev.info.push({
|
prev.timeSlots[timeSlot] = [cleanedRow];
|
||||||
name: cleanedRow.Chunk,
|
prev.info.push({
|
||||||
time: cleanedRow.Start
|
name: cleanedRow.Chunk,
|
||||||
});
|
time: cleanedRow.Start
|
||||||
} else {
|
});
|
||||||
prev.timeSlots[timeSlot].push(cleanedRow);
|
} else {
|
||||||
}
|
prev.timeSlots[timeSlot].push(cleanedRow);
|
||||||
return prev;
|
}
|
||||||
}, {timeSlots: [], info: []});
|
return prev;
|
||||||
|
}, {timeSlots: [], info: []});
|
||||||
|
|
||||||
scheduleByTimeSlot.info.sort(module.exports.sortTimeSlots);
|
scheduleByTimeSlot.info.sort(module.exports.sortTimeSlots);
|
||||||
var schedule = scheduleByTimeSlot.info.map(function (timeSlot) {
|
const schedule = scheduleByTimeSlot.info.map(timeSlot => ({
|
||||||
return {
|
info: timeSlot,
|
||||||
info: timeSlot,
|
items: scheduleByTimeSlot.timeSlots[timeSlot.name + timeSlot.time]
|
||||||
items: scheduleByTimeSlot.timeSlots[timeSlot.name + timeSlot.time]
|
}));
|
||||||
};
|
dispatch(module.exports.setSchedule({
|
||||||
});
|
timeSlots: schedule,
|
||||||
dispatch(module.exports.setSchedule({
|
day: day
|
||||||
timeSlots: schedule,
|
}));
|
||||||
day: day
|
return;
|
||||||
}));
|
}
|
||||||
return;
|
dispatch(module.exports.setScheduleError('An unexpected error occurred'));
|
||||||
} else {
|
return;
|
||||||
dispatch(module.exports.setScheduleError('An unexpected error occurred'));
|
});
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
});
|
module.exports.startGetSchedule = day => (dispatch => {
|
||||||
};
|
dispatch(module.exports.setScheduleFetching());
|
||||||
};
|
dispatch(module.exports.getDaySchedule(day));
|
||||||
|
});
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
|
|
||||||
var api = require('../lib/api');
|
const api = require('../lib/api');
|
||||||
|
|
||||||
var Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_MESSAGE_COUNT: null,
|
SET_MESSAGE_COUNT: null,
|
||||||
SET_MESSAGE_COUNT_ERROR: null,
|
SET_MESSAGE_COUNT_ERROR: null,
|
||||||
SET_STATUS: null
|
SET_STATUS: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.getInitialState = function (){
|
const getInitialState = () => ({
|
||||||
return {messageCount: 0};
|
messageCount: 0
|
||||||
};
|
});
|
||||||
|
|
||||||
module.exports.messageCountReducer = function (state, action) {
|
module.exports.messageCountReducer = (state, action) => {
|
||||||
// Reducer for handling changes to session state
|
// Reducer for handling changes to session state
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = module.exports.getInitialState();
|
state = getInitialState();
|
||||||
}
|
}
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case Types.SET_MESSAGE_COUNT:
|
case Types.SET_MESSAGE_COUNT:
|
||||||
|
@ -31,40 +31,32 @@ module.exports.messageCountReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setSessionError = function (error) {
|
module.exports.setSessionError = error => ({
|
||||||
return {
|
type: Types.SET_MESSAGE_COUNT_ERROR,
|
||||||
type: Types.SET_MESSAGE_COUNT_ERROR,
|
error: error
|
||||||
error: error
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setCount = function (count) {
|
module.exports.setCount = count => ({
|
||||||
return {
|
type: Types.SET_MESSAGE_COUNT,
|
||||||
type: Types.SET_MESSAGE_COUNT,
|
count: count
|
||||||
count: count
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setStatus = function (status){
|
module.exports.setStatus = status => ({
|
||||||
return {
|
type: Types.SET_STATUS,
|
||||||
type: Types.SET_STATUS,
|
status: status
|
||||||
status: status
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.getCount = function (username) {
|
module.exports.getCount = username => (dispatch => {
|
||||||
return function (dispatch) {
|
api({
|
||||||
api({
|
method: 'get',
|
||||||
method: 'get',
|
uri: `/users/${username}/messages/count`
|
||||||
uri: '/users/' + username + '/messages/count'
|
}, (err, body) => {
|
||||||
}, function (err, body) {
|
if (err) {
|
||||||
if (err) {
|
dispatch(module.exports.setCount(0));
|
||||||
dispatch(module.exports.setCount(0));
|
dispatch(module.exports.setSessionError(err));
|
||||||
dispatch(module.exports.setSessionError(err));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
const count = parseInt(body.count, 10);
|
||||||
var count = parseInt(body.count, 10);
|
dispatch(module.exports.setCount(count));
|
||||||
dispatch(module.exports.setCount(count));
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
var defaultsDeep = require('lodash.defaultsdeep');
|
const defaultsDeep = require('lodash.defaultsdeep');
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
|
|
||||||
var api = require('../lib/api');
|
const api = require('../lib/api');
|
||||||
var log = require('../lib/log');
|
const log = require('../lib/log');
|
||||||
var messageCountActions = require('./message-count.js');
|
const messageCountActions = require('./message-count.js');
|
||||||
|
|
||||||
module.exports.Status = keyMirror({
|
module.exports.Status = keyMirror({
|
||||||
FETCHED: null,
|
FETCHED: null,
|
||||||
|
@ -17,23 +17,21 @@ module.exports.Status = keyMirror({
|
||||||
DELETE_ERROR: null
|
DELETE_ERROR: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.getInitialState = function () {
|
module.exports.getInitialState = () => ({
|
||||||
return {
|
status: {
|
||||||
status: {
|
admin: module.exports.Status.NOT_FETCHED,
|
||||||
admin: module.exports.Status.NOT_FETCHED,
|
message: module.exports.Status.NOT_FETCHED,
|
||||||
message: module.exports.Status.NOT_FETCHED,
|
clear: module.exports.Status.NOT_FETCHED,
|
||||||
clear: module.exports.Status.NOT_FETCHED,
|
delete: module.exports.Status.NOT_FETCHED
|
||||||
delete: module.exports.Status.NOT_FETCHED
|
},
|
||||||
},
|
messages: {
|
||||||
messages: {
|
admin: [],
|
||||||
admin: [],
|
social: [],
|
||||||
social: [],
|
invite: {}
|
||||||
invite: {}
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.messagesReducer = function (state, action) {
|
module.exports.messagesReducer = (state, action) => {
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = module.exports.getInitialState();
|
state = module.exports.getInitialState();
|
||||||
}
|
}
|
||||||
|
@ -68,75 +66,61 @@ module.exports.messagesReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setMessagesError = function (error) {
|
module.exports.setMessagesError = error => ({
|
||||||
return {
|
type: 'ERROR',
|
||||||
type: 'ERROR',
|
error: error
|
||||||
error: error
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setMessages = function (messages) {
|
module.exports.setMessages = messages => ({
|
||||||
return {
|
type: 'SET_MESSAGES',
|
||||||
type: 'SET_MESSAGES',
|
messages: messages
|
||||||
messages: messages
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setMessagesOffset = function (offset) {
|
module.exports.setMessagesOffset = offset => ({
|
||||||
return {
|
type: 'SET_MESSAGES_OFFSET',
|
||||||
type: 'SET_MESSAGES_OFFSET',
|
offset: offset
|
||||||
offset: offset
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setAdminMessages = function (messages) {
|
module.exports.setAdminMessages = messages => ({
|
||||||
return {
|
type: 'SET_ADMIN_MESSAGES',
|
||||||
type: 'SET_ADMIN_MESSAGES',
|
messages: messages
|
||||||
messages: messages
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setScratcherInvite = function (invite) {
|
module.exports.setScratcherInvite = invite => ({
|
||||||
return {
|
type: 'SET_SCRATCHER_INVITE',
|
||||||
type: 'SET_SCRATCHER_INVITE',
|
invite: invite
|
||||||
invite: invite
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setStatus = function (type, status){
|
module.exports.setStatus = (type, status) => ({
|
||||||
return {
|
type: type,
|
||||||
type: type,
|
status: status
|
||||||
status: status
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a request to mark one's unread messages count as cleared.
|
* Sends a request to mark one's unread messages count as cleared.
|
||||||
* @return {null} returns nothing
|
* @return {null} returns nothing
|
||||||
*/
|
*/
|
||||||
module.exports.clearMessageCount = function () {
|
module.exports.clearMessageCount = () => (dispatch => {
|
||||||
return function (dispatch) {
|
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
|
||||||
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
|
api({
|
||||||
api({
|
host: '',
|
||||||
host: '',
|
uri: '/site-api/messages/messages-clear/',
|
||||||
uri: '/site-api/messages/messages-clear/',
|
method: 'POST',
|
||||||
method: 'POST',
|
useCsrf: true
|
||||||
useCsrf: true
|
}, (err, body) => {
|
||||||
}, function (err, body) {
|
if (err) {
|
||||||
if (err) {
|
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
|
||||||
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
|
dispatch(module.exports.setMessagesError(err));
|
||||||
dispatch(module.exports.setMessagesError(err));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (typeof body !== 'undefined' && !body.success) {
|
||||||
if (typeof body !== 'undefined' && !body.success) {
|
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
|
||||||
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
|
dispatch(module.exports.setMessagesError('messages not cleared'));
|
||||||
dispatch(module.exports.setMessagesError('messages not cleared'));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHED));
|
||||||
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHED));
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks an admin message as read, dismissing it from the page
|
* Marks an admin message as read, dismissing it from the page
|
||||||
|
@ -146,8 +130,8 @@ module.exports.clearMessageCount = function () {
|
||||||
* @param {object[]} adminMessages current list of admin messages retrieved
|
* @param {object[]} adminMessages current list of admin messages retrieved
|
||||||
* @return {null} returns nothing
|
* @return {null} returns nothing
|
||||||
*/
|
*/
|
||||||
module.exports.clearAdminMessage = function (messageType, messageId, messageCount, adminMessages) {
|
module.exports.clearAdminMessage = (messageType, messageId, messageCount, adminMessages) => (
|
||||||
return function (dispatch) {
|
dispatch => {
|
||||||
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
|
dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
|
||||||
api({
|
api({
|
||||||
host: '',
|
host: '',
|
||||||
|
@ -158,7 +142,7 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
|
||||||
alertType: messageType,
|
alertType: messageType,
|
||||||
alertId: messageId
|
alertId: messageId
|
||||||
}
|
}
|
||||||
}, function (err, body) {
|
}, (err, body) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.DELETE_ERROR));
|
dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.DELETE_ERROR));
|
||||||
dispatch(module.exports.setMessagesError(err));
|
dispatch(module.exports.setMessagesError(err));
|
||||||
|
@ -175,9 +159,9 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
|
||||||
dispatch(module.exports.setScratcherInvite({}));
|
dispatch(module.exports.setScratcherInvite({}));
|
||||||
} else {
|
} else {
|
||||||
// find the admin message and remove it
|
// find the admin message and remove it
|
||||||
var toRemove = -1;
|
let toRemove = -1;
|
||||||
for (var i in adminMessages) {
|
for (const i of adminMessages) {
|
||||||
if (adminMessages[i].id === messageId) {
|
if (i.id === messageId) {
|
||||||
toRemove = i;
|
toRemove = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -188,8 +172,8 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
|
||||||
dispatch(messageCountActions.setCount(messageCount - 1));
|
dispatch(messageCountActions.setCount(messageCount - 1));
|
||||||
dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.FETCHED));
|
dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.FETCHED));
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
};
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a user's messages to be displayed on the /messages page
|
* Gets a user's messages to be displayed on the /messages page
|
||||||
|
@ -201,7 +185,7 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
|
||||||
* @param {string} [opts.filter] type of messages to return
|
* @param {string} [opts.filter] type of messages to return
|
||||||
* @return {null} returns nothing
|
* @return {null} returns nothing
|
||||||
*/
|
*/
|
||||||
module.exports.getMessages = function (username, token, opts) {
|
module.exports.getMessages = (username, token, opts) => {
|
||||||
opts = defaults(opts, {
|
opts = defaults(opts, {
|
||||||
messages: [],
|
messages: [],
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
@ -209,17 +193,17 @@ module.exports.getMessages = function (username, token, opts) {
|
||||||
clearCount: true
|
clearCount: true
|
||||||
});
|
});
|
||||||
|
|
||||||
var filterArg = '';
|
let filterArg = '';
|
||||||
if (opts.filter.length > 0) {
|
if (opts.filter.length > 0) {
|
||||||
filterArg = '&filter=' + opts.filter;
|
filterArg = `&filter=${opts.filter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (dispatch) {
|
return dispatch => {
|
||||||
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHING));
|
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHING));
|
||||||
api({
|
api({
|
||||||
uri: '/users/' + username + '/messages?limit=40&offset=' + opts.offset + filterArg,
|
uri: `/users/${username}/messages?limit=40&offset=${opts.offset}${filterArg}`,
|
||||||
authentication: token
|
authentication: token
|
||||||
}, function (err, body) {
|
}, (err, body) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.MESSAGES_ERROR));
|
dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.MESSAGES_ERROR));
|
||||||
dispatch(module.exports.setMessagesError(err));
|
dispatch(module.exports.setMessagesError(err));
|
||||||
|
@ -246,30 +230,28 @@ module.exports.getMessages = function (username, token, opts) {
|
||||||
* @param {string} token the user's unique token for auth
|
* @param {string} token the user's unique token for auth
|
||||||
* @return {null} returns nothing
|
* @return {null} returns nothing
|
||||||
*/
|
*/
|
||||||
module.exports.getAdminMessages = function (username, token) {
|
module.exports.getAdminMessages = (username, token) => (dispatch => {
|
||||||
return function (dispatch) {
|
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHING));
|
||||||
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHING));
|
api({
|
||||||
api({
|
uri: `/users/${username}/messages/admin`,
|
||||||
uri: '/users/' + username + '/messages/admin',
|
authentication: token
|
||||||
authentication: token
|
}, (err, body) => {
|
||||||
}, function (err, body) {
|
if (err) {
|
||||||
if (err) {
|
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
|
||||||
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
|
dispatch(module.exports.setMessagesError(err));
|
||||||
dispatch(module.exports.setMessagesError(err));
|
dispatch(module.exports.setAdminMessages([]));
|
||||||
dispatch(module.exports.setAdminMessages([]));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (typeof body === 'undefined') {
|
||||||
if (typeof body === 'undefined') {
|
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
|
||||||
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
|
dispatch(module.exports.setMessagesError('No session content'));
|
||||||
dispatch(module.exports.setMessagesError('No session content'));
|
dispatch(module.exports.setAdminMessages([]));
|
||||||
dispatch(module.exports.setAdminMessages([]));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
dispatch(module.exports.setAdminMessages(body));
|
||||||
dispatch(module.exports.setAdminMessages(body));
|
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHED));
|
||||||
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHED));
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the invitation to become a Scratcher for a user, if one exists
|
* Gets the invitation to become a Scratcher for a user, if one exists
|
||||||
|
@ -277,20 +259,18 @@ module.exports.getAdminMessages = function (username, token) {
|
||||||
* @param {string} token the user's unique token for auth
|
* @param {string} token the user's unique token for auth
|
||||||
* @return {null} returns nothing
|
* @return {null} returns nothing
|
||||||
*/
|
*/
|
||||||
module.exports.getScratcherInvite = function (username, token) {
|
module.exports.getScratcherInvite = (username, token) => (dispatch => {
|
||||||
return function (dispatch) {
|
api({
|
||||||
api({
|
uri: `/users/${username}/invites`,
|
||||||
uri: '/users/' + username + '/invites',
|
authentication: token
|
||||||
authentication: token
|
}, (err, body) => {
|
||||||
}, function (err, body) {
|
if (err) {
|
||||||
if (err) {
|
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.INVITE_ERROR));
|
||||||
dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.INVITE_ERROR));
|
dispatch(module.exports.setMessagesError(err));
|
||||||
dispatch(module.exports.setMessagesError(err));
|
dispatch(module.exports.setScratcherInvite({}));
|
||||||
dispatch(module.exports.setScratcherInvite({}));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (typeof body === 'undefined') return dispatch(module.exports.setMessagesError('No session content'));
|
||||||
if (typeof body === 'undefined') return dispatch(module.exports.setMessagesError('No session content'));
|
dispatch(module.exports.setScratcherInvite(body));
|
||||||
dispatch(module.exports.setScratcherInvite(body));
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
|
|
||||||
var Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_SEARCH_TERM: null
|
SET_SEARCH_TERM: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.navigationReducer = function (state, action) {
|
module.exports.navigationReducer = (state, action) => {
|
||||||
if(typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = '';
|
state = '';
|
||||||
}
|
}
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -16,9 +16,7 @@ module.exports.navigationReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setSearchTerm = function (searchTerm) {
|
module.exports.setSearchTerm = searchTerm => ({
|
||||||
return {
|
type: Types.SET_SEARCH_TERM,
|
||||||
type: Types.SET_SEARCH_TERM,
|
searchTerm: searchTerm
|
||||||
searchTerm: searchTerm
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
var keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
var jar = require('../lib/jar.js');
|
const jar = require('../lib/jar.js');
|
||||||
|
|
||||||
var Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_PERMISSIONS: null,
|
SET_PERMISSIONS: null,
|
||||||
SET_PERMISSIONS_ERROR: null
|
SET_PERMISSIONS_ERROR: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.permissionsReducer = function (state, action) {
|
module.exports.permissionsReducer = (state, action) => {
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = '';
|
state = {};
|
||||||
}
|
}
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case Types.SET_PERMISSIONS:
|
case Types.SET_PERMISSIONS:
|
||||||
|
@ -20,43 +20,37 @@ module.exports.permissionsReducer = function (state, action) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.storePermissions = function (permissions) {
|
module.exports.storePermissions = permissions => {
|
||||||
permissions = permissions || {};
|
permissions = permissions || {};
|
||||||
return function (dispatch) {
|
return dispatch => {
|
||||||
jar.set('permissions', permissions, {
|
jar.set('permissions', permissions, {
|
||||||
encode: function (value) {
|
encode: value => (
|
||||||
return encodeURIComponent(JSON.stringify(value));
|
encodeURIComponent(JSON.stringify(value))
|
||||||
}
|
)
|
||||||
});
|
});
|
||||||
return dispatch(module.exports.setPermissions(permissions));
|
return dispatch(module.exports.setPermissions(permissions));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.getPermissions = function () {
|
module.exports.getPermissions = () => (dispatch => {
|
||||||
return function (dispatch) {
|
jar.get('permissions', (err, value) => {
|
||||||
jar.get('permissions', function (err, value) {
|
if (err) return dispatch(module.exports.setPermissionsError(err));
|
||||||
if (err) return dispatch(module.exports.setPermissionsError(err));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
value = JSON.parse(decodeURIComponent(value)) || {};
|
value = JSON.parse(decodeURIComponent(value)) || {};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
value = {};
|
value = {};
|
||||||
}
|
}
|
||||||
return dispatch(module.exports.setPermissions(value));
|
return dispatch(module.exports.setPermissions(value));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setPermissions = function (permissions) {
|
module.exports.setPermissions = permissions => ({
|
||||||
return {
|
type: Types.SET_PERMISSIONS,
|
||||||
type: Types.SET_PERMISSIONS,
|
permissions: permissions
|
||||||
permissions: permissions
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setPermissionsError = function (error) {
|
module.exports.setPermissionsError = error => ({
|
||||||
return {
|
type: Types.SET_PERMISSIONS_ERROR,
|
||||||
type: Types.SET_PERMISSIONS_ERROR,
|
error: error
|
||||||
error: error
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
var combineReducers = require('redux').combineReducers;
|
const combineReducers = require('redux').combineReducers;
|
||||||
var defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
|
|
||||||
var messageCountReducer = require('./message-count.js').messageCountReducer;
|
const messageCountReducer = require('./message-count.js').messageCountReducer;
|
||||||
var permissionsReducer = require('./permissions.js').permissionsReducer;
|
const permissionsReducer = require('./permissions.js').permissionsReducer;
|
||||||
var sessionReducer = require('./session.js').sessionReducer;
|
const sessionReducer = require('./session.js').sessionReducer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a combined reducer to be used for a page in `render.jsx`.
|
* Returns a combined reducer to be used for a page in `render.jsx`.
|
||||||
|
@ -11,11 +11,11 @@ var sessionReducer = require('./session.js').sessionReducer;
|
||||||
* - and any reducers specific to the page should be passed into
|
* - and any reducers specific to the page should be passed into
|
||||||
* `render()` as an object (which will then be passed to the function
|
* `render()` as an object (which will then be passed to the function
|
||||||
* below).
|
* below).
|
||||||
* @param {Object} opts key/value where the key is the name of the
|
* @param {object} opts key/value where the key is the name of the
|
||||||
* redux state, value is the reducer function.
|
* redux state, value is the reducer function.
|
||||||
* @return {Object} combined reducer to be used in the redux store
|
* @return {object} combined reducer to be used in the redux store
|
||||||
*/
|
*/
|
||||||
module.exports = function (opts) {
|
module.exports = opts => {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
return combineReducers(defaults(opts, {
|
return combineReducers(defaults(opts, {
|
||||||
session: sessionReducer,
|
session: sessionReducer,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue