fix merge conflicts
This commit is contained in:
commit
53f7217786
30 changed files with 1000 additions and 347 deletions
|
@ -37,7 +37,8 @@
|
|||
"react/prefer-stateless-function": [2,
|
||||
{ "ignorePureComponents": true
|
||||
}],
|
||||
"class-methods-use-this": 0
|
||||
"class-methods-use-this": 0,
|
||||
"react/jsx-no-bind": [2, {"allowBind": true, "allowArrowFunctions": true}]
|
||||
},
|
||||
"plugins": [
|
||||
"react", "jsx-a11y", "import"
|
||||
|
|
|
@ -1,167 +1,463 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
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 {
|
||||
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() {
|
||||
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 (
|
||||
<nav className="nav" role="navigation" title="main-navigation">
|
||||
<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
|
||||
className="nav__new"
|
||||
onClick={() => {
|
||||
if (!this.props.unsavedChanges) {
|
||||
this.props.newProject();
|
||||
} else if (this.props.warnIfUnsavedChanges()) {
|
||||
this.props.newProject();
|
||||
}
|
||||
}}
|
||||
onClick={this.toggleDropdown.bind(this, 'file')}
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.clearHideTimeout}
|
||||
>
|
||||
New
|
||||
<span className="nav__item-header">File</span>
|
||||
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
|
||||
</button>
|
||||
</li>
|
||||
{(() => { // eslint-disable-line
|
||||
if (
|
||||
!this.props.project.owner ||
|
||||
(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>
|
||||
<ul className="nav__dropdown">
|
||||
<li className="nav__dropdown-heading">
|
||||
<span>File</span>
|
||||
<InlineSVG src={triangleUrl} />
|
||||
</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>
|
||||
{ !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">
|
||||
This is a preview version of the editor, that has not yet been officially released.
|
||||
It is in development, you can report bugs <a
|
||||
|
@ -171,6 +467,7 @@ class Nav extends React.PureComponent {
|
|||
>here</a>.
|
||||
Please use with caution.
|
||||
</div>
|
||||
*/}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
@ -196,14 +493,25 @@ Nav.propTypes = {
|
|||
showShareModal: PropTypes.func.isRequired,
|
||||
showErrorModal: PropTypes.func.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 = {
|
||||
project: {
|
||||
id: undefined,
|
||||
owner: undefined
|
||||
}
|
||||
},
|
||||
cmController: {}
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
|
|
18
client/images/down-arrow-white.svg
Normal file
18
client/images/down-arrow-white.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="14px" height="9px" viewBox="0 0 14 9" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-shape-copy-2</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" opacity="0.539999962">
|
||||
<g id="down-arrow" fill-rule="nonzero" fill="#FFFFFF">
|
||||
<g id="IDEs">
|
||||
<g id="p5js-IDE-styles-foundation-pt-2">
|
||||
<g id="Icons">
|
||||
<polygon id="arrow-shape-copy-2" transform="translate(7.000000, 4.198314) rotate(-180.000000) translate(-7.000000, -4.198314) " points="8.4 1.396628 14 6.996628 12.6 8.396628 7 2.796628 1.4 8.396628 0 6.996628 5.6 1.396628 6.996628 -2.84217094e-14"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
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 |
16
client/images/right-arrow-white.svg
Normal file
16
client/images/right-arrow-white.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="10px" height="15px" viewBox="0 0 10 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-shape-copy</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" opacity="0.539999962">
|
||||
<g id="right-arrow" fill-rule="nonzero" fill="#FFFFFF">
|
||||
<g id="IDEs">
|
||||
<g id="p5js-IDE-styles-foundation-pt-2">
|
||||
<polygon id="arrow-shape-copy" transform="translate(4.999999, 7.198314) rotate(90.000000) translate(-4.999999, -7.198314) " points="6.399999 4.396629 11.999999 9.996629 10.599999 11.396629 4.999999 5.796629 -0.600001 11.396629 -2.000001 9.996629 3.599999 4.396629 4.996627 2.999999"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1,005 B |
|
@ -57,14 +57,15 @@ Overlay.propTypes = {
|
|||
closeOverlay: PropTypes.func,
|
||||
title: PropTypes.string,
|
||||
ariaLabel: PropTypes.string,
|
||||
previousPath: PropTypes.string.isRequired
|
||||
previousPath: PropTypes.string
|
||||
};
|
||||
|
||||
Overlay.defaultProps = {
|
||||
children: null,
|
||||
title: 'Modal',
|
||||
closeOverlay: null,
|
||||
ariaLabel: 'modal'
|
||||
ariaLabel: 'modal',
|
||||
previousPath: '/'
|
||||
};
|
||||
|
||||
export default Overlay;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import * as ActionTypes from '../../../constants';
|
||||
import { clearConsole } from './console';
|
||||
|
||||
export function startSketch() {
|
||||
export function startVisualSketch() {
|
||||
return {
|
||||
type: ActionTypes.START_SKETCH
|
||||
};
|
||||
}
|
||||
|
||||
export function stopSketch() {
|
||||
export function stopVisualSketch() {
|
||||
return {
|
||||
type: ActionTypes.STOP_SKETCH
|
||||
};
|
||||
|
@ -20,7 +21,7 @@ export function startRefreshSketch() {
|
|||
|
||||
export function startSketchAndRefresh() {
|
||||
return (dispatch) => {
|
||||
dispatch(startSketch());
|
||||
dispatch(startVisualSketch());
|
||||
dispatch(startRefreshSketch());
|
||||
};
|
||||
}
|
||||
|
@ -245,3 +246,25 @@ export function showRuntimeErrorWarning() {
|
|||
type: ActionTypes.SHOW_RUNTIME_ERROR_WARNING
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ import 'codemirror/addon/lint/lint';
|
|||
import 'codemirror/addon/lint/javascript-lint';
|
||||
import 'codemirror/addon/lint/css-lint';
|
||||
import 'codemirror/addon/lint/html-lint';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/comment-fold';
|
||||
import 'codemirror/addon/fold/foldcode';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/fold/indent-fold';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
import 'codemirror/keymap/sublime';
|
||||
import 'codemirror/addon/search/searchcursor';
|
||||
|
@ -40,7 +45,6 @@ window.CSSLint = CSSLint;
|
|||
window.HTMLHint = HTMLHint;
|
||||
|
||||
const beepUrl = require('../../../sounds/audioAlert.mp3');
|
||||
const downArrowUrl = require('../../../images/down-arrow.svg');
|
||||
const unsavedChangesDotUrl = require('../../../images/unsaved-changes-dot.svg');
|
||||
const rightArrowUrl = require('../../../images/right-arrow.svg');
|
||||
const leftArrowUrl = require('../../../images/left-arrow.svg');
|
||||
|
@ -61,7 +65,11 @@ class Editor extends React.Component {
|
|||
this.beep.play();
|
||||
}
|
||||
}, 2000);
|
||||
this.showFind = this.showFind.bind(this);
|
||||
this.findNext = this.findNext.bind(this);
|
||||
this.findPrev = this.findPrev.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.beep = new Audio(beepUrl);
|
||||
this.widgets = [];
|
||||
|
@ -72,7 +80,9 @@ class Editor extends React.Component {
|
|||
inputStyle: 'contenteditable',
|
||||
lineWrapping: false,
|
||||
fixedGutter: false,
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
foldGutter: true,
|
||||
foldOptions: { widget: '\u2026' },
|
||||
gutters: ['CodeMirror-foldgutter'],
|
||||
keyMap: 'sublime',
|
||||
highlightSelectionMatches: true, // highlight current search match
|
||||
lint: {
|
||||
|
@ -83,7 +93,8 @@ class Editor extends React.Component {
|
|||
options: {
|
||||
'asi': true,
|
||||
'eqeqeq': false,
|
||||
'-W041': false
|
||||
'-W041': false,
|
||||
'esversion': 6
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -123,6 +134,13 @@ class Editor extends React.Component {
|
|||
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
|
||||
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
|
||||
this._cm.setOption('tabSize', this.props.indentationAmount);
|
||||
|
||||
this.props.provideController({
|
||||
tidyCode: this.tidyCode,
|
||||
showFind: this.showFind,
|
||||
findNext: this.findNext,
|
||||
findPrev: this.findPrev
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
|
@ -178,6 +196,7 @@ class Editor extends React.Component {
|
|||
|
||||
componentWillUnmount() {
|
||||
this._cm = null;
|
||||
this.props.provideController(null);
|
||||
}
|
||||
|
||||
getFileMode(fileName) {
|
||||
|
@ -221,6 +240,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() {
|
||||
if (this.props.editorOptionsVisible) {
|
||||
this.props.closeEditorOptions();
|
||||
|
@ -270,26 +303,6 @@ class Editor extends React.Component {
|
|||
isUserOwner={this.props.isUserOwner}
|
||||
/>
|
||||
</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>
|
||||
<div ref={(element) => { this.codemirrorContainer = element; }} className="editor-holder" tabIndex="0">
|
||||
</div>
|
||||
|
@ -327,7 +340,6 @@ Editor.propTypes = {
|
|||
editorOptionsVisible: PropTypes.bool.isRequired,
|
||||
showEditorOptions: PropTypes.func.isRequired,
|
||||
closeEditorOptions: PropTypes.func.isRequired,
|
||||
showKeyboardShortcutModal: PropTypes.func.isRequired,
|
||||
setUnsavedChanges: PropTypes.func.isRequired,
|
||||
startRefreshSketch: PropTypes.func.isRequired,
|
||||
autorefresh: PropTypes.bool.isRequired,
|
||||
|
@ -348,6 +360,7 @@ Editor.propTypes = {
|
|||
showRuntimeErrorWarning: PropTypes.func.isRequired,
|
||||
hideRuntimeErrorWarning: PropTypes.func.isRequired,
|
||||
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
||||
provideController: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
Editor.defaultProps = {
|
||||
|
|
|
@ -10,24 +10,24 @@ class GridOutput extends React.Component {
|
|||
id="gridOutput-content"
|
||||
ref={(element) => { this.GridOutputModal = element; }}
|
||||
>
|
||||
<h2> Grid Output </h2>
|
||||
<h2> table Output </h2>
|
||||
<p
|
||||
tabIndex="0"
|
||||
role="main"
|
||||
id="gridOutput-content-summary"
|
||||
aria-label="grid output summary"
|
||||
aria-label="table output summary"
|
||||
>
|
||||
</p>
|
||||
<table
|
||||
id="gridOutput-content-table"
|
||||
summary="grid output details"
|
||||
summary="table output details"
|
||||
>
|
||||
</table>
|
||||
<div
|
||||
tabIndex="0"
|
||||
role="main"
|
||||
id="gridOutput-content-details"
|
||||
aria-label="grid output details"
|
||||
aria-label="table output details"
|
||||
>
|
||||
</div>
|
||||
</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 (
|
||||
<ul className="keyboard-shortcuts" title="keyboard shortcuts">
|
||||
<li className="keyboard-shortcut-item">
|
||||
<span className="keyboard-shortcut__command">Shift + Tab</span>
|
||||
<span className="keyboard-shortcut__command">{'\u21E7'} + Tab</span>
|
||||
<span>Tidy</span>
|
||||
</li>
|
||||
<li className="keyboard-shortcut-item">
|
||||
|
@ -31,7 +31,7 @@ function KeyboardShortcutModal() {
|
|||
</li>
|
||||
<li className="keyboard-shortcut-item">
|
||||
<span className="keyboard-shortcut__command">
|
||||
{metaKeyName} + Shift + G
|
||||
{metaKeyName} + {'\u21E7'} + G
|
||||
</span>
|
||||
<span>Find Previous Text Match</span>
|
||||
</li>
|
||||
|
@ -61,21 +61,21 @@ function KeyboardShortcutModal() {
|
|||
</li>
|
||||
<li className="keyboard-shortcut-item">
|
||||
<span className="keyboard-shortcut__command">
|
||||
{metaKeyName} + Shift + Enter
|
||||
{metaKeyName} + {'\u21E7'} + Enter
|
||||
</span>
|
||||
<span>Stop Sketch</span>
|
||||
</li>
|
||||
<li className="keyboard-shortcut-item">
|
||||
<span className="keyboard-shortcut__command">
|
||||
{metaKeyName} + Shift + 1
|
||||
{metaKeyName} + {'\u21E7'} + 1
|
||||
</span>
|
||||
<span>Toggle Text-based Canvas</span>
|
||||
<span>Turn on Accessible Output</span>
|
||||
</li>
|
||||
<li className="keyboard-shortcut-item">
|
||||
<span className="keyboard-shortcut__command">
|
||||
{metaKeyName} + Shift + 2
|
||||
{metaKeyName} + {'\u21E7'} + 2
|
||||
</span>
|
||||
<span>Turn Off Text-based Canvas</span>
|
||||
<span>Turn off Accessible Output</span>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
|
|
@ -88,6 +88,7 @@ class PreviewFrame extends React.Component {
|
|||
}
|
||||
|
||||
window.addEventListener('message', (messageEvent) => {
|
||||
console.log(messageEvent);
|
||||
messageEvent.data.forEach((message) => {
|
||||
const args = message.arguments;
|
||||
Object.keys(args).forEach((key) => {
|
||||
|
@ -152,6 +153,14 @@ class PreviewFrame extends React.Component {
|
|||
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() {
|
||||
const htmlFile = this.props.htmlFile.content;
|
||||
let scriptOffs = [];
|
||||
|
@ -227,7 +236,7 @@ class PreviewFrame extends React.Component {
|
|||
scriptOffs = getAllScriptOffsets(sketchDocString);
|
||||
const consoleErrorsScript = sketchDoc.createElement('script');
|
||||
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
||||
// sketchDoc.head.appendChild(consoleErrorsScript);
|
||||
this.addLoopProtect(sketchDoc);
|
||||
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
|
||||
|
||||
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);
|
||||
return newContent;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import classNames from 'classnames';
|
|||
import InlineSVG from 'react-inlinesvg';
|
||||
|
||||
const playUrl = require('../../../images/play.svg');
|
||||
const logoUrl = require('../../../images/p5js-logo.svg');
|
||||
const stopUrl = require('../../../images/stop.svg');
|
||||
const preferencesUrl = require('../../../images/preferences.svg');
|
||||
const editProjectNameUrl = require('../../../images/pencil.svg');
|
||||
|
@ -59,14 +58,9 @@ class Toolbar extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="toolbar">
|
||||
<InlineSVG className="toolbar__logo" src={logoUrl} alt="p5js Logo" />
|
||||
<button
|
||||
className="toolbar__play-sketch-button"
|
||||
onClick={() => {
|
||||
this.props.clearConsole();
|
||||
this.props.startAccessibleOutput();
|
||||
this.props.startSketchAndRefresh();
|
||||
}}
|
||||
onClick={this.props.startAccessibleSketch}
|
||||
aria-label="play sketch"
|
||||
disabled={this.props.infiniteLoop}
|
||||
>
|
||||
|
@ -74,10 +68,7 @@ class Toolbar extends React.Component {
|
|||
</button>
|
||||
<button
|
||||
className={playButtonClass}
|
||||
onClick={() => {
|
||||
this.props.clearConsole();
|
||||
this.props.startSketchAndRefresh();
|
||||
}}
|
||||
onClick={this.props.startSketch}
|
||||
aria-label="play only visual sketch"
|
||||
disabled={this.props.infiniteLoop}
|
||||
>
|
||||
|
@ -85,7 +76,7 @@ class Toolbar extends React.Component {
|
|||
</button>
|
||||
<button
|
||||
className={stopButtonClass}
|
||||
onClick={() => { this.props.stopAccessibleOutput(); this.props.stopSketch(); }}
|
||||
onClick={this.props.stopSketch}
|
||||
aria-label="stop sketch"
|
||||
>
|
||||
<InlineSVG src={stopUrl} alt="Stop Sketch" />
|
||||
|
@ -187,8 +178,6 @@ Toolbar.propTypes = {
|
|||
isPlaying: PropTypes.bool.isRequired,
|
||||
preferencesIsVisible: PropTypes.bool.isRequired,
|
||||
stopSketch: PropTypes.func.isRequired,
|
||||
startAccessibleOutput: PropTypes.func.isRequired,
|
||||
stopAccessibleOutput: PropTypes.func.isRequired,
|
||||
setProjectName: PropTypes.func.isRequired,
|
||||
openPreferences: PropTypes.func.isRequired,
|
||||
owner: PropTypes.shape({
|
||||
|
@ -207,10 +196,10 @@ Toolbar.propTypes = {
|
|||
autorefresh: PropTypes.bool.isRequired,
|
||||
setAutorefresh: PropTypes.func.isRequired,
|
||||
setServeSecure: PropTypes.func.isRequired,
|
||||
startSketchAndRefresh: PropTypes.func.isRequired,
|
||||
startSketch: PropTypes.func.isRequired,
|
||||
startAccessibleSketch: PropTypes.func.isRequired,
|
||||
saveProject: PropTypes.func.isRequired,
|
||||
currentUser: PropTypes.string,
|
||||
clearConsole: PropTypes.func.isRequired
|
||||
currentUser: PropTypes.string
|
||||
};
|
||||
|
||||
Toolbar.defaultProps = {
|
||||
|
|
|
@ -15,7 +15,7 @@ import NewFolderModal from '../components/NewFolderModal';
|
|||
import ShareModal from '../components/ShareModal';
|
||||
import KeyboardShortcutModal from '../components/KeyboardShortcutModal';
|
||||
import ErrorModal from '../components/ErrorModal';
|
||||
import HelpModal from '../components/HelpModal';
|
||||
import HTTPSModal from '../components/HTTPSModal';
|
||||
import Nav from '../../../components/Nav';
|
||||
import Console from '../components/Console';
|
||||
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))) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.clearConsole();
|
||||
this.props.startSketchAndRefresh();
|
||||
this.props.startSketch();
|
||||
// 50 === 2
|
||||
} else if (e.keyCode === 50 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.props.setTextOutput(false);
|
||||
this.props.setGridOutput(false);
|
||||
this.props.setSoundOutput(false);
|
||||
this.props.setAllAccessibleOutput(false);
|
||||
// 49 === 1
|
||||
} else if (e.keyCode === 49 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.props.setTextOutput(true);
|
||||
this.props.setGridOutput(true);
|
||||
this.props.setSoundOutput(true);
|
||||
this.props.setAllAccessibleOutput(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,18 +210,20 @@ class IDEView extends React.Component {
|
|||
cloneProject={this.props.cloneProject}
|
||||
project={this.props.project}
|
||||
logoutUser={this.props.logoutUser}
|
||||
startSketch={this.props.startSketch}
|
||||
stopSketch={this.props.stopSketch}
|
||||
showShareModal={this.props.showShareModal}
|
||||
showErrorModal={this.props.showErrorModal}
|
||||
unsavedChanges={this.props.ide.unsavedChanges}
|
||||
warnIfUnsavedChanges={this.warnIfUnsavedChanges}
|
||||
showKeyboardShortcutModal={this.props.showKeyboardShortcutModal}
|
||||
cmController={this.cmController}
|
||||
setAllAccessibleOutput={this.props.setAllAccessibleOutput}
|
||||
/>
|
||||
<Toolbar
|
||||
className="Toolbar"
|
||||
isPlaying={this.props.ide.isPlaying}
|
||||
stopSketch={this.props.stopSketch}
|
||||
startAccessibleOutput={this.props.startAccessibleOutput}
|
||||
stopAccessibleOutput={this.props.stopAccessibleOutput}
|
||||
projectName={this.props.project.name}
|
||||
setProjectName={this.props.setProjectName}
|
||||
showEditProjectName={this.props.showEditProjectName}
|
||||
|
@ -241,10 +240,10 @@ class IDEView extends React.Component {
|
|||
infiniteLoop={this.props.ide.infiniteLoop}
|
||||
autorefresh={this.props.preferences.autorefresh}
|
||||
setAutorefresh={this.props.setAutorefresh}
|
||||
startSketchAndRefresh={this.props.startSketchAndRefresh}
|
||||
startSketch={this.props.startSketch}
|
||||
startAccessibleSketch={this.props.startAccessibleSketch}
|
||||
saveProject={this.props.saveProject}
|
||||
currentUser={this.props.user.username}
|
||||
clearConsole={this.props.clearConsole}
|
||||
showHelpModal={this.props.showHelpModal}
|
||||
/>
|
||||
<Preferences
|
||||
|
@ -300,8 +299,8 @@ class IDEView extends React.Component {
|
|||
<SplitPane
|
||||
split="vertical"
|
||||
defaultSize={'50%'}
|
||||
onChange={() => (this.overlay.style.display = 'block')}
|
||||
onDragFinished={() => (this.overlay.style.display = 'none')}
|
||||
onChange={() => { this.overlay.style.display = 'block'; }}
|
||||
onDragFinished={() => { this.overlay.style.display = 'none'; }}
|
||||
>
|
||||
<SplitPane
|
||||
split="horizontal"
|
||||
|
@ -345,6 +344,7 @@ class IDEView extends React.Component {
|
|||
showRuntimeErrorWarning={this.props.showRuntimeErrorWarning}
|
||||
hideRuntimeErrorWarning={this.props.hideRuntimeErrorWarning}
|
||||
runtimeErrorWarningVisible={this.props.ide.runtimeErrorWarningVisible}
|
||||
provideController={(ctl) => { this.cmController = ctl; }}
|
||||
/>
|
||||
<Console
|
||||
consoleEvents={this.props.console}
|
||||
|
@ -520,17 +520,16 @@ class IDEView extends React.Component {
|
|||
{(() => { // eslint-disable-line
|
||||
if (this.props.ide.helpType) {
|
||||
return (
|
||||
<Overlay>
|
||||
<HelpModal
|
||||
type={this.props.ide.helpType}
|
||||
closeModal={this.props.hideHelpModal}
|
||||
/>
|
||||
<Overlay
|
||||
title="Serve over HTTPS"
|
||||
closeOverlay={this.props.hideHelpModal}
|
||||
>
|
||||
<HTTPSModal />
|
||||
</Overlay>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -577,8 +576,6 @@ IDEView.propTypes = {
|
|||
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
||||
}).isRequired,
|
||||
stopSketch: PropTypes.func.isRequired,
|
||||
startAccessibleOutput: PropTypes.func.isRequired,
|
||||
stopAccessibleOutput: PropTypes.func.isRequired,
|
||||
project: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
|
@ -619,6 +616,7 @@ IDEView.propTypes = {
|
|||
setTextOutput: PropTypes.func.isRequired,
|
||||
setGridOutput: PropTypes.func.isRequired,
|
||||
setSoundOutput: PropTypes.func.isRequired,
|
||||
setAllAccessibleOutput: PropTypes.func.isRequired,
|
||||
files: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
|
@ -677,7 +675,6 @@ IDEView.propTypes = {
|
|||
setUnsavedChanges: PropTypes.func.isRequired,
|
||||
setTheme: PropTypes.func.isRequired,
|
||||
setAutorefresh: PropTypes.func.isRequired,
|
||||
startSketchAndRefresh: PropTypes.func.isRequired,
|
||||
endSketchRefresh: PropTypes.func.isRequired,
|
||||
startRefreshSketch: PropTypes.func.isRequired,
|
||||
setBlobUrl: PropTypes.func.isRequired,
|
||||
|
@ -693,8 +690,13 @@ IDEView.propTypes = {
|
|||
persistState: PropTypes.func.isRequired,
|
||||
showHelpModal: PropTypes.func.isRequired,
|
||||
hideHelpModal: PropTypes.func.isRequired,
|
||||
<<<<<<< HEAD
|
||||
showRuntimeErrorWarning: PropTypes.func.isRequired,
|
||||
hideRuntimeErrorWarning: PropTypes.func.isRequired
|
||||
=======
|
||||
startSketch: PropTypes.func.isRequired,
|
||||
startAccessibleSketch: PropTypes.func.isRequired
|
||||
>>>>>>> master
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
|
@ -27,6 +27,10 @@ class LoginView extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.props.user.authenticated) {
|
||||
this.gotoHomePage();
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="form-container">
|
||||
<div className="form-container__header">
|
||||
|
@ -70,7 +74,16 @@ function mapDispatchToProps() {
|
|||
}
|
||||
|
||||
LoginView.propTypes = {
|
||||
previousPath: PropTypes.string.isRequired
|
||||
previousPath: PropTypes.string.isRequired,
|
||||
user: {
|
||||
authenticated: PropTypes.bool
|
||||
}
|
||||
};
|
||||
|
||||
LoginView.defaultProps = {
|
||||
user: {
|
||||
authenticated: false
|
||||
}
|
||||
};
|
||||
|
||||
export default reduxForm({
|
||||
|
|
|
@ -27,6 +27,10 @@ class SignupView extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.props.user.authenticated) {
|
||||
this.gotoHomePage();
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="form-container">
|
||||
<div className="form-container__header">
|
||||
|
@ -84,7 +88,16 @@ function onSubmitFail(errors) {
|
|||
}
|
||||
|
||||
SignupView.propTypes = {
|
||||
previousPath: PropTypes.string.isRequired
|
||||
previousPath: PropTypes.string.isRequired,
|
||||
user: {
|
||||
authenticated: PropTypes.bool
|
||||
}
|
||||
};
|
||||
|
||||
SignupView.defaultProps = {
|
||||
user: {
|
||||
authenticated: false
|
||||
}
|
||||
};
|
||||
|
||||
export default reduxForm({
|
||||
|
|
|
@ -15,7 +15,7 @@ $themes: (
|
|||
primary-text-color: #333,
|
||||
modal-button-color: #333,
|
||||
heading-text-color: #333,
|
||||
secondary-text-color: #6b6b6b,
|
||||
secondary-text-color: #a8a8a8,
|
||||
inactive-text-color: #b5b5b5,
|
||||
background-color: #fbfbfb,
|
||||
preview-placeholder-color: #dcdcdc,
|
||||
|
@ -30,7 +30,8 @@ $themes: (
|
|||
button-active-color: $white,
|
||||
modal-background-color: #f4f4f4,
|
||||
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-hover-color: $icon-hover-color,
|
||||
icon-toast-hover-color: $white,
|
||||
|
@ -44,14 +45,19 @@ $themes: (
|
|||
input-text-color: #333,
|
||||
input-border-color: #b5b5b5,
|
||||
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,
|
||||
codefold-icon-open: url(../images/down-arrow.svg),
|
||||
codefold-icon-closed: url(../images/right-arrow.svg)
|
||||
),
|
||||
dark: (
|
||||
logo-color: $p5js-pink,
|
||||
primary-text-color: $white,
|
||||
modal-button-color: $white,
|
||||
heading-text-color: $white,
|
||||
secondary-text-color: #c2c2c2,
|
||||
secondary-text-color: #DADADA,
|
||||
inactive-text-color: #b5b5b5,
|
||||
background-color: #333,
|
||||
preview-placeholder-color: #dcdcdc,
|
||||
|
@ -80,7 +86,12 @@ $themes: (
|
|||
input-text-color: #333,
|
||||
input-border-color: #b5b5b5,
|
||||
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,
|
||||
codefold-icon-open: url(../images/down-arrow-white.svg),
|
||||
codefold-icon-closed: url(../images/right-arrow-white.svg)
|
||||
),
|
||||
contrast: (
|
||||
logo-color: $yellow,
|
||||
|
@ -115,7 +126,12 @@ $themes: (
|
|||
input-text-color: #333,
|
||||
input-border-color: #b5b5b5,
|
||||
about-list-text-color: #f4f4f4,
|
||||
search-background-color: $white
|
||||
search-background-color: $white,
|
||||
dropdown-color: #e1e1e1,
|
||||
keyboard-shortcut-color: #e1e1e1,
|
||||
nav-hover-color: $yellow,
|
||||
codefold-icon-open: url(../images/down-arrow-white.svg),
|
||||
codefold-icon-closed: url(../images/right-arrow-white.svg)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -13,17 +13,17 @@
|
|||
|
||||
.CodeMirror-linenumber {
|
||||
width: #{32 / $base-font-size}rem;
|
||||
left: 0 !important;
|
||||
left: #{-3 / $base-font-size}rem !important;
|
||||
@include themify() {
|
||||
color: getThemifyVariable('inactive-text-color');
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding-top: #{25 / $base-font-size}rem;
|
||||
padding-top: #{25 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.CodeMirror-line {
|
||||
pre.CodeMirror-line {
|
||||
padding-left: #{5 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,40 @@
|
|||
background: transparent url(../images/exit.svg) no-repeat;
|
||||
}
|
||||
|
||||
.CodeMirror-foldgutter-open:after {
|
||||
@include themify() {
|
||||
background-image: getThemifyVariable('codefold-icon-open');
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-foldgutter-folded:after {
|
||||
@include themify() {
|
||||
background-image: getThemifyVariable('codefold-icon-closed');
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-foldgutter-folded:after,
|
||||
.CodeMirror-foldgutter-open:after {
|
||||
background-size: 10px 10px;
|
||||
content: "";
|
||||
padding-left: 15px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.CodeMirror-foldmarker {
|
||||
text-shadow: none;
|
||||
border-radius: 5px;
|
||||
opacity: 1;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 0.85em;
|
||||
line-height: 0.7;
|
||||
padding: 0 #{5 / $base-font-size}rem;
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
.editor-holder {
|
||||
height: calc(100% - #{29 / $base-font-size}rem);
|
||||
width: 100%;
|
||||
|
|
|
@ -1,34 +1,41 @@
|
|||
.nav {
|
||||
width: 100%;
|
||||
padding: #{10 / $base-font-size}rem #{32 / $base-font-size}rem 0 #{32 / $base-font-size}rem;
|
||||
width: calc(100% - #{10 / $base-font-size}rem);
|
||||
height: #{42 / $base-font-size}rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav__items-left, .nav__items-right {
|
||||
@include themify() {
|
||||
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
||||
}
|
||||
& button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.nav__items-left, .nav__items-right {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding: #{3 / $base-font-size}rem 0;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav__items-left {
|
||||
& button {
|
||||
@include themify() {
|
||||
color: getThemifyVariable('inactive-text-color');
|
||||
color: getThemifyVariable('secondary-text-color');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav__item {
|
||||
position: relative;
|
||||
padding: 0 #{24 / $base-font-size}rem;
|
||||
text-align: center;
|
||||
padding: 0 #{10 / $base-font-size}rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nav__item:first-child {
|
||||
|
@ -39,38 +46,57 @@
|
|||
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 {
|
||||
@include themify() {
|
||||
background-color: map-get($theme-map, 'modal-background-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;
|
||||
width: #{140 / $base-font-size}rem;
|
||||
.nav__item:hover & {
|
||||
width: #{180 / $base-font-size}rem;
|
||||
.nav__item--open & {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
flex-direction: column;
|
||||
top: #{-8 / $base-font-size}rem;;
|
||||
left: #{-11 / $base-font-size}rem;
|
||||
top: 4px;
|
||||
left: 0;
|
||||
height: auto;
|
||||
}
|
||||
padding-bottom: #{8 / $base-font-size}rem;
|
||||
z-index: 1;
|
||||
z-index: 9999;
|
||||
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 {
|
||||
@include themify() {
|
||||
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 {
|
||||
|
@ -93,9 +119,24 @@
|
|||
@include themify() {
|
||||
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
|
||||
}
|
||||
margin-top: #{3 / $base-font-size}rem;
|
||||
text-align: center;
|
||||
margin-bottom: #{4 / $base-font-size}rem;
|
||||
height: #{(42 - 5) / $base-font-size}rem;
|
||||
display: flex;
|
||||
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 {
|
||||
|
@ -106,6 +147,48 @@
|
|||
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 {
|
||||
position: absolute;
|
||||
top: #{40 / $base-font-size}rem;
|
||||
|
@ -121,3 +204,25 @@
|
|||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,3 +125,8 @@ $p5-contrast-activeline: #999999;
|
|||
color: $p5-contrast-pink;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-p5-contrast .CodeMirror-foldmarker {
|
||||
background-color: white;
|
||||
color: #333;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ $p5-dark-white: #FDFDFD;
|
|||
$p5-dark-orange: #EE9900;
|
||||
$p5-dark-lightgray: #E0D7D1;
|
||||
$p5-dark-darkgray: #666666;
|
||||
$p5-dark-green: #58a10b;
|
||||
$p5-dark-goldbrown: #b58317;
|
||||
|
||||
$p5-dark-gutter: #f4f4f4;
|
||||
$p5-dark-number: #b5b5b5;
|
||||
|
@ -41,7 +43,7 @@ $p5-dark-activeline: rgb(207, 207, 207);
|
|||
}
|
||||
|
||||
.cm-s-p5-dark .cm-string {
|
||||
color: $p5-dark-lightblue;
|
||||
color: $p5-dark-green;
|
||||
}
|
||||
|
||||
.cm-s-p5-dark .cm-string-2 {
|
||||
|
@ -49,11 +51,11 @@ $p5-dark-activeline: rgb(207, 207, 207);
|
|||
}
|
||||
|
||||
.cm-s-p5-dark .cm-number {
|
||||
color: $p5-dark-pink;
|
||||
color: $p5-dark-white;
|
||||
}
|
||||
|
||||
.cm-s-p5-dark .cm-keyword {
|
||||
color: $p5-light-green;
|
||||
color: $p5-dark-goldbrown;
|
||||
}
|
||||
|
||||
.cm-s-p5-dark .cm-variable {
|
||||
|
@ -126,3 +128,8 @@ $p5-dark-activeline: rgb(207, 207, 207);
|
|||
color: $p5-dark-pink;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-p5-dark .CodeMirror-foldmarker {
|
||||
background-color: white;
|
||||
color: #333;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
$p5-light-lightbrown: #A67F59;
|
||||
$p5-light-brown: #704F21;
|
||||
$p5-light-black: #333;
|
||||
$p5-light-black: #333333;
|
||||
$p5-light-pink: #D9328F;
|
||||
$p5-light-gray: #A0A0A0;
|
||||
$p5-light-lightblue: #00A1D3;
|
||||
|
@ -21,6 +21,7 @@ $p5-light-white: #FDFDFD;
|
|||
$p5-light-orange: #EE9900;
|
||||
$p5-light-lightgray: #E0D7D1;
|
||||
$p5-light-darkgray: #666666;
|
||||
$p5-light-green: #58a10b;
|
||||
|
||||
$p5-light-gutter: #f4f4f4;
|
||||
$p5-light-number: #b5b5b5;
|
||||
|
@ -37,11 +38,11 @@ $p5-light-activeline: rgb(207, 207, 207);
|
|||
}
|
||||
|
||||
.cm-s-p5-light .cm-def {
|
||||
color: $p5-light-darkblue;
|
||||
color: $p5-light-lightblue;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-string {
|
||||
color: $p5-light-lightblue;
|
||||
color: $p5-light-green;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-string-2 {
|
||||
|
@ -49,7 +50,7 @@ $p5-light-activeline: rgb(207, 207, 207);
|
|||
}
|
||||
|
||||
.cm-s-p5-light .cm-number {
|
||||
color: $p5-light-pink;
|
||||
color: $p5-light-black;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-keyword {
|
||||
|
@ -60,7 +61,7 @@ $p5-light-activeline: rgb(207, 207, 207);
|
|||
color: $p5-light-lightblue;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-variable-2 {
|
||||
.cm-s-p5-light .cm-variable2 {
|
||||
color: $p5-light-black;
|
||||
}
|
||||
|
||||
|
@ -114,14 +115,19 @@ $p5-light-activeline: rgb(207, 207, 207);
|
|||
}
|
||||
|
||||
.cm-s-p5-light .cm-attribute {
|
||||
color: $p5-light-lightblue;
|
||||
color: $p5-light-black;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-p5-function {
|
||||
color: $p5-light-darkblue;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .cm-p5-variable {
|
||||
color: $p5-light-pink;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-p5-light .CodeMirror-foldmarker {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
|
|
30
client/styles/vendors/_codemirror.scss
vendored
30
client/styles/vendors/_codemirror.scss
vendored
|
@ -336,3 +336,33 @@ div.CodeMirror-dragcursors {
|
|||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
||||
|
||||
/* CODE FOLDING (FOLDGUTTER.JS) */
|
||||
|
||||
.CodeMirror-foldmarker {
|
||||
text-shadow: -1px 0 #ed225d, 0 1px #ed225d, 1px 0 #ed225d, 0 -1px #ed225d;
|
||||
color: #FFF;
|
||||
/* background-color: rgba(237, 34, 93, 0.42); */
|
||||
/* border-radius: 3px; */
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
line-height: .3;
|
||||
cursor: pointer;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.CodeMirror-foldgutter {
|
||||
width: 2.7em;
|
||||
}
|
||||
.CodeMirror-foldgutter-open,
|
||||
.CodeMirror-foldgutter-folded {
|
||||
cursor: pointer;
|
||||
padding-bottom: 0.4em;
|
||||
text-align: right;
|
||||
line-height: 1.0;
|
||||
}
|
||||
.CodeMirror-foldgutter-open:after {
|
||||
content: "\25BE";
|
||||
}
|
||||
.CodeMirror-foldgutter-folded:after {
|
||||
content: "\25B8";
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ const metaKey = (() => {
|
|||
return 'Ctrl';
|
||||
})();
|
||||
|
||||
const metaKeyName = metaKey === 'Cmd' ? 'Command' : 'Control';
|
||||
const metaKeyName = metaKey === 'Cmd' ? '\u2318' : 'Ctrl';
|
||||
|
||||
export {
|
||||
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) {
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Router } from 'express';
|
|||
import { renderIndex } from '../views/index';
|
||||
import { get404Sketch } from '../views/404Page';
|
||||
import { userExists } from '../controllers/user.controller';
|
||||
import { getProjectAsset } from '../controllers/project.controller';
|
||||
|
||||
const router = new Router();
|
||||
|
||||
|
@ -13,6 +14,9 @@ router.route('/').get((req, res) => {
|
|||
});
|
||||
|
||||
router.route('/signup').get((req, res) => {
|
||||
if (req.user) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
res.send(renderIndex());
|
||||
});
|
||||
|
||||
|
@ -24,11 +28,18 @@ router.route('/:username/sketches/:project_id').get((req, res) => {
|
|||
res.send(renderIndex());
|
||||
});
|
||||
|
||||
router.route('/:username/sketches/:project_id/*').get((req, res) => {
|
||||
getProjectAsset(req,res);
|
||||
});
|
||||
|
||||
// router.route('/full/:project_id').get((req, res) => {
|
||||
// res.send(renderIndex());
|
||||
// });
|
||||
|
||||
router.route('/login').get((req, res) => {
|
||||
if (req.user) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
res.send(renderIndex());
|
||||
});
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0958be54482722821159cd3e07777988ee349f37
|
||||
Subproject commit 51087283c090ab1f1f0f733a01fdf46ef1382544
|
Loading…
Reference in a new issue