Unverified Commit 75ee8d76 authored by Ray Schamp's avatar Ray Schamp Committed by GitHub

Merge pull request #1871 from LLK/release/april-2018

[Master] April 2018 Release
parents 4e9e791e 1cfc5a81
......@@ -138,4 +138,10 @@ type = KEYVALUEJSON
file_filter = localizations/preview-faq/<lang>.json
source_file = src/views/preview-faq/l10n.json
source_lang = en
type = KEYVALUEJSON
\ No newline at end of file
type = KEYVALUEJSON
[scratch-website.research-l10njson]
file_filter = localizations/research/<lang>.json
source_file = src/views/research/l10n.json
source_lang = en
type = KEYVALUEJSON
ESLINT=./node_modules/.bin/eslint
NODE=node
NODE= NODE_OPTIONS=--max_old_space_size=8000 node
SASSLINT=./node_modules/.bin/sass-lint -v
S3CMD=s3cmd sync -P --delete-removed --add-header=Cache-Control:no-cache,public,max-age=3600
TAP=./node_modules/.bin/tap
WATCH=./node_modules/.bin/watch
WEBPACK=./node_modules/.bin/webpack
WATCH= NODE_OPTIONS=--max_old_space_size=8000 ./node_modules/.bin/watch
WEBPACK= NODE_OPTIONS=--max_old_space_size=8000 ./node_modules/.bin/webpack
# ------------------------------------
......
......@@ -176,7 +176,7 @@ async.auto({
if (err) throw new Error(err);
if (process.env.FASTLY_ACTIVATE_CHANGES) {
fastly.activateVersion(results.version, function (e, resp) {
if (err) throw new Error(e);
if (e) throw new Error(e);
process.stdout.write('Successfully configured and activated version ' + resp.number + '\n');
if (process.env.FASTLY_PURGE_ALL) {
fastly.purgeAll(FASTLY_SERVICE_ID, function (error) {
......
......@@ -34,6 +34,7 @@
"is": "Íslenska",
"it": "Italiano",
"kn": "ಭಾಷೆ-ಹೆಸರು",
"kk": "Қазақша",
"rw": "Kinyarwanda",
"ht": "Kreyòl",
"ku": "Kurdî",
......
const bindAll = require('lodash.bindall');
const React = require('react');
const PropTypes = require('prop-types');
const FRCInput = require('formsy-react-components').Input;
const FRCTextarea = require('formsy-react-components').Textarea;
const classNames = require('classnames');
require('./row.scss');
require('./inplace-input.scss');
class InplaceInput extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleBlur',
'setRef'
]);
}
handleBlur (name, value) {
if (this.inputRef.props.errorMessages.length === 0) {
const jsonData = {};
jsonData[name] = value;
this.props.handleUpdate(jsonData);
}
}
setRef (input) {
this.inputRef = input;
}
render () {
const {
className,
type,
handleUpdate, // eslint-disable-line no-unused-vars
...props
} = this.props;
return (
(type === 'textarea') ?
<FRCTextarea
className="inplace-textarea"
componentRef={this.setRef}
rowClassName={classNames('textarea-row no-label', className)}
onBlur={this.handleBlur}
{...props}
/> :
<FRCInput
className="inplace-input"
componentRef={this.setRef}
rowClassName={classNames(
className,
'no-label'
)}
onBlur={this.handleBlur}
{...props}
/>
);
}
}
InplaceInput.propTypes = {
className: PropTypes.string,
handleUpdate: PropTypes.func.isRequired,
type: PropTypes.string
};
InplaceInput.defaultProps = {
type: 'text',
value: ''
};
module.exports = InplaceInput;
@import "../../colors";
@import "../../frameless";
.inplace-input {
transition: all .5s ease;
border: 2px dashed $ui-dark-gray;
border-radius: 5px;
background-color: transparent;
padding: 0 1rem;
color: $type-gray;
&:focus {
transition: all .5s ease;
outline: none;
border: 1px solid $ui-blue;
}
&.fail {
border: 1px solid $ui-orange;
}
&.pass {
border: 1px solid $active-dark-gray;
}
/* IE10/11-specific style resets */
&::-ms-reveal, &::-ms-clear {
display: none;
}
}
.inplace-textarea {
transition: all 1s ease;
margin-bottom: .75rem;
border: 2px dashed $ui-dark-gray;
border-radius: 5px;
background-color: $ui-light-gray;
padding: .75rem 1rem;
width: calc(100% - 2.25rem);
min-height: 20rem;
line-height: 1.75em;
color: $type-gray;
font-size: .875rem;
resize: none;
&:focus {
transition: all 1s ease;
outline: none;
border: 1px solid $ui-blue;
}
&.fail {
border: 1px solid $ui-orange;
}
}
const classNames = require('classnames');
const PropTypes = require('prop-types');
const React = require('react');
const ThumbnailColumn = require('../../components/thumbnailcolumn/thumbnailcolumn.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./remixlist.scss');
/*
* Container for a list of project remixes
*/
const RemixList = props => (
<FlexRow className={classNames('remix-list', props.className)}>
<h1>Remixes</h1>
{props.items.length === 0 ? (
<span>No remixes</span>
) : (
<ThumbnailColumn
cards
showAvatar
itemType="preview"
items={props.items.slice(0, 5)}
showFavorites={false}
showLoves={false}
showViews={false}
/>
)}
</FlexRow>
);
RemixList.propTypes = {
className: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.object).isRequired
};
module.exports = RemixList;
.remix-list {
flex-direction: column;
.project {
margin-bottom: 1.5rem;
}
.creator-image img {
max-width: 2rem;
max-height: 2rem;
}
.thumbnail-column {
display: inline-block;
width: 100%;
}
}
const React = require('react'); // eslint-disable-line
const reactStringReplace = require('react-string-replace');
/**
* Helper method that replaces @mentions and #hashtags in plain text
*
* @param {string} text string to convert
* @return {string} string with links for @mentions and #hashtags
*/
module.exports = text => {
let replacedText;
// Match @-mentions (username is alphanumeric, underscore and dash)
replacedText = reactStringReplace(text, /@([\w-]+)/g, (match, i) => (
<a
href={`/users/${match}`}
key={match + i}
>@{match}</a>
));
// Match hashtags
replacedText = reactStringReplace(replacedText, /(#[\w-]+)/g, (match, i) => (
<a
href={`/search/projects?q=${match}`}
key={match + i}
>{match}</a>
));
return replacedText;
};
......@@ -15,6 +15,8 @@ module.exports.getInitialState = () => ({
project: module.exports.Status.NOT_FETCHED,
credit: module.exports.Status.NOT_FETCHED,
comments: module.exports.Status.NOT_FETCHED,
faved: module.exports.Status.NOT_FETCHED,
loved: module.exports.Status.NOT_FETCHED,
remixes: module.exports.Status.NOT_FETCHED,
studios: module.exports.Status.NOT_FETCHED
},
......@@ -22,6 +24,8 @@ module.exports.getInitialState = () => ({
remixes: [],
credit: {},
comments: [],
faved: false,
loved: false,
studios: []
});
......@@ -39,6 +43,10 @@ module.exports.previewReducer = (state, action) => {
return Object.assign({}, state, {
remixes: action.items
});
case 'SET_STUDIOS':
return Object.assign({}, state, {
studios: action.items
});
case 'SET_CREDIT':
return Object.assign({}, state, {
credit: action.info
......@@ -47,6 +55,14 @@ module.exports.previewReducer = (state, action) => {
return Object.assign({}, state, {
comments: action.items
});
case 'SET_LOVED':
return Object.assign({}, state, {
loved: action.info
});
case 'SET_FAVED':
return Object.assign({}, state, {
faved: action.info
});
case 'SET_FETCH_STATUS':
state = JSON.parse(JSON.stringify(state));
state.status[action.infoType] = action.status;
......@@ -74,22 +90,41 @@ module.exports.setCreditInfo = info => ({
info: info
});
module.exports.setFaved = info => ({
type: 'SET_FAVED',
info: info
});
module.exports.setLoved = info => ({
type: 'SET_LOVED',
info: info
});
module.exports.setRemixes = items => ({
type: 'SET_REMIXES',
items: items
});
module.exports.setStudios = items => ({
type: 'SET_STUDIOS',
items: items
});
module.exports.setFetchStatus = (type, status) => ({
type: 'SET_FETCH_STATUS',
infoType: type,
status: status
});
module.exports.getProjectInfo = id => (dispatch => {
dispatch(module.exports.setFetchStatus('project', module.exports.Status.FETCHING));
api({
module.exports.getProjectInfo = (id, token) => (dispatch => {
const opts = {
uri: `/projects/${id}`
}, (err, body) => {
};
if (token) {
Object.assign(opts, {authentication: token});
}
dispatch(module.exports.setFetchStatus('project', module.exports.Status.FETCHING));
api(opts, (err, body) => {
if (err) {
dispatch(module.exports.setFetchStatus('project', module.exports.Status.ERROR));
dispatch(module.exports.setError(err));
......@@ -125,6 +160,124 @@ module.exports.getCreditInfo = id => (dispatch => {
});
});
module.exports.getFavedStatus = (id, username, token) => (dispatch => {
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.FETCHING));
api({
uri: `/projects/${id}/favorites/user/${username}`,
authentication: token
}, (err, body) => {
if (err) {
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.ERROR));
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.ERROR));
dispatch(module.exports.setError('No faved info'));
return;
}
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.FETCHED));
dispatch(module.exports.setFaved(body.userFavorite));
});
});
module.exports.setFavedStatus = (faved, id, username, token) => (dispatch => {
if (faved) {
api({
uri: `/projects/${id}/favorites/user/${username}`,
authentication: token,
method: 'POST'
}, (err, body) => {
if (err) {
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setError('Set favorites returned no data'));
return;
}
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.FETCHED));
dispatch(module.exports.setFaved(body.userFavorite));
});
} else {
api({
uri: `/projects/${id}/favorites/user/${username}`,
authentication: token,
method: 'DELETE'
}, (err, body) => {
if (err) {
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setError('Set favorites returned no data'));
return;
}
dispatch(module.exports.setFetchStatus('faved', module.exports.Status.FETCHED));
dispatch(module.exports.setFaved(false));
});
}
});
module.exports.getLovedStatus = (id, username, token) => (dispatch => {
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHING));
api({
uri: `/projects/${id}/loves/user/${username}`,
authentication: token
}, (err, body) => {
if (err) {
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
dispatch(module.exports.setError('No loved info'));
return;
}
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
dispatch(module.exports.setLoved(body.userLove));
});
});
module.exports.setLovedStatus = (loved, id, username, token) => (dispatch => {
if (loved) {
api({
uri: `/projects/${id}/loves/user/${username}`,
authentication: token,
method: 'POST'
}, (err, body) => {
if (err) {
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setError('Set loved returned no data'));
return;
}
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
dispatch(module.exports.setLoved(body.userLove));
});
} else {
api({
uri: `/projects/${id}/loves/user/${username}`,
authentication: token,
method: 'DELETE'
}, (err, body) => {
if (err) {
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setError('Set loved returned no data'));
return;
}
dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
dispatch(module.exports.setLoved(body.userLove));
});
}
});
module.exports.getRemixes = id => (dispatch => {
dispatch(module.exports.setFetchStatus('remixes', module.exports.Status.FETCHING));
api({
......@@ -148,3 +301,53 @@ module.exports.getRemixes = id => (dispatch => {
dispatch(module.exports.setRemixes(body));
});
});
module.exports.getStudios = id => (dispatch => {
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHING));
api({
uri: `/projects/${id}/studios?limit=5`
}, (err, body, res) => {
if (err) {
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
dispatch(module.exports.setError('No studios info'));
return;
}
if (res.statusCode === 404) { // NotFound
body = [];
}
dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHED));
dispatch(module.exports.setStudios(body));
});
});
module.exports.updateProject = (id, jsonData, username, token) => (dispatch => {
api({
uri: `/projects/${id}`,
authentication: token,
method: 'PUT',
json: jsonData
}, (err, body, res) => {
if (err) {
dispatch(module.exports.setFetchStatus('project', module.exports.Status.ERROR));
dispatch(module.exports.setError(err));
return;
}
if (typeof body === 'undefined') {
dispatch(module.exports.setFetchStatus('project', module.exports.Status.ERROR));
dispatch(module.exports.setError('No project info'));
return;
}
if (res.statusCode === 500) { // InternalServer
dispatch(module.exports.setFetchStatus('project', module.exports.Status.ERROR));
dispatch(module.exports.setError('API Internal Server Error'));
return;
}
dispatch(module.exports.setFetchStatus('project', module.exports.Status.FETCHED));
dispatch(module.exports.setProjectInfo(body));
});
});
......@@ -193,7 +193,7 @@
},
{
"name": "preview",
"pattern": "^/preview/?(\\d+)?/?$",
"pattern": "^/preview(/editor|(/\\d+(/editor|/fullscreen)?)?)?/?$",
"routeAlias": "/preview/?$",
"view": "preview/preview",
"title": "Scratch 3.0 Preview"
......@@ -212,6 +212,13 @@
"view": "privacypolicy/privacypolicy",
"title": "Privacy Policy"
},
{
"name": "research",
"pattern": "^/research/?$",
"routeAlias": "/research",
"view": "research/research",
"title": "Research"
},
{
"name": "search",
"pattern": "^/search/:projects/?$",
......@@ -336,6 +343,12 @@
"routeAlias": "/info/?(\\?.*)?$",
"redirect": "/tips"
},
{
"name": "research-redirect",
"pattern": "^/info/research/?$",
"routeAlias": "/info/research",
"redirect": "/research"
},
{
"name": "search-redirect",
"pattern": "^/search/?$",
......
......@@ -9,6 +9,8 @@ const InformationPage = require('../../components/informationpage/informationpag
const Dmca = () => (
<InformationPage title={'DMCA'}>
<div className="inner info-inner">
<span className="nav-spacer" />
<p><FormattedMessage id="dmca.intro" /></p>
<p>
Copyright Agent / Mitchel Resnick<br />
......@@ -22,6 +24,40 @@ const Dmca = () => (
<p><FormattedMessage id="dmca.assessment" /></p>
<p><FormattedMessage id="dmca.eyetoeye" /></p>
<p><FormattedMessage id="dmca.afterfiling" /></p>
<h3><FormattedMessage id="dmca.counternotification" /></h3>
<p><FormattedMessage id="dmca.ifremoved" /></p>
<p><FormattedMessage id="dmca.mailcounter" /></p>
<p>
Copyright Agent / Mitchel Resnick<br />
MIT Media Laboratory<br />
77 Massachusetts Ave<br />
Room E14-445A<br />
Cambridge, MA 02139<br />
Tel: (617) 253-9783
</p>
<p><FormattedMessage id="dmca.mustinclude" /></p>
<p>
<ul>
<li><FormattedMessage id="dmca.fullname" /></li>
<li><FormattedMessage id="dmca.address" /></li>
<li><FormattedMessage id="dmca.phone" /></li>
<li><FormattedMessage id="dmca.email" /></li>
<li><FormattedMessage id="dmca.username" /></li>
<li><FormattedMessage id="dmca.projecturl" /></li>
<li><FormattedMessage id="dmca.statementerror" /></li>
<li><FormattedMessage id="dmca.statementjurisdiction" /></li>
<li><FormattedMessage id="dmca.signature" /></li>
</ul>
</p>
<p><FormattedMessage id="dmca.valid" /></p>
<p><FormattedMessage id="dmca.lawsuit" /></p>
<h3><FormattedMessage id="dmca.repeat" /></h3>
<p><FormattedMessage id="dmca.disableaccess" /></p>
<span className="nav-spacer" />
</div>
</InformationPage>
);
......
......@@ -3,5 +3,22 @@
"dmca.llkresponse": "The Lifelong Kindergarten Group will promptly process and investigate notices of alleged infringement and will take appropriate actions under the Digital Millennium Copyright Act (“DMCA”) and other applicable intellectual property laws. Upon receipt of notices complying or substantially complying with the DMCA, the Lifelong Kindergarten Group may act expeditiously to remove or disable access to any material claimed to be infringing. Repeat infringers of third-party copyrights are subject to termination in appropriate circumstances.",
"dmca.assessment": "In assessing whether or not a Scratch user has violated your copyrights, please keep in mind that Scratch is an educational and not-for-profit initiative, seeking to aid children’s learning by providing the tools for them to learn and express themselves using digital technology. Please also keep in mind the “Fair Use” doctrine incorporated into the Copyright Act of 1976, 17 U.S.C. § 107.",
"dmca.eyetoeye": "We hope you also see Scratch not only as a good way of popularizing your creations/website but also as an opportunity to do something good for children’s education.",
"dmca.afterfiling": "If you choose to make a copyright infringement complaint, please note that we may post your notification, with personally identifiable information redacted, to a clearinghouse such as chillingeffects.org. Please also note that you may be liable for damages (including costs and attorneys’ fees) if you materially misrepresent that an activity is infringing your copyright."
"dmca.afterfiling": "If you choose to make a copyright infringement complaint, please note that we may post your notification, with personally identifiable information redacted, to a clearinghouse such as chillingeffects.org. Please also note that you may be liable for damages (including costs and attorneys’ fees) if you materially misrepresent that an activity is infringing your copyright.",
"dmca.counternotification": "Counter-notification",
"dmca.ifremoved": "If your content has been removed due to a DMCA takedown notice, you believe you have a legal right to use the material, and you want to legally dispute this claim, you can file a DMCA counter-notification. You should only submit a counter-notification if the content was removed because of a mistake or misidentification and you are willing to go to court to defend your use of the material.",
"dmca.mailcounter": "A DMCA counter-notification can be emailed to copyright@scratch.mit.edu or mailed to:",
"dmca.mustinclude": "This counter-notice must include:",
"dmca.fullname": "Your full name",
"dmca.address": "Your address",
"dmca.phone": "Your phone number",
"dmca.email": "Your email address",
"dmca.username": "The username of your Scratch account",
"dmca.projecturl": "The URLs of the projects which were taken down",
"dmca.statementerror": "A statement made under penalty of perjury that the content was removed in error",
"dmca.statementjurisdiction": "A statement consenting to jurisdiction in the area which you reside",
"dmca.signature": "Your signature",
"dmca.valid": "Upon receipt of a valid DMCA counter-notice, Scratch will share the information you provide with the person who made the original claim of copyright infringement against you. They will be able to use this information to contact you or to notify you if they choose to file a lawsuit against you.",
"dmca.lawsuit": "If we are not notified about a lawsuit being filed ten (10) business days after providing a counter-notification to a person who has filed a DMCA takedown notice, access to the content which was taken down will be restored.",
"dmca.repeat": "Repeat Infringers",
"dmca.disableaccess": "We are required by the DMCA to disable access to our service to repeat copyright infringers. If we receive a DMCA compliant takedown notice against a person, and that person doesn’t submit a counter-notification, a strike is added to their account. After three (3) strikes have been received, accounts belonging to the person will be blocked and standard measures will be taken to block their access to Scratch. We assess strikes ten (10) business days after a DMCA takedown notice is received to ensure that no person is blocked before they have a chance to review the issue and submit a valid counter-notice."
}
......@@ -82,7 +82,7 @@
"faq.presentScratchTitle":"Can I present Scratch at a conference?",
"faq.presentScratchBody":"Please feel free to make presentations about Scratch to educators or other groups. We grant our permission to make presentations.",
"faq.supportMaterialTitle":"May I use / remix Scratch support materials, sprites, images, sounds or sample projects I’ve found on the website?",
"faq.supportMaterialBody":"Yes - Scratch support materials made available on the Scratch website by the Scratch Team are available under the <a href=\"http://creativecommons.org/licenses/by-sa/2.0/deed.en\">Creative Commons Attribution-ShareAlike license</a>, with the exception of the Scratch Logo, Scratch Cat, Gobo, Pico, Nano, and Tera which are Scratch trademarks.",
"faq.supportMaterialBody":"Yes - Scratch support materials made available on the Scratch website by the Scratch Team are available under the <a href=\"http://creativecommons.org/licenses/by-sa/2.0/deed.en\">Creative Commons Attribution-ShareAlike license</a>, with the exception of the Scratch Logo, Scratch Cat, Gobo, Pico, Nano, Giga, and Tera which are Scratch trademarks.",
"faq.sellProjectsTitle":"Can I sell my Scratch projects?",
"faq.sellProjectsBody":"Certainly - your project is your creation. Keep in mind that once you share your project on Scratch, everyone is free to download, remix, and reuse it as per the terms of the <a href=\"http://creativecommons.org/licenses/by-sa/2.0/deed.en\">CC-BY-SA 2.0 license</a>. So if you intend to sell your project, you may want to un-share it from Scratch.",
"faq.sourceCodeTitle":"Where can I find the source code for Scratch?",
......
This diff is collapsed.
This diff is collapsed.
@import "../../colors";
@import "../../frameless";
html,
body,
#app,
/* override view padding for share banner */
#view {
padding: 0 0 20px 0;
}
.gui {
position: absolute;
top: 0;
z-index: 11;
margin: 0;
width: 100%;
height: 100%;
}
/* override view padding for share banner */
#view {
padding: 0 0 20px 0;
}
.gui * { box-sizing: border-box; }
// .gui * { box-sizing: border-box; }
.preview {
.title-text {
font-size: 2rem;
.project-title {
font-size: 1.75rem;
font-weight: 500;
&.has-error {
.validation-message {
transform: translate(22rem, 0);
}
}
}
img {
......@@ -44,6 +51,57 @@ body,
font-size: .8rem;
}
.validation-message {
$arrow-border-width: 1rem;
display: block;
position: absolute;
top: 0;
left: 0;
margin-left: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
padding: 1rem;
max-width: 18.75rem;
min-height: 1rem;
overflow: visible;
color: $type-white;
font-size: 1rem;
&:before {
display: block;
position: absolute;
top: 1rem;
left: -$arrow-border-width / 2;
transform: rotate(45deg);
border-bottom: 1px solid $active-gray;
border-left: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
width: $arrow-border-width;
height: $arrow-border-width;
content: "";
}
}
.row {
&.has-error {
.inplace-input,
.inplace-textarea {
border: 1px solid $ui-orange;
}
}
.col-sm-9 {
position: relative;
}
}
.button {
margin-left: 1rem;
}
......@@ -52,10 +110,6 @@ body,
width: 60%;
}
.remix-list {
width: 25%;
}
.share-button,
.remix-button,
.see-inside-button {
......@@ -108,15 +162,11 @@ body,
align-items: flex-start;
}
.placeholder {
.guiPlayer {
display: inline-block;
width: 480px;
}
.placeholder img {
width: 100%;
}
.project-notes {
width: 45%;
height: 404px;
......@@ -154,6 +204,19 @@ body,
white-space: pre-line;
overflow-y: scroll;
flex: 1;
&.textarea-row {
border: 0;
background-color: inherit;
padding: 0;
overflow: visible;
}
&.has-error {
.validation-message {
transform: translate(26rem, 0);
}
}
}
.copyleft {
......@@ -192,6 +255,8 @@ body,
.project-loves {
cursor: pointer;
&:before {
background-image: url("/svgs/love/love_type-gray.svg");
}
......@@ -206,6 +271,8 @@ body,
.project-favorites {
cursor: pointer;
&:before {
background-image: url("/svgs/favorite/favorite_type-gray.svg");
}
......@@ -285,4 +352,23 @@ body,
}
}
}
.remix-list,
.studio-list {
flex-direction: column;
.project {
margin-bottom: 1.5rem;
}
.creator-image img {
max-width: 2rem;
max-height: 2rem;
}
.thumbnail-column {
display: inline-block;
width: 100%;
}
}
}
{
"research.title":"Research on Scratch",
"research.conductors":"Research on Scratch is being conducted by members of the MIT Scratch Team and researchers at other universities, including Yasmin Kafai at the University of Pennsylvania Graduate School of Education, Karen Brennan at the Harvard Graduate School of Education, Benjamin Mako Hill at the University of Washington, Andrés Monroy Hernandez at Microsoft Research, Mimi Ito and Crystle Martin at the University of California, Irvine, Quinn Burke at College of Charleston, Deborah Fields at Utah State University, and Kylie Peppler at Indiana University.",
"research.privacy":"By sharing projects and participating in the Scratch online community, you are helping us better understand how people can use and learn with Scratch. Any publicly shared projects, comments, or other material on the Scratch site may be included in the research analysis, presentations, papers, and reports. No personally identifiable information is shared. (If you have any questions, please use the {contactLink} form.)",
"research.contactLinkText":"Contact Us",
"research.intro":"Below are selected research papers, presentations, and theses on Scratch and the Scratch online community, followed by a list of National Science Foundation grants awarded to support Scratch.",
"research.papers":"Research Papers & Presentations",
"research.grants":"National Science Foundation Grants"
}
This diff is collapsed.
......@@ -142,7 +142,21 @@ module.exports = {
new CopyWebpackPlugin([
{from: 'static'},
{from: 'intl', to: 'js'}
])
]),
new CopyWebpackPlugin([{
from: 'node_modules/scratch-gui/dist/static/blocks-media',
to: 'static/blocks-media'
}]),
new CopyWebpackPlugin([{
from: 'node_modules/scratch-gui/dist/extension-worker.js'
}]),
new CopyWebpackPlugin([{
from: 'node_modules/scratch-gui/dist/extension-worker.js.map'
}]),
new CopyWebpackPlugin([{
from: 'node_modules/scratch-gui/dist/static/assets',
to: 'static/assets'
}])
])
.concat(process.env.NODE_ENV === 'production' ? [
new webpack.optimize.UglifyJsPlugin({
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment