resolve mc

This commit is contained in:
Gaurang Tandon 2019-02-10 07:00:48 +05:30
commit 57b5f2f5bb
31 changed files with 9306 additions and 24591 deletions

View File

@ -1,22 +1,22 @@
API_URL=/api API_URL=/api
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
PORT=8000
SESSION_SECRET=whatever_you_want_this_to_be_it_only_matters_for_production
AWS_ACCESS_KEY=<your-aws-access-key> AWS_ACCESS_KEY=<your-aws-access-key>
AWS_SECRET_KEY=<your-aws-secret-key>
AWS_REGION=<your-aws-region> AWS_REGION=<your-aws-region>
S3_BUCKET=<your-s3-bucket> AWS_SECRET_KEY=<your-aws-secret-key>
EMAIL_SENDER=<transactional-email-sender>
EMAIL_VERIFY_SECRET_TOKEN=whatever_you_want_this_to_be_it_only_matters_for_production
EXAMPLE_USER_EMAIL=examples@p5js.org
EXAMPLE_USER_PASSWORD=hellop5js
GG_EXAMPLES_EMAIL=benedikt.gross@generative-gestaltung.de
GG_EXAMPLES_PASS=generativedesign
GG_EXAMPLES_USERNAME=generative-design
GITHUB_ID=<your-github-client-id> GITHUB_ID=<your-github-client-id>
GITHUB_SECRET=<your-github-client-secret> GITHUB_SECRET=<your-github-client-secret>
GOOGLE_ID=<your-google-client-id> (use google+ api) GOOGLE_ID=<your-google-client-id> (use google+ api)
GOOGLE_SECRET=<your-google-client-secret> (use google+ api) GOOGLE_SECRET=<your-google-client-secret> (use google+ api)
MAILGUN_DOMAIN=<your-mailgun-domain> MAILGUN_DOMAIN=<your-mailgun-domain>
MAILGUN_KEY=<your-mailgun-api-key> MAILGUN_KEY=<your-mailgun-api-key>
EMAIL_SENDER=<transactional-email-sender> MONGO_URL=mongodb://localhost:27017/p5js-web-editor
EMAIL_VERIFY_SECRET_TOKEN=whatever_you_want_this_to_be_it_only_matters_for_production PORT=8000
S3_BUCKET=<your-s3-bucket>
S3_BUCKET_URL_BASE=<alt-for-s3-url> S3_BUCKET_URL_BASE=<alt-for-s3-url>
EXAMPLE_USER_EMAIL=examples@p5js.org SESSION_SECRET=whatever_you_want_this_to_be_it_only_matters_for_production
EXAMPLE_USER_PASSWORD=hellop5js
GG_EXAMPLES_USERNAME=generative-design
GG_EXAMPLES_PASS=generativedesign
GG_EXAMPLES_EMAIL=benedikt.gross@generative-gestaltung.de

View File

@ -20,7 +20,7 @@
"no-console": 0, "no-console": 0,
"no-alert": 0, "no-alert": 0,
"no-underscore-dangle": 0, "no-underscore-dangle": 0,
"max-len": [1, 120, 2, {ignoreComments: true}], "max-len": [1, 120, 2, {"ignoreComments": true, "ignoreTemplateLiterals": true}],
"quote-props": [1, "consistent-as-needed"], "quote-props": [1, "consistent-as-needed"],
"no-unused-vars": [1, {"vars": "local", "args": "none"}], "no-unused-vars": [1, {"vars": "local", "args": "none"}],
"consistent-return": ["error", { "treatUndefinedAsUnspecified": true }], "consistent-return": ["error", { "treatUndefinedAsUnspecified": true }],

View File

@ -11,13 +11,14 @@
Use your best judgement. If it will possibly make others uncomfortable, do not post it. Use your best judgement. If it will possibly make others uncomfortable, do not post it.
* **Be respectful.** Disagreement is not an opportunity to attack someone else's thoughts or opinions. Although views may differ, remember to approach every situation with patience and care. * **Be respectful.** Disagreement is not an opportunity to attack someone else's thoughts or opinions. Although views may differ, remember to approach every situation with patience and care.
* **Be considerate.** Think about how your contribution will affect others in the community. * **Be considerate.** Think about how your contribution will affect others in the community.
* **Be open minded.** Embrace new people and new ideas. Our community is continually evolving and we welcome positive change. * **Be open minded.** Embrace new people and new ideas. Our community is continually evolving and we welcome positive change.
If you believe someone is violating the code of conduct, we ask that you report it by emailing [hello@p5js.org](mailto:hello@p5js.org). Please include your name and a description of the incident, and we will get back to you ASAP. If you believe someone is violating the code of conduct, we ask that you report it by emailing [hello@p5js.org](mailto:hello@p5js.org). Please include your name and a description of the incident, and we will get back to you ASAP.
Participants asked to stop any harassing behavior are expected to comply immediately. If a participant engages in harassing behavior, the p5.js Team may take any action they deem appropriate, up to and including expulsion from all p5.js spaces and identification of the participant as a harasser to other p5.js members or the general public. Participants asked to stop any harassing behavior are expected to comply immediately. If a participant engages in harassing behavior, the p5.js Team may take any action they deem appropriate, up to and including expulsion from all p5.js spaces and identification of the participant as a harasser to other p5.js members or the general public.
## Also ## Also
* You can read our [community statement](http://p5js.org/community/) on our website. * You can read our [community statement](http://p5js.org/community/) on our website.

View File

@ -1,13 +1,13 @@
# Contributing to the p5.js Web Editor # Contributing to the p5.js Web Editor
Hello! We welcome community contributions to the p5.js Web Editor. Contributing takes many forms and doesn't have to be **writing code**, it can be **report bugs**, **proposing new features**, **creating UI/UX designs**, and **updating documentation**. Hello! We welcome community contributions to the p5.js Web Editor. Contributing takes many forms and doesn't have to be **writing code**, it can be **report bugs**, **proposing new features**, **creating UI/UX designs**, and **updating documentation**.
Here are links to all the sections in this document: Here are links to all the sections in this document:
<!-- If you change any of the headings in this document, remember to update the table of contents. --> <!-- If you change any of the headings in this document, remember to update the table of contents. -->
- [Code of Conduct](#code-of-conduct) - [Code of Conduct](#code-of-conduct)
- [How Can I Contribute ?](#how-can-i-contribute?) - [How Can I Contribute?](#how-can-i-contribute)
- [First Timers](#first-timers) - [First Timers](#first-timers)
- [Want something more challenging](#want-something-more-challenging) - [Want something more challenging](#want-something-more-challenging)
- [Feature Enhancement](#feature-enhancement) - [Feature Enhancement](#feature-enhancement)
@ -72,6 +72,4 @@ Structure your commit message like this:
Tips Tips
---- ----
* If it seems difficult to summarize what your commit does, it may be because it includes several logical changes or bug fixes, and are better split up into several commits using `git add -p`. * If it seems difficult to summarize what your commit does, it may be because it includes several logical changes or bug fixes, and are better split up into several commits using `git add -p`.

View File

@ -1,23 +1,22 @@
<!-- <!--
Hi there! If you are here to report a bug, or to discuss a feature (new or existing), you can use the below template to get started quickly. Fill out all those parts which you're comfortable with, and delete the remaining ones. Hi there! If you are here to report a bug, or to discuss a feature (new or existing), you can use the below template to get started quickly. Fill out all those parts which you're comfortable with, and delete the remaining ones.
To check any option, replace the "[ ]" with a "[x]". Be sure to check out how it looks in the Preview tab!
Feel free to remove any portion of the template that is not relevant for your issue.
--> -->
#### Nature of issue? #### Nature of issue?
- [ ] Found a bug <!-- Select any one issue and delete the other two -->
- [ ] Existing feature enhancement
- [ ] New feature request - Found a bug
- Existing feature enhancement
- New feature request
<!-- If you found a bug, the following information might prove to be helpful for us. Simply remove whatever you can't determine/don't know. --> <!-- If you found a bug, the following information might prove to be helpful for us. Simply remove whatever you can't determine/don't know. -->
#### Details about the bug: #### Details about the bug:
- Web browser and version: <!-- On Chrome/FireFox/Opera you can enter "about:" in the address bar to find out the version --> - Web browser and version: <!-- On Chrome/FireFox/Opera you can enter "about:" in the address bar to find out the version -->
- Operating System: <!-- Ex: Windows/MacOSX/Linux along with version --> - Operating System: <!-- Ex: Windows/MacOSX/Linux along with version -->
- Steps to reproduce this: - Steps to reproduce this bug:
<!-- Include a simple code snippet that demonstrates the problem, along with any console errors produced. If this isn't possible, then simply describe the issue as best you can! Feel free to link to the web editor or include pictures or a video. --> <!-- Include a simple code snippet that demonstrates the problem, along with any console errors produced. If this isn't possible, then simply describe the issue as best you can! Feel free to link to the web editor or include pictures or a video. -->
<!-- If you want to enhance an existing feature, please describe here, otherwise remove this section --> <!-- If you want to enhance an existing feature, please describe here, otherwise remove this section -->

View File

@ -1,7 +1,5 @@
Before your pull request is reviewed and merged, please ensure that: I have verified that this pull request:
* [ ] there are no linting errors -- `npm run lint` * [ ] has no linting errors (`npm run lint`)
* [ ] your code is in a uniquely-named feature branch and has been rebased on top of the latest master. If you're asked to make more changes, make sure you rebase onto master then too! * [ ] is from a uniquely-named feature branch and has been rebased on top of the latest master. (If I was asked to make more changes, I have made sure to rebase onto master then too)
* [ ] your pull request is descriptively named and links to an issue number, i.e. `Fixes #123` * [ ] is descriptively named and links to an issue number, i.e. `Fixes #123`
Thank you!

View File

@ -1,7 +1,7 @@
sudo: required sudo: required
language: node_js language: node_js
node_js: node_js:
- "8.11.1" - "10.15.0"
cache: cache:
directories: directories:

View File

@ -1,4 +1,4 @@
FROM node:8.11.1 as base FROM node:10.15.0 as base
ENV APP_HOME=/usr/src/app \ ENV APP_HOME=/usr/src/app \
TERM=xterm TERM=xterm
RUN mkdir -p $APP_HOME RUN mkdir -p $APP_HOME

1
Procfile Normal file
View File

@ -0,0 +1 @@
web: MONGO_URL=$MONGO_URI MAILGUN_KEY=$MAILGUN_API_KEY npm run start:prod

93
app.json Normal file
View File

@ -0,0 +1,93 @@
{
"name": "p5.js Web Editor",
"description": "The p5.js Web Editor is an in-browser editor for creative coding, specifically for writing p5.js sketches.",
"repository": "https://github.com/processing/p5.js-web-editor",
"logo": "https://p5js.org/assets/img/p5js.svg",
"keywords": ["processing", "p5js", "p5.js"],
"addons": [
{
"plan": "mongolab:sandbox",
"as": "MONGO"
},
{
"plan": "mailgun:starter",
"as": "MAILGUN"
}
],
"env": {
"API_URL": {
"value": "/api"
},
"AWS_ACCESS_KEY": {
"description": "AWS Access Key",
"value": "placeholder"
},
"AWS_SECRET_KEY": {
"description": "AWS Secret Key",
"value": "placeholder"
},
"AWS_REGION": {
"description": "AWS Region for the S3_BUCKET",
"value": "placeholder"
},
"EMAIL_SENDER": {
"description": "The sending email address for transactional emails.",
"value": "no-reply@mydomain.com"
},
"EMAIL_VERIFY_SECRET_TOKEN": {
"description": "A secret key for...? Not sure where used.",
"generator": "secret"
},
"EXAMPLE_USER_EMAIL": {
"description": "The email address for the account holding the default Example sketches",
"value": "examples@p5js.org"
},
"EXAMPLE_USER_PASSWORD": {
"value": "hellop5js"
},
"GG_EXAMPLES_EMAIL": {
"description": "The email address for the account holding the Generative Design Example sketches",
"value": "benedikt.gross@generative-gestaltung.de"
},
"GG_EXAMPLES_PASS": {
"value": "generativedesign"
},
"GG_EXAMPLES_USERNAME": {
"value": "generative-design"
},
"GITHUB_ID": {
"description": "The GitHub Client Id for sign in with GitHub support",
"value": "placeholder"
},
"GITHUB_SECRET": {
"description": "The GitHub Client Secret",
"value": "placeholder"
},
"GOOGLE_ID": {
"description": "The Google Client Id for sign in with Google support",
"value": "placeholder"
},
"GOOGLE_SECRET": {
"description": "The Google Client Secret",
"value": "placeholder"
},
"NODE_ENV": {
"value": "production"
},
"S3_BUCKET": {
"desription": "Name of the S3 bucket for asset storage",
"value": "placeholder"
},
"S3_BUCKET_URL_BASE": {
"description": "S3 bucket URL base",
"required": false
},
"SESSION_SECRET": {
"description": "A secret key for verifying the integrity of signed cookies.",
"generator": "secret"
}
},
"scripts": {
"postdeploy": "MONGO_URL=$MONGO_URI MAILGUN_KEY=$MAILGUN_API_KEY npm run fetch-examples:prod"
}
}

View File

@ -8,9 +8,7 @@ import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import * as IDEActions from '../modules/IDE/actions/ide'; import * as IDEActions from '../modules/IDE/actions/ide';
import { import { metaKeyName, } from '../utils/metaKey';
metaKeyName,
} from '../utils/metaKey';
const triangleUrl = require('../images/down-filled-triangle.svg'); const triangleUrl = require('../images/down-filled-triangle.svg');
const logoUrl = require('../images/p5js-logo-small.svg'); const logoUrl = require('../images/p5js-logo-small.svg');
@ -221,6 +219,7 @@ class Nav extends React.PureComponent {
Open Open
</Link> </Link>
</li> } </li> }
{ __process.env.EXAMPLES_ENABLED &&
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link <Link
to="/p5/sketches" to="/p5/sketches"
@ -230,7 +229,7 @@ class Nav extends React.PureComponent {
> >
Examples Examples
</Link> </Link>
</li> </li> }
</ul> </ul>
</li> </li>
<li className={navDropdownState.edit}> <li className={navDropdownState.edit}>

11
client/images/share.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<g>
<path d="M77.202,60.609v12.399c0,2.08-1.681,3.761-3.761,3.761H25.761c-2.08,0-3.84-1.681-3.84-3.761v-35.44
c0-2.16,1.76-3.84,3.84-3.84h14.64v5.76h-12.72v31.521h43.761V60.609H77.202z M60.002,37.248l-2.801-11.36l22.161,16.4
L57.201,57.809l2.801-11.28c0,0-16.241-0.24-16.881,16.72h-2.88C40.241,63.248,40.001,38.368,60.002,37.248z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 828 B

View File

@ -5,10 +5,12 @@ import each from 'async/each';
import { isEqual, pick } from 'lodash'; import { isEqual, pick } from 'lodash';
import * as ActionTypes from '../../../constants'; import * as ActionTypes from '../../../constants';
import { showToast, setToastText } from './toast'; import { showToast, setToastText } from './toast';
import { setUnsavedChanges, import {
setUnsavedChanges,
justOpenedProject, justOpenedProject,
resetJustOpenedProject, resetJustOpenedProject,
showErrorModal } from './ide'; showErrorModal
} from './ide';
import { clearState, saveState } from '../../../persistState'; import { clearState, saveState } from '../../../persistState';
const __process = (typeof global !== 'undefined' ? global : window).process; const __process = (typeof global !== 'undefined' ? global : window).process;

View File

@ -1,6 +1,10 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Clipboard from 'clipboard'; import Clipboard from 'clipboard';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames';
import shareUrl from '../../../images/share.svg';
class CopyableInput extends React.Component { class CopyableInput extends React.Component {
constructor(props) { constructor(props) {
@ -34,8 +38,12 @@ class CopyableInput extends React.Component {
value, value,
hasPreviewLink hasPreviewLink
} = this.props; } = this.props;
const copyableInputClass = classNames({
'copyable-input': true,
'copyable-input--with-preview': hasPreviewLink
});
return ( return (
<div className="copyable-input"> <div className={copyableInputClass}>
<div <div
className="copyable-input__value-container tooltipped-no-delay" className="copyable-input__value-container tooltipped-no-delay"
aria-label="Copied to Clipboard!" aria-label="Copied to Clipboard!"
@ -44,10 +52,7 @@ class CopyableInput extends React.Component {
> >
<label className="copyable-input__label" htmlFor={`copyable-input__value-${label}`}> <label className="copyable-input__label" htmlFor={`copyable-input__value-${label}`}>
<div className="copyable-input__label-container"> <div className="copyable-input__label-container">
{label} {hasPreviewLink && {label}
<a target="_blank" href={value}>
Open
</a>}
</div> </div>
<input <input
type="text" type="text"
@ -59,6 +64,17 @@ class CopyableInput extends React.Component {
/> />
</label> </label>
</div> </div>
{hasPreviewLink &&
<a
target="_blank"
rel="noopener noreferrer"
href={value}
className="copyable-input__preview"
title={`open ${label.toLowerCase()} view in new tab`}
>
<InlineSVG src={shareUrl} alt={`open ${label} view in new tab`} />
</a>
}
</div> </div>
); );
} }

View File

@ -32,9 +32,7 @@ import '../../../utils/p5-javascript';
import '../../../utils/webGL-clike'; import '../../../utils/webGL-clike';
import Timer from '../components/Timer'; import Timer from '../components/Timer';
import EditorAccessibility from '../components/EditorAccessibility'; import EditorAccessibility from '../components/EditorAccessibility';
import { import { metaKey, } from '../../../utils/metaKey';
metaKey,
} from '../../../utils/metaKey';
import search from '../../../utils/codemirror-search'; import search from '../../../utils/codemirror-search';

View File

@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
import { import { metaKeyName, } from '../../../utils/metaKey';
metaKeyName,
} from '../../../utils/metaKey';
function KeyboardShortcutModal() { function KeyboardShortcutModal() {
return ( return (

View File

@ -81,7 +81,10 @@ class PreviewFrame extends React.Component {
handleConsoleEvent(messageEvent) { handleConsoleEvent(messageEvent) {
if (Array.isArray(messageEvent.data)) { if (Array.isArray(messageEvent.data)) {
const decodedMessages = messageEvent.data.map(message => Object.assign(Decode(message.log), { source: message.source })); const decodedMessages = messageEvent.data.map(message =>
Object.assign(Decode(message.log), {
source: message.source
}));
decodedMessages.every((message, index, arr) => { decodedMessages.every((message, index, arr) => {
const { data: args } = message; const { data: args } = message;

View File

@ -19,6 +19,11 @@ class ShareModal extends React.PureComponent {
label="Embed" label="Embed"
value={`<iframe src="${hostname}/${ownerUsername}/embed/${projectId}"></iframe>`} value={`<iframe src="${hostname}/${ownerUsername}/embed/${projectId}"></iframe>`}
/> />
<CopyableInput
label="Present"
hasPreviewLink
value={`${hostname}/${ownerUsername}/present/${projectId}`}
/>
<CopyableInput <CopyableInput
label="Fullscreen" label="Fullscreen"
hasPreviewLink hasPreviewLink
@ -26,6 +31,7 @@ class ShareModal extends React.PureComponent {
/> />
<CopyableInput <CopyableInput
label="Edit" label="Edit"
hasPreviewLink
value={`${hostname}/${ownerUsername}/sketches/${projectId}`} value={`${hostname}/${ownerUsername}/sketches/${projectId}`}
/> />
</div> </div>

View File

@ -87,11 +87,17 @@
border-color: getThemifyVariable('button-background-hover-color'); border-color: getThemifyVariable('button-background-hover-color');
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');
& g {
fill: getThemifyVariable('button-hover-color');
}
} }
&:enabled:active { &:enabled:active {
border-color: getThemifyVariable('button-background-active-color'); border-color: getThemifyVariable('button-background-active-color');
background-color: getThemifyVariable('button-background-active-color'); background-color: getThemifyVariable('button-background-active-color');
color: getThemifyVariable('button-active-color'); color: getThemifyVariable('button-active-color');
& g {
fill: getThemifyVariable('button-active-color');
}
} }
} }
} }

View File

@ -13,6 +13,9 @@
.copyable-input__value { .copyable-input__value {
width: 100%; width: 100%;
font-size: #{16 / $base-font-size}rem; font-size: #{16 / $base-font-size}rem;
.copyable-input--with-preview & {
border-radius: 2px 0 0 2px;
}
} }
.copyable-input__label { .copyable-input__label {
@ -32,7 +35,6 @@
.copyable-input { .copyable-input {
padding-bottom: #{30 / $base-font-size}rem; padding-bottom: #{30 / $base-font-size}rem;
display: flex; display: flex;
flex-wrap: wrap;
} }
.copyable-input__value-container { .copyable-input__value-container {
@ -56,3 +58,16 @@
border-top-color: getThemifyVariable('button-background-hover-color'); border-top-color: getThemifyVariable('button-background-hover-color');
} }
} }
.copyable-input__preview {
@extend %button;
@include themify() {
align-self: flex-end;
border-radius: 0 2px 2px 0;
padding: #{2 / $base-font-size}rem 0;
}
& svg {
height: #{30 / $base-font-size}rem;
width: #{30 / $base-font-size}rem;
}
}

View File

@ -1,6 +1,4 @@
import { import { EXTERNAL_LINK_REGEX } from '../../server/utils/fileUtils';
EXTERNAL_LINK_REGEX
} from '../../server/utils/fileUtils';
export const hijackConsoleErrorsScript = (offs) => { export const hijackConsoleErrorsScript = (offs) => {
const s = ` const s = `

View File

@ -1,7 +1,7 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others // CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE // Distributed under an MIT license: https://codemirror.net/LICENSE
/* eslint-disable */
/*eslint-disable*/
(function(mod) { (function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("codemirror")); mod(require("codemirror"));
@ -12,11 +12,6 @@
})(function(CodeMirror) { })(function(CodeMirror) {
"use strict"; "use strict";
function expressionAllowed(stream, state, backUp) {
return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}
CodeMirror.defineMode("javascript", function(config, parserConfig) { CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit; var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent; var statementIndent = parserConfig.statementIndent;
@ -26,13 +21,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
// Tokenizer // Tokenizer
var keywords = function(){ var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};} function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"}; var operator = kw("operator"), atom = {type: "atom", style: "atom"};
var p5Function = {type: "p5-function", style: "p5-function"}; var p5Function = {type: "variable", style: "p5-function"};
var p5Variable = {type: "p5-variable", style: "p5-variable"}; var p5Variable = {type: "variable", style: "p5-variable"};
var jsKeywords = { var jsKeywords = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
@ -149,9 +143,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} }
return jsKeywords; return jsKeywords;
}(); }();
var isOperatorChar = /[+\-*&%=<>!?|~^]/; var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
function readRegexp(stream) { function readRegexp(stream) {
@ -186,17 +180,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return ret(ch); return ret(ch);
} else if (ch == "=" && stream.eat(">")) { } else if (ch == "=" && stream.eat(">")) {
return ret("=>", "operator"); return ret("=>", "operator");
} else if (ch == "0" && stream.eat(/x/i)) { } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/o/i)) {
stream.eatWhile(/[0-7]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/b/i)) {
stream.eatWhile(/[01]/i);
return ret("number", "number"); return ret("number", "number");
} else if (/\d/.test(ch)) { } else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
return ret("number", "number"); return ret("number", "number");
} else if (ch == "/") { } else if (ch == "/") {
if (stream.eat("*")) { if (stream.eat("*")) {
@ -207,10 +194,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return ret("comment", "comment"); return ret("comment", "comment");
} else if (expressionAllowed(stream, state, 1)) { } else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream); readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
return ret("regexp", "string-2"); return ret("regexp", "string-2");
} else { } else {
stream.eatWhile(isOperatorChar); stream.eat("=");
return ret("operator", "operator", stream.current()); return ret("operator", "operator", stream.current());
} }
} else if (ch == "`") { } else if (ch == "`") {
@ -220,13 +207,27 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
stream.skipToEnd(); stream.skipToEnd();
return ret("error", "error"); return ret("error", "error");
} else if (isOperatorChar.test(ch)) { } else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar); if (ch != ">" || !state.lexical || state.lexical.type != ">") {
if (stream.eat("=")) {
if (ch == "!" || ch == "=") stream.eat("=")
} else if (/[<>*+\-]/.test(ch)) {
stream.eat(ch)
if (ch == ">") stream.eat(ch)
}
}
return ret("operator", "operator", stream.current()); return ret("operator", "operator", stream.current());
} else if (wordRE.test(ch)) { } else if (wordRE.test(ch)) {
stream.eatWhile(wordRE); stream.eatWhile(wordRE);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; var word = stream.current()
return (known && state.lastType != ".") ? ret(known.type, known.style, word) : if (state.lastType != ".") {
ret("variable", "variable", word); if (keywords.propertyIsEnumerable(word)) {
var kw = keywords[word]
return ret(kw.type, kw.style, word)
}
if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
return ret("async", "keyword", word)
}
return ret("variable", "variable", word)
} }
} }
@ -362,35 +363,69 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
pass.apply(null, arguments); pass.apply(null, arguments);
return true; return true;
} }
function inList(name, list) {
for (var v = list; v; v = v.next) if (v.name == name) return true
return false;
}
function register(varname) { function register(varname) {
function inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state; var state = cx.state;
cx.marked = "def"; cx.marked = "def";
if (state.context) { if (state.context) {
if (inList(state.localVars)) return; if (state.lexical.info == "var" && state.context && state.context.block) {
state.localVars = {name: varname, next: state.localVars}; // FIXME function decls are also not block scoped
} else { var newContext = registerVarScoped(varname, state.context)
if (inList(state.globalVars)) return; if (newContext != null) {
if (parserConfig.globalVars) state.context = newContext
state.globalVars = {name: varname, next: state.globalVars}; return
}
} else if (!inList(varname, state.localVars)) {
state.localVars = new Var(varname, state.localVars)
return
}
} }
window.register = register;
// Fall through means this is global
if (parserConfig.globalVars && !inList(varname, state.globalVars))
state.globalVars = new Var(varname, state.globalVars)
}
function registerVarScoped(varname, context) {
if (!context) {
return null
} else if (context.block) {
var inner = registerVarScoped(varname, context.prev)
if (!inner) return null
if (inner == context.prev) return context
return new Context(inner, context.vars, true)
} else if (inList(varname, context.vars)) {
return context
} else {
return new Context(context.prev, new Var(varname, context.vars), false)
}
}
function isModifier(name) {
return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
} }
// Combinators // Combinators
var defaultVars = {name: "this", next: {name: "arguments"}}; function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
function Var(name, next) { this.name = name; this.next = next }
var defaultVars = new Var("this", new Var("arguments", null))
function pushcontext() { function pushcontext() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
cx.state.localVars = defaultVars; cx.state.localVars = defaultVars
}
function pushblockcontext() {
cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
cx.state.localVars = null
} }
function popcontext() { function popcontext() {
cx.state.localVars = cx.state.context.vars; cx.state.localVars = cx.state.context.vars
cx.state.context = cx.state.context.prev; cx.state.context = cx.state.context.prev
} }
popcontext.lex = true
function pushlex(type, info) { function pushlex(type, info) {
var result = function() { var result = function() {
var state = cx.state, indent = state.indented; var state = cx.state, indent = state.indented;
@ -415,17 +450,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expect(wanted) { function expect(wanted) {
function exp(type) { function exp(type) {
if (type == wanted) return cont(); if (type == wanted) return cont();
else if (wanted == ";") return pass(); else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
else return cont(exp); else return cont(exp);
}; };
return exp; return exp;
} }
function statement(type, value) { function statement(type, value) {
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex); if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
if (type == "debugger") return cont(expect(";"));
if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
if (type == ";") return cont(); if (type == ";") return cont();
if (type == "if") { if (type == "if") {
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
@ -434,59 +471,78 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} }
if (type == "function") return cont(functiondef); if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel); if (type == "class" || (isTS && value == "interface")) {
if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"), cx.marked = "keyword"
block, poplex, poplex); return cont(pushlex("form", type == "class" ? type : value), className, poplex)
}
if (type == "variable") {
if (isTS && value == "declare") {
cx.marked = "keyword"
return cont(statement)
} else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
cx.marked = "keyword"
if (value == "enum") return cont(enumdef);
else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
} else if (isTS && value == "namespace") {
cx.marked = "keyword"
return cont(pushlex("form"), expression, statement, poplex)
} else if (isTS && value == "abstract") {
cx.marked = "keyword"
return cont(statement)
} else {
return cont(pushlex("stat"), maybelabel);
}
}
if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
block, poplex, poplex, popcontext);
if (type == "case") return cont(expression, expect(":")); if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":")); if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
statement, poplex, popcontext);
if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
if (type == "import") return cont(pushlex("stat"), afterImport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
if (type == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
if (type == "async") return cont(statement) if (type == "async") return cont(statement)
if (value == "@") return cont(expression, statement)
return pass(pushlex("stat"), expression, expect(";"), poplex); return pass(pushlex("stat"), expression, expect(";"), poplex);
} }
function expression(type) { function maybeCatchBinding(type) {
return expressionInner(type, false); if (type == "(") return cont(funarg, expect(")"))
} }
function expressionNoComma(type) { function expression(type, value) {
return expressionInner(type, true); return expressionInner(type, value, false);
}
function expressionNoComma(type, value) {
return expressionInner(type, value, true);
} }
function parenExpr(type) { function parenExpr(type) {
if (type != "(") return pass() if (type != "(") return pass()
return cont(pushlex(")"), expression, expect(")"), poplex) return cont(pushlex(")"), expression, expect(")"), poplex)
} }
function expressionInner(type, noComma) { function expressionInner(type, value, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) { if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody; var body = noComma ? arrowBodyNoComma : arrowBody;
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
} }
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef, maybeop); if (type == "function") return cont(functiondef, maybeop);
if (type == "class") return cont(pushlex("form"), classExpression, poplex); if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression); if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "{") return contCommasep(objprop, "}", null, maybeop);
if (type == "quasi") return pass(quasi, maybeop); if (type == "quasi") return pass(quasi, maybeop);
if (type == "new") return cont(maybeTarget(noComma)); if (type == "new") return cont(maybeTarget(noComma));
if (type == "import") return cont(expression);
return cont(); return cont();
} }
function maybeexpression(type) { function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass(); if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression); return pass(expression);
} }
function maybeexpressionNoComma(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expressionNoComma);
}
function maybeoperatorComma(type, value) { function maybeoperatorComma(type, value) {
if (type == ",") return cont(expression); if (type == ",") return cont(expression);
@ -497,7 +553,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var expr = noComma == false ? expression : expressionNoComma; var expr = noComma == false ? expression : expressionNoComma;
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") { if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me); if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
if (value == "?") return cont(expression, expect(":"), expr); if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr); return cont(expr);
} }
@ -506,6 +564,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, me); if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
if (type == "regexp") {
cx.state.lastType = cx.marked = "operator"
cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
return cont(expr)
}
} }
function quasi(type, value) { function quasi(type, value) {
if (type != "quasi") return pass(); if (type != "quasi") return pass();
@ -530,6 +594,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeTarget(noComma) { function maybeTarget(noComma) {
return function(type) { return function(type) {
if (type == ".") return cont(noComma ? targetNoComma : target); if (type == ".") return cont(noComma ? targetNoComma : target);
else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
else return pass(noComma ? expressionNoComma : expression); else return pass(noComma ? expressionNoComma : expression);
}; };
} }
@ -553,18 +618,25 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (type == "variable" || cx.style == "keyword") { } else if (type == "variable" || cx.style == "keyword") {
cx.marked = "property"; cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter); if (value == "get" || value == "set") return cont(getterSetter);
var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
cx.state.fatArrowAt = cx.stream.pos + m[0].length
return cont(afterprop); return cont(afterprop);
} else if (type == "number" || type == "string") { } else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property"); cx.marked = jsonldMode ? "property" : (cx.style + " property");
return cont(afterprop); return cont(afterprop);
} else if (type == "jsonld-keyword") { } else if (type == "jsonld-keyword") {
return cont(afterprop); return cont(afterprop);
} else if (type == "modifier") { } else if (isTS && isModifier(value)) {
cx.marked = "keyword"
return cont(objprop) return cont(objprop)
} else if (type == "[") { } else if (type == "[") {
return cont(expression, expect("]"), afterprop); return cont(expression, maybetype, expect("]"), afterprop);
} else if (type == "spread") { } else if (type == "spread") {
return cont(expression); return cont(expressionNoComma, afterprop);
} else if (value == "*") {
cx.marked = "keyword";
return cont(objprop);
} else if (type == ":") { } else if (type == ":") {
return pass(afterprop) return pass(afterprop)
} }
@ -578,9 +650,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ":") return cont(expressionNoComma); if (type == ":") return cont(expressionNoComma);
if (type == "(") return pass(functiondef); if (type == "(") return pass(functiondef);
} }
function commasep(what, end) { function commasep(what, end, sep) {
function proceed(type, value) { function proceed(type, value) {
if (type == ",") { if (sep ? sep.indexOf(type) > -1 : type == ",") {
var lex = cx.state.lexical; var lex = cx.state.lexical;
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
return cont(function(type, value) { return cont(function(type, value) {
@ -589,6 +661,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}, proceed); }, proceed);
} }
if (type == end || value == end) return cont(); if (type == end || value == end) return cont();
if (sep && sep.indexOf(";") > -1) return pass(what)
return cont(expect(end)); return cont(expect(end));
} }
return function(type, value) { return function(type, value) {
@ -611,38 +684,79 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "?") return cont(maybetype); if (value == "?") return cont(maybetype);
} }
} }
function typeexpr(type) { function mayberettype(type) {
if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} if (isTS && type == ":") {
if (type == "{") return cont(commasep(typeprop, "}")) if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
else return cont(typeexpr)
}
}
function isKW(_, value) {
if (value == "is") {
cx.marked = "keyword"
return cont()
}
}
function typeexpr(type, value) {
if (value == "keyof" || value == "typeof") {
cx.marked = "keyword"
return cont(value == "keyof" ? typeexpr : expressionNoComma)
}
if (type == "variable" || value == "void") {
cx.marked = "type"
return cont(afterType)
}
if (type == "string" || type == "number" || type == "atom") return cont(afterType);
if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
} }
function maybeReturnType(type) { function maybeReturnType(type) {
if (type == "=>") return cont(typeexpr) if (type == "=>") return cont(typeexpr)
} }
function typeprop(type) { function typeprop(type, value) {
if (type == "variable" || cx.style == "keyword") { if (type == "variable" || cx.style == "keyword") {
cx.marked = "property" cx.marked = "property"
return cont(typeprop) return cont(typeprop)
} else if (value == "?") {
return cont(typeprop)
} else if (type == ":") { } else if (type == ":") {
return cont(typeexpr) return cont(typeexpr)
} else if (type == "[") {
return cont(expression, maybetype, expect("]"), typeprop)
} else if (type == "(") {
return cont(pushlex(")"), commasep(funarg, ")"), poplex, typeprop)
} }
} }
function typearg(type) { function typearg(type, value) {
if (type == "variable") return cont(typearg) if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
else if (type == ":") return cont(typeexpr) if (type == ":") return cont(typeexpr)
return pass(typeexpr)
} }
function afterType(type, value) { function afterType(type, value) {
if (value == "<") return cont(commasep(typeexpr, ">"), afterType) if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
if (value == "|" || type == "." || value == "&") return cont(typeexpr)
if (type == "[") return cont(expect("]"), afterType) if (type == "[") return cont(expect("]"), afterType)
if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
} }
function vardef() { function maybeTypeArgs(_, value) {
if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
}
function typeparam() {
return pass(typeexpr, maybeTypeDefault)
}
function maybeTypeDefault(_, value) {
if (value == "=") return cont(typeexpr)
}
function vardef(_, value) {
if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
return pass(pattern, maybetype, maybeAssign, vardefCont); return pass(pattern, maybetype, maybeAssign, vardefCont);
} }
function pattern(type, value) { function pattern(type, value) {
if (type == "modifier") return cont(pattern) if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
if (type == "variable") { register(value); return cont(); } if (type == "variable") { register(value); return cont(); }
if (type == "spread") return cont(pattern); if (type == "spread") return cont(pattern);
if (type == "[") return contCommasep(pattern, "]"); if (type == "[") return contCommasep(eltpattern, "]");
if (type == "{") return contCommasep(proppattern, "}"); if (type == "{") return contCommasep(proppattern, "}");
} }
function proppattern(type, value) { function proppattern(type, value) {
@ -653,8 +767,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") cx.marked = "property"; if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern); if (type == "spread") return cont(pattern);
if (type == "}") return pass(); if (type == "}") return pass();
if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
return cont(expect(":"), pattern, maybeAssign); return cont(expect(":"), pattern, maybeAssign);
} }
function eltpattern() {
return pass(pattern, maybeAssign)
}
function maybeAssign(_type, value) { function maybeAssign(_type, value) {
if (value == "=") return cont(expressionNoComma); if (value == "=") return cont(expressionNoComma);
} }
@ -664,7 +782,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeelse(type, value) { function maybeelse(type, value) {
if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
} }
function forspec(type) { function forspec(type, value) {
if (value == "await") return cont(forspec);
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
} }
function forspec1(type) { function forspec1(type) {
@ -688,10 +807,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function functiondef(type, value) { function functiondef(type, value) {
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
if (type == "variable") {register(value); return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
} }
function funarg(type) { function functiondecl(type, value) {
if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
if (type == "variable") {register(value); return cont(functiondecl);}
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
}
function funarg(type, value) {
if (value == "@") cont(expression, funarg)
if (type == "spread") return cont(funarg); if (type == "spread") return cont(funarg);
if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
return pass(pattern, maybetype, maybeAssign); return pass(pattern, maybetype, maybeAssign);
} }
function classExpression(type, value) { function classExpression(type, value) {
@ -703,40 +831,56 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") {register(value); return cont(classNameAfter);} if (type == "variable") {register(value); return cont(classNameAfter);}
} }
function classNameAfter(type, value) { function classNameAfter(type, value) {
if (value == "extends" || value == "implements") return cont(isTS ? typeexpr : expression, classNameAfter); if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
if (value == "extends" || value == "implements" || (isTS && type == ",")) {
if (value == "implements") cx.marked = "keyword";
return cont(isTS ? typeexpr : expression, classNameAfter);
}
if (type == "{") return cont(pushlex("}"), classBody, poplex); if (type == "{") return cont(pushlex("}"), classBody, poplex);
} }
function classBody(type, value) { function classBody(type, value) {
if (type == "async" ||
(type == "variable" &&
(value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
cx.marked = "keyword";
return cont(classBody);
}
if (type == "variable" || cx.style == "keyword") { if (type == "variable" || cx.style == "keyword") {
if ((value == "static" || value == "get" || value == "set" ||
(isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
cx.marked = "keyword";
return cont(classBody);
}
cx.marked = "property"; cx.marked = "property";
return cont(isTS ? classfield : functiondef, classBody); return cont(isTS ? classfield : functiondef, classBody);
} }
if (type == "[")
return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
if (value == "*") { if (value == "*") {
cx.marked = "keyword"; cx.marked = "keyword";
return cont(classBody); return cont(classBody);
} }
if (type == ";") return cont(classBody); if (type == ";") return cont(classBody);
if (type == "}") return cont(); if (type == "}") return cont();
if (value == "@") return cont(expression, classBody)
} }
function classfield(type, value) { function classfield(type, value) {
if (value == "?") return cont(classfield) if (value == "?") return cont(classfield)
if (type == ":") return cont(typeexpr, maybeAssign) if (type == ":") return cont(typeexpr, maybeAssign)
return pass(functiondef) if (value == "=") return cont(expressionNoComma)
var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
return pass(isInterface ? functiondecl : functiondef)
} }
function afterExport(_type, value) { function afterExport(type, value) {
if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
return pass(statement); return pass(statement);
} }
function exportField(type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
if (type == "variable") return pass(expressionNoComma, exportField);
}
function afterImport(type) { function afterImport(type) {
if (type == "string") return cont(); if (type == "string") return cont();
return pass(importSpec, maybeFrom); if (type == "(") return pass(expression);
return pass(importSpec, maybeMoreImports, maybeFrom);
} }
function importSpec(type, value) { function importSpec(type, value) {
if (type == "{") return contCommasep(importSpec, "}"); if (type == "{") return contCommasep(importSpec, "}");
@ -744,6 +888,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "*") cx.marked = "keyword"; if (value == "*") cx.marked = "keyword";
return cont(maybeAs); return cont(maybeAs);
} }
function maybeMoreImports(type) {
if (type == ",") return cont(importSpec, maybeMoreImports)
}
function maybeAs(_type, value) { function maybeAs(_type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
} }
@ -754,6 +901,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "]") return cont(); if (type == "]") return cont();
return pass(commasep(expressionNoComma, "]")); return pass(commasep(expressionNoComma, "]"));
} }
function enumdef() {
return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
}
function enummember() {
return pass(pattern, maybeAssign);
}
function isContinuedStatement(state, textAfter) { function isContinuedStatement(state, textAfter) {
return state.lastType == "operator" || state.lastType == "," || return state.lastType == "operator" || state.lastType == "," ||
@ -761,6 +914,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
/[,.]/.test(textAfter.charAt(0)); /[,.]/.test(textAfter.charAt(0));
} }
function expressionAllowed(stream, state, backUp) {
return state.tokenize == tokenBase &&
/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}
// Interface // Interface
return { return {
@ -771,7 +930,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
cc: [], cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars, localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars}, context: parserConfig.localVars && new Context(null, null, false),
indented: basecolumn || 0 indented: basecolumn || 0
}; };
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
@ -812,7 +971,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
lexical = lexical.prev; lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type; var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit; else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat") else if (type == "stat")
@ -826,6 +985,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
blockCommentStart: jsonMode ? null : "/*", blockCommentStart: jsonMode ? null : "/*",
blockCommentEnd: jsonMode ? null : "*/", blockCommentEnd: jsonMode ? null : "*/",
blockCommentContinue: jsonMode ? null : " * ",
lineComment: jsonMode ? null : "//", lineComment: jsonMode ? null : "//",
fold: "brace", fold: "brace",
closeBrackets: "()[]{}''\"\"``", closeBrackets: "()[]{}''\"\"``",
@ -835,6 +995,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
jsonMode: jsonMode, jsonMode: jsonMode,
expressionAllowed: expressionAllowed, expressionAllowed: expressionAllowed,
skipExpression: function(state) { skipExpression: function(state) {
var top = state.cc[state.cc.length - 1] var top = state.cc[state.cc.length - 1]
if (top == expression || top == expressionNoComma) state.cc.pop() if (top == expression || top == expressionNoComma) state.cc.pop()

View File

@ -23,11 +23,11 @@ These are the steps that happen when you deploy the application.
4. Travis CI runs some tests, which in this case, is just `npm run lint`. This could be updated in the future to include more extensive tests. If the tests fail, the build stops here. 4. Travis CI runs some tests, which in this case, is just `npm run lint`. This could be updated in the future to include more extensive tests. If the tests fail, the build stops here.
5. If the tests pass, then Travis CI builds a (production) Docker image of the whole application. 5. If the tests pass, then Travis CI builds a (production) Docker image of the whole application.
6. This image is pushed to [Docker Hub](https://hub.docker.com/r/catarak/p5.js-web-editor/) with a unique tag name (the Travis commit) and also to the `latest` tag. 6. This image is pushed to [Docker Hub](https://hub.docker.com/r/catarak/p5.js-web-editor/) with a unique tag name (the Travis commit) and also to the `latest` tag.
7. The Kubernetes deployment is updated to image just pushed to Docker Hub on the cluster on Google Kubernetes Engine. 7. The Kubernetes deployment is updated to image just pushed to Docker Hub on the cluster on Google Kubernetes Engine.
## Production Installation ## Production Installation
You'll only need to do this if you're testing the production environment locally. You'll only need to do this if you're testing the production environment locally.
1. Clone this repository and `cd` into it 1. Clone this repository and `cd` into it
2. `$ npm install` 2. `$ npm install`
@ -38,3 +38,14 @@ You'll only need to do this if you're testing the production environment locally
7. `$ npm run build` 7. `$ npm run build`
8. Since production assumes your environment variables are in the shell environment, and not in a `.env` file, you will have to run `export $(grep -v '^#' .env | xargs)` or a similar command, see this [Stack Overflow answer](https://stackoverflow.com/a/20909045/4086967). 8. Since production assumes your environment variables are in the shell environment, and not in a `.env` file, you will have to run `export $(grep -v '^#' .env | xargs)` or a similar command, see this [Stack Overflow answer](https://stackoverflow.com/a/20909045/4086967).
9. `$ npm run start:prod` 9. `$ npm run start:prod`
## Self Hosting - Heroku Deployment
If you are interested in hosting and deploying your own p5.js Web Editor instance, you can! It would be the same as the official editor instance at editor.p5js.org, except with a different domain, and you would be in charge of the maintenance. We recommend using Heroku as you can host it for free.
1. Sign up for a free account at: [Heroku](https://www.heroku.com/)
2. Click here: [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/processing/p5.js-web-editor/tree/master)
3. Enter a unique *App name*, this will become part of the url (i.e. https://app-name.herokuapp.com/)
4. Update any configuration variables, or accept the defaults for a quick evaluation (they can be changed later to enable full functionality)
5. Click on the "Deploy app" button
6. When complete, click on the "View app" button

32823
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,145 +1,147 @@
{ {
"name": "p5.js-web-editor", "name": "p5.js-web-editor",
"version": "0.0.1", "version": "0.0.1",
"description": "The web editor for p5.js.", "description": "The web editor for p5.js.",
"scripts": { "scripts": {
"clean": "rimraf dist", "clean": "rimraf dist",
"start": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js", "start": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js",
"start:prod": "cross-env NODE_ENV=production node index.js", "start:prod": "cross-env NODE_ENV=production node index.js",
"lint": "eslint client server --ext .jsx --ext .js", "lint": "eslint client server --ext .jsx --ext .js",
"lint-fix": "eslint client server --ext .jsx --ext .js --fix", "lint-fix": "eslint client server --ext .jsx --ext .js --fix",
"build": "npm run build:client && npm run build:server && npm run build:examples", "build": "npm run build:client && npm run build:server && npm run build:examples",
"build:client": "cross-env NODE_ENV=production webpack --config webpack/config.prod.js", "build:client": "cross-env NODE_ENV=production webpack --config webpack/config.prod.js",
"build:server": "cross-env NODE_ENV=production webpack --config webpack/config.server.js", "build:server": "cross-env NODE_ENV=production webpack --config webpack/config.server.js",
"build:examples": "cross-env NODE_ENV=production webpack --config webpack/config.examples.js", "build:examples": "cross-env NODE_ENV=production webpack --config webpack/config.examples.js",
"test": "npm run lint", "test": "npm run lint",
"fetch-examples": "cross-env NODE_ENV=development node ./server/scripts/fetch-examples.js", "fetch-examples": "cross-env NODE_ENV=development node ./server/scripts/fetch-examples.js",
"fetch-examples-gg": "cross-env NODE_ENV=development node ./server/scripts/fetch-examples-gg.js", "fetch-examples-gg": "cross-env NODE_ENV=development node ./server/scripts/fetch-examples-gg.js",
"fetch-examples:prod": "cross-env NODE_ENV=production node ./dist/fetch-examples.bundle.js", "fetch-examples:prod": "cross-env NODE_ENV=production node ./dist/fetch-examples.bundle.js",
"fetch-examples-gg:prod": "cross-env NODE_ENV=production node ./dist/fetch-examples-gg.bundle.js" "fetch-examples-gg:prod": "cross-env NODE_ENV=production node ./dist/fetch-examples-gg.bundle.js",
}, "heroku-postbuild": "touch .env; npm run build"
"main": "index.js", },
"author": "Cassie Tarakajian", "main": "index.js",
"license": "LGPL-2.1", "author": "Cassie Tarakajian",
"repository": { "license": "LGPL-2.1",
"type": "git", "repository": {
"url": "git+https://github.com/catarak/p5.js-web-editor.git" "type": "git",
}, "url": "git+https://github.com/catarak/p5.js-web-editor.git"
"devDependencies": { },
"babel-eslint": "^7.1.1", "devDependencies": {
"babel-loader": "^7.1.4", "babel-eslint": "^7.1.1",
"babel-plugin-transform-react-constant-elements": "^6.8.0", "babel-loader": "^7.1.5",
"babel-plugin-transform-react-inline-elements": "^6.8.0", "babel-plugin-transform-react-constant-elements": "^6.23.0",
"babel-plugin-transform-react-remove-prop-types": "^0.2.6", "babel-plugin-transform-react-inline-elements": "^6.22.0",
"babel-plugin-webpack-loaders": "^0.9.0", "babel-plugin-transform-react-remove-prop-types": "^0.2.12",
"babel-preset-env": "^1.6.1", "babel-plugin-webpack-loaders": "^0.9.0",
"babel-preset-react": "^6.5.0", "babel-preset-env": "^1.7.0",
"babel-preset-react-optimize": "^1.0.1", "babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.5.0", "babel-preset-react-optimize": "^1.0.1",
"chunk-manifest-webpack-plugin": "^1.1.2", "babel-preset-stage-0": "^6.24.1",
"css-loader": "^0.23.1", "chunk-manifest-webpack-plugin": "github:catarak/chunk-manifest-webpack-plugin",
"cssnano": "^3.7.1", "css-loader": "^0.23.1",
"eslint": "^4.9.0", "cssnano": "^3.10.0",
"eslint-config-airbnb": "^16.1.0", "eslint": "^4.19.1",
"eslint-plugin-import": "^2.2.0", "eslint-config-airbnb": "^16.1.0",
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-import": "^2.14.0",
"eslint-plugin-react": "^7.7.0", "eslint-plugin-jsx-a11y": "^6.1.2",
"extract-text-webpack-plugin": "^3.0.2", "eslint-plugin-react": "^7.12.3",
"file-loader": "^2.0.0", "extract-text-webpack-plugin": "^3.0.2",
"node-sass": "^4.9.0", "file-loader": "^2.0.0",
"nodemon": "^1.9.2", "node-sass": "^4.11.0",
"postcss-cssnext": "^2.7.0", "nodemon": "^1.18.9",
"postcss-focus": "^1.0.0", "postcss-cssnext": "^2.11.0",
"postcss-loader": "^0.9.1", "postcss-focus": "^1.0.0",
"postcss-reporter": "^1.3.3", "postcss-loader": "^0.9.1",
"rimraf": "^2.6.2", "postcss-reporter": "^1.4.1",
"sass-loader": "^6.0.6", "rimraf": "^2.6.3",
"style-loader": "^0.13.1", "sass-loader": "^6.0.7",
"webpack-manifest-plugin": "^2.0.0", "style-loader": "^0.13.2",
"webpack-node-externals": "^1.7.2" "webpack-manifest-plugin": "^2.0.4",
}, "webpack-node-externals": "^1.7.2"
"engines": { },
"node": ">=8.9.0" "engines": {
}, "node": "10.15.0",
"dependencies": { "npm": "6.4.1"
"archiver": "^1.1.0", },
"async": "^2.0.0", "dependencies": {
"axios": "^0.12.0", "archiver": "^1.1.0",
"babel-core": "^6.26.0", "async": "^2.6.1",
"babel-polyfill": "^6.8.0", "axios": "^0.12.0",
"babel-register": "^6.8.0", "babel-core": "^6.26.3",
"bcrypt-nodejs": "0.0.3", "babel-polyfill": "^6.26.0",
"blob-util": "^1.2.1", "babel-register": "^6.26.0",
"body-parser": "^1.15.1", "bcrypt-nodejs": "0.0.3",
"bson-objectid": "^1.1.4", "blob-util": "^1.2.1",
"classnames": "^2.2.5", "body-parser": "^1.18.3",
"clipboard": "^1.7.1", "bson-objectid": "^1.2.4",
"codemirror": "^5.38.0", "classnames": "^2.2.6",
"connect-mongo": "^1.2.0", "clipboard": "^1.7.1",
"console-feed": "^2.8.1", "codemirror": "^5.42.2",
"cookie-parser": "^1.4.1", "connect-mongo": "^1.3.2",
"cors": "^2.8.1", "console-feed": "^2.8.5",
"cross-env": "^5.1.3", "cookie-parser": "^1.4.3",
"csslint": "^0.10.0", "cors": "^2.8.5",
"decomment": "^0.8.7", "cross-env": "^5.2.0",
"dotenv": "^2.0.0", "csslint": "^0.10.0",
"dropzone": "^4.3.0", "decomment": "^0.8.7",
"escape-string-regexp": "^1.0.5", "dotenv": "^2.0.0",
"eslint-loader": "^1.3.0", "dropzone": "^4.3.0",
"express": "^4.13.4", "escape-string-regexp": "^1.0.5",
"express-basic-auth": "^1.1.5", "eslint-loader": "^1.3.0",
"express-session": "^1.13.0", "express": "^4.16.4",
"htmlhint": "^0.9.13", "express-basic-auth": "^1.1.6",
"is-url": "^1.2.2", "express-session": "^1.15.6",
"js-beautify": "^1.6.4", "htmlhint": "^0.10.1",
"jsdom": "^9.8.3", "is-url": "^1.2.4",
"jshint": "^2.9.4", "js-beautify": "^1.8.9",
"lodash": "^4.16.4", "jsdom": "^9.8.3",
"loop-protect": "github:catarak/loop-protect", "jshint": "^2.9.7",
"mjml": "^3.3.2", "lodash": "^4.17.11",
"moment": "^2.14.1", "loop-protect": "github:catarak/loop-protect",
"mongoose": "^4.6.8", "mjml": "^3.3.2",
"node-uuid": "^1.4.7", "moment": "^2.23.0",
"nodemailer": "^2.6.4", "mongoose": "^4.6.8",
"nodemailer-mailgun-transport": "^1.2.2", "node-uuid": "^1.4.7",
"passport": "^0.3.2", "nodemailer": "^2.6.4",
"passport-github": "^1.1.0", "nodemailer-mailgun-transport": "^1.4.0",
"passport-google-oauth20": "^1.0.0", "passport": "^0.3.2",
"passport-local": "^1.0.0", "passport-github": "^1.1.0",
"pretty-bytes": "^3.0.1", "passport-google-oauth20": "^1.0.0",
"primer-tooltips": "^1.4.1", "passport-local": "^1.0.0",
"project-name-generator": "^2.1.3", "pretty-bytes": "^3.0.1",
"prop-types": "^15.6.0", "primer-tooltips": "^1.5.11",
"q": "^1.4.1", "project-name-generator": "^2.1.5",
"react": "^16.4.0", "prop-types": "^15.6.2",
"react-dom": "^16.4.0", "q": "^1.4.1",
"react-helmet": "^5.1.3", "react": "^16.7.0",
"react-hot-loader": "^4.1.2", "react-dom": "^16.7.0",
"react-inlinesvg": "^0.7.5", "react-helmet": "^5.1.3",
"react-redux": "^5.0.6", "react-hot-loader": "^4.6.3",
"react-router": "^3.2.0", "react-inlinesvg": "^0.7.5",
"react-split-pane": "^0.1.44", "react-redux": "^5.1.1",
"react-tabs": "^2.2.1", "react-router": "^3.2.1",
"redux": "^3.5.2", "react-split-pane": "^0.1.76",
"redux-devtools": "^3.4.1", "react-tabs": "^2.3.0",
"redux-devtools-dock-monitor": "^1.1.3", "redux": "^3.7.2",
"redux-devtools-log-monitor": "^1.4.0", "redux-devtools": "^3.4.2",
"redux-form": "^5.3.3", "redux-devtools-dock-monitor": "^1.1.3",
"redux-thunk": "^2.1.0", "redux-devtools-log-monitor": "^1.4.0",
"request": "^2.76.0", "redux-form": "^5.3.3",
"request-promise": "^4.1.1", "redux-thunk": "^2.3.0",
"s3": "^4.4.0", "request": "^2.88.0",
"s3-policy": "^0.2.0", "request-promise": "^4.1.1",
"sass-extract": "^2.1.0", "s3": "^4.4.0",
"sass-extract-js": "^0.4.0", "s3-policy": "^0.2.0",
"sass-extract-loader": "^1.1.0", "sass-extract": "^2.1.0",
"shortid": "^2.2.6", "sass-extract-js": "^0.4.0",
"slugify": "^1.3.4", "sass-extract-loader": "^1.1.0",
"srcdoc-polyfill": "^0.2.0", "shortid": "^2.2.14",
"url": "^0.11.0", "slugify": "^1.3.4",
"webpack": "^3.1.0", "srcdoc-polyfill": "^0.2.0",
"webpack-dev-middleware": "^2.0.6", "url": "^0.11.0",
"webpack-hot-middleware": "^2.10.0", "webpack": "^3.12.0",
"xhr": "^2.2.1" "webpack-dev-middleware": "^2.0.6",
} "webpack-hot-middleware": "^2.24.3",
"xhr": "^2.5.0"
}
} }

View File

@ -4,7 +4,8 @@ import {
injectMediaUrls, injectMediaUrls,
resolvePathsForElementsWithAttribute, resolvePathsForElementsWithAttribute,
resolveScripts, resolveScripts,
resolveStyles } from '../utils/previewGeneration'; resolveStyles
} from '../utils/previewGeneration';
import { get404Sketch } from '../views/404Page'; import { get404Sketch } from '../views/404Page';
export function serveProject(req, res) { export function serveProject(req, res) {

View File

@ -4,6 +4,7 @@ import * as EmbedController from '../controllers/embed.controller';
const router = new Router(); const router = new Router();
router.get('/:username/embed/:project_id', EmbedController.serveProject); router.get('/:username/embed/:project_id', EmbedController.serveProject);
router.get('/:username/present/:project_id', EmbedController.serveProject);
router.get('/embed/:project_id', EmbedController.serveProject); router.get('/embed/:project_id', EmbedController.serveProject);
export default router; export default router;

View File

@ -49,7 +49,7 @@ mongoose.connection.on('error', () => {
function getCategories() { function getCategories() {
const categories = []; const categories = [];
const options = { const options = {
url: `https://api.github.com/repos/processing/p5.js-website/contents/dist/assets/examples/en?client_id=${ url: `https://api.github.com/repos/processing/p5.js-website/contents/src/data/examples/en?client_id=${
clientId}&client_secret=${clientSecret}`, clientId}&client_secret=${clientSecret}`,
method: 'GET', method: 'GET',
headers, headers,
@ -134,7 +134,7 @@ function getSketchContent(projectsInAllCategories) {
function createProjectsInP5user(projectsInAllCategories) { function createProjectsInP5user(projectsInAllCategories) {
const options = { const options = {
url: `https://api.github.com/repos/processing/p5.js-website/contents/dist/assets/examples/assets?client_id=${ url: `https://api.github.com/repos/processing/p5.js-website/contents/src/data/examples/assets?client_id=${
clientId}&client_secret=${clientSecret}`, clientId}&client_secret=${clientSecret}`,
method: 'GET', method: 'GET',
headers, headers,
@ -264,7 +264,7 @@ function createProjectsInP5user(projectsInAllCategories) {
const fileID = objectID().toHexString(); const fileID = objectID().toHexString();
newProject.files.push({ newProject.files.push({
name: assetName, name: assetName,
url: `https://rawgit.com/processing/p5.js-website/master/dist/assets/examples/assets/${assetName}`, url: `https://rawgit.com/processing/p5.js-website/master/src/data/examples/assets/${assetName}`,
id: fileID, id: fileID,
_id: fileID, _id: fileID,
children: [], children: [],

View File

@ -1,11 +1,77 @@
import User from '../models/user'; import User from '../models/user';
import Project from '../models/project'; import Project from '../models/project';
function insertErrorMessage(htmlFile) {
const html = htmlFile.split('</head>');
const metaDescription = 'A web editor for p5.js, a JavaScript library with the goal of making coding accessible to artists, designers, educators, and beginners.'; // eslint-disable-line
html[0] = `
${html[0]}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="p5.js, p5.js web editor, web editor, processing, code editor" />
<meta name="description" content="${metaDescription}" />
<title>404 Page Not Found - p5.js Web Editor</title>
<style>
.header {
position: fixed;
height: 200px;
width: 100%;
z-index: 1;
background: white;
color: #ed225d;
font-family: Montserrat, sans-serif;
text-align: center;
display: table;
}
.message-container {
display: table-cell;
vertical-align: middle;
}
.message {
color: #6b6b6b;
margin: 10px;
}
.home-link {
color: #b5b5b5;
text-decoration: none;
}
canvas {
position: fixed;
width: 100% !important;
height: 100% !important;
}
</style>
<link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<link
rel='shortcut icon'
href='https://raw.githubusercontent.com/processing/p5.js-website-OLD/master/favicon.ico'
type='image/x-icon'
>
`;
const body = html[1].split('<body>');
html[1] = `
${body[0]}
<body>
<div class="header">
<div class="message-container">
<h1>404 Page Not Found</h1>
<h6 class="message">The page you are trying to reach does not exist.</h6>
<h6 class="message">
Please check the URL or return to the <a href="/" class="home-link">home page</a>.
</h6>
</div>
</div>
${body[1]}
`;
return html.join('</head>');
}
export function get404Sketch(callback) { export function get404Sketch(callback) {
User.findOne({ username: 'p5' }, (userErr, user) => { // Find p5 user User.findOne({ username: 'p5' }, (userErr, user) => { // Find p5 user
if (userErr) { if (userErr) {
throw userErr; throw userErr;
} else { } else if (user) {
Project.find({ user: user._id }, (projErr, projects) => { // Find example projects Project.find({ user: user._id }, (projErr, projects) => { // Find example projects
// Choose a random sketch // Choose a random sketch
const randomIndex = Math.floor(Math.random() * projects.length); const randomIndex = Math.floor(Math.random() * projects.length);
@ -39,66 +105,7 @@ export function get404Sketch(callback) {
}); });
// Add 404 html and position canvas // Add 404 html and position canvas
const html = htmlFile.split('</head>'); htmlFile = insertErrorMessage(htmlFile);
const metaDescription = 'A web editor for p5.js, a JavaScript library with the goal of making coding accessible to artists, designers, educators, and beginners.'; // eslint-disable-line
html[0] = `
${html[0]}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="p5.js, p5.js web editor, web editor, processing, code editor" />
<meta name="description" content="${metaDescription}" />
<title>404 Page Not Found - p5.js Web Editor</title>
<style>
.header {
position: fixed;
height: 200px;
width: 100%;
z-index: 1;
background: white;
color: #ed225d;
font-family: Montserrat, sans-serif;
text-align: center;
display: table;
}
.message-container {
display: table-cell;
vertical-align: middle;
}
.message {
color: #6b6b6b;
margin: 10px;
}
.home-link {
color: #b5b5b5;
text-decoration: none;
}
canvas {
position: fixed;
width: 100% !important;
height: 100% !important;
}
</style>
<link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<link
rel='shortcut icon'
href='https://raw.githubusercontent.com/processing/p5.js-website-OLD/master/favicon.ico'
type='image/x-icon'
>
`;
html[1] = `
<div class="header">
<div class="message-container">
<h1>404 Page Not Found</h1>
<h6 class="message">The page you are trying to reach does not exist.</h6>
<h6 class="message">
Please check the URL or return to the <a href="/" class="home-link">home page</a>.
</h6>
</div>
</div>
${html[1]}
`;
htmlFile = html.join('</head>');
// Fix links to assets // Fix links to assets
htmlFile = htmlFile.replace( htmlFile = htmlFile.replace(
@ -118,9 +125,17 @@ export function get404Sketch(callback) {
callback(htmlFile); callback(htmlFile);
}); });
} else {
callback(insertErrorMessage(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
</body>
</html>`));
} }
}); });
} }
export default get404Sketch; export default get404Sketch;

View File

@ -1,7 +1,6 @@
export function renderIndex() { export function renderIndex() {
const assetsManifest = process.env.webpackAssets && JSON.parse(process.env.webpackAssets); const assetsManifest = process.env.webpackAssets && JSON.parse(process.env.webpackAssets);
const chunkManifest = process.env.webpackChunkAssets && JSON.parse(process.env.webpackChunkAssets); const chunkManifest = process.env.webpackChunkAssets && JSON.parse(process.env.webpackChunkAssets);
return ` return `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -29,7 +28,8 @@ export function renderIndex() {
window.process.env.AWS_REGION = '${process.env.AWS_REGION}'; window.process.env.AWS_REGION = '${process.env.AWS_REGION}';
window.process.env.FORCE_TO_HTTPS = ${process.env.FORCE_TO_HTTPS === 'false' ? false : undefined}; window.process.env.FORCE_TO_HTTPS = ${process.env.FORCE_TO_HTTPS === 'false' ? false : undefined};
window.process.env.CLIENT = true; window.process.env.CLIENT = true;
window.process.env.LOGIN_ENABLED = ${process.env.LOGIN_ENABLED === 'false' ? false : true} window.process.env.LOGIN_ENABLED = ${process.env.LOGIN_ENABLED === 'false' ? false : true};
window.process.env.EXAMPLES_ENABLED = ${process.env.EXAMPLES_ENABLED === 'false' ? false : true};
</script> </script>
</head> </head>
<body> <body>

View File

@ -116,6 +116,7 @@ module.exports = [{
new ChunkManifestPlugin({ new ChunkManifestPlugin({
filename: 'chunk-manifest.json', filename: 'chunk-manifest.json',
manifestVariable: 'webpackManifest', manifestVariable: 'webpackManifest',
inlineManifest: false
}), }),
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
sourceMap: true, sourceMap: true,