Merge branch 'develop' into hotfix/langauges_fix
|
@ -42,23 +42,43 @@ async.auto({
|
|||
// on any of those route conditions.
|
||||
var notPassStatement = fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname);
|
||||
|
||||
// For all the routes in routes.json, construct a varnish-style regex that matches
|
||||
// only if NONE of those routes are matched.
|
||||
var passStatement = fastlyConfig.negateConditionStatement(notPassStatement);
|
||||
|
||||
// For a non-pass condition, point backend at s3
|
||||
var backendCondition = fastlyConfig.setBackend(
|
||||
'F_s3',
|
||||
S3_BUCKET_NAME,
|
||||
notPassStatement
|
||||
);
|
||||
// For a pass condition, set forwarding headers
|
||||
var forwardCondition = fastlyConfig.setForwardHeaders(passStatement);
|
||||
var recvCondition = '' +
|
||||
'if (' + notPassStatement + ') {\n' +
|
||||
' set req.backend = F_s3;\n' +
|
||||
' set req.http.host = \"' + S3_BUCKET_NAME + '\";\n' +
|
||||
'} else {\n' +
|
||||
' if (!req.http.Fastly-FF) {\n' +
|
||||
' if (req.http.X-Forwarded-For) {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
||||
' }\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
|
||||
' }\n' +
|
||||
' set req.grace = 60s;\n' +
|
||||
' if (req.http.Cookie:scratchlanguage) {\n' +
|
||||
' set req.http.Accept-Language = req.http.Cookie:scratchlanguage;\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Accept-Language = accept.language_lookup("' +
|
||||
Object.keys(languages).join(':') + '", ' +
|
||||
'"en", ' +
|
||||
'std.tolower(req.http.Accept-Language)' +
|
||||
');\n' +
|
||||
' }\n' +
|
||||
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
|
||||
' set req.http.Cookie = req.http.Cookie:scratchlanguage;\n' +
|
||||
' } else {\n' +
|
||||
' return(pass);\n' +
|
||||
' }\n' +
|
||||
'}\n';
|
||||
|
||||
|
||||
fastly.setCustomVCL(
|
||||
results.version,
|
||||
'recv-condition',
|
||||
backendCondition + forwardCondition,
|
||||
recvCondition,
|
||||
cb
|
||||
);
|
||||
}],
|
||||
|
|
|
@ -84,45 +84,6 @@ var FastlyConfigMethods = {
|
|||
return 'redirects/' + route.pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns custom vcl configuration as a string for setting the backend
|
||||
* of a request to the given backend/host.
|
||||
*
|
||||
* @param {string} backend name of the backend declared in fastly
|
||||
* @param {string} host name of the s3 bucket to be set as the host
|
||||
* @param {string} condition condition under which backend should be set
|
||||
*/
|
||||
setBackend: function (backend, host, condition) {
|
||||
return '' +
|
||||
'if (' + condition + ') {\n' +
|
||||
' set req.backend = ' + backend + ';\n' +
|
||||
' set req.http.host = \"' + host + '\";\n' +
|
||||
'}\n';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns custom vcl configuration as a string for headers that
|
||||
* should be added for the condition in which a request is forwarded.
|
||||
*
|
||||
* @param {string} condition condition under which to set pass headers
|
||||
*/
|
||||
setForwardHeaders: function (condition) {
|
||||
return '' +
|
||||
'if (' + condition + ') {\n' +
|
||||
' if (!req.http.Fastly-FF) {\n' +
|
||||
' if (req.http.X-Forwarded-For) {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
||||
' }\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
|
||||
' }\n' +
|
||||
' set req.grace = 60s;\n' +
|
||||
' return(pass);\n' +
|
||||
'}\n';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns custom vcl configuration as a string that sets the varnish
|
||||
* Time to Live (TTL) for responses that come from s3.
|
||||
|
@ -132,9 +93,13 @@ var FastlyConfigMethods = {
|
|||
setResponseTTL: function (condition) {
|
||||
return '' +
|
||||
'if (' + condition + ') {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
|
||||
' set beresp.http.Vary = "Accept-Encoding, Accept-Language";\n' +
|
||||
' } else {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
' }\n' +
|
||||
'}\n';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -32,33 +32,41 @@ var Grid = React.createClass({
|
|||
|
||||
if (this.props.itemType == 'projects') {
|
||||
return (
|
||||
<Thumbnail key={key}
|
||||
showLoves={this.props.showLoves}
|
||||
showFavorites={this.props.showFavorites}
|
||||
showRemixes={this.props.showRemixes}
|
||||
showViews={this.props.showViews}
|
||||
showAvatar={this.props.showAvatar}
|
||||
type={'project'}
|
||||
href={href}
|
||||
title={item.title}
|
||||
src={item.image}
|
||||
avatar={'https://cdn2.scratch.mit.edu/get_image/user/'
|
||||
+ item.author.id + '_32x32.png'}
|
||||
creator={item.author.username}
|
||||
loves={item.stats.loves}
|
||||
favorites={item.stats.favorites}
|
||||
remixes={item.stats.remixes}
|
||||
views={item.stats.views} />
|
||||
<Thumbnail
|
||||
key={key}
|
||||
showLoves={this.props.showLoves}
|
||||
showFavorites={this.props.showFavorites}
|
||||
showRemixes={this.props.showRemixes}
|
||||
showViews={this.props.showViews}
|
||||
showAvatar={this.props.showAvatar}
|
||||
type={'project'}
|
||||
href={href}
|
||||
title={item.title}
|
||||
src={item.image}
|
||||
avatar={
|
||||
'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}
|
||||
owner={item.owner} />
|
||||
<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))}
|
||||
|
|
|
@ -11,7 +11,7 @@ var Navigation = React.createClass({
|
|||
<NavigationBox>
|
||||
<ul className="ul mod-2018">
|
||||
<li className="li-left mod-logo mod-2018">
|
||||
<a href="/conference" className="logo-a">
|
||||
<a href="/" className="logo-a">
|
||||
<img
|
||||
src="/images/logo_sm.png"
|
||||
alt="Scratch Logo"
|
||||
|
|
|
@ -8,12 +8,20 @@ var Thumbnail = React.createClass({
|
|||
propTypes: {
|
||||
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,
|
||||
|
@ -24,6 +32,12 @@ var Thumbnail = React.createClass({
|
|||
alt: ''
|
||||
};
|
||||
},
|
||||
handleSrcError: function () {
|
||||
this.setState({srcFallback: true});
|
||||
},
|
||||
handleAvatarError: function () {
|
||||
this.setState({avatarFallback: true});
|
||||
},
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'thumbnail',
|
||||
|
@ -58,7 +72,8 @@ var Thumbnail = React.createClass({
|
|||
<div
|
||||
key="remixes"
|
||||
className="thumbnail-remixes"
|
||||
title={this.props.remixes + ' remixes'}>
|
||||
title={this.props.remixes + ' remixes'}
|
||||
>
|
||||
{this.props.remixes}
|
||||
</div>
|
||||
);
|
||||
|
@ -68,19 +83,48 @@ var Thumbnail = React.createClass({
|
|||
<div
|
||||
key="views"
|
||||
className="thumbnail-views"
|
||||
title={this.props.views + ' views'}>
|
||||
title={this.props.views + ' views'}
|
||||
>
|
||||
{this.props.views}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
var imgElement,titleElement,avatarElement;
|
||||
|
||||
var imgElement, titleElement, avatarElement;
|
||||
if (this.props.linkTitle) {
|
||||
imgElement = <a className="thumbnail-image" href={this.props.href} key="imgElement">
|
||||
<img src={this.props.src} alt={this.props.alt} />
|
||||
</a>;
|
||||
titleElement = <a href={this.props.href} key="titleElement">
|
||||
{this.props.title}
|
||||
</a>;
|
||||
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;
|
||||
|
@ -97,10 +141,32 @@ var Thumbnail = React.createClass({
|
|||
}
|
||||
|
||||
if (this.props.avatar && this.props.showAvatar) {
|
||||
avatarElement =
|
||||
<a className="creator-image" href={'/users/' + this.props.creator + '/'}>
|
||||
<img src={this.props.avatar} alt={this.props.creator} />
|
||||
</a>;
|
||||
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} >
|
||||
|
|
|
@ -47,7 +47,7 @@ var TTTTile = React.createClass({
|
|||
{this.props.onGuideClick && (
|
||||
<div className="ttt-tile-guides" onClick={this.props.onGuideClick}>
|
||||
<FormattedMessage id='tile.guides' defaultMessage='See Cards and Guides'/>
|
||||
<img className="ttt-tile-see-more" src="/svgs/ttt/see-more.svg" />
|
||||
<img className="ttt-tile-open-modal" src="/svgs/modal/open-blue.svg" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -105,14 +105,15 @@
|
|||
font-size: .75rem;
|
||||
font-weight: 500;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($link-blue, 40%);
|
||||
}
|
||||
}
|
||||
|
||||
.ttt-tile-see-more {
|
||||
.ttt-tile-open-modal {
|
||||
display: inline-block;
|
||||
padding: 0 .25rem;
|
||||
vertical-align: middle;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ var Developers = React.createClass({
|
|||
<div className="inner">
|
||||
<section id="projects">
|
||||
<span className="nav-spacer"></span>
|
||||
<h2>Projects</h2>
|
||||
<h2><FormattedMessage id='developers.projectsTitle' /></h2>
|
||||
<p className="intro">
|
||||
<FormattedMessage id='developers.projectsIntro' />
|
||||
</p>
|
||||
|
@ -94,13 +94,7 @@ var Developers = React.createClass({
|
|||
<div className="body-copy column">
|
||||
<h3>ScratchJr</h3>
|
||||
<p>
|
||||
ScratchJr is an introductory programming language{' '}
|
||||
that enables young children (ages 5-7) to create{' '}
|
||||
their own interactive stories and games. For more{' '}
|
||||
information, visit the{' '}
|
||||
<a href="https://www.scratchjr.org/">ScratchJr website</a>{' '}
|
||||
or access the code and documentation{' '}
|
||||
<a href="https://github.com/LLK/scratchjr">here</a>.
|
||||
<FormattedHTMLMessage id='developers.jrBody' />
|
||||
</p>
|
||||
</div>
|
||||
</FlexRow>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"developers.wwwTitle": "Scratch WWW",
|
||||
"developers.wwwIntro": "Scratch-www is a standalone web client for the Scratch Community, built using React and Redux. Access the code and documentation through Github <a href=\"https://github.com/LLK/scratch-www\">here</a>.",
|
||||
"developers.principlesIntro": "We created Scratch to empower young people to think creatively, reason systematically, and work collaboratively. We are guided by a set of <b>Learning Principles</b> and <b>Design Principles</b> that we hope you will follow as you develop new tools and technologies with Scratch Blocks.",
|
||||
"developers.jrBody": "ScratchJr is an introductory programming language that enables young children (ages 5-7) to create their own interactive stories and games. For more information, visit the <a href=\"https://www.scratchjr.org/\">ScratchJr website</a> or access the code and documentation <a href=\"https://github.com/LLK/scratchjr\">on GitHub</a>.",
|
||||
"developers.learningPrinciplesTitle": "Learning Principles",
|
||||
"developers.learningPrinciplesProjectsBody": "People learn best when they are actively working on projects — generating new ideas, designing prototypes, making improvements and creating final products.",
|
||||
"developers.learningPrinciplesPassionTitle": "Passion",
|
||||
|
@ -37,7 +38,7 @@
|
|||
"developers.donateThanks": "Thanks for supporting Scratch!",
|
||||
"developers.partnersIntro": "The creation and maintenance of this open source code would not be possible without generous technical and financial support from our partners:",
|
||||
"developers.faqAboutTitle": "Where can I learn more about Scratch?",
|
||||
"developers.faqAboutBody": "Scratch is a free programming language and online community where young people can create their own interactive stories, games, and animations. Scratch is a project of the <a href=\"https://llk.media.mit.edu/\">Lifelong Kindergarten</a> Group at the <a href=\"http://media.mit.edu/\">MIT Media Lab</a>. You can learn more about Scratch <a href=\"/about\">here</a>.",
|
||||
"developers.faqAboutBody": "Scratch is a free programming language and online community where young people can create their own interactive stories, games, and animations. Scratch is a project of the <a href=\"https://www.media.mit.edu/groups/lifelong-kindergarten/overview\">Lifelong Kindergarten</a> Group at the <a href=\"http://media.mit.edu/\">MIT Media Lab</a>. You can learn more about Scratch <a href=\"/about\">here</a>.",
|
||||
"developers.faqRulesTitle": "Are there rules to using this code in my application?",
|
||||
"developers.faqRulesBody": "You may use this code in accordance with the license which governs each project. We also strongly encourage you to consider the learning and design principles (above, on this page) when building creative learning experiences for kids of all ages.",
|
||||
"developers.faqNameTitle": "Am I allowed to use the name \"Scratch Blocks\" in the description of my app and other public messaging?",
|
||||
|
|
|
@ -231,6 +231,7 @@ var Download = injectIntl(React.createClass({
|
|||
<p><FormattedMessage id='download.knownIssuesOne' /></p>
|
||||
<p><FormattedMessage id='download.knownIssuesTwo' /></p>
|
||||
<p><FormattedHTMLMessage id='download.knownIssuesThree' /></p>
|
||||
<p><FormattedMessage id='download.knownIssuesFour' /></p>
|
||||
<a href="https://scratch.mit.edu/discuss/3/" className='button mod-link'>
|
||||
<FormattedMessage id='download.reportBugs' />
|
||||
</a>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"download.title": "Scratch 2.0 Offline Editor",
|
||||
"download.intro": "You can install the Scratch 2.0 editor to work on projects without an internet connection. This version will work on Mac, Windows, and some versions of Linux (32 bit).",
|
||||
"download.introMac": "<b>Note for Mac Users:</b> the latest version of Scratch 2.0 Offline requires Adobe Air 20. To upgrade to Adobe Air 20 manually, go <a href=\"https://get.adobe.com/air/\">here</a>.",
|
||||
"download.introMac": "<b>Note for Mac Users:</b> the latest version of Scratch 2.0 Offline requires Adobe AIR 20. To upgrade to Adobe AIR 20 manually, go <a href=\"https://get.adobe.com/air/\">here</a>.",
|
||||
"download.installation": "Installation",
|
||||
"download.airTitle": "Adobe AIR",
|
||||
"download.airBody": "If you don't already have it, download and install the latest <a href=\"http://get.adobe.com/air/\">Adobe AIR</a>",
|
||||
|
@ -24,9 +24,10 @@
|
|||
"download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.",
|
||||
"download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.",
|
||||
"download.knownIssuesTitle": "Known issues",
|
||||
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe Air version 14 (released April 2014).",
|
||||
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe AIR version 14 (released April 2014).",
|
||||
"download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.",
|
||||
"download.knownIssuesThree": "The <b>backpack</b> is not yet available.",
|
||||
"download.knownIssuesFour": "On Mac OS you may see a prompt indicating that \"Scratch 2 is trying to install a new helper tool\" and asking for your user name and password. We are currently investigating a solution to this problem.",
|
||||
"download.reportBugs": "Report Bugs and Glitches",
|
||||
"download.notAvailable": "Hmm, editor downloads are not available right now - please refresh the page to try again."
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ var Jobs = React.createClass({
|
|||
<h3><FormattedMessage id='jobs.openings' /></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://www.media.mit.edu/about/job-opportunities/junior-web-designer-scratch/">
|
||||
Junior Designer
|
||||
<a href="https://www.media.mit.edu/about/job-opportunities/web-designer-scratch/">
|
||||
Designer
|
||||
</a>
|
||||
<span>
|
||||
MIT Media Lab, Cambridge, MA
|
||||
|
|
115
src/views/splash/hoc/middle-banner.jsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var injectIntl = require('react-intl').injectIntl;
|
||||
var MediaQuery = require('react-responsive');
|
||||
var React = require('react');
|
||||
|
||||
var FlexRow = require('../../../components/flex-row/flex-row.jsx');
|
||||
var TitleBanner = require('../../../components/title-banner/title-banner.jsx');
|
||||
var TTTModal = require('../../../components/modal/ttt/modal.jsx');
|
||||
var TTTTile = require('../../../components/ttt-tile/ttt-tile.jsx');
|
||||
|
||||
var frameless = require('../../../lib/frameless');
|
||||
var tiles = require('../../tips/ttt');
|
||||
|
||||
require('../../../components/forms/button.scss');
|
||||
require('./middle-banner.scss');
|
||||
|
||||
var MiddleBanner = injectIntl(React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
currentTile: tiles[1],
|
||||
TTTModalOpen: false
|
||||
};
|
||||
},
|
||||
showTTTModal: function (tile) {
|
||||
return this.setState({
|
||||
currentTile: tile,
|
||||
TTTModalOpen: true
|
||||
});
|
||||
},
|
||||
hideTTTModal: function () {
|
||||
return this.setState({TTTModalOpen: false});
|
||||
},
|
||||
renderTTTTiles: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
|
||||
var tileObjects = {
|
||||
flyTile: {
|
||||
title: formatMessage({id: tiles[1].title}),
|
||||
description: formatMessage({id: tiles[1].description}),
|
||||
tutorialLoc: tiles[1].tutorialLoc,
|
||||
activityLoc: formatMessage({id: tiles[1].activityLoc}),
|
||||
guideLoc: formatMessage({id: tiles[1].guideLoc}),
|
||||
thumbUrl: tiles[1].thumbUrl,
|
||||
bannerUrl: tiles[1].bannerUrl
|
||||
},
|
||||
musicTile: {
|
||||
title: formatMessage({id: tiles[2].title}),
|
||||
description: formatMessage({id: tiles[2].description}),
|
||||
tutorialLoc: tiles[2].tutorialLoc,
|
||||
activityLoc: formatMessage({id: tiles[2].activityLoc}),
|
||||
guideLoc: formatMessage({id: tiles[2].guideLoc}),
|
||||
thumbUrl: tiles[2].thumbUrl,
|
||||
bannerUrl: tiles[2].bannerUrl
|
||||
},
|
||||
pongTile: {
|
||||
title: formatMessage({id: tiles[7].title}),
|
||||
description: formatMessage({id: tiles[7].description}),
|
||||
tutorialLoc: tiles[7].tutorialLoc,
|
||||
activityLoc: formatMessage({id: tiles[7].activityLoc}),
|
||||
guideLoc: formatMessage({id: tiles[7].guideLoc}),
|
||||
thumbUrl: tiles[7].thumbUrl,
|
||||
bannerUrl: tiles[7].bannerUrl
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
<TTTTile
|
||||
key={1}
|
||||
className="mod-banner"
|
||||
onGuideClick={this.showTTTModal.bind(this, tileObjects.flyTile)}
|
||||
{...tileObjects.flyTile}
|
||||
/>,
|
||||
<TTTTile
|
||||
key={2}
|
||||
className="mod-banner"
|
||||
onGuideClick={this.showTTTModal.bind(this, tileObjects.musicTile)}
|
||||
{...tileObjects.musicTile}
|
||||
/>,
|
||||
<TTTTile
|
||||
key={7}
|
||||
className="mod-banner mod-last-tile"
|
||||
onGuideClick={this.showTTTModal.bind(this, tileObjects.pongTile)}
|
||||
{...tileObjects.pongTile}
|
||||
/>
|
||||
];
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<TitleBanner className="mod-splash-middle">
|
||||
<div className="middle-banner inner">
|
||||
<FlexRow className="middle-banner-header">
|
||||
<h1 className="middle-banner-header-h1">
|
||||
<FormattedMessage id="middleBanner.header" />
|
||||
</h1>
|
||||
<a href="/tips" className="button mod-ttt-try-button">
|
||||
<FormattedMessage id="middleBanner.ttt" />
|
||||
</a>
|
||||
</FlexRow>
|
||||
<MediaQuery minWidth={frameless.tablet}>
|
||||
<FlexRow className="middle-banner-tiles">
|
||||
{this.renderTTTTiles()}
|
||||
</FlexRow>
|
||||
<TTTModal
|
||||
isOpen={this.state.TTTModalOpen}
|
||||
onRequestClose={this.hideTTTModal}
|
||||
{...this.state.currentTile}
|
||||
/>
|
||||
</MediaQuery>
|
||||
</div>
|
||||
</TitleBanner>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
module.exports = MiddleBanner;
|
49
src/views/splash/hoc/middle-banner.scss
Normal file
|
@ -0,0 +1,49 @@
|
|||
@import "../../../colors";
|
||||
@import "../../../frameless";
|
||||
|
||||
.title-banner.mod-splash-middle {
|
||||
background: url("/images/blocks-pattern.png");
|
||||
background-color: $ui-purple;
|
||||
background-repeat: repeat;
|
||||
background-size: 180px 180px;
|
||||
}
|
||||
|
||||
.middle-banner-header {
|
||||
margin-bottom: 1rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.middle-banner-header-h1 {
|
||||
color: $type-white;
|
||||
}
|
||||
|
||||
.mod-ttt-try-button {
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active
|
||||
&:hover {
|
||||
color: $type-white;
|
||||
}
|
||||
}
|
||||
|
||||
.ttt-tile.mod-banner {
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
|
||||
.mod-last-tile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.middle-banner-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
.title-banner.mod-splash-middle {
|
||||
display: none;
|
||||
}
|
||||
}
|
115
src/views/splash/hoc/top-banner.jsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var injectIntl = require('react-intl').injectIntl;
|
||||
var React = require('react');
|
||||
|
||||
var FlexRow = require('../../../components/flex-row/flex-row.jsx');
|
||||
var TitleBanner = require('../../../components/title-banner/title-banner.jsx');
|
||||
var TTTModal = require('../../../components/modal/ttt/modal.jsx');
|
||||
|
||||
require('../../../components/forms/button.scss');
|
||||
require('./top-banner.scss');
|
||||
|
||||
var nameTile = {
|
||||
title: 'ttt.AnimateYourNameTitle',
|
||||
description: 'ttt.AnimateYourNameDescription',
|
||||
thumbUrl: '/images/ttt/animate-your-name.jpg',
|
||||
bannerUrl: '/images/ttt/animate-your-name-banner.jpg',
|
||||
tutorialLoc: '/projects/editor/?tip_bar=name',
|
||||
activityLoc: 'cards.nameCardsLink',
|
||||
guideLoc: 'guides.NameGuideLink'
|
||||
};
|
||||
|
||||
var TopBanner = injectIntl(React.createClass({
|
||||
type: 'TopBanner',
|
||||
propTypes: {
|
||||
loggedIn: React.PropTypes.bool.isRequired
|
||||
},
|
||||
getInitialState: function () {
|
||||
// use translated tile
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
var translatedTile = {};
|
||||
translatedTile = {
|
||||
title: formatMessage({id: nameTile.title}),
|
||||
description: formatMessage({id: nameTile.description}),
|
||||
tutorialLoc: nameTile.tutorialLoc,
|
||||
activityLoc: formatMessage({id: nameTile.activityLoc}),
|
||||
guideLoc: formatMessage({id: nameTile.guideLoc}),
|
||||
thumbUrl: nameTile.thumbUrl,
|
||||
bannerUrl: nameTile.bannerUrl
|
||||
};
|
||||
return {currentTile: translatedTile, TTTModalOpen: false};
|
||||
},
|
||||
showTTTModal: function () {
|
||||
this.setState({TTTModalOpen: true});
|
||||
},
|
||||
hideTTTModal: function () {
|
||||
this.setState({TTTModalOpen: false});
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<TitleBanner className="mod-splash-top">
|
||||
<FlexRow className="banner-top inner">
|
||||
<a href="/projects/editor/?tip_bar=name">
|
||||
<FlexRow className="top-animation">
|
||||
<img
|
||||
src="/images/hoc/s.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-s"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/c1.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-c1"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/r.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-r"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/a.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-a"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/t.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-t"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/c2.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-c2"
|
||||
/>
|
||||
<img
|
||||
src="/images/hoc/h.png"
|
||||
alt=""
|
||||
className="top-animation-letter mod-letter-h"
|
||||
/>
|
||||
</FlexRow>
|
||||
</a>
|
||||
<div className="top-links">
|
||||
<a href="/projects/editor/?tip_bar=name" className="button mod-top-button">
|
||||
{ this.props.loggedIn ?
|
||||
<FormattedMessage id="ttt.AnimateYourNameTitle" /> :
|
||||
<FormattedMessage id="topBanner.getStarted" />
|
||||
}
|
||||
</a>
|
||||
<div className="mod-guides-link" onClick={this.showTTTModal}>
|
||||
|
||||
<FormattedMessage id="tile.guides" />
|
||||
<img className="top-open-modal" src="/svgs/modal/open-white.svg" />
|
||||
<TTTModal
|
||||
isOpen={this.state.TTTModalOpen}
|
||||
onRequestClose={this.hideTTTModal}
|
||||
{...this.state.currentTile}/>
|
||||
</div>
|
||||
</div>
|
||||
</FlexRow>
|
||||
</TitleBanner>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
module.exports = TopBanner;
|
||||
|
228
src/views/splash/hoc/top-banner.scss
Normal file
|
@ -0,0 +1,228 @@
|
|||
@import "../../../colors";
|
||||
@import "../../../frameless";
|
||||
|
||||
.title-banner.mod-splash-top {
|
||||
background-color: $ui-aqua;
|
||||
background-image: url("/images/hoc/splash-left.png"), url("/images/hoc/splash-right.png");
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-position: left bottom, right bottom;
|
||||
background-size: 40% auto, 40% auto;
|
||||
}
|
||||
|
||||
.banner-top {
|
||||
background-image: url("/images/hoc/doodads.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 70%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.top-banner-header {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-banner-header-h1 {
|
||||
margin-bottom: 1.25rem;
|
||||
color: $type-white;
|
||||
}
|
||||
|
||||
.banner-image.mod-top {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top-animation {
|
||||
margin: auto;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.top-animation-letter {
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: both;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes jump {
|
||||
from,
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
12.5%,
|
||||
62.5% {
|
||||
transform: translate3d(0, 10px, 0);
|
||||
}
|
||||
|
||||
37.5%,
|
||||
87.5% {
|
||||
transform: translate3d(0, -10px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-s {
|
||||
// width: 16.6%;
|
||||
animation-name: jump;
|
||||
width: 13.3%;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale3d(1.25, 1.25, 1.25);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-c1 {
|
||||
// width: 12.5%;
|
||||
animation-name: pulse;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
@keyframes spin-left {
|
||||
90% {
|
||||
transform: rotate3d(0, 0, 1, -360deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate3d(0, 0, 1, -360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-r {
|
||||
// width: 14%;
|
||||
animation-name: spin-left;
|
||||
width: 11.2%;
|
||||
}
|
||||
|
||||
@keyframes swing {
|
||||
25% {
|
||||
transform: rotate3d(0, 0, 1, 40deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: rotate3d(0, 0, 1, -40deg);
|
||||
}
|
||||
|
||||
from,
|
||||
to {
|
||||
transform: rotate3d(0, 0, 1, 0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-a {
|
||||
// width: 14.4%;
|
||||
animation-name: swing;
|
||||
width: 11.5%;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from,
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
12.5%,
|
||||
62.5% {
|
||||
transform: translate3d(-10px, 0, 0);
|
||||
}
|
||||
|
||||
37.5%,
|
||||
87.5% {
|
||||
transform: translate3d(10px, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-t {
|
||||
// width: 15.5%;
|
||||
animation-name: shake;
|
||||
width: 12.4%;
|
||||
}
|
||||
|
||||
@keyframes spin-right {
|
||||
90% {
|
||||
transform: rotate3d(0, 0, 1, 360deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate3d(0, 0, 1, 360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-c2 {
|
||||
// width: 12.5%;
|
||||
animation-name: spin-right;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
@keyframes inverse-jump {
|
||||
from,
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
12.5%,
|
||||
62.5% {
|
||||
transform: translate3d(0, -10px, 0);
|
||||
}
|
||||
|
||||
37.5%,
|
||||
87.5% {
|
||||
transform: translate3d(0, 10px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.mod-letter-h {
|
||||
// width: 14.4%;
|
||||
animation-name: inverse-jump;
|
||||
width: 11.5%;
|
||||
}
|
||||
|
||||
.mod-top-button {
|
||||
&:active &:hover,
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $type-white;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mod-top-button {
|
||||
border: 1px solid $active-gray;
|
||||
box-shadow: none;
|
||||
background-color: $ui-blue;
|
||||
}
|
||||
|
||||
.top-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mod-guides-link {
|
||||
cursor: pointer;
|
||||
padding: 1.25rem 0;
|
||||
color: $ui-white;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.top-open-modal {
|
||||
display: inline-block;
|
||||
padding: 0 .25rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
.flex-row.top-animation {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
10
src/views/splash/l10n-static.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"cards.nameCardsLink": "https://resources.scratch.mit.edu/www/cards/en/nameCards.pdf",
|
||||
"cards.flyCardsLink": "https://resources.scratch.mit.edu/www/cards/en/flyCards.pdf",
|
||||
"cards.musicCardsLink": "https://resources.scratch.mit.edu/www/cards/en/musicCards.pdf",
|
||||
"cards.pongCardsLink": "https://resources.scratch.mit.edu/www/cards/en/pongCards.pdf",
|
||||
"guides.NameGuideLink": "https://resources.scratch.mit.edu/www/guides/en/NameGuide.pdf",
|
||||
"guides.FlyGuideLink": "https://resources.scratch.mit.edu/www/guides/en/FlyGuide.pdf",
|
||||
"guides.MusicGuideLink": "https://resources.scratch.mit.edu/www/guides/en/MusicGuide.pdf",
|
||||
"guides.PongGuideLink": "https://resources.scratch.mit.edu/www/guides/en/PongGuide.pdf"
|
||||
}
|
|
@ -28,6 +28,28 @@
|
|||
"teacherbanner.subgreeting": "Teacher Account",
|
||||
"teacherbanner.classesButton": "My Classes",
|
||||
"teacherbanner.faqButton": "Teacher Account FAQ",
|
||||
|
||||
"middleBanner.header": "Get Creative with Coding",
|
||||
"middleBanner.ttt": "See more activities",
|
||||
"topBanner.getStarted": "Get Started with Coding!",
|
||||
"ttt.tutorial": "Tutorial",
|
||||
"ttt.open": "Open",
|
||||
"ttt.tutorialSubtitle": "Find out how to make this project using a step-by-step tutorial in Scratch.",
|
||||
"ttt.activityTitle": "Activity Cards",
|
||||
"ttt.activitySubtitle": "Explore new coding ideas using this set of illustrated cards you can print out.",
|
||||
"ttt.educatorTitle": "Educator Guide",
|
||||
"ttt.educatorSubtitle": "Use this educator guide to plan and lead a one-hour Scratch workshop.",
|
||||
"tile.tryIt": "Try It",
|
||||
"tile.guides": "See Cards and Guides",
|
||||
"ttt.download": "Download",
|
||||
"ttt.AnimateYourNameTitle": "Animate a Name",
|
||||
"ttt.AnimateYourNameDescription": "Animate the letters of your username, initials, or favorite word.",
|
||||
"ttt.MakeItFlyTitle": "Make It Fly",
|
||||
"ttt.MakeItFlyDescription": "Animate the Scratch Cat, The Powerpuff Girls, or even a taco!",
|
||||
"ttt.MakeMusicTitle": "Make Music",
|
||||
"ttt.MakeMusicDescription": "Choose instruments, add sounds, and press keys to play music.",
|
||||
"ttt.PongTitle": "Pong Game",
|
||||
"ttt.PongDescription": "Make a bouncing ball game with sounds, points, and other effects.",
|
||||
|
||||
"welcome.welcomeToScratch": "Welcome to Scratch!",
|
||||
"welcome.learn": "Learn how to make a project in Scratch",
|
||||
|
|
|
@ -11,6 +11,8 @@ var Box = require('../../components/box/box.jsx');
|
|||
var Button = require('../../components/forms/button.jsx');
|
||||
var Carousel = require('../../components/carousel/carousel.jsx');
|
||||
var LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx');
|
||||
var TopBanner = require('./hoc/top-banner.jsx');
|
||||
var MiddleBanner = require('./hoc/middle-banner.jsx');
|
||||
var Intro = require('../../components/intro/intro.jsx');
|
||||
var IframeModal = require('../../components/modal/iframe/modal.jsx');
|
||||
var News = require('../../components/news/news.jsx');
|
||||
|
@ -253,6 +255,8 @@ var SplashPresentation = injectIntl(React.createClass({
|
|||
{this.props.isEducator ? [
|
||||
<TeacherBanner key="teacherbanner" messages={messages} />
|
||||
] : []}
|
||||
<TopBanner loggedIn={this.props.sessionStatus === sessionActions.Status.FETCHED
|
||||
&& Object.keys(this.props.user).length !== 0}/>
|
||||
<div key="inner" className="inner mod-splash">
|
||||
{this.props.sessionStatus === sessionActions.Status.FETCHED ? (
|
||||
Object.keys(this.props.user).length !== 0 ? [
|
||||
|
@ -273,6 +277,12 @@ var SplashPresentation = injectIntl(React.createClass({
|
|||
]) : []
|
||||
}
|
||||
|
||||
{featured.shift()}
|
||||
{featured.shift()}
|
||||
</div>
|
||||
<MiddleBanner />
|
||||
<div key="inner2" className="inner mod-splash">
|
||||
|
||||
{featured}
|
||||
|
||||
{this.props.isAdmin ? [
|
||||
|
|
BIN
static/images/hoc/a.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
static/images/hoc/c1.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
static/images/hoc/c2.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
static/images/hoc/doodads.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
static/images/hoc/h.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
static/images/hoc/r.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
static/images/hoc/s.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
static/images/hoc/splash-left.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
static/images/hoc/splash-right.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
static/images/hoc/t.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
12
static/svgs/modal/open-blue.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>open-blue</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="open-blue" fill="#4C97FF">
|
||||
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z" id="open-white"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
12
static/svgs/modal/open-white.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>open-modal-icon</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="open-modal-icon" fill="#FFFFFF">
|
||||
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.2;fill:#4C97FF;stroke:#4C97FF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
.st1{fill:none;stroke:#4C97FF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
.st2{fill:#4C97FF;}
|
||||
</style>
|
||||
<path class="st0" d="M13,11H6c-0.6,0-1-0.4-1-1V3c0-0.6,0.4-1,1-1h7c0.6,0,1,0.4,1,1v7C14,10.6,13.6,11,13,11z"/>
|
||||
<path class="st1" d="M9,13c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1V8c0-0.6,0.4-1,1-1"/>
|
||||
<path class="st1" d="M13,11H6c-0.6,0-1-0.4-1-1V3c0-0.6,0.4-1,1-1h7c0.6,0,1,0.4,1,1v7C14,10.6,13.6,11,13,11z"/>
|
||||
<g>
|
||||
<path class="st2" d="M12,4.2v2.4c0,0.2-0.3,0.3-0.4,0.2L10.8,6l-3,2.7c-0.1,0.1-0.3,0.1-0.4,0c-0.1-0.1-0.1-0.3,0-0.4L10,5.2
|
||||
L9.2,4.4C9,4.3,9.1,4,9.4,4h2.4C11.9,4,12,4.1,12,4.2z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -18,6 +18,10 @@ const clickText = (text) => {
|
|||
return clickXpath(`//*[contains(text(), '${text}')]`);
|
||||
};
|
||||
|
||||
const findText = (text) => {
|
||||
return driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
|
||||
};
|
||||
|
||||
const clickButton = (text) => {
|
||||
return clickXpath(`//button[contains(text(), '${text}')]`);
|
||||
};
|
||||
|
@ -57,6 +61,7 @@ module.exports = {
|
|||
clickXpath,
|
||||
findByXpath,
|
||||
clickText,
|
||||
findText,
|
||||
clickButton,
|
||||
findByCss,
|
||||
getLogs
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"testDependencies": {
|
||||
"selenium-webdriver": "2.44.0",
|
||||
"chromedriver": "2.27.0"
|
||||
"devDependencies": {
|
||||
"selenium-webdriver": "2.45.0",
|
||||
"chromedriver": "2.33.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
require('chromedriver');
|
||||
var tap = require('tap');
|
||||
var seleniumWebdriver = require('selenium-webdriver');
|
||||
|
||||
const {
|
||||
driver,
|
||||
webdriver
|
||||
} = require('../../helpers/selenium-helpers.js');
|
||||
|
||||
// Selenium's promise driver will be deprecated, so we should not rely on it
|
||||
seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
|
||||
|
||||
//chrome driver
|
||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||
webdriver.SELENIUM_PROMISE_MANAGER=0;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
//timeout for each test; timeout for suite set at command line level
|
||||
var options = { timeout: 20000 };
|
||||
var options = { timeout: 30000 };
|
||||
|
||||
//number of tests in the plan
|
||||
tap.plan(25);
|
||||
|
@ -33,20 +33,16 @@ tap.beforeEach(function () {
|
|||
});
|
||||
|
||||
// Function clicks the link and returns the url of the resulting page
|
||||
function clickFooterLinks ( linkText ) {
|
||||
// Not sure if I need this first wait - maybe it solved intermittent initial failure problem?
|
||||
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id('view')))
|
||||
.then( function () {
|
||||
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By
|
||||
.id('footer')))
|
||||
.then( function () {
|
||||
return driver.findElement(seleniumWebdriver.By.linkText(linkText)); })
|
||||
.then( function (element) {
|
||||
return element.click(); })
|
||||
.then(function () {
|
||||
return driver.getCurrentUrl();
|
||||
});
|
||||
});
|
||||
|
||||
function clickFooterLinks (linkText) {
|
||||
return driver.wait(webdriver.until.elementLocated(webdriver.By.id('footer')))
|
||||
.then( function (element) {
|
||||
return element.findElement(webdriver.By.linkText(linkText)); })
|
||||
.then( function (element) {
|
||||
return element.click(); })
|
||||
.then(function () {
|
||||
return driver.getCurrentUrl();
|
||||
});
|
||||
}
|
||||
|
||||
// ==== ABOUT SCRATCH column ====
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Tests from:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
*/
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findByXpath,
|
||||
findText,
|
||||
clickXpath,
|
||||
clickButton,
|
||||
driver
|
||||
} = require('../../helpers/selenium-helpers.js');
|
||||
|
||||
var username = process.env.SMOKE_USERNAME;
|
||||
var password = process.env.SMOKE_PASSWORD;
|
||||
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
var url = rootUrl + '/discuss';
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(url);
|
||||
});
|
||||
|
||||
test('Sign in to Scratch using scratchr2 navbar', t => {
|
||||
clickText('Sign in')
|
||||
.then(() => findByXpath('//input[@id="login_dropdown_username"]'))
|
||||
.then((element) => element.sendKeys(username))
|
||||
.then(() => findByXpath('//input[@name="password"]'))
|
||||
.then((element) => element.sendKeys(password))
|
||||
.then(() => clickButton('Sign in'))
|
||||
.then(() => findByXpath('//li[contains(@class, "logged-in-user")'
|
||||
+ 'and contains(@class, "dropdown")]/span'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
|
||||
'first part of username should be displayed in navbar'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Sign out of Scratch using scratchr2 navbar', t => {
|
||||
clickXpath('//span[contains(@class, "user-name")'
|
||||
+ ' and contains(@class, "dropdown-toggle")]/img[@class="user-icon"]')
|
||||
.then(() => clickXpath('//input[@value="Sign out"]'))
|
||||
.then(() => findText('Sign in'))
|
||||
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
|
||||
.then(() => t.end());
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Tests signing in according to smoke-tests at:
|
||||
* Tests from:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
|||
|
||||
const {
|
||||
clickText,
|
||||
findText,
|
||||
findByXpath,
|
||||
clickXpath,
|
||||
driver
|
||||
|
@ -20,7 +21,7 @@ const test = tap.test;
|
|||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
tap.plan(1);
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
|
@ -44,3 +45,14 @@ test('Sign in to Scratch using scratch-www navbar', t => {
|
|||
'first part of username should be displayed in navbar'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Sign out of Scratch using scratch-www navbar', t => {
|
||||
clickXpath('//a[@class="user-info"]')
|
||||
.then(() => clickText('Sign out'))
|
||||
.then(() => findText('Sign in'))
|
||||
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -59,44 +59,17 @@ tap.test('getAppRouteCondition', function (t) {
|
|||
t.end();
|
||||
});
|
||||
|
||||
tap.test('testSetBackend', function (t) {
|
||||
var backend = fastlyConfig.setBackend('wemust', 'goback', 'marty');
|
||||
t.equal(backend, '' +
|
||||
'if (marty) {\n' +
|
||||
' set req.backend = wemust;\n' +
|
||||
' set req.http.host = \"goback\";\n' +
|
||||
'}\n'
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('testSetForward', function (t) {
|
||||
var forward = fastlyConfig.setForwardHeaders('alwaysforward');
|
||||
t.equal(forward, '' +
|
||||
'if (alwaysforward) {\n' +
|
||||
' if (!req.http.Fastly-FF) {\n' +
|
||||
' if (req.http.X-Forwarded-For) {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
||||
' }\n' +
|
||||
' } else {\n' +
|
||||
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
|
||||
' }\n' +
|
||||
' set req.grace = 60s;\n' +
|
||||
' return(pass);\n' +
|
||||
'}\n'
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('testSetTTL', function (t) {
|
||||
var ttl = fastlyConfig.setResponseTTL('itsactuallyttyl');
|
||||
t.equal(ttl, '' +
|
||||
'if (itsactuallyttyl) {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
|
||||
' set beresp.http.Vary = "Accept-Encoding, Accept-Language";\n' +
|
||||
' } else {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
' }\n' +
|
||||
'}\n'
|
||||
);
|
||||
t.end();
|
||||
|
|