Merge branch 'master' into zrispo-feature-code-folding
This commit is contained in:
commit
1f86eb6711
25 changed files with 871 additions and 347 deletions
|
@ -37,7 +37,8 @@
|
||||||
"react/prefer-stateless-function": [2,
|
"react/prefer-stateless-function": [2,
|
||||||
{ "ignorePureComponents": true
|
{ "ignorePureComponents": true
|
||||||
}],
|
}],
|
||||||
"class-methods-use-this": 0
|
"class-methods-use-this": 0,
|
||||||
|
"react/jsx-no-bind": [2, {"allowBind": true, "allowArrowFunctions": true}]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"react", "jsx-a11y", "import"
|
"react", "jsx-a11y", "import"
|
||||||
|
|
|
@ -1,167 +1,463 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import InlineSVG from 'react-inlinesvg';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import {
|
||||||
|
metaKeyName,
|
||||||
|
} from '../utils/metaKey';
|
||||||
|
|
||||||
|
const triangleUrl = require('../images/down-filled-triangle.svg');
|
||||||
|
const logoUrl = require('../images/p5js-logo-small.svg');
|
||||||
|
|
||||||
class Nav extends React.PureComponent {
|
class Nav extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
dropdownOpen: 'none'
|
||||||
|
};
|
||||||
|
this.handleFocus = this.handleFocus.bind(this);
|
||||||
|
this.handleBlur = this.handleBlur.bind(this);
|
||||||
|
this.clearHideTimeout = this.clearHideTimeout.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDropdown(dropdown) {
|
||||||
|
this.setState({
|
||||||
|
dropdownOpen: dropdown
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDropdown(dropdown) {
|
||||||
|
if (this.state.dropdownOpen === 'none') {
|
||||||
|
this.setState({
|
||||||
|
dropdownOpen: dropdown
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
dropdownOpen: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isUserOwner() {
|
||||||
|
return this.props.project.owner && this.props.project.owner.id === this.props.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFocus(dropdown) {
|
||||||
|
this.clearHideTimeout();
|
||||||
|
this.setDropdown(dropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHideTimeout() {
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const navDropdownState = {
|
||||||
|
file: classNames({
|
||||||
|
'nav__item': true,
|
||||||
|
'nav__item--open': this.state.dropdownOpen === 'file'
|
||||||
|
}),
|
||||||
|
edit: classNames({
|
||||||
|
'nav__item': true,
|
||||||
|
'nav__item--open': this.state.dropdownOpen === 'edit'
|
||||||
|
}),
|
||||||
|
sketch: classNames({
|
||||||
|
'nav__item': true,
|
||||||
|
'nav__item--open': this.state.dropdownOpen === 'sketch'
|
||||||
|
}),
|
||||||
|
help: classNames({
|
||||||
|
'nav__item': true,
|
||||||
|
'nav__item--open': this.state.dropdownOpen === 'help'
|
||||||
|
}),
|
||||||
|
account: classNames({
|
||||||
|
'nav__item': true,
|
||||||
|
'nav__item--open': this.state.dropdownOpen === 'account'
|
||||||
|
})
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<nav className="nav" role="navigation" title="main-navigation">
|
<nav className="nav" role="navigation" title="main-navigation">
|
||||||
<ul className="nav__items-left" title="project-menu">
|
<ul className="nav__items-left" title="project-menu">
|
||||||
<li className="nav__item">
|
<li className="nav__item-logo">
|
||||||
|
<InlineSVG src={logoUrl} alt="p5.js logo" />
|
||||||
|
</li>
|
||||||
|
<li className={navDropdownState.file}>
|
||||||
<button
|
<button
|
||||||
className="nav__new"
|
onClick={this.toggleDropdown.bind(this, 'file')}
|
||||||
onClick={() => {
|
onBlur={this.handleBlur}
|
||||||
if (!this.props.unsavedChanges) {
|
onFocus={this.clearHideTimeout}
|
||||||
this.props.newProject();
|
|
||||||
} else if (this.props.warnIfUnsavedChanges()) {
|
|
||||||
this.props.newProject();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
New
|
<span className="nav__item-header">File</span>
|
||||||
|
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
<ul className="nav__dropdown">
|
||||||
{(() => { // eslint-disable-line
|
<li className="nav__dropdown-heading">
|
||||||
if (
|
<span>File</span>
|
||||||
!this.props.project.owner ||
|
<InlineSVG src={triangleUrl} />
|
||||||
(this.props.project.owner && this.props.project.owner.id === this.props.user.id)
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<button
|
|
||||||
className="nav__save"
|
|
||||||
onClick={() => {
|
|
||||||
if (this.props.user.authenticated) {
|
|
||||||
this.props.saveProject();
|
|
||||||
} else {
|
|
||||||
this.props.showErrorModal('forceAuthentication');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
{(() => { // eslint-disable-line
|
|
||||||
if (this.props.project.id && this.props.user.authenticated) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<button className="nav__clone" onClick={this.props.cloneProject}>
|
|
||||||
Duplicate
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
{(() => { // eslint-disable-line
|
|
||||||
if (this.props.project.id) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<button className="nav__export" onClick={() => this.props.exportProjectAsZip(this.props.project.id)}>
|
|
||||||
Download
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
{(() => { // eslint-disable-line
|
|
||||||
if (this.props.project.id) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<button onClick={this.props.showShareModal}>
|
|
||||||
Share
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
{(() => { // eslint-disable-line
|
|
||||||
if (this.props.user.authenticated) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<p className="nav__open">
|
|
||||||
<Link
|
|
||||||
to={`/${this.props.user.username}/sketches`}
|
|
||||||
>
|
|
||||||
Open
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
<li className="nav__item">
|
|
||||||
<p className="nav__open">
|
|
||||||
<Link
|
|
||||||
to="/p5/sketches"
|
|
||||||
>
|
|
||||||
Examples
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li className="nav__item">
|
|
||||||
<p className="nav__reference">
|
|
||||||
<a
|
|
||||||
href="https://p5js.org/reference/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>Reference</a>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li className="nav__item">
|
|
||||||
<p className="nav__about">
|
|
||||||
<Link to="/about">
|
|
||||||
About
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul className="nav__items-right" title="user-menu">
|
|
||||||
{(() => {
|
|
||||||
if (!this.props.user.authenticated) {
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<p>
|
|
||||||
<Link to="/login">Log in</Link>
|
|
||||||
<span className="nav__item-spacer">or</span>
|
|
||||||
<Link to="/signup">Sign up</Link>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<li className="nav__item">
|
|
||||||
<a>Hello, {this.props.user.username}!</a>
|
|
||||||
<ul className="nav__dropdown">
|
|
||||||
<li className="nav__dropdown-heading">
|
|
||||||
<a>Hello, {this.props.user.username}!</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to={`/${this.props.user.username}/sketches`}>
|
|
||||||
My sketches
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to={`/${this.props.user.username}/assets`}>
|
|
||||||
My assets
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to={`/${this.props.user.username}/account`}>
|
|
||||||
My account
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onClick={this.props.logoutUser} >
|
|
||||||
Log out
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
<li className="nav__dropdown-item">
|
||||||
})()}
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (!this.props.unsavedChanges) {
|
||||||
|
this.props.newProject();
|
||||||
|
} else if (this.props.warnIfUnsavedChanges()) {
|
||||||
|
this.props.newProject();
|
||||||
|
}
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
New
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{ (!this.props.project.owner || this.isUserOwner()) &&
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (this.props.user.authenticated) {
|
||||||
|
this.props.saveProject();
|
||||||
|
} else {
|
||||||
|
this.props.showErrorModal('forceAuthentication');
|
||||||
|
}
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
<span className="nav__keyboard-shortcut">{metaKeyName}+s</span>
|
||||||
|
</button>
|
||||||
|
</li> }
|
||||||
|
{ this.props.project.id && this.props.user.authenticated &&
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.cloneProject();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Duplicate
|
||||||
|
</button>
|
||||||
|
</li> }
|
||||||
|
{ this.props.project.id &&
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.showShareModal();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Share
|
||||||
|
</button>
|
||||||
|
</li> }
|
||||||
|
{ this.props.project.id &&
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.exportProjectAsZip(this.props.project.id);
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</button>
|
||||||
|
</li> }
|
||||||
|
{ this.props.user.authenticated &&
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to={`/${this.props.user.username}/sketches`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
Open
|
||||||
|
</Link>
|
||||||
|
</li> }
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to="/p5/sketches"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
Examples
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li className={navDropdownState.edit}>
|
||||||
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
|
<span className="nav__item-header">Edit</span>
|
||||||
|
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||||
|
</button>
|
||||||
|
<ul className="nav__dropdown">
|
||||||
|
<li className="nav__dropdown-heading">
|
||||||
|
<span>Edit</span>
|
||||||
|
<InlineSVG src={triangleUrl} />
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.cmController.tidyCode();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Tidy Code
|
||||||
|
<span className="nav__keyboard-shortcut">{'\u21E7'}+Tab</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.cmController.showFind();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Find
|
||||||
|
<span className="nav__keyboard-shortcut">{metaKeyName}+F</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.cmController.findNext();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Find Next
|
||||||
|
<span className="nav__keyboard-shortcut">{metaKeyName}+G</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.cmController.findPrev();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Find Previous
|
||||||
|
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+G</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li className={navDropdownState.sketch}>
|
||||||
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
|
<span className="nav__item-header">Sketch</span>
|
||||||
|
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||||
|
</button>
|
||||||
|
<ul className="nav__dropdown">
|
||||||
|
<li className="nav__dropdown-heading">
|
||||||
|
<span>Sketch</span>
|
||||||
|
<InlineSVG src={triangleUrl} />
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.startSketch();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Run
|
||||||
|
<span className="nav__keyboard-shortcut">{metaKeyName}+Enter</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.stopSketch();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Stop
|
||||||
|
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+Enter</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.setAllAccessibleOutput(true);
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Start Accessible
|
||||||
|
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+1</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.setAllAccessibleOutput(false);
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Stop Accessible
|
||||||
|
<span className="nav__keyboard-shortcut">{'\u21E7'}+{metaKeyName}+2</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li className={navDropdownState.help}>
|
||||||
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
|
<span className="nav__item-header">Help</span>
|
||||||
|
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||||
|
</button>
|
||||||
|
<ul className="nav__dropdown">
|
||||||
|
<li className="nav__dropdown-heading">
|
||||||
|
<span>Help</span>
|
||||||
|
<InlineSVG src={triangleUrl} />
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.showKeyboardShortcutModal();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Keyboard Shortcuts
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<a
|
||||||
|
href="https://p5js.org/reference/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>Reference</a>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to="/about"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
About
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{ !this.props.user.authenticated &&
|
||||||
|
<ul className="nav__items-right" title="user-menu">
|
||||||
|
<li className="nav__item">
|
||||||
|
<p>
|
||||||
|
<Link to="/login">Log in</Link>
|
||||||
|
<span className="nav__item-spacer">or</span>
|
||||||
|
<Link to="/signup">Sign up</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>}
|
||||||
|
{ this.props.user.authenticated &&
|
||||||
|
<ul className="nav__items-right" title="user-menu">
|
||||||
|
<li className="nav__item">
|
||||||
|
<span>Hello, {this.props.user.username}!</span>
|
||||||
|
</li>
|
||||||
|
<span className="nav__item-spacer">|</span>
|
||||||
|
<li className={navDropdownState.account}>
|
||||||
|
<button
|
||||||
|
className="nav__item-header"
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
|
My Account
|
||||||
|
</button>
|
||||||
|
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||||
|
<ul className="nav__dropdown">
|
||||||
|
<li className="nav__dropdown-heading">
|
||||||
|
<span>My Account</span>
|
||||||
|
<InlineSVG src={triangleUrl} />
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to={`/${this.props.user.username}/sketches`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
My sketches
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to={`/${this.props.user.username}/assets`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
My assets
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<Link
|
||||||
|
to={`/${this.props.user.username}/account`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onClick={this.setDropdown.bind(this, 'none')}
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className="nav__dropdown-item">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.props.logoutUser();
|
||||||
|
this.setDropdown('none');
|
||||||
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
|
Log out
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul> }
|
||||||
|
{/*
|
||||||
<div className="nav__announce">
|
<div className="nav__announce">
|
||||||
This is a preview version of the editor, that has not yet been officially released.
|
This is a preview version of the editor, that has not yet been officially released.
|
||||||
It is in development, you can report bugs <a
|
It is in development, you can report bugs <a
|
||||||
|
@ -171,6 +467,7 @@ class Nav extends React.PureComponent {
|
||||||
>here</a>.
|
>here</a>.
|
||||||
Please use with caution.
|
Please use with caution.
|
||||||
</div>
|
</div>
|
||||||
|
*/}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -196,14 +493,25 @@ Nav.propTypes = {
|
||||||
showShareModal: PropTypes.func.isRequired,
|
showShareModal: PropTypes.func.isRequired,
|
||||||
showErrorModal: PropTypes.func.isRequired,
|
showErrorModal: PropTypes.func.isRequired,
|
||||||
unsavedChanges: PropTypes.bool.isRequired,
|
unsavedChanges: PropTypes.bool.isRequired,
|
||||||
warnIfUnsavedChanges: PropTypes.func.isRequired
|
warnIfUnsavedChanges: PropTypes.func.isRequired,
|
||||||
|
showKeyboardShortcutModal: PropTypes.func.isRequired,
|
||||||
|
cmController: PropTypes.shape({
|
||||||
|
tidyCode: PropTypes.func,
|
||||||
|
showFind: PropTypes.func,
|
||||||
|
findNext: PropTypes.func,
|
||||||
|
findPrev: PropTypes.func
|
||||||
|
}),
|
||||||
|
startSketch: PropTypes.func.isRequired,
|
||||||
|
stopSketch: PropTypes.func.isRequired,
|
||||||
|
setAllAccessibleOutput: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
Nav.defaultProps = {
|
Nav.defaultProps = {
|
||||||
project: {
|
project: {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
owner: undefined
|
owner: undefined
|
||||||
}
|
},
|
||||||
|
cmController: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Nav;
|
export default Nav;
|
||||||
|
|
10
client/images/down-filled-triangle.svg
Normal file
10
client/images/down-filled-triangle.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="9px" height="6px" viewBox="0 0 9 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Triangle</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<polygon id="Triangle" fill="#B5B5B5" transform="translate(4.500000, 3.000000) rotate(180.000000) translate(-4.500000, -3.000000) " points="4.5 0 9 6 0 6"></polygon>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 633 B |
20
client/images/p5js-logo-small.svg
Normal file
20
client/images/p5js-logo-small.svg
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="56" height="42" viewBox="0 0 56 42">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M0 0h36v36H0z"/>
|
||||||
|
<path id="b" d="M0 0h36v36H0z"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<path fill="#ED225D" d="M0 0h56v42H0z"/>
|
||||||
|
<g transform="translate(10)">
|
||||||
|
<use fill="#FFF" xlink:href="#a"/>
|
||||||
|
<path stroke="#979797" d="M.5.5h35v35H.5z"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#ED225D" d="M21.025 20.617a2.826 2.826 0 0 0-.941-.795c-.38-.201-.824-.302-1.334-.302-.481 0-.912.104-1.29.314-.38.209-.704.478-.974.805-.27.329-.474.702-.612 1.12a3.85 3.85 0 0 0 0 2.496c.138.411.342.78.612 1.109.27.328.594.593.974.794.378.202.809.302 1.29.302.51 0 .955-.104 1.334-.313.38-.209.693-.477.94-.806.248-.328.434-.701.559-1.12a4.283 4.283 0 0 0 0-2.497c-.125-.409-.31-.778-.558-1.107"/>
|
||||||
|
<g transform="translate(10)">
|
||||||
|
<mask id="c" fill="#fff">
|
||||||
|
<use xlink:href="#b"/>
|
||||||
|
</mask>
|
||||||
|
<path fill="#ED225D" d="M32.75 16.44l-.989.717-1.026-1.361-1.001 1.326-.963-.734.975-1.337-1.582-.571.374-1.15 1.586.511v-1.644h1.273v1.647l1.574-.476.373 1.15-1.57.536.975 1.385zm-7.045 8.959a5.208 5.208 0 0 1-1.25 1.769c-.524.481-1.139.85-1.844 1.105a6.559 6.559 0 0 1-2.25.383c-1.334 0-2.46-.312-3.374-.936a5.238 5.238 0 0 1-1.533-1.614l1.953-1.835.03-.01a3.14 3.14 0 0 0 1.125 1.52c.525.384 1.147.576 1.867.576.405 0 .787-.068 1.147-.204.36-.134.675-.33.945-.585.27-.256.484-.575.641-.958.158-.383.237-.815.237-1.296 0-.616-.106-1.127-.315-1.533a2.775 2.775 0 0 0-.821-.98 3.075 3.075 0 0 0-1.136-.519 5.634 5.634 0 0 0-1.282-.146c-.3 0-.619.022-.957.067-.337.046-.667.11-.99.192-.322.083-.637.177-.944.281a6.717 6.717 0 0 0-.821.338l.27-8.722h9.065v2.434h-6.501l-.157 3.449c.254-.075.558-.128.91-.158.353-.03.672-.045.957-.045.78 0 1.503.112 2.17.338a5.15 5.15 0 0 1 1.744.98c.495.429.88.955 1.158 1.578.278.624.416 1.341.416 2.153 0 .887-.153 1.679-.46 2.378zm-11.601-.36a5.504 5.504 0 0 1-1.058 1.825 5.08 5.08 0 0 1-1.664 1.24c-.653.307-1.392.462-2.216.462a4.678 4.678 0 0 1-2.103-.474c-.638-.315-1.13-.743-1.474-1.285h-.045v6.853h-2.7V17.43h2.588v1.51h.067c.135-.21.311-.424.529-.642.217-.218.484-.413.799-.587a4.908 4.908 0 0 1 2.407-.597c.764 0 1.469.147 2.114.44.645.293 1.2.695 1.665 1.206a5.465 5.465 0 0 1 1.08 1.803c.254.691.382 1.435.382 2.231 0 .797-.124 1.544-.371 2.243zM0 36.07h36V0H0v36.071z" mask="url(#c)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -57,14 +57,15 @@ Overlay.propTypes = {
|
||||||
closeOverlay: PropTypes.func,
|
closeOverlay: PropTypes.func,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
ariaLabel: PropTypes.string,
|
ariaLabel: PropTypes.string,
|
||||||
previousPath: PropTypes.string.isRequired
|
previousPath: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
Overlay.defaultProps = {
|
Overlay.defaultProps = {
|
||||||
children: null,
|
children: null,
|
||||||
title: 'Modal',
|
title: 'Modal',
|
||||||
closeOverlay: null,
|
closeOverlay: null,
|
||||||
ariaLabel: 'modal'
|
ariaLabel: 'modal',
|
||||||
|
previousPath: '/'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Overlay;
|
export default Overlay;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as ActionTypes from '../../../constants';
|
import * as ActionTypes from '../../../constants';
|
||||||
|
import { clearConsole } from './console';
|
||||||
|
|
||||||
export function startSketch() {
|
export function startVisualSketch() {
|
||||||
return {
|
return {
|
||||||
type: ActionTypes.START_SKETCH
|
type: ActionTypes.START_SKETCH
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stopSketch() {
|
export function stopVisualSketch() {
|
||||||
return {
|
return {
|
||||||
type: ActionTypes.STOP_SKETCH
|
type: ActionTypes.STOP_SKETCH
|
||||||
};
|
};
|
||||||
|
@ -20,7 +21,7 @@ export function startRefreshSketch() {
|
||||||
|
|
||||||
export function startSketchAndRefresh() {
|
export function startSketchAndRefresh() {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(startSketch());
|
dispatch(startVisualSketch());
|
||||||
dispatch(startRefreshSketch());
|
dispatch(startRefreshSketch());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -233,3 +234,26 @@ export function hideHelpModal() {
|
||||||
type: ActionTypes.HIDE_HELP_MODAL
|
type: ActionTypes.HIDE_HELP_MODAL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function startSketch() {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(clearConsole());
|
||||||
|
dispatch(startSketchAndRefresh());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startAccessibleSketch() {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(clearConsole());
|
||||||
|
dispatch(startAccessibleOutput());
|
||||||
|
dispatch(startSketchAndRefresh());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopSketch() {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(stopAccessibleOutput());
|
||||||
|
dispatch(stopVisualSketch());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,3 +216,12 @@ export function setAutorefresh(value) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setAllAccessibleOutput(value) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(setTextOutput(value));
|
||||||
|
dispatch(setGridOutput(value));
|
||||||
|
dispatch(setSoundOutput(value));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,6 @@ window.CSSLint = CSSLint;
|
||||||
window.HTMLHint = HTMLHint;
|
window.HTMLHint = HTMLHint;
|
||||||
|
|
||||||
const beepUrl = require('../../../sounds/audioAlert.mp3');
|
const beepUrl = require('../../../sounds/audioAlert.mp3');
|
||||||
const downArrowUrl = require('../../../images/down-arrow.svg');
|
|
||||||
const unsavedChangesDotUrl = require('../../../images/unsaved-changes-dot.svg');
|
const unsavedChangesDotUrl = require('../../../images/unsaved-changes-dot.svg');
|
||||||
const rightArrowUrl = require('../../../images/right-arrow.svg');
|
const rightArrowUrl = require('../../../images/right-arrow.svg');
|
||||||
const leftArrowUrl = require('../../../images/left-arrow.svg');
|
const leftArrowUrl = require('../../../images/left-arrow.svg');
|
||||||
|
@ -54,6 +53,9 @@ class Editor extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.tidyCode = this.tidyCode.bind(this);
|
this.tidyCode = this.tidyCode.bind(this);
|
||||||
|
this.showFind = this.showFind.bind(this);
|
||||||
|
this.findNext = this.findNext.bind(this);
|
||||||
|
this.findPrev = this.findPrev.bind(this);
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.beep = new Audio(beepUrl);
|
this.beep = new Audio(beepUrl);
|
||||||
|
@ -85,7 +87,8 @@ class Editor extends React.Component {
|
||||||
options: {
|
options: {
|
||||||
'asi': true,
|
'asi': true,
|
||||||
'eqeqeq': false,
|
'eqeqeq': false,
|
||||||
'-W041': false
|
'-W041': false,
|
||||||
|
'esversion': 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -126,6 +129,13 @@ class Editor extends React.Component {
|
||||||
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
|
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
|
||||||
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
|
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
|
||||||
this._cm.setOption('tabSize', this.props.indentationAmount);
|
this._cm.setOption('tabSize', this.props.indentationAmount);
|
||||||
|
|
||||||
|
this.props.provideController({
|
||||||
|
tidyCode: this.tidyCode,
|
||||||
|
showFind: this.showFind,
|
||||||
|
findNext: this.findNext,
|
||||||
|
findPrev: this.findPrev
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate(nextProps) {
|
componentWillUpdate(nextProps) {
|
||||||
|
@ -165,6 +175,7 @@ class Editor extends React.Component {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._cm = null;
|
this._cm = null;
|
||||||
|
this.props.provideController(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileMode(fileName) {
|
getFileMode(fileName) {
|
||||||
|
@ -208,6 +219,20 @@ class Editor extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showFind() {
|
||||||
|
this._cm.execCommand('findPersistent');
|
||||||
|
}
|
||||||
|
|
||||||
|
findNext() {
|
||||||
|
this._cm.focus();
|
||||||
|
this._cm.execCommand('findNext');
|
||||||
|
}
|
||||||
|
|
||||||
|
findPrev() {
|
||||||
|
this._cm.focus();
|
||||||
|
this._cm.execCommand('findPrev');
|
||||||
|
}
|
||||||
|
|
||||||
toggleEditorOptions() {
|
toggleEditorOptions() {
|
||||||
if (this.props.editorOptionsVisible) {
|
if (this.props.editorOptionsVisible) {
|
||||||
this.props.closeEditorOptions();
|
this.props.closeEditorOptions();
|
||||||
|
@ -257,26 +282,6 @@ class Editor extends React.Component {
|
||||||
isUserOwner={this.props.isUserOwner}
|
isUserOwner={this.props.isUserOwner}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="editor__options-button"
|
|
||||||
aria-label="editor options"
|
|
||||||
tabIndex="0"
|
|
||||||
ref={(element) => { this.optionsButton = element; }}
|
|
||||||
onClick={() => {
|
|
||||||
this.toggleEditorOptions();
|
|
||||||
}}
|
|
||||||
onBlur={() => setTimeout(this.props.closeEditorOptions, 200)}
|
|
||||||
>
|
|
||||||
<InlineSVG src={downArrowUrl} />
|
|
||||||
</button>
|
|
||||||
<ul className="editor__options" title="editor options">
|
|
||||||
<li>
|
|
||||||
<button onClick={this.tidyCode}>Tidy</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onClick={this.props.showKeyboardShortcutModal}>Keyboard shortcuts</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</header>
|
</header>
|
||||||
<div ref={(element) => { this.codemirrorContainer = element; }} className="editor-holder" tabIndex="0">
|
<div ref={(element) => { this.codemirrorContainer = element; }} className="editor-holder" tabIndex="0">
|
||||||
</div>
|
</div>
|
||||||
|
@ -310,7 +315,6 @@ Editor.propTypes = {
|
||||||
editorOptionsVisible: PropTypes.bool.isRequired,
|
editorOptionsVisible: PropTypes.bool.isRequired,
|
||||||
showEditorOptions: PropTypes.func.isRequired,
|
showEditorOptions: PropTypes.func.isRequired,
|
||||||
closeEditorOptions: PropTypes.func.isRequired,
|
closeEditorOptions: PropTypes.func.isRequired,
|
||||||
showKeyboardShortcutModal: PropTypes.func.isRequired,
|
|
||||||
setUnsavedChanges: PropTypes.func.isRequired,
|
setUnsavedChanges: PropTypes.func.isRequired,
|
||||||
startRefreshSketch: PropTypes.func.isRequired,
|
startRefreshSketch: PropTypes.func.isRequired,
|
||||||
autorefresh: PropTypes.bool.isRequired,
|
autorefresh: PropTypes.bool.isRequired,
|
||||||
|
@ -327,7 +331,8 @@ Editor.propTypes = {
|
||||||
collapseSidebar: PropTypes.func.isRequired,
|
collapseSidebar: PropTypes.func.isRequired,
|
||||||
expandSidebar: PropTypes.func.isRequired,
|
expandSidebar: PropTypes.func.isRequired,
|
||||||
isUserOwner: PropTypes.bool,
|
isUserOwner: PropTypes.bool,
|
||||||
clearConsole: PropTypes.func.isRequired
|
clearConsole: PropTypes.func.isRequired,
|
||||||
|
provideController: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
Editor.defaultProps = {
|
Editor.defaultProps = {
|
||||||
|
|
|
@ -10,24 +10,24 @@ class GridOutput extends React.Component {
|
||||||
id="gridOutput-content"
|
id="gridOutput-content"
|
||||||
ref={(element) => { this.GridOutputModal = element; }}
|
ref={(element) => { this.GridOutputModal = element; }}
|
||||||
>
|
>
|
||||||
<h2> Grid Output </h2>
|
<h2> table Output </h2>
|
||||||
<p
|
<p
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
role="main"
|
role="main"
|
||||||
id="gridOutput-content-summary"
|
id="gridOutput-content-summary"
|
||||||
aria-label="grid output summary"
|
aria-label="table output summary"
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<table
|
<table
|
||||||
id="gridOutput-content-table"
|
id="gridOutput-content-table"
|
||||||
summary="grid output details"
|
summary="table output details"
|
||||||
>
|
>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<div
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
role="main"
|
role="main"
|
||||||
id="gridOutput-content-details"
|
id="gridOutput-content-details"
|
||||||
aria-label="grid output details"
|
aria-label="table output details"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
24
client/modules/IDE/components/HTTPSModal.jsx
Normal file
24
client/modules/IDE/components/HTTPSModal.jsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function HTTPSModal() {
|
||||||
|
return (
|
||||||
|
<section className="help-modal">
|
||||||
|
<div className="help-modal__section">
|
||||||
|
<div>
|
||||||
|
<p>Use the checkbox to choose whether this sketch should be loaded using HTTPS or HTTP.</p>
|
||||||
|
<p>You should choose HTTPS if you need to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>access a webcam or microphone</li>
|
||||||
|
<li>access an API served over HTTPS</li>
|
||||||
|
</ul>
|
||||||
|
<p>Choose HTTP if you need to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>access an API served over HTTP</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HTTPSModal;
|
|
@ -1,61 +0,0 @@
|
||||||
import React, { PropTypes } from 'react';
|
|
||||||
import InlineSVG from 'react-inlinesvg';
|
|
||||||
|
|
||||||
const exitUrl = require('../../../images/exit.svg');
|
|
||||||
|
|
||||||
const helpContent = {
|
|
||||||
serveSecure: {
|
|
||||||
title: 'Serve over HTTPS',
|
|
||||||
body: (
|
|
||||||
<div>
|
|
||||||
<p>Use the checkbox to choose whether this sketch should be loaded using HTTPS or HTTP.</p>
|
|
||||||
<p>You should choose HTTPS if you need to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>access a webcam or microphone</li>
|
|
||||||
<li>access an API served over HTTPS</li>
|
|
||||||
</ul>
|
|
||||||
<p>Choose HTTP if you need to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>access an API served over HTTP</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fallbackContent = {
|
|
||||||
title: 'No content for this topic',
|
|
||||||
body: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
class HelpModal extends React.Component {
|
|
||||||
componentDidMount() {
|
|
||||||
this.shareModal.focus();
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
const content = helpContent[this.props.type] == null ?
|
|
||||||
fallbackContent :
|
|
||||||
helpContent[this.props.type];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="help-modal" ref={(element) => { this.shareModal = element; }} tabIndex="0">
|
|
||||||
<header className="help-modal__header">
|
|
||||||
<h2>{content.title}</h2>
|
|
||||||
<button className="about__exit-button" onClick={this.props.closeModal}>
|
|
||||||
<InlineSVG src={exitUrl} alt="Close Help Overlay" />
|
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
<div className="help-modal__section">
|
|
||||||
{content.body}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HelpModal.propTypes = {
|
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
closeModal: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HelpModal;
|
|
|
@ -8,7 +8,7 @@ function KeyboardShortcutModal() {
|
||||||
return (
|
return (
|
||||||
<ul className="keyboard-shortcuts" title="keyboard shortcuts">
|
<ul className="keyboard-shortcuts" title="keyboard shortcuts">
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
<span className="keyboard-shortcut__command">Shift + Tab</span>
|
<span className="keyboard-shortcut__command">{'\u21E7'} + Tab</span>
|
||||||
<span>Tidy</span>
|
<span>Tidy</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
|
@ -31,7 +31,7 @@ function KeyboardShortcutModal() {
|
||||||
</li>
|
</li>
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
<span className="keyboard-shortcut__command">
|
<span className="keyboard-shortcut__command">
|
||||||
{metaKeyName} + Shift + G
|
{metaKeyName} + {'\u21E7'} + G
|
||||||
</span>
|
</span>
|
||||||
<span>Find Previous Text Match</span>
|
<span>Find Previous Text Match</span>
|
||||||
</li>
|
</li>
|
||||||
|
@ -61,21 +61,21 @@ function KeyboardShortcutModal() {
|
||||||
</li>
|
</li>
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
<span className="keyboard-shortcut__command">
|
<span className="keyboard-shortcut__command">
|
||||||
{metaKeyName} + Shift + Enter
|
{metaKeyName} + {'\u21E7'} + Enter
|
||||||
</span>
|
</span>
|
||||||
<span>Stop Sketch</span>
|
<span>Stop Sketch</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
<span className="keyboard-shortcut__command">
|
<span className="keyboard-shortcut__command">
|
||||||
{metaKeyName} + Shift + 1
|
{metaKeyName} + {'\u21E7'} + 1
|
||||||
</span>
|
</span>
|
||||||
<span>Toggle Text-based Canvas</span>
|
<span>Turn on Accessible Output</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="keyboard-shortcut-item">
|
<li className="keyboard-shortcut-item">
|
||||||
<span className="keyboard-shortcut__command">
|
<span className="keyboard-shortcut__command">
|
||||||
{metaKeyName} + Shift + 2
|
{metaKeyName} + {'\u21E7'} + 2
|
||||||
</span>
|
</span>
|
||||||
<span>Turn Off Text-based Canvas</span>
|
<span>Turn off Accessible Output</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,8 +34,8 @@ function getAllScriptOffsets(htmlFile) {
|
||||||
} else {
|
} else {
|
||||||
endFilenameInd = htmlFile.indexOf('.js', ind + startTag.length + 3);
|
endFilenameInd = htmlFile.indexOf('.js', ind + startTag.length + 3);
|
||||||
filename = htmlFile.substring(ind + startTag.length, endFilenameInd);
|
filename = htmlFile.substring(ind + startTag.length, endFilenameInd);
|
||||||
// the length of hijackConsoleErrorsScript is 35 lines, already needed a -1 offset.
|
// the length of hijackConsoleErrorsScript is 31 lines
|
||||||
lineOffset = htmlFile.substring(0, ind).split('\n').length + 34;
|
lineOffset = htmlFile.substring(0, ind).split('\n').length + 31;
|
||||||
offs.push([lineOffset, filename]);
|
offs.push([lineOffset, filename]);
|
||||||
lastInd = ind + 1;
|
lastInd = ind + 1;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,7 @@ class PreviewFrame extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('message', (messageEvent) => {
|
window.addEventListener('message', (messageEvent) => {
|
||||||
|
console.log(messageEvent);
|
||||||
messageEvent.data.forEach((message) => {
|
messageEvent.data.forEach((message) => {
|
||||||
const args = message.arguments;
|
const args = message.arguments;
|
||||||
Object.keys(args).forEach((key) => {
|
Object.keys(args).forEach((key) => {
|
||||||
|
@ -152,6 +153,14 @@ class PreviewFrame extends React.Component {
|
||||||
doc.srcDoc = '';
|
doc.srcDoc = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addLoopProtect(sketchDoc) {
|
||||||
|
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
|
||||||
|
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
|
||||||
|
scriptsInHTMLArray.forEach((script) => {
|
||||||
|
script.innerHTML = loopProtect(script.innerHTML); // eslint-disable-line
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
injectLocalFiles() {
|
injectLocalFiles() {
|
||||||
const htmlFile = this.props.htmlFile.content;
|
const htmlFile = this.props.htmlFile.content;
|
||||||
let scriptOffs = [];
|
let scriptOffs = [];
|
||||||
|
@ -227,7 +236,7 @@ class PreviewFrame extends React.Component {
|
||||||
scriptOffs = getAllScriptOffsets(sketchDocString);
|
scriptOffs = getAllScriptOffsets(sketchDocString);
|
||||||
const consoleErrorsScript = sketchDoc.createElement('script');
|
const consoleErrorsScript = sketchDoc.createElement('script');
|
||||||
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
||||||
// sketchDoc.head.appendChild(consoleErrorsScript);
|
this.addLoopProtect(sketchDoc);
|
||||||
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
|
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
|
||||||
|
|
||||||
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;
|
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;
|
||||||
|
@ -280,7 +289,10 @@ class PreviewFrame extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
newContent = decomment(newContent, { ignore: /noprotect/g });
|
newContent = decomment(newContent, {
|
||||||
|
ignore: /noprotect/g,
|
||||||
|
space: true
|
||||||
|
});
|
||||||
newContent = loopProtect(newContent);
|
newContent = loopProtect(newContent);
|
||||||
return newContent;
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import classNames from 'classnames';
|
||||||
import InlineSVG from 'react-inlinesvg';
|
import InlineSVG from 'react-inlinesvg';
|
||||||
|
|
||||||
const playUrl = require('../../../images/play.svg');
|
const playUrl = require('../../../images/play.svg');
|
||||||
const logoUrl = require('../../../images/p5js-logo.svg');
|
|
||||||
const stopUrl = require('../../../images/stop.svg');
|
const stopUrl = require('../../../images/stop.svg');
|
||||||
const preferencesUrl = require('../../../images/preferences.svg');
|
const preferencesUrl = require('../../../images/preferences.svg');
|
||||||
const editProjectNameUrl = require('../../../images/pencil.svg');
|
const editProjectNameUrl = require('../../../images/pencil.svg');
|
||||||
|
@ -59,14 +58,9 @@ class Toolbar extends React.Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="toolbar">
|
<div className="toolbar">
|
||||||
<InlineSVG className="toolbar__logo" src={logoUrl} alt="p5js Logo" />
|
|
||||||
<button
|
<button
|
||||||
className="toolbar__play-sketch-button"
|
className="toolbar__play-sketch-button"
|
||||||
onClick={() => {
|
onClick={this.props.startAccessibleSketch}
|
||||||
this.props.clearConsole();
|
|
||||||
this.props.startAccessibleOutput();
|
|
||||||
this.props.startSketchAndRefresh();
|
|
||||||
}}
|
|
||||||
aria-label="play sketch"
|
aria-label="play sketch"
|
||||||
disabled={this.props.infiniteLoop}
|
disabled={this.props.infiniteLoop}
|
||||||
>
|
>
|
||||||
|
@ -74,10 +68,7 @@ class Toolbar extends React.Component {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={playButtonClass}
|
className={playButtonClass}
|
||||||
onClick={() => {
|
onClick={this.props.startSketch}
|
||||||
this.props.clearConsole();
|
|
||||||
this.props.startSketchAndRefresh();
|
|
||||||
}}
|
|
||||||
aria-label="play only visual sketch"
|
aria-label="play only visual sketch"
|
||||||
disabled={this.props.infiniteLoop}
|
disabled={this.props.infiniteLoop}
|
||||||
>
|
>
|
||||||
|
@ -85,7 +76,7 @@ class Toolbar extends React.Component {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={stopButtonClass}
|
className={stopButtonClass}
|
||||||
onClick={() => { this.props.stopAccessibleOutput(); this.props.stopSketch(); }}
|
onClick={this.props.stopSketch}
|
||||||
aria-label="stop sketch"
|
aria-label="stop sketch"
|
||||||
>
|
>
|
||||||
<InlineSVG src={stopUrl} alt="Stop Sketch" />
|
<InlineSVG src={stopUrl} alt="Stop Sketch" />
|
||||||
|
@ -187,8 +178,6 @@ Toolbar.propTypes = {
|
||||||
isPlaying: PropTypes.bool.isRequired,
|
isPlaying: PropTypes.bool.isRequired,
|
||||||
preferencesIsVisible: PropTypes.bool.isRequired,
|
preferencesIsVisible: PropTypes.bool.isRequired,
|
||||||
stopSketch: PropTypes.func.isRequired,
|
stopSketch: PropTypes.func.isRequired,
|
||||||
startAccessibleOutput: PropTypes.func.isRequired,
|
|
||||||
stopAccessibleOutput: PropTypes.func.isRequired,
|
|
||||||
setProjectName: PropTypes.func.isRequired,
|
setProjectName: PropTypes.func.isRequired,
|
||||||
openPreferences: PropTypes.func.isRequired,
|
openPreferences: PropTypes.func.isRequired,
|
||||||
owner: PropTypes.shape({
|
owner: PropTypes.shape({
|
||||||
|
@ -207,10 +196,10 @@ Toolbar.propTypes = {
|
||||||
autorefresh: PropTypes.bool.isRequired,
|
autorefresh: PropTypes.bool.isRequired,
|
||||||
setAutorefresh: PropTypes.func.isRequired,
|
setAutorefresh: PropTypes.func.isRequired,
|
||||||
setServeSecure: PropTypes.func.isRequired,
|
setServeSecure: PropTypes.func.isRequired,
|
||||||
startSketchAndRefresh: PropTypes.func.isRequired,
|
startSketch: PropTypes.func.isRequired,
|
||||||
|
startAccessibleSketch: PropTypes.func.isRequired,
|
||||||
saveProject: PropTypes.func.isRequired,
|
saveProject: PropTypes.func.isRequired,
|
||||||
currentUser: PropTypes.string,
|
currentUser: PropTypes.string
|
||||||
clearConsole: PropTypes.func.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Toolbar.defaultProps = {
|
Toolbar.defaultProps = {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import NewFolderModal from '../components/NewFolderModal';
|
||||||
import ShareModal from '../components/ShareModal';
|
import ShareModal from '../components/ShareModal';
|
||||||
import KeyboardShortcutModal from '../components/KeyboardShortcutModal';
|
import KeyboardShortcutModal from '../components/KeyboardShortcutModal';
|
||||||
import ErrorModal from '../components/ErrorModal';
|
import ErrorModal from '../components/ErrorModal';
|
||||||
import HelpModal from '../components/HelpModal';
|
import HTTPSModal from '../components/HTTPSModal';
|
||||||
import Nav from '../../../components/Nav';
|
import Nav from '../../../components/Nav';
|
||||||
import Console from '../components/Console';
|
import Console from '../components/Console';
|
||||||
import Toast from '../components/Toast';
|
import Toast from '../components/Toast';
|
||||||
|
@ -165,18 +165,15 @@ class IDEView extends React.Component {
|
||||||
} else if (e.keyCode === 13 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
} else if (e.keyCode === 13 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.clearConsole();
|
this.props.startSketch();
|
||||||
this.props.startSketchAndRefresh();
|
// 50 === 2
|
||||||
} else if (e.keyCode === 50 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
} else if (e.keyCode === 50 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.setTextOutput(false);
|
this.props.setAllAccessibleOutput(false);
|
||||||
this.props.setGridOutput(false);
|
// 49 === 1
|
||||||
this.props.setSoundOutput(false);
|
|
||||||
} else if (e.keyCode === 49 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
} else if (e.keyCode === 49 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.setTextOutput(true);
|
this.props.setAllAccessibleOutput(true);
|
||||||
this.props.setGridOutput(true);
|
|
||||||
this.props.setSoundOutput(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,18 +210,20 @@ class IDEView extends React.Component {
|
||||||
cloneProject={this.props.cloneProject}
|
cloneProject={this.props.cloneProject}
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
logoutUser={this.props.logoutUser}
|
logoutUser={this.props.logoutUser}
|
||||||
|
startSketch={this.props.startSketch}
|
||||||
stopSketch={this.props.stopSketch}
|
stopSketch={this.props.stopSketch}
|
||||||
showShareModal={this.props.showShareModal}
|
showShareModal={this.props.showShareModal}
|
||||||
showErrorModal={this.props.showErrorModal}
|
showErrorModal={this.props.showErrorModal}
|
||||||
unsavedChanges={this.props.ide.unsavedChanges}
|
unsavedChanges={this.props.ide.unsavedChanges}
|
||||||
warnIfUnsavedChanges={this.warnIfUnsavedChanges}
|
warnIfUnsavedChanges={this.warnIfUnsavedChanges}
|
||||||
|
showKeyboardShortcutModal={this.props.showKeyboardShortcutModal}
|
||||||
|
cmController={this.cmController}
|
||||||
|
setAllAccessibleOutput={this.props.setAllAccessibleOutput}
|
||||||
/>
|
/>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
className="Toolbar"
|
className="Toolbar"
|
||||||
isPlaying={this.props.ide.isPlaying}
|
isPlaying={this.props.ide.isPlaying}
|
||||||
stopSketch={this.props.stopSketch}
|
stopSketch={this.props.stopSketch}
|
||||||
startAccessibleOutput={this.props.startAccessibleOutput}
|
|
||||||
stopAccessibleOutput={this.props.stopAccessibleOutput}
|
|
||||||
projectName={this.props.project.name}
|
projectName={this.props.project.name}
|
||||||
setProjectName={this.props.setProjectName}
|
setProjectName={this.props.setProjectName}
|
||||||
showEditProjectName={this.props.showEditProjectName}
|
showEditProjectName={this.props.showEditProjectName}
|
||||||
|
@ -241,10 +240,10 @@ class IDEView extends React.Component {
|
||||||
infiniteLoop={this.props.ide.infiniteLoop}
|
infiniteLoop={this.props.ide.infiniteLoop}
|
||||||
autorefresh={this.props.preferences.autorefresh}
|
autorefresh={this.props.preferences.autorefresh}
|
||||||
setAutorefresh={this.props.setAutorefresh}
|
setAutorefresh={this.props.setAutorefresh}
|
||||||
startSketchAndRefresh={this.props.startSketchAndRefresh}
|
startSketch={this.props.startSketch}
|
||||||
|
startAccessibleSketch={this.props.startAccessibleSketch}
|
||||||
saveProject={this.props.saveProject}
|
saveProject={this.props.saveProject}
|
||||||
currentUser={this.props.user.username}
|
currentUser={this.props.user.username}
|
||||||
clearConsole={this.props.clearConsole}
|
|
||||||
showHelpModal={this.props.showHelpModal}
|
showHelpModal={this.props.showHelpModal}
|
||||||
/>
|
/>
|
||||||
<Preferences
|
<Preferences
|
||||||
|
@ -300,8 +299,8 @@ class IDEView extends React.Component {
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split="vertical"
|
split="vertical"
|
||||||
defaultSize={'50%'}
|
defaultSize={'50%'}
|
||||||
onChange={() => (this.overlay.style.display = 'block')}
|
onChange={() => { this.overlay.style.display = 'block'; }}
|
||||||
onDragFinished={() => (this.overlay.style.display = 'none')}
|
onDragFinished={() => { this.overlay.style.display = 'none'; }}
|
||||||
>
|
>
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split="horizontal"
|
split="horizontal"
|
||||||
|
@ -341,6 +340,7 @@ class IDEView extends React.Component {
|
||||||
collapseSidebar={this.props.collapseSidebar}
|
collapseSidebar={this.props.collapseSidebar}
|
||||||
isUserOwner={this.isUserOwner()}
|
isUserOwner={this.isUserOwner()}
|
||||||
clearConsole={this.props.clearConsole}
|
clearConsole={this.props.clearConsole}
|
||||||
|
provideController={(ctl) => { this.cmController = ctl; }}
|
||||||
/>
|
/>
|
||||||
<Console
|
<Console
|
||||||
consoleEvents={this.props.console}
|
consoleEvents={this.props.console}
|
||||||
|
@ -516,17 +516,16 @@ class IDEView extends React.Component {
|
||||||
{(() => { // eslint-disable-line
|
{(() => { // eslint-disable-line
|
||||||
if (this.props.ide.helpType) {
|
if (this.props.ide.helpType) {
|
||||||
return (
|
return (
|
||||||
<Overlay>
|
<Overlay
|
||||||
<HelpModal
|
title="Serve over HTTPS"
|
||||||
type={this.props.ide.helpType}
|
closeOverlay={this.props.hideHelpModal}
|
||||||
closeModal={this.props.hideHelpModal}
|
>
|
||||||
/>
|
<HTTPSModal />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -572,8 +571,6 @@ IDEView.propTypes = {
|
||||||
helpType: PropTypes.string
|
helpType: PropTypes.string
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
stopSketch: PropTypes.func.isRequired,
|
stopSketch: PropTypes.func.isRequired,
|
||||||
startAccessibleOutput: PropTypes.func.isRequired,
|
|
||||||
stopAccessibleOutput: PropTypes.func.isRequired,
|
|
||||||
project: PropTypes.shape({
|
project: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
|
@ -614,6 +611,7 @@ IDEView.propTypes = {
|
||||||
setTextOutput: PropTypes.func.isRequired,
|
setTextOutput: PropTypes.func.isRequired,
|
||||||
setGridOutput: PropTypes.func.isRequired,
|
setGridOutput: PropTypes.func.isRequired,
|
||||||
setSoundOutput: PropTypes.func.isRequired,
|
setSoundOutput: PropTypes.func.isRequired,
|
||||||
|
setAllAccessibleOutput: PropTypes.func.isRequired,
|
||||||
files: PropTypes.arrayOf(PropTypes.shape({
|
files: PropTypes.arrayOf(PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
|
@ -672,7 +670,6 @@ IDEView.propTypes = {
|
||||||
setUnsavedChanges: PropTypes.func.isRequired,
|
setUnsavedChanges: PropTypes.func.isRequired,
|
||||||
setTheme: PropTypes.func.isRequired,
|
setTheme: PropTypes.func.isRequired,
|
||||||
setAutorefresh: PropTypes.func.isRequired,
|
setAutorefresh: PropTypes.func.isRequired,
|
||||||
startSketchAndRefresh: PropTypes.func.isRequired,
|
|
||||||
endSketchRefresh: PropTypes.func.isRequired,
|
endSketchRefresh: PropTypes.func.isRequired,
|
||||||
startRefreshSketch: PropTypes.func.isRequired,
|
startRefreshSketch: PropTypes.func.isRequired,
|
||||||
setBlobUrl: PropTypes.func.isRequired,
|
setBlobUrl: PropTypes.func.isRequired,
|
||||||
|
@ -687,7 +684,9 @@ IDEView.propTypes = {
|
||||||
clearPersistedState: PropTypes.func.isRequired,
|
clearPersistedState: PropTypes.func.isRequired,
|
||||||
persistState: PropTypes.func.isRequired,
|
persistState: PropTypes.func.isRequired,
|
||||||
showHelpModal: PropTypes.func.isRequired,
|
showHelpModal: PropTypes.func.isRequired,
|
||||||
hideHelpModal: PropTypes.func.isRequired
|
hideHelpModal: PropTypes.func.isRequired,
|
||||||
|
startSketch: PropTypes.func.isRequired,
|
||||||
|
startAccessibleSketch: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
|
|
|
@ -27,6 +27,10 @@ class LoginView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.user.authenticated) {
|
||||||
|
this.gotoHomePage();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="form-container">
|
<div className="form-container">
|
||||||
<div className="form-container__header">
|
<div className="form-container__header">
|
||||||
|
@ -70,7 +74,16 @@ function mapDispatchToProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginView.propTypes = {
|
LoginView.propTypes = {
|
||||||
previousPath: PropTypes.string.isRequired
|
previousPath: PropTypes.string.isRequired,
|
||||||
|
user: {
|
||||||
|
authenticated: PropTypes.bool
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LoginView.defaultProps = {
|
||||||
|
user: {
|
||||||
|
authenticated: false
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default reduxForm({
|
export default reduxForm({
|
||||||
|
|
|
@ -27,6 +27,10 @@ class SignupView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.user.authenticated) {
|
||||||
|
this.gotoHomePage();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="form-container">
|
<div className="form-container">
|
||||||
<div className="form-container__header">
|
<div className="form-container__header">
|
||||||
|
@ -84,7 +88,16 @@ function onSubmitFail(errors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SignupView.propTypes = {
|
SignupView.propTypes = {
|
||||||
previousPath: PropTypes.string.isRequired
|
previousPath: PropTypes.string.isRequired,
|
||||||
|
user: {
|
||||||
|
authenticated: PropTypes.bool
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SignupView.defaultProps = {
|
||||||
|
user: {
|
||||||
|
authenticated: false
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default reduxForm({
|
export default reduxForm({
|
||||||
|
|
|
@ -15,7 +15,7 @@ $themes: (
|
||||||
primary-text-color: #333,
|
primary-text-color: #333,
|
||||||
modal-button-color: #333,
|
modal-button-color: #333,
|
||||||
heading-text-color: #333,
|
heading-text-color: #333,
|
||||||
secondary-text-color: #6b6b6b,
|
secondary-text-color: #a8a8a8,
|
||||||
inactive-text-color: #b5b5b5,
|
inactive-text-color: #b5b5b5,
|
||||||
background-color: #fbfbfb,
|
background-color: #fbfbfb,
|
||||||
preview-placeholder-color: #dcdcdc,
|
preview-placeholder-color: #dcdcdc,
|
||||||
|
@ -30,7 +30,8 @@ $themes: (
|
||||||
button-active-color: $white,
|
button-active-color: $white,
|
||||||
modal-background-color: #f4f4f4,
|
modal-background-color: #f4f4f4,
|
||||||
modal-button-background-color: #e6e6e6,
|
modal-button-background-color: #e6e6e6,
|
||||||
modal-border-color: #B9D0E1,
|
modal-border-color: rgba(17, 17, 17, 0.3),
|
||||||
|
modal-boder-selected-color: #B9D0E1,
|
||||||
icon-color: $icon-color,
|
icon-color: $icon-color,
|
||||||
icon-hover-color: $icon-hover-color,
|
icon-hover-color: $icon-hover-color,
|
||||||
icon-toast-hover-color: $white,
|
icon-toast-hover-color: $white,
|
||||||
|
@ -44,14 +45,17 @@ $themes: (
|
||||||
input-text-color: #333,
|
input-text-color: #333,
|
||||||
input-border-color: #b5b5b5,
|
input-border-color: #b5b5b5,
|
||||||
about-list-text-color: #4a4a4a,
|
about-list-text-color: #4a4a4a,
|
||||||
search-background-color: #ebebeb
|
search-background-color: #ebebeb,
|
||||||
|
dropdown-color: #414141,
|
||||||
|
keyboard-shortcut-color: #757575,
|
||||||
|
nav-hover-color: $p5js-pink,
|
||||||
),
|
),
|
||||||
dark: (
|
dark: (
|
||||||
logo-color: $p5js-pink,
|
logo-color: $p5js-pink,
|
||||||
primary-text-color: $white,
|
primary-text-color: $white,
|
||||||
modal-button-color: $white,
|
modal-button-color: $white,
|
||||||
heading-text-color: $white,
|
heading-text-color: $white,
|
||||||
secondary-text-color: #c2c2c2,
|
secondary-text-color: #DADADA,
|
||||||
inactive-text-color: #b5b5b5,
|
inactive-text-color: #b5b5b5,
|
||||||
background-color: #333,
|
background-color: #333,
|
||||||
preview-placeholder-color: #dcdcdc,
|
preview-placeholder-color: #dcdcdc,
|
||||||
|
@ -80,7 +84,10 @@ $themes: (
|
||||||
input-text-color: #333,
|
input-text-color: #333,
|
||||||
input-border-color: #b5b5b5,
|
input-border-color: #b5b5b5,
|
||||||
about-list-text-color: #f4f4f4,
|
about-list-text-color: #f4f4f4,
|
||||||
search-background-color: #ebebeb
|
search-background-color: #ebebeb,
|
||||||
|
dropdown-color: #dadada,
|
||||||
|
keyboard-shortcut-color: #B5B5B5,
|
||||||
|
nav-hover-color: $p5js-pink,
|
||||||
),
|
),
|
||||||
contrast: (
|
contrast: (
|
||||||
logo-color: $yellow,
|
logo-color: $yellow,
|
||||||
|
@ -115,7 +122,10 @@ $themes: (
|
||||||
input-text-color: #333,
|
input-text-color: #333,
|
||||||
input-border-color: #b5b5b5,
|
input-border-color: #b5b5b5,
|
||||||
about-list-text-color: #f4f4f4,
|
about-list-text-color: #f4f4f4,
|
||||||
search-background-color: $white
|
search-background-color: $white,
|
||||||
|
dropdown-color: #e1e1e1,
|
||||||
|
keyboard-shortcut-color: #e1e1e1,
|
||||||
|
nav-hover-color: $yellow
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,41 @@
|
||||||
.nav {
|
.nav {
|
||||||
width: 100%;
|
width: calc(100% - #{10 / $base-font-size}rem);
|
||||||
padding: #{10 / $base-font-size}rem #{32 / $base-font-size}rem 0 #{32 / $base-font-size}rem;
|
height: #{42 / $base-font-size}rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
|
||||||
|
|
||||||
.nav__items-left, .nav__items-right {
|
|
||||||
@include themify() {
|
@include themify() {
|
||||||
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
||||||
}
|
}
|
||||||
|
& button {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__items-left, .nav__items-right {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: #{3 / $base-font-size}rem 0;
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__items-left {
|
.nav__items-left {
|
||||||
& button {
|
& button {
|
||||||
@include themify() {
|
@include themify() {
|
||||||
color: getThemifyVariable('inactive-text-color');
|
color: getThemifyVariable('secondary-text-color');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__item {
|
.nav__item {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 #{24 / $base-font-size}rem;
|
padding: 0 #{10 / $base-font-size}rem;
|
||||||
text-align: center;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__item:first-child {
|
.nav__item:first-child {
|
||||||
|
@ -39,38 +46,57 @@
|
||||||
padding-right: #{15 / $base-font-size}rem;
|
padding-right: #{15 / $base-font-size}rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav__item-header {
|
||||||
|
margin-right: #{5 / $base-font-size}rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__item:hover {
|
||||||
|
.nav__item-header {
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('nav-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nav__item-header-triangle polygon {
|
||||||
|
@include themify() {
|
||||||
|
fill: getThemifyVariable('nav-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.nav__dropdown {
|
.nav__dropdown {
|
||||||
@include themify() {
|
@include themify() {
|
||||||
background-color: map-get($theme-map, 'modal-background-color');
|
background-color: map-get($theme-map, 'modal-background-color');
|
||||||
border: 1px solid map-get($theme-map, 'modal-border-color');
|
border: 1px solid map-get($theme-map, 'modal-border-color');
|
||||||
box-shadow: 0 0 18px getThemifyVariable('shadow-color');
|
box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color');
|
||||||
|
color: getThemifyVariable('dropdown-color');
|
||||||
}
|
}
|
||||||
@extend %hidden-element;
|
display: none;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: #{140 / $base-font-size}rem;
|
width: #{180 / $base-font-size}rem;
|
||||||
.nav__item:hover & {
|
.nav__item--open & {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
top: #{-8 / $base-font-size}rem;;
|
top: 4px;
|
||||||
left: #{-11 / $base-font-size}rem;
|
left: 0;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
padding-bottom: #{8 / $base-font-size}rem;
|
z-index: 9999;
|
||||||
z-index: 1;
|
border-radius: #{6 / $base-font-size}rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__items-right {
|
||||||
|
padding-right: #{20 / $base-font-size}rem;
|
||||||
|
& .nav__dropdown {
|
||||||
|
width: #{121 / $base-font-size}rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__item-spacer {
|
.nav__item-spacer {
|
||||||
@include themify() {
|
@include themify() {
|
||||||
color: map-get($theme-map, 'inactive-text-color');
|
color: map-get($theme-map, 'inactive-text-color');
|
||||||
|
margin: 0 #{8 / $base-font-size}rem;
|
||||||
}
|
}
|
||||||
padding: 0 #{15 / $base-font-size}rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav__dropdown li {
|
|
||||||
padding: #{4 / $base-font-size}rem #{16 / $base-font-size}rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__dropdown a, button {
|
.nav__dropdown a, button {
|
||||||
|
@ -93,9 +119,24 @@
|
||||||
@include themify() {
|
@include themify() {
|
||||||
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
||||||
}
|
}
|
||||||
margin-top: #{3 / $base-font-size}rem;
|
height: #{(42 - 5) / $base-font-size}rem;
|
||||||
text-align: center;
|
display: flex;
|
||||||
margin-bottom: #{4 / $base-font-size}rem;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 #{16 / $base-font-size}rem;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
span {
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('nav-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
polygon {
|
||||||
|
@include themify() {
|
||||||
|
fill: getThemifyVariable('nav-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__dropdown-heading a, .nav__dropdown-heading a:hover {
|
.nav__dropdown-heading a, .nav__dropdown-heading a:hover {
|
||||||
|
@ -106,6 +147,48 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav__dropdown-heading svg {
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__dropdown-item {
|
||||||
|
height: #{32 / $base-font-size}rem;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 #{16 / $base-font-size}rem;
|
||||||
|
cursor: pointer;
|
||||||
|
& button, & a {
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('dropdown-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
@include themify() {
|
||||||
|
background-color: getThemifyVariable('button-background-hover-color');
|
||||||
|
color: getThemifyVariable('button-hover-color')
|
||||||
|
}
|
||||||
|
& button, & a {
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('button-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& button, & a {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__dropdown-item:last-child {
|
||||||
|
border-radius: 0 0 #{6 / $base-font-size}rem #{6 / $base-font-size}rem;
|
||||||
|
}
|
||||||
|
|
||||||
.nav__announce {
|
.nav__announce {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: #{40 / $base-font-size}rem;
|
top: #{40 / $base-font-size}rem;
|
||||||
|
@ -121,3 +204,25 @@
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav__item-logo {
|
||||||
|
position: relative;
|
||||||
|
height: #{42 / $base-font-size}rem;
|
||||||
|
width: #{56 / $base-font-size}rem;
|
||||||
|
& span {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__keyboard-shortcut {
|
||||||
|
font-size: #{12 / $base-font-size}rem;
|
||||||
|
font-family: Inconsololata, monospace;
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('keyboard-shortcut-color');
|
||||||
|
}
|
||||||
|
.nav__dropdown-item:hover & {
|
||||||
|
@include themify() {
|
||||||
|
color: getThemifyVariable('button-hover-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ $p5-dark-white: #FDFDFD;
|
||||||
$p5-dark-orange: #EE9900;
|
$p5-dark-orange: #EE9900;
|
||||||
$p5-dark-lightgray: #E0D7D1;
|
$p5-dark-lightgray: #E0D7D1;
|
||||||
$p5-dark-darkgray: #666666;
|
$p5-dark-darkgray: #666666;
|
||||||
|
$p5-dark-green: #58a10b;
|
||||||
|
$p5-dark-goldbrown: #b58317;
|
||||||
|
|
||||||
$p5-dark-gutter: #f4f4f4;
|
$p5-dark-gutter: #f4f4f4;
|
||||||
$p5-dark-number: #b5b5b5;
|
$p5-dark-number: #b5b5b5;
|
||||||
|
@ -41,7 +43,7 @@ $p5-dark-activeline: rgb(207, 207, 207);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-dark .cm-string {
|
.cm-s-p5-dark .cm-string {
|
||||||
color: $p5-dark-lightblue;
|
color: $p5-dark-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-dark .cm-string-2 {
|
.cm-s-p5-dark .cm-string-2 {
|
||||||
|
@ -49,11 +51,11 @@ $p5-dark-activeline: rgb(207, 207, 207);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-dark .cm-number {
|
.cm-s-p5-dark .cm-number {
|
||||||
color: $p5-dark-pink;
|
color: $p5-dark-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-dark .cm-keyword {
|
.cm-s-p5-dark .cm-keyword {
|
||||||
color: $p5-light-green;
|
color: $p5-dark-goldbrown;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-dark .cm-variable {
|
.cm-s-p5-dark .cm-variable {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
$p5-light-lightbrown: #A67F59;
|
$p5-light-lightbrown: #A67F59;
|
||||||
$p5-light-brown: #704F21;
|
$p5-light-brown: #704F21;
|
||||||
$p5-light-black: #333;
|
$p5-light-black: #333333;
|
||||||
$p5-light-pink: #D9328F;
|
$p5-light-pink: #D9328F;
|
||||||
$p5-light-gray: #A0A0A0;
|
$p5-light-gray: #A0A0A0;
|
||||||
$p5-light-lightblue: #00A1D3;
|
$p5-light-lightblue: #00A1D3;
|
||||||
|
@ -21,6 +21,7 @@ $p5-light-white: #FDFDFD;
|
||||||
$p5-light-orange: #EE9900;
|
$p5-light-orange: #EE9900;
|
||||||
$p5-light-lightgray: #E0D7D1;
|
$p5-light-lightgray: #E0D7D1;
|
||||||
$p5-light-darkgray: #666666;
|
$p5-light-darkgray: #666666;
|
||||||
|
$p5-light-green: #58a10b;
|
||||||
|
|
||||||
$p5-light-gutter: #f4f4f4;
|
$p5-light-gutter: #f4f4f4;
|
||||||
$p5-light-number: #b5b5b5;
|
$p5-light-number: #b5b5b5;
|
||||||
|
@ -37,11 +38,11 @@ $p5-light-activeline: rgb(207, 207, 207);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-def {
|
.cm-s-p5-light .cm-def {
|
||||||
color: $p5-light-darkblue;
|
color: $p5-light-lightblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-string {
|
.cm-s-p5-light .cm-string {
|
||||||
color: $p5-light-lightblue;
|
color: $p5-light-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-string-2 {
|
.cm-s-p5-light .cm-string-2 {
|
||||||
|
@ -49,7 +50,7 @@ $p5-light-activeline: rgb(207, 207, 207);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-number {
|
.cm-s-p5-light .cm-number {
|
||||||
color: $p5-light-pink;
|
color: $p5-light-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-keyword {
|
.cm-s-p5-light .cm-keyword {
|
||||||
|
@ -60,7 +61,7 @@ $p5-light-activeline: rgb(207, 207, 207);
|
||||||
color: $p5-light-lightblue;
|
color: $p5-light-lightblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-variable-2 {
|
.cm-s-p5-light .cm-variable2 {
|
||||||
color: $p5-light-black;
|
color: $p5-light-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,14 +115,14 @@ $p5-light-activeline: rgb(207, 207, 207);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-attribute {
|
.cm-s-p5-light .cm-attribute {
|
||||||
color: $p5-light-lightblue;
|
color: $p5-light-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-p5-function {
|
.cm-s-p5-light .cm-p5-function {
|
||||||
color: $p5-light-darkblue;
|
color: $p5-light-darkblue;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-p5-light .cm-p5-variable {
|
.cm-s-p5-light .cm-p5-variable {
|
||||||
color: $p5-light-pink;
|
color: $p5-light-pink;
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ const metaKey = (() => {
|
||||||
return 'Ctrl';
|
return 'Ctrl';
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const metaKeyName = metaKey === 'Cmd' ? 'Command' : 'Control';
|
const metaKeyName = metaKey === 'Cmd' ? '\u2318' : 'Ctrl';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
metaKey,
|
metaKey,
|
||||||
|
|
|
@ -136,6 +136,34 @@ export function getProjectsForUserId(userId) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProjectAsset(req, res) {
|
||||||
|
Project.findById(req.params.project_id)
|
||||||
|
.populate('user', 'username')
|
||||||
|
.exec((err, project) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(404).send({ message: 'Project with that id does not exist' });
|
||||||
|
}
|
||||||
|
|
||||||
|
var assetURL = null;
|
||||||
|
var seekPath = req.params[0]; // req.params.asset_path;
|
||||||
|
var seekPathSplit = seekPath.split('/');
|
||||||
|
var seekFilename = seekPathSplit[seekPathSplit.length-1];
|
||||||
|
project.files.forEach((file) => {
|
||||||
|
if(file.name === seekFilename) {
|
||||||
|
assetURL = file.url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!assetURL) {
|
||||||
|
return res.status(404).send({ message: 'Asset does not exist' });
|
||||||
|
} else {
|
||||||
|
request({ method: 'GET', url: assetURL, encoding: null }, (err, response, body) => {
|
||||||
|
res.send(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getProjectsForUserName(username) {
|
export function getProjectsForUserName(username) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Router } from 'express';
|
||||||
import { renderIndex } from '../views/index';
|
import { renderIndex } from '../views/index';
|
||||||
import { get404Sketch } from '../views/404Page';
|
import { get404Sketch } from '../views/404Page';
|
||||||
import { userExists } from '../controllers/user.controller';
|
import { userExists } from '../controllers/user.controller';
|
||||||
|
import { getProjectAsset } from '../controllers/project.controller';
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
|
@ -13,6 +14,9 @@ router.route('/').get((req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.route('/signup').get((req, res) => {
|
router.route('/signup').get((req, res) => {
|
||||||
|
if (req.user) {
|
||||||
|
return res.redirect('/');
|
||||||
|
}
|
||||||
res.send(renderIndex());
|
res.send(renderIndex());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,11 +28,18 @@ router.route('/:username/sketches/:project_id').get((req, res) => {
|
||||||
res.send(renderIndex());
|
res.send(renderIndex());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.route('/:username/sketches/:project_id/*').get((req, res) => {
|
||||||
|
getProjectAsset(req,res);
|
||||||
|
});
|
||||||
|
|
||||||
// router.route('/full/:project_id').get((req, res) => {
|
// router.route('/full/:project_id').get((req, res) => {
|
||||||
// res.send(renderIndex());
|
// res.send(renderIndex());
|
||||||
// });
|
// });
|
||||||
|
|
||||||
router.route('/login').get((req, res) => {
|
router.route('/login').get((req, res) => {
|
||||||
|
if (req.user) {
|
||||||
|
return res.redirect('/');
|
||||||
|
}
|
||||||
res.send(renderIndex());
|
res.send(renderIndex());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0958be54482722821159cd3e07777988ee349f37
|
Subproject commit 51087283c090ab1f1f0f733a01fdf46ef1382544
|
Loading…
Reference in a new issue