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
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_SECRET_KEY=<your-aws-secret-key>
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_SECRET=<your-github-client-secret>
GOOGLE_ID=<your-google-client-id> (use google+ api)
GOOGLE_SECRET=<your-google-client-secret> (use google+ api)
MAILGUN_DOMAIN=<your-mailgun-domain>
MAILGUN_KEY=<your-mailgun-api-key>
EMAIL_SENDER=<transactional-email-sender>
EMAIL_VERIFY_SECRET_TOKEN=whatever_you_want_this_to_be_it_only_matters_for_production
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
PORT=8000
S3_BUCKET=<your-s3-bucket>
S3_BUCKET_URL_BASE=<alt-for-s3-url>
EXAMPLE_USER_EMAIL=examples@p5js.org
EXAMPLE_USER_PASSWORD=hellop5js
GG_EXAMPLES_USERNAME=generative-design
GG_EXAMPLES_PASS=generativedesign
GG_EXAMPLES_EMAIL=benedikt.gross@generative-gestaltung.de
SESSION_SECRET=whatever_you_want_this_to_be_it_only_matters_for_production

View File

@ -20,7 +20,7 @@
"no-console": 0,
"no-alert": 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"],
"no-unused-vars": [1, {"vars": "local", "args": "none"}],
"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.
* **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 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 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.
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
* 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
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:
<!-- If you change any of the headings in this document, remember to update the table of contents. -->
- [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)
- [Want something more challenging](#want-something-more-challenging)
- [Feature Enhancement](#feature-enhancement)
@ -72,6 +72,4 @@ Structure your commit message like this:
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.
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?
- [ ] Found a bug
- [ ] Existing feature enhancement
- [ ] New feature request
<!-- Select any one issue and delete the other two -->
- 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. -->
#### 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 -->
- 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. -->
<!-- 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`
* [ ] 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!
* [ ] your pull request is descriptively named and links to an issue number, i.e. `Fixes #123`
Thank you!
* [ ] has no linting errors (`npm run lint`)
* [ ] 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)
* [ ] is descriptively named and links to an issue number, i.e. `Fixes #123`

View File

@ -1,7 +1,7 @@
sudo: required
language: node_js
node_js:
- "8.11.1"
- "10.15.0"
cache:
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 \
TERM=xterm
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 * as IDEActions from '../modules/IDE/actions/ide';
import {
metaKeyName,
} from '../utils/metaKey';
import { metaKeyName, } from '../utils/metaKey';
const triangleUrl = require('../images/down-filled-triangle.svg');
const logoUrl = require('../images/p5js-logo-small.svg');
@ -221,6 +219,7 @@ class Nav extends React.PureComponent {
Open
</Link>
</li> }
{ __process.env.EXAMPLES_ENABLED &&
<li className="nav__dropdown-item">
<Link
to="/p5/sketches"
@ -230,7 +229,7 @@ class Nav extends React.PureComponent {
>
Examples
</Link>
</li>
</li> }
</ul>
</li>
<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 * as ActionTypes from '../../../constants';
import { showToast, setToastText } from './toast';
import { setUnsavedChanges,
import {
setUnsavedChanges,
justOpenedProject,
resetJustOpenedProject,
showErrorModal } from './ide';
showErrorModal
} from './ide';
import { clearState, saveState } from '../../../persistState';
const __process = (typeof global !== 'undefined' ? global : window).process;

View File

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

View File

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

View File

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

View File

@ -81,7 +81,10 @@ class PreviewFrame extends React.Component {
handleConsoleEvent(messageEvent) {
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) => {
const { data: args } = message;

View File

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

View File

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

View File

@ -13,6 +13,9 @@
.copyable-input__value {
width: 100%;
font-size: #{16 / $base-font-size}rem;
.copyable-input--with-preview & {
border-radius: 2px 0 0 2px;
}
}
.copyable-input__label {
@ -32,7 +35,6 @@
.copyable-input {
padding-bottom: #{30 / $base-font-size}rem;
display: flex;
flex-wrap: wrap;
}
.copyable-input__value-container {
@ -56,3 +58,16 @@
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 {
EXTERNAL_LINK_REGEX
} from '../../server/utils/fileUtils';
import { EXTERNAL_LINK_REGEX } from '../../server/utils/fileUtils';
export const hijackConsoleErrorsScript = (offs) => {
const s = `

View File

@ -1,7 +1,7 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
/* eslint-disable */
// Distributed under an MIT license: https://codemirror.net/LICENSE
/*eslint-disable*/
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("codemirror"));
@ -12,11 +12,6 @@
})(function(CodeMirror) {
"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) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
@ -26,13 +21,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
var p5Function = {type: "p5-function", style: "p5-function"};
var p5Variable = {type: "p5-variable", style: "p5-variable"};
var p5Function = {type: "variable", style: "p5-function"};
var p5Variable = {type: "variable", style: "p5-variable"};
var jsKeywords = {
"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;
}();
}();
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
function readRegexp(stream) {
@ -186,17 +180,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return ret(ch);
} else if (ch == "=" && stream.eat(">")) {
return ret("=>", "operator");
} else if (ch == "0" && stream.eat(/x/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);
} else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
return ret("number", "number");
} else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
return ret("number", "number");
} else if (ch == "/") {
if (stream.eat("*")) {
@ -207,10 +194,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return ret("comment", "comment");
} else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
return ret("regexp", "string-2");
} else {
stream.eatWhile(isOperatorChar);
stream.eat("=");
return ret("operator", "operator", stream.current());
}
} else if (ch == "`") {
@ -220,13 +207,27 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
stream.skipToEnd();
return ret("error", "error");
} 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());
} else if (wordRE.test(ch)) {
stream.eatWhile(wordRE);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
var word = stream.current()
if (state.lastType != ".") {
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);
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 inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state;
cx.marked = "def";
if (state.context) {
if (inList(state.localVars)) return;
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
if (parserConfig.globalVars)
state.globalVars = {name: varname, next: state.globalVars};
if (state.lexical.info == "var" && state.context && state.context.block) {
// FIXME function decls are also not block scoped
var newContext = registerVarScoped(varname, state.context)
if (newContext != null) {
state.context = newContext
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
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() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
cx.state.localVars = defaultVars
}
function pushblockcontext() {
cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
cx.state.localVars = null
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
cx.state.localVars = cx.state.context.vars
cx.state.context = cx.state.context.prev
}
popcontext.lex = true
function pushlex(type, info) {
var result = function() {
var state = cx.state, indent = state.indented;
@ -415,17 +450,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expect(wanted) {
function exp(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
else return cont(exp);
};
return exp;
}
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 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 == "if") {
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 == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "class" || (isTS && value == "interface")) {
cx.marked = "keyword"
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 == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
if (type == "export") return cont(pushlex("stat"), afterExport, 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 (value == "@") return cont(expression, statement)
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
return expressionInner(type, false);
function maybeCatchBinding(type) {
if (type == "(") return cont(funarg, expect(")"))
}
function expressionNoComma(type) {
return expressionInner(type, true);
function expression(type, value) {
return expressionInner(type, value, false);
}
function expressionNoComma(type, value) {
return expressionInner(type, value, true);
}
function parenExpr(type) {
if (type != "(") return pass()
return cont(pushlex(")"), expression, expect(")"), poplex)
}
function expressionInner(type, noComma) {
function expressionInner(type, value, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
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);
}
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef, maybeop);
if (type == "class") return cont(pushlex("form"), classExpression, poplex);
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
if (type == "quasi") return pass(quasi, maybeop);
if (type == "new") return cont(maybeTarget(noComma));
if (type == "import") return cont(expression);
return cont();
}
function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
function maybeexpressionNoComma(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expressionNoComma);
}
function maybeoperatorComma(type, value) {
if (type == ",") return cont(expression);
@ -497,7 +553,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var expr = noComma == false ? expression : expressionNoComma;
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
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);
return cont(expr);
}
@ -506,6 +564,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, 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) {
if (type != "quasi") return pass();
@ -530,6 +594,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeTarget(noComma) {
return function(type) {
if (type == ".") return cont(noComma ? targetNoComma : target);
else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
else return pass(noComma ? expressionNoComma : expression);
};
}
@ -553,18 +618,25 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
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);
} else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property");
return cont(afterprop);
} else if (type == "jsonld-keyword") {
return cont(afterprop);
} else if (type == "modifier") {
} else if (isTS && isModifier(value)) {
cx.marked = "keyword"
return cont(objprop)
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
return cont(expression, maybetype, expect("]"), afterprop);
} else if (type == "spread") {
return cont(expression);
return cont(expressionNoComma, afterprop);
} else if (value == "*") {
cx.marked = "keyword";
return cont(objprop);
} else if (type == ":") {
return pass(afterprop)
}
@ -578,9 +650,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ":") return cont(expressionNoComma);
if (type == "(") return pass(functiondef);
}
function commasep(what, end) {
function commasep(what, end, sep) {
function proceed(type, value) {
if (type == ",") {
if (sep ? sep.indexOf(type) > -1 : type == ",") {
var lex = cx.state.lexical;
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
return cont(function(type, value) {
@ -589,6 +661,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}, proceed);
}
if (type == end || value == end) return cont();
if (sep && sep.indexOf(";") > -1) return pass(what)
return cont(expect(end));
}
return function(type, value) {
@ -611,38 +684,79 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "?") return cont(maybetype);
}
}
function typeexpr(type) {
if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
if (type == "{") return cont(commasep(typeprop, "}"))
function mayberettype(type) {
if (isTS && type == ":") {
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(typeexpr, ">"), typeexpr)
}
function maybeReturnType(type) {
if (type == "=>") return cont(typeexpr)
}
function typeprop(type) {
function typeprop(type, value) {
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property"
return cont(typeprop)
} else if (value == "?") {
return cont(typeprop)
} else if (type == ":") {
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) {
if (type == "variable") return cont(typearg)
else if (type == ":") return cont(typeexpr)
function typearg(type, value) {
if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
if (type == ":") return cont(typeexpr)
return pass(typeexpr)
}
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 (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);
}
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 == "spread") return cont(pattern);
if (type == "[") return contCommasep(pattern, "]");
if (type == "[") return contCommasep(eltpattern, "]");
if (type == "{") return contCommasep(proppattern, "}");
}
function proppattern(type, value) {
@ -653,8 +767,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern);
if (type == "}") return pass();
if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
return cont(expect(":"), pattern, maybeAssign);
}
function eltpattern() {
return pass(pattern, maybeAssign)
}
function maybeAssign(_type, value) {
if (value == "=") return cont(expressionNoComma);
}
@ -664,7 +782,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeelse(type, value) {
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);
}
function forspec1(type) {
@ -688,10 +807,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function functiondef(type, value) {
if (value == "*") {cx.marked = "keyword"; 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 (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
return pass(pattern, maybetype, maybeAssign);
}
function classExpression(type, value) {
@ -703,40 +831,56 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
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);
}
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 ((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";
return cont(isTS ? classfield : functiondef, classBody);
}
if (type == "[")
return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
if (value == "*") {
cx.marked = "keyword";
return cont(classBody);
}
if (type == ";") return cont(classBody);
if (type == "}") return cont();
if (value == "@") return cont(expression, classBody)
}
function classfield(type, value) {
if (value == "?") return cont(classfield)
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 == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
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) {
if (type == "string") return cont();
return pass(importSpec, maybeFrom);
if (type == "(") return pass(expression);
return pass(importSpec, maybeMoreImports, maybeFrom);
}
function importSpec(type, value) {
if (type == "{") return contCommasep(importSpec, "}");
@ -744,6 +888,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "*") cx.marked = "keyword";
return cont(maybeAs);
}
function maybeMoreImports(type) {
if (type == ",") return cont(importSpec, maybeMoreImports)
}
function maybeAs(_type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
}
@ -754,6 +901,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "]") return cont();
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) {
return state.lastType == "operator" || state.lastType == "," ||
@ -761,6 +914,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
/[,.]/.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
return {
@ -771,7 +930,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
context: parserConfig.localVars && new Context(null, null, false),
indented: basecolumn || 0
};
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
@ -812,7 +971,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
lexical = lexical.prev;
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") return lexical.indented + indentUnit;
else if (type == "stat")
@ -826,6 +985,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
blockCommentStart: jsonMode ? null : "/*",
blockCommentEnd: jsonMode ? null : "*/",
blockCommentContinue: jsonMode ? null : " * ",
lineComment: jsonMode ? null : "//",
fold: "brace",
closeBrackets: "()[]{}''\"\"``",
@ -835,6 +995,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
jsonMode: jsonMode,
expressionAllowed: expressionAllowed,
skipExpression: function(state) {
var top = state.cc[state.cc.length - 1]
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.
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.
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
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
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`
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`
## 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",
"version": "0.0.1",
"description": "The web editor for p5.js.",
"scripts": {
"clean": "rimraf dist",
"start": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js",
"start:prod": "cross-env NODE_ENV=production node index.js",
"lint": "eslint client server --ext .jsx --ext .js",
"lint-fix": "eslint client server --ext .jsx --ext .js --fix",
"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: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",
"test": "npm run lint",
"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: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"
},
"main": "index.js",
"author": "Cassie Tarakajian",
"license": "LGPL-2.1",
"repository": {
"type": "git",
"url": "git+https://github.com/catarak/p5.js-web-editor.git"
},
"devDependencies": {
"babel-eslint": "^7.1.1",
"babel-loader": "^7.1.4",
"babel-plugin-transform-react-constant-elements": "^6.8.0",
"babel-plugin-transform-react-inline-elements": "^6.8.0",
"babel-plugin-transform-react-remove-prop-types": "^0.2.6",
"babel-plugin-webpack-loaders": "^0.9.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.5.0",
"babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.5.0",
"chunk-manifest-webpack-plugin": "^1.1.2",
"css-loader": "^0.23.1",
"cssnano": "^3.7.1",
"eslint": "^4.9.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^2.0.0",
"node-sass": "^4.9.0",
"nodemon": "^1.9.2",
"postcss-cssnext": "^2.7.0",
"postcss-focus": "^1.0.0",
"postcss-loader": "^0.9.1",
"postcss-reporter": "^1.3.3",
"rimraf": "^2.6.2",
"sass-loader": "^6.0.6",
"style-loader": "^0.13.1",
"webpack-manifest-plugin": "^2.0.0",
"webpack-node-externals": "^1.7.2"
},
"engines": {
"node": ">=8.9.0"
},
"dependencies": {
"archiver": "^1.1.0",
"async": "^2.0.0",
"axios": "^0.12.0",
"babel-core": "^6.26.0",
"babel-polyfill": "^6.8.0",
"babel-register": "^6.8.0",
"bcrypt-nodejs": "0.0.3",
"blob-util": "^1.2.1",
"body-parser": "^1.15.1",
"bson-objectid": "^1.1.4",
"classnames": "^2.2.5",
"clipboard": "^1.7.1",
"codemirror": "^5.38.0",
"connect-mongo": "^1.2.0",
"console-feed": "^2.8.1",
"cookie-parser": "^1.4.1",
"cors": "^2.8.1",
"cross-env": "^5.1.3",
"csslint": "^0.10.0",
"decomment": "^0.8.7",
"dotenv": "^2.0.0",
"dropzone": "^4.3.0",
"escape-string-regexp": "^1.0.5",
"eslint-loader": "^1.3.0",
"express": "^4.13.4",
"express-basic-auth": "^1.1.5",
"express-session": "^1.13.0",
"htmlhint": "^0.9.13",
"is-url": "^1.2.2",
"js-beautify": "^1.6.4",
"jsdom": "^9.8.3",
"jshint": "^2.9.4",
"lodash": "^4.16.4",
"loop-protect": "github:catarak/loop-protect",
"mjml": "^3.3.2",
"moment": "^2.14.1",
"mongoose": "^4.6.8",
"node-uuid": "^1.4.7",
"nodemailer": "^2.6.4",
"nodemailer-mailgun-transport": "^1.2.2",
"passport": "^0.3.2",
"passport-github": "^1.1.0",
"passport-google-oauth20": "^1.0.0",
"passport-local": "^1.0.0",
"pretty-bytes": "^3.0.1",
"primer-tooltips": "^1.4.1",
"project-name-generator": "^2.1.3",
"prop-types": "^15.6.0",
"q": "^1.4.1",
"react": "^16.4.0",
"react-dom": "^16.4.0",
"react-helmet": "^5.1.3",
"react-hot-loader": "^4.1.2",
"react-inlinesvg": "^0.7.5",
"react-redux": "^5.0.6",
"react-router": "^3.2.0",
"react-split-pane": "^0.1.44",
"react-tabs": "^2.2.1",
"redux": "^3.5.2",
"redux-devtools": "^3.4.1",
"redux-devtools-dock-monitor": "^1.1.3",
"redux-devtools-log-monitor": "^1.4.0",
"redux-form": "^5.3.3",
"redux-thunk": "^2.1.0",
"request": "^2.76.0",
"request-promise": "^4.1.1",
"s3": "^4.4.0",
"s3-policy": "^0.2.0",
"sass-extract": "^2.1.0",
"sass-extract-js": "^0.4.0",
"sass-extract-loader": "^1.1.0",
"shortid": "^2.2.6",
"slugify": "^1.3.4",
"srcdoc-polyfill": "^0.2.0",
"url": "^0.11.0",
"webpack": "^3.1.0",
"webpack-dev-middleware": "^2.0.6",
"webpack-hot-middleware": "^2.10.0",
"xhr": "^2.2.1"
}
"name": "p5.js-web-editor",
"version": "0.0.1",
"description": "The web editor for p5.js.",
"scripts": {
"clean": "rimraf dist",
"start": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js",
"start:prod": "cross-env NODE_ENV=production node index.js",
"lint": "eslint client server --ext .jsx --ext .js",
"lint-fix": "eslint client server --ext .jsx --ext .js --fix",
"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: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",
"test": "npm run lint",
"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: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",
"heroku-postbuild": "touch .env; npm run build"
},
"main": "index.js",
"author": "Cassie Tarakajian",
"license": "LGPL-2.1",
"repository": {
"type": "git",
"url": "git+https://github.com/catarak/p5.js-web-editor.git"
},
"devDependencies": {
"babel-eslint": "^7.1.1",
"babel-loader": "^7.1.5",
"babel-plugin-transform-react-constant-elements": "^6.23.0",
"babel-plugin-transform-react-inline-elements": "^6.22.0",
"babel-plugin-transform-react-remove-prop-types": "^0.2.12",
"babel-plugin-webpack-loaders": "^0.9.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.24.1",
"chunk-manifest-webpack-plugin": "github:catarak/chunk-manifest-webpack-plugin",
"css-loader": "^0.23.1",
"cssnano": "^3.10.0",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "^7.12.3",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^2.0.0",
"node-sass": "^4.11.0",
"nodemon": "^1.18.9",
"postcss-cssnext": "^2.11.0",
"postcss-focus": "^1.0.0",
"postcss-loader": "^0.9.1",
"postcss-reporter": "^1.4.1",
"rimraf": "^2.6.3",
"sass-loader": "^6.0.7",
"style-loader": "^0.13.2",
"webpack-manifest-plugin": "^2.0.4",
"webpack-node-externals": "^1.7.2"
},
"engines": {
"node": "10.15.0",
"npm": "6.4.1"
},
"dependencies": {
"archiver": "^1.1.0",
"async": "^2.6.1",
"axios": "^0.12.0",
"babel-core": "^6.26.3",
"babel-polyfill": "^6.26.0",
"babel-register": "^6.26.0",
"bcrypt-nodejs": "0.0.3",
"blob-util": "^1.2.1",
"body-parser": "^1.18.3",
"bson-objectid": "^1.2.4",
"classnames": "^2.2.6",
"clipboard": "^1.7.1",
"codemirror": "^5.42.2",
"connect-mongo": "^1.3.2",
"console-feed": "^2.8.5",
"cookie-parser": "^1.4.3",
"cors": "^2.8.5",
"cross-env": "^5.2.0",
"csslint": "^0.10.0",
"decomment": "^0.8.7",
"dotenv": "^2.0.0",
"dropzone": "^4.3.0",
"escape-string-regexp": "^1.0.5",
"eslint-loader": "^1.3.0",
"express": "^4.16.4",
"express-basic-auth": "^1.1.6",
"express-session": "^1.15.6",
"htmlhint": "^0.10.1",
"is-url": "^1.2.4",
"js-beautify": "^1.8.9",
"jsdom": "^9.8.3",
"jshint": "^2.9.7",
"lodash": "^4.17.11",
"loop-protect": "github:catarak/loop-protect",
"mjml": "^3.3.2",
"moment": "^2.23.0",
"mongoose": "^4.6.8",
"node-uuid": "^1.4.7",
"nodemailer": "^2.6.4",
"nodemailer-mailgun-transport": "^1.4.0",
"passport": "^0.3.2",
"passport-github": "^1.1.0",
"passport-google-oauth20": "^1.0.0",
"passport-local": "^1.0.0",
"pretty-bytes": "^3.0.1",
"primer-tooltips": "^1.5.11",
"project-name-generator": "^2.1.5",
"prop-types": "^15.6.2",
"q": "^1.4.1",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-helmet": "^5.1.3",
"react-hot-loader": "^4.6.3",
"react-inlinesvg": "^0.7.5",
"react-redux": "^5.1.1",
"react-router": "^3.2.1",
"react-split-pane": "^0.1.76",
"react-tabs": "^2.3.0",
"redux": "^3.7.2",
"redux-devtools": "^3.4.2",
"redux-devtools-dock-monitor": "^1.1.3",
"redux-devtools-log-monitor": "^1.4.0",
"redux-form": "^5.3.3",
"redux-thunk": "^2.3.0",
"request": "^2.88.0",
"request-promise": "^4.1.1",
"s3": "^4.4.0",
"s3-policy": "^0.2.0",
"sass-extract": "^2.1.0",
"sass-extract-js": "^0.4.0",
"sass-extract-loader": "^1.1.0",
"shortid": "^2.2.14",
"slugify": "^1.3.4",
"srcdoc-polyfill": "^0.2.0",
"url": "^0.11.0",
"webpack": "^3.12.0",
"webpack-dev-middleware": "^2.0.6",
"webpack-hot-middleware": "^2.24.3",
"xhr": "^2.5.0"
}
}

View File

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

View File

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

View File

@ -49,7 +49,7 @@ mongoose.connection.on('error', () => {
function getCategories() {
const categories = [];
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}`,
method: 'GET',
headers,
@ -134,7 +134,7 @@ function getSketchContent(projectsInAllCategories) {
function createProjectsInP5user(projectsInAllCategories) {
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}`,
method: 'GET',
headers,
@ -264,7 +264,7 @@ function createProjectsInP5user(projectsInAllCategories) {
const fileID = objectID().toHexString();
newProject.files.push({
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,
children: [],

View File

@ -1,11 +1,77 @@
import User from '../models/user';
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) {
User.findOne({ username: 'p5' }, (userErr, user) => { // Find p5 user
if (userErr) {
throw userErr;
} else {
} else if (user) {
Project.find({ user: user._id }, (projErr, projects) => { // Find example projects
// Choose a random sketch
const randomIndex = Math.floor(Math.random() * projects.length);
@ -39,66 +105,7 @@ export function get404Sketch(callback) {
});
// Add 404 html and position canvas
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'
>
`;
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>');
htmlFile = insertErrorMessage(htmlFile);
// Fix links to assets
htmlFile = htmlFile.replace(
@ -118,9 +125,17 @@ export function get404Sketch(callback) {
callback(htmlFile);
});
} else {
callback(insertErrorMessage(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
</body>
</html>`));
}
});
}
export default get404Sketch;

View File

@ -1,7 +1,6 @@
export function renderIndex() {
const assetsManifest = process.env.webpackAssets && JSON.parse(process.env.webpackAssets);
const chunkManifest = process.env.webpackChunkAssets && JSON.parse(process.env.webpackChunkAssets);
return `
<!DOCTYPE html>
<html>
@ -29,7 +28,8 @@ export function renderIndex() {
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.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>
</head>
<body>

View File

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