mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 09:35:56 -05:00
Merge remote-tracking branch 'LLK/develop' into develop
This commit is contained in:
commit
ff228e022c
30 changed files with 472 additions and 319 deletions
|
@ -1,6 +1,8 @@
|
|||
rules:
|
||||
class-name-format: 0
|
||||
color-literals: 2
|
||||
final-newline: 2
|
||||
force-element-nesting: 0
|
||||
hex-notation: 2
|
||||
indentation:
|
||||
- 2
|
||||
|
@ -13,6 +15,12 @@ rules:
|
|||
max-depth: 4
|
||||
no-css-comments: 0
|
||||
no-ids: 0
|
||||
no-mergeable-selectors: 0
|
||||
no-qualifying-elements:
|
||||
- 1
|
||||
-
|
||||
- allow-element-with-attribute
|
||||
no-transition-all: 0
|
||||
property-sort-order:
|
||||
- 2
|
||||
-
|
||||
|
@ -21,4 +29,5 @@ rules:
|
|||
- 2
|
||||
-
|
||||
style: double
|
||||
shorthand-values: 0
|
||||
zero-unit: 2
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
"lodash.range": "3.0.1",
|
||||
"minilog": "2.0.8",
|
||||
"node-sass": "3.3.3",
|
||||
"pako": "0.2.8",
|
||||
"po2icu": "git://github.com/LLK/po2icu.git#develop",
|
||||
"react-addons-test-utils": "0.14.7",
|
||||
"react-modal": "0.6.1",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"react-slick": "0.9.2",
|
||||
"redux-thunk": "2.0.1",
|
||||
"routes-to-nginx-conf": "0.0.4",
|
||||
"sass-lint": "1.3.2",
|
||||
"sass-lint": "1.5.1",
|
||||
"sass-loader": "2.0.1",
|
||||
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
|
||||
"slick-carousel": "1.5.8",
|
||||
|
|
|
@ -55,7 +55,9 @@
|
|||
|
||||
<!-- Error logging (Sentry) -->
|
||||
<script>
|
||||
Raven.config('{{&sentry_dsn}}').install()
|
||||
if ('{{&sentry_dsn}}' !== '') {
|
||||
Raven.config('{{&sentry_dsn}}').install();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Analytics (GA) -->
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
font-size: large;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
@ -51,21 +51,22 @@
|
|||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
font-size: small;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
.button-row {
|
||||
display: flex;
|
||||
font-size: small;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&.inprogress {
|
||||
background-color: $ui-dark-gray;
|
||||
color: $type-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
|
||||
&.inprogress {
|
||||
background-color: $ui-dark-gray;
|
||||
color: $type-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ $navigation-height: 50px;
|
|||
text-align: center;
|
||||
line-height: $navigation-height;
|
||||
|
||||
&, a {
|
||||
&,
|
||||
a {
|
||||
color: $ui-white;
|
||||
}
|
||||
|
||||
|
@ -23,14 +24,14 @@ $navigation-height: 50px;
|
|||
|
||||
.close {
|
||||
float: right;
|
||||
margin-top: $navigation-height/4;
|
||||
border-radius: $navigation-height/4;
|
||||
margin-top: $navigation-height / 4;
|
||||
border-radius: $navigation-height / 4;
|
||||
background-color: $box-shadow-gray;
|
||||
width: $navigation-height/2;
|
||||
height: $navigation-height/2;
|
||||
width: $navigation-height / 2;
|
||||
height: $navigation-height / 2;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
line-height: $navigation-height/2;
|
||||
line-height: $navigation-height / 2;
|
||||
color: $ui-white;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ $base-bg: $ui-white;
|
|||
//4 columns
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
width: $cols4;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
font-size: .9rem;
|
||||
|
@ -21,9 +22,10 @@ $base-bg: $ui-white;
|
|||
//6 columns
|
||||
@media only screen and (min-width: $mobile) and (max-width: $tablet - 1) {
|
||||
width: $cols6;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
font-size: 1.0rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +33,7 @@ $base-bg: $ui-white;
|
|||
//8 columns
|
||||
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
|
||||
width: $cols8;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
font-size: 1.1rem;
|
||||
|
@ -41,6 +44,7 @@ $base-bg: $ui-white;
|
|||
//12 columns
|
||||
@media only screen and (min-width: $desktop) {
|
||||
width: $cols12;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
font-size: 1.1rem;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.slick-next,
|
||||
.slick-prev {
|
||||
margin-top: -$icon-size/2;
|
||||
margin-top: -$icon-size / 2;
|
||||
width: $icon-size;
|
||||
height: $icon-size;
|
||||
|
||||
|
|
|
@ -57,14 +57,6 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&:hover .costume-1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .costume-2 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.circle {
|
||||
display: block;
|
||||
top: 15px;
|
||||
|
@ -98,52 +90,74 @@
|
|||
}
|
||||
|
||||
|
||||
&.sprite-1 .circle {
|
||||
background-color: $splash-green;
|
||||
&.sprite-1 {
|
||||
.circle {
|
||||
background-color: $splash-green;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 60px;
|
||||
left: 50px;
|
||||
color: $splash-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-2 .circle {
|
||||
background-color: $splash-pink;
|
||||
&.sprite-2 {
|
||||
.circle {
|
||||
background-color: $splash-pink;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 77px;
|
||||
left: 50px;
|
||||
color: $splash-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-3 .circle {
|
||||
background-color: $splash-blue;
|
||||
&.sprite-3 {
|
||||
.circle {
|
||||
background-color: $splash-blue;
|
||||
}
|
||||
|
||||
.text {
|
||||
top: 37px;
|
||||
left: 45px;
|
||||
color: $splash-blue;
|
||||
}
|
||||
|
||||
.subtext {
|
||||
top: 63px;
|
||||
left: 60px;
|
||||
color: $ui-white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover.sprite-1 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-green;
|
||||
}
|
||||
&:hover {
|
||||
.costume-1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover.sprite-2 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-pink;
|
||||
}
|
||||
.costume-2 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover.sprite-3 .circle {
|
||||
box-shadow: 0 0 10px 2px $splash-blue;
|
||||
}
|
||||
&.sprite-1 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-1 .text {
|
||||
top: 60px;
|
||||
left: 50px;
|
||||
color: $splash-green;
|
||||
}
|
||||
&.sprite-2 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.sprite-2 .text {
|
||||
top: 77px;
|
||||
left: 50px;
|
||||
color: $splash-pink;
|
||||
}
|
||||
|
||||
&.sprite-3 .text {
|
||||
top: 37px;
|
||||
left: 45px;
|
||||
color: $splash-blue;
|
||||
}
|
||||
|
||||
&.sprite-3 .subtext {
|
||||
top: 63px;
|
||||
left: 60px;
|
||||
color: $ui-white;
|
||||
&.sprite-3 {
|
||||
.circle {
|
||||
box-shadow: 0 0 10px 2px $splash-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ var jar = require('../../lib/jar.js');
|
|||
var languages = require('../../../languages.json');
|
||||
var Select = require('../forms/select.jsx');
|
||||
|
||||
require('./languagechooser.scss');
|
||||
|
||||
/**
|
||||
* Footer dropdown menu that allows one to change their language.
|
||||
*/
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.language-chooser {
|
||||
|
||||
}
|
|
@ -40,30 +40,30 @@ var Login = React.createClass({
|
|||
return (
|
||||
<div className="login">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<label htmlFor="username">
|
||||
<label htmlFor="username" key="usernameLabel">
|
||||
<FormattedMessage
|
||||
id='general.username'
|
||||
defaultMessage={'Username'} />
|
||||
</label>
|
||||
<Input type="text" ref="username" name="username" maxLength="30" />
|
||||
<label htmlFor="password">
|
||||
<Input type="text" ref="username" name="username" maxLength="30" key="usernameInput" />
|
||||
<label htmlFor="password" key="passwordLabel">
|
||||
<FormattedMessage
|
||||
id='general.password'
|
||||
defaultMessage={'Password'} />
|
||||
</label>
|
||||
<Input type="password" ref="password" name="password" />
|
||||
<Input type="password" ref="password" name="password" key="passwordInput" />
|
||||
{this.state.waiting ? [
|
||||
<Button className="submit-button white" type="submit" disabled="disabled">
|
||||
<Button className="submit-button white" type="submit" disabled="disabled" key="submitButton">
|
||||
<Spinner />
|
||||
</Button>
|
||||
] : [
|
||||
<Button className="submit-button white" type="submit">
|
||||
<Button className="submit-button white" type="submit" key="submitButton">
|
||||
<FormattedMessage
|
||||
id='general.signIn'
|
||||
defaultMessage={'Sign in'} />
|
||||
</Button>
|
||||
]}
|
||||
<a className="right" href="/accounts/password_reset/">
|
||||
<a className="right" href="/accounts/password_reset/" key="passwordResetLink">
|
||||
<FormattedMessage
|
||||
id='login.forgotPassword'
|
||||
defaultMessage={'Forgot Password?'} />
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
a {
|
||||
margin-top: 15px;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.error {
|
||||
border: 1px solid $active-dark-gray;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import "../../colors";
|
||||
|
||||
&.ReactModal__Content {
|
||||
.ReactModal__Content {
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
@ -11,10 +11,10 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-top: -$modal-close-size/2;
|
||||
margin-right: -$modal-close-size/2;
|
||||
margin-top: -$modal-close-size / 2;
|
||||
margin-right: -$modal-close-size / 2;
|
||||
border: 2px solid $ui-border;
|
||||
border-radius: $modal-close-size/2;
|
||||
border-radius: $modal-close-size / 2;
|
||||
background-color: $active-dark-gray;
|
||||
cursor: pointer;
|
||||
width: $modal-close-size;
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -$arrow-border-width/2;
|
||||
top: -$arrow-border-width / 2;
|
||||
right: 10%;
|
||||
|
||||
transform: rotate(45deg);
|
||||
|
|
|
@ -193,7 +193,7 @@ var Navigation = React.createClass({
|
|||
'logged-in': this.props.session.user
|
||||
});
|
||||
var messageClasses = classNames({
|
||||
'messageCount': true,
|
||||
'message-count': true,
|
||||
'show': this.state.unreadMessageCount > 0
|
||||
});
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
|
@ -269,7 +269,7 @@ var Navigation = React.createClass({
|
|||
</a>
|
||||
</li>,
|
||||
<li className="link right account-nav" key="account-nav">
|
||||
<a className="userInfo" href="#" onClick={this.handleAccountNavClick}>
|
||||
<a className="user-info" href="#" onClick={this.handleAccountNavClick}>
|
||||
<Avatar src={this.props.session.user.thumbnailUrl} alt="" />
|
||||
{this.props.session.user.username}
|
||||
</a>
|
||||
|
@ -331,15 +331,17 @@ var Navigation = React.createClass({
|
|||
<a
|
||||
href="#"
|
||||
onClick={this.handleLoginClick}
|
||||
className="ignore-react-onclickoutside">
|
||||
className="ignore-react-onclickoutside"
|
||||
key="login-link">
|
||||
<FormattedMessage
|
||||
id='general.signIn'
|
||||
defaultMessage={'Sign In'} />
|
||||
</a>
|
||||
</a>
|
||||
<Dropdown
|
||||
className="login-dropdown with-arrow"
|
||||
isOpen={this.state.loginOpen}
|
||||
onRequestClose={this.closeLogin}>
|
||||
onRequestClose={this.closeLogin}
|
||||
key="login-dropdown">
|
||||
<Login
|
||||
onLogIn={this.handleLogIn}
|
||||
error={this.state.loginError} />
|
||||
|
|
|
@ -51,72 +51,73 @@
|
|||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 10px;
|
||||
.logo {
|
||||
margin-right: 10px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
transition: .15s ease all;
|
||||
margin: 0 6px 0 0;
|
||||
border: 0;
|
||||
a {
|
||||
display: block;
|
||||
transition: .15s ease all;
|
||||
margin: 0 6px 0 0;
|
||||
border: 0;
|
||||
|
||||
background-image: url("/images/logo_sm.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 95%;
|
||||
width: 81px;
|
||||
height: 50px;
|
||||
background-image: url("/images/logo_sm.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 95%;
|
||||
width: 81px;
|
||||
height: 50px;
|
||||
|
||||
&:hover {
|
||||
transition: .15s ease all;
|
||||
background-size: 100%;
|
||||
}
|
||||
&:hover {
|
||||
transition: .15s ease all;
|
||||
background-size: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 17px 15px 0 15px;
|
||||
height: 33px;
|
||||
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
color: $type-white;
|
||||
font-size: .85rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> a:hover {
|
||||
background-color: $active-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 20px;
|
||||
border-right: 0;
|
||||
.link {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 17px 15px 0 15px;
|
||||
height: 33px;
|
||||
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
color: $type-white;
|
||||
flex-grow: 3;
|
||||
font-size: .85rem;
|
||||
font-weight: bold;
|
||||
|
||||
.ie9 & {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
&:hover {
|
||||
background-color: $active-gray;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 20px;
|
||||
border-right: 0;
|
||||
color: $type-white;
|
||||
flex-grow: 3;
|
||||
|
||||
.ie9 & {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background-color: $active-gray;
|
||||
height: 14px;
|
||||
|
||||
&[type=submit] {
|
||||
position: absolute;
|
||||
|
||||
background-color: transparent;
|
||||
|
@ -129,7 +130,7 @@
|
|||
height: 40px;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
&[type=text] {
|
||||
transition: .15s ease background-color;
|
||||
padding: 0;
|
||||
padding-right: 10px;
|
||||
|
@ -148,122 +149,125 @@
|
|||
transition: .15s ease background-color;
|
||||
background-color: $active-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.ie9 input[type=text] {
|
||||
width: 70px;
|
||||
.ie9 & {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
align-self: flex-end;
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
align-self: flex-end;
|
||||
|
||||
.ie9 & {
|
||||
float: none;
|
||||
}
|
||||
.ie9 & {
|
||||
float: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
a {
|
||||
&:hover {
|
||||
background-color: $active-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.messages,
|
||||
.mystuff {
|
||||
> a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 45%;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
width: 30px;
|
||||
overflow: hidden;
|
||||
text-indent: 50px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.messages,
|
||||
.mystuff {
|
||||
> a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 45%;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
width: 30px;
|
||||
overflow: hidden;
|
||||
text-indent: 50px;
|
||||
white-space: nowrap;
|
||||
|
||||
> a:hover {
|
||||
&:hover {
|
||||
background-size: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.messages {
|
||||
> a {
|
||||
background-image: url("/images/nav-notifications.png");
|
||||
}
|
||||
}
|
||||
|
||||
.messageCount {
|
||||
display: none;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: .25rem;
|
||||
border-radius: 1rem;
|
||||
background-color: $ui-orange;
|
||||
padding: 0 .25rem;
|
||||
text-indent: 0;
|
||||
line-height: 1rem;
|
||||
color: $type-white;
|
||||
font-size: .7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.messages {
|
||||
> a {
|
||||
background-image: url("/images/nav-notifications.png");
|
||||
}
|
||||
|
||||
.mystuff {
|
||||
> a {
|
||||
background-image: url("/images/mystuff.png");
|
||||
}
|
||||
}
|
||||
.message-count {
|
||||
display: none;
|
||||
|
||||
.login-dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.account-nav {
|
||||
.userInfo {
|
||||
padding-top: 14px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .8125rem;
|
||||
font-weight: normal;
|
||||
|
||||
.avatar {
|
||||
margin-right: 10px;
|
||||
border-radius: 3px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
|
||||
background-image: url("/images/dropdown.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
content: " ";
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
&.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: .25rem;
|
||||
border-radius: 1rem;
|
||||
background-color: $ui-orange;
|
||||
padding: 0 .25rem;
|
||||
text-indent: 0;
|
||||
line-height: 1rem;
|
||||
color: $type-white;
|
||||
font-size: .7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mystuff {
|
||||
> a {
|
||||
background-image: url("/images/mystuff.png");
|
||||
}
|
||||
}
|
||||
|
||||
.login-dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.account-nav {
|
||||
.user-info {
|
||||
padding-top: 14px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .8125rem;
|
||||
font-weight: normal;
|
||||
|
||||
.avatar {
|
||||
margin-right: 10px;
|
||||
border-radius: 3px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
|
||||
background-image: url("/images/dropdown.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
content: " ";
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,10 @@
|
|||
color: $type-gray;
|
||||
font-size: .85rem;
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(even) {
|
||||
border-top: 1px solid $ui-border;
|
||||
border-bottom: 1px solid $ui-border;
|
||||
&:nth-child(even) {
|
||||
border-top: 1px solid $ui-border;
|
||||
border-bottom: 1px solid $ui-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,27 +21,33 @@
|
|||
width: 15%;
|
||||
height: 15%;
|
||||
content: "";
|
||||
-webkit-animation: circleFadeDelay 1.2s infinite ease-in-out both;
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
$rotation: 30deg * ($i - 1);
|
||||
$delay: -1.3s + $i * .1;
|
||||
|
||||
.circle#{$i} {
|
||||
transform: rotate($rotation);
|
||||
-ms-transform: rotate($rotation);
|
||||
-webkit-transform: rotate($rotation);
|
||||
}
|
||||
.circle#{$i}:before {
|
||||
animation-delay: $delay;
|
||||
-webkit-animation-delay: $delay;
|
||||
|
||||
&:before {
|
||||
animation-delay: $delay;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@keyframes circleFadeDelay {
|
||||
0%, 39%, 100% { opacity: 0; }
|
||||
40% { opacity: 1; }
|
||||
0%,
|
||||
39%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
&.description {
|
||||
/* clear styling for info element */
|
||||
border: none;
|
||||
border: 0;
|
||||
border-radius: none;
|
||||
text-decoration: none;
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
}
|
||||
|
||||
&:active {
|
||||
border: none;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
.thumbnail {
|
||||
.thumbnail-image {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumbnail-image img {
|
||||
margin-bottom: 2px;
|
||||
border: 1px solid $ui-border;
|
||||
img {
|
||||
margin-bottom: 2px;
|
||||
border: 1px solid $ui-border;
|
||||
}
|
||||
}
|
||||
|
||||
$extras: ".thumbnail-creator, .thumbnail-loves, .thumbnail-remixes";
|
||||
|
@ -26,7 +26,7 @@
|
|||
|
||||
.thumbnail-title {
|
||||
margin-bottom: 1px;
|
||||
font-size: .9230em;
|
||||
font-size: .923em;
|
||||
font-weight: 800;
|
||||
|
||||
a {
|
||||
|
@ -58,12 +58,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thumbnail-loves:before {
|
||||
background-image: url("/svgs/love/love_type-gray.svg");
|
||||
.thumbnail-loves {
|
||||
&:before {
|
||||
background-image: url("/svgs/love/love_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-remixes:before {
|
||||
background-image: url("/svgs/remix/remix_type-gray.svg");
|
||||
.thumbnail-remixes {
|
||||
&:before {
|
||||
background-image: url("/svgs/remix/remix_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
&.project {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
.box-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.welcome-col {
|
||||
display: inline-block;
|
||||
margin: 10px 15px;
|
||||
|
@ -33,26 +34,32 @@
|
|||
height: 10px;
|
||||
content: "";
|
||||
}
|
||||
|
||||
&.blue {
|
||||
#{$color-bars} {
|
||||
background-color: $splash-blue;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $splash-blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.green {
|
||||
#{$color-bars} {
|
||||
background-color: $splash-green;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $splash-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.pink {
|
||||
#{$color-bars} {
|
||||
background-color: $splash-pink;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $splash-pink;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var cookie = require('cookie');
|
||||
var xhr = require('xhr');
|
||||
var pako = require('pako');
|
||||
|
||||
/**
|
||||
* Module that handles coookie interactions.
|
||||
|
@ -9,41 +10,70 @@ var xhr = require('xhr');
|
|||
* set(name, value) – synchronously sets the cookie
|
||||
* use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there.
|
||||
*/
|
||||
var Jar = {};
|
||||
var Jar = {
|
||||
unsign: function (value, callback) {
|
||||
// Return the usable content portion of a signed, compressed cookie generated by
|
||||
// Django's signing module
|
||||
// https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
|
||||
if (!value) return callback('No value to unsign');
|
||||
try {
|
||||
var b64Data = value.split(':')[0];
|
||||
var decompress = false;
|
||||
if (b64Data[0] === '.') {
|
||||
decompress = true;
|
||||
b64Data = b64Data.substring(1);
|
||||
}
|
||||
|
||||
Jar.get = function (name, callback) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
// Django makes its base64 strings url safe by replacing + and / with - and _ respectively
|
||||
// using base64.urlsafe_b64encode
|
||||
// https://docs.python.org/2/library/base64.html#base64.b64encode
|
||||
b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; });
|
||||
var strData = atob(b64Data);
|
||||
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
if (decompress) {
|
||||
var charData = strData.split('').map(function (c) { return c.charCodeAt(0); });
|
||||
var binData = new Uint8Array(charData);
|
||||
var data = pako.inflate(binData);
|
||||
strData = String.fromCharCode.apply(null, new Uint16Array(data));
|
||||
}
|
||||
|
||||
return obj[name];
|
||||
};
|
||||
return callback(null, strData);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
},
|
||||
get: function (name, callback) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
|
||||
Jar.use = function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
return obj[name];
|
||||
},
|
||||
use: function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Jar.set = function (name, value) {
|
||||
var obj = cookie.serialize(name, value);
|
||||
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
|
||||
var path = '; path=/';
|
||||
document.cookie = obj + expires + path;
|
||||
},
|
||||
set: function (name, value) {
|
||||
var obj = cookie.serialize(name, value);
|
||||
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
|
||||
var path = '; path=/';
|
||||
document.cookie = obj + expires + path;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Jar;
|
||||
|
|
|
@ -32,29 +32,31 @@ h1 {
|
|||
|
||||
h4 {
|
||||
line-height: 1.1rem;
|
||||
font-size: 1.0rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p.legal {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
p {
|
||||
&.legal {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: $link-blue;
|
||||
}
|
||||
/* Links */
|
||||
a {
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
color: $link-blue;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Classes */
|
||||
|
|
|
@ -33,6 +33,13 @@ var Api = {
|
|||
// custom headers.
|
||||
defaults(opts, {useXDR: true});
|
||||
delete opts.headers;
|
||||
if (opts.authentication) {
|
||||
var authenticationParams = ['x-token=' + opts.authentication];
|
||||
var parts = opts.uri.split('?');
|
||||
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
|
||||
opts.uri = parts[0] + '?' + qs;
|
||||
|
||||
}
|
||||
}
|
||||
xhr(opts, function (err, res, body) {
|
||||
if (err) log.error(err);
|
||||
|
@ -50,6 +57,9 @@ var Api = {
|
|||
if (typeof jar.get('scratchlanguage') !== 'undefined') {
|
||||
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
|
||||
}
|
||||
if (opts.authentication) {
|
||||
opts.headers['X-Token'] = opts.authentication;
|
||||
}
|
||||
if (opts.useCsrf) {
|
||||
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
|
||||
if (err) return log.error('Error while retrieving CSRF token', err);
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
var keyMirror = require('keymirror');
|
||||
var api = require('../mixins/api.jsx').api;
|
||||
var jar = require('../lib/jar.js');
|
||||
|
||||
var Types = keyMirror({
|
||||
REFRESH_SESSION: null,
|
||||
SET_SESSION: null,
|
||||
SET_SESSION_ERROR: null
|
||||
SET_SESSION_ERROR: null,
|
||||
SET_TOKEN: null,
|
||||
SET_TOKEN_ERROR: null,
|
||||
USE_TOKEN: null
|
||||
});
|
||||
|
||||
var Actions = {
|
||||
|
@ -36,12 +39,45 @@ var Actions = {
|
|||
if (body.banned) {
|
||||
return window.location = url;
|
||||
} else {
|
||||
return dispatch(Actions.setSession(body));
|
||||
dispatch(Actions.getToken());
|
||||
dispatch(Actions.setSession(body));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
getToken: function () {
|
||||
return function (dispatch) {
|
||||
jar.get('scratchsessionsid', function (err, value) {
|
||||
if (err) return dispatch(Actions.setTokenError(err));
|
||||
jar.unsign(value, function (err, contents) {
|
||||
if (err) return dispatch(Actions.setTokenError(err));
|
||||
try {
|
||||
var sessionData = JSON.parse(contents);
|
||||
} catch (err) {
|
||||
return dispatch(Actions.setTokenError(err));
|
||||
}
|
||||
return dispatch(Actions.setToken(sessionData.token));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setToken: function (token) {
|
||||
return {
|
||||
type: Types.SET_TOKEN,
|
||||
token: token
|
||||
};
|
||||
},
|
||||
|
||||
setTokenError: function (error) {
|
||||
return {
|
||||
type: Types.SET_SESSION_ERROR,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Actions;
|
||||
|
|
|
@ -5,7 +5,6 @@ var actionTypes = require('./actions.js').types;
|
|||
|
||||
var sessionReducer = function (state, action) {
|
||||
// Reducer for handling changes to session state
|
||||
|
||||
if (typeof state === 'undefined') {
|
||||
state = {};
|
||||
}
|
||||
|
@ -20,8 +19,25 @@ var sessionReducer = function (state, action) {
|
|||
}
|
||||
};
|
||||
|
||||
var tokenReducer = function (state, action) {
|
||||
// Reducer for updating the api token
|
||||
if (typeof state === 'undefined') {
|
||||
state = '';
|
||||
}
|
||||
switch (action.type) {
|
||||
case actionTypes.SET_TOKEN:
|
||||
return action.token;
|
||||
case actionTypes.SET_TOKEN_ERROR:
|
||||
// TODO: do something with the error
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
var appReducer = combineReducers({
|
||||
session: sessionReducer
|
||||
session: sessionReducer,
|
||||
token: tokenReducer
|
||||
});
|
||||
|
||||
module.exports = appReducer;
|
||||
|
|
|
@ -60,7 +60,8 @@
|
|||
width: calc(384px + 5px + 5px);
|
||||
}
|
||||
|
||||
img, iframe {
|
||||
img,
|
||||
iframe {
|
||||
display: block;
|
||||
border: 1px solid $ui-gray;
|
||||
padding: 5px;
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
#view {
|
||||
p {
|
||||
line-height: 1.5rem;
|
||||
|
||||
a {
|
||||
word-wrap: break-word; /* Overrides: https://github.com/LLK/scratch-www/blob/develop/src/main.scss#L43-L47 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
|
|
|
@ -61,7 +61,7 @@ $base-bg: $ui-white;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.card-deck {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
|
|
6
static/js/lib/polyfill.min.js
vendored
6
static/js/lib/polyfill.min.js
vendored
|
@ -26,6 +26,12 @@
|
|||
*/
|
||||
(function(){try{new e("test")}catch(t){var e=function(t,e){var n;return e=e||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n};e.prototype=window.Event.prototype,window.CustomEvent=e}})();
|
||||
|
||||
/*!
|
||||
* https://github.com/davidchambers/Base64.js
|
||||
* see https://github.com/davidchambers/Base64.js/blob/master/LICENSE
|
||||
*/
|
||||
!function(){function t(t){this.message=t}var r="undefined"!=typeof exports?exports:this,e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=new Error,t.prototype.name="InvalidCharacterError",r.btoa||(r.btoa=function(r){for(var o,n,a=String(r),i=0,c=e,d="";a.charAt(0|i)||(c="=",i%1);d+=c.charAt(63&o>>8-i%1*8)){if(n=a.charCodeAt(i+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return d}),r.atob||(r.atob=function(r){var o=String(r).replace(/=+$/,"");if(o.length%4==1)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,a,i=0,c=0,d="";a=o.charAt(c++);~a&&(n=i%4?64*n+a:a,i++%4)?d+=String.fromCharCode(255&n>>(-2*i&6)):0)a=e.indexOf(a);return d})}();
|
||||
|
||||
/*!
|
||||
* https://github.com/andyearnshaw/Intl.js
|
||||
* @license The MIT License (MIT) Copyright (c) 2013 Andy Earnshaw
|
||||
|
|
Loading…
Reference in a new issue