Compare commits

..

No commits in common. "dev/prod" and "fix/s3v4" have entirely different histories.

79 changed files with 213 additions and 127336 deletions

View File

@ -19,13 +19,12 @@ CMD ["npm", "start"]
FROM development as build
ENV NODE_ENV production
RUN npm run build:server
RUN npm run build:client
RUN npm run build
FROM build as production
FROM base as production
ENV NODE_ENV=production
COPY package.json package-lock.json index.js ./
RUN npm install --production
RUN npm rebuild node-sass
# COPY --from=build $APP_HOME/dist ./dist
COPY --from=build $APP_HOME/dist ./dist
CMD ["npm", "run", "start:prod"]

View File

@ -1,31 +0,0 @@
# Usage
There is a hidden link to have an overview of all users registered on the server: `/users`.
# Development
`docker-compose -f docker-compose-development.yml up`
# Deployment
* Push to git (in case linting fails on commit use `git commit --no-verify`)
* Pull on server.
* `service p5.js-web-editor stop`
* `npm run build:server`
* `npm run build:client`
* `service p5.js-web-editor start`
Gebruik s3cmd om bucket in te stellen.
## Make bucket:
s3cmd mb s3://digitalplayground-p5
## Set the CORS rules
s3cmd -c .s3cfg setcors CORS.xml s3://digitalplayground-p5
## Delete the CORS rules
s3cmd -c .s3cfg delcors s3://digitalplayground-p5
## Get bucket info including CORS rules
s3cmd -c .s3cfg info s3://digitalplayground-p5

View File

@ -38,10 +38,6 @@
"description": "A secret key for...? Not sure where used.",
"generator": "secret"
},
"EXAMPLE_USERNAME": {
"description": "Username of the default account.",
"value": "p5"
},
"EXAMPLE_USER_EMAIL": {
"description": "The email address for the account holding the default Example sketches",
"value": "examples@p5js.org"

View File

@ -17,8 +17,7 @@ import { metaKeyName, } from '../utils/metaKey';
import CaretLeftIcon from '../images/left-arrow.svg';
import TriangleIcon from '../images/down-filled-triangle.svg';
// import LogoIcon from '../images/p5js-logo-small.svg';
import LogoIcon from '../images/dp-logo-line-drawing.svg';
import LogoIcon from '../images/p5js-logo-small.svg';
class Nav extends React.PureComponent {
constructor(props) {
@ -352,7 +351,7 @@ class Nav extends React.PureComponent {
{ getConfig('EXAMPLES_ENABLED') &&
<li className="nav__dropdown-item">
<Link
to={`/${getConfig('EXAMPLE_USERNAME')}/sketches`}
to="/p5/sketches"
onFocus={this.handleFocusForFile}
onBlur={this.handleBlur}
onClick={this.setDropdownForNone}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -127,7 +127,6 @@ function getSynchedProject(currentState, responseProject) {
}
export function saveProject(selectedFile = null, autosave = false, mobile = false) {
console.trace('saving inproject.js');
return (dispatch, getState) => {
const state = getState();
if (state.project.isSaving) {
@ -262,7 +261,7 @@ function generateNewIdsForChildren(file, files) {
export function cloneProject(id) {
return (dispatch, getState) => {
dispatch(setUnsavedChanges(false));
return new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
if (!id) {
resolve(getState());
} else {

View File

@ -326,12 +326,6 @@ class Editor extends React.Component {
'editor-holder--hidden': this.props.file.fileType === 'folder' || this.props.file.url
});
let preview = '';
if (this.props.file.fileType === 'file' && this.props.file.url) {
// TODO check if it's an image
preview = (<div><img src={this.props.file.url} alt="preview" /></div>);
}
return (
<section className={editorSectionClass} >
<header className="editor__header">
@ -366,7 +360,6 @@ class Editor extends React.Component {
</header>
<article ref={(element) => { this.codemirrorContainer = element; }} className={editorHolderClass} >
</article>
{preview}
<EditorAccessibility
lintMessages={this.props.lintMessages}
/>

View File

@ -23,8 +23,6 @@ import { hijackConsoleErrorsScript, startTag, getAllScriptOffsets }
from '../../../utils/consoleUtils';
// let lastUpdate = null;
const shouldRenderSketch = (props, prevProps = undefined) => {
const { isPlaying, previewIsRefreshing, fullView } = props;
@ -59,9 +57,6 @@ class PreviewFrame extends React.Component {
}
componentDidUpdate(prevProps) {
if (prevProps.isPlaying === true && this.props.isPlaying === false) {
console.trace('update', this.props, prevProps);
}
if (shouldRenderSketch(this.props, prevProps)) this.renderSketch();
// small bug - if autorefresh is on, and the usr changes files
// in the sketch, preview will reload
@ -332,91 +327,16 @@ class PreviewFrame extends React.Component {
renderSketch() {
const doc = this.iframeElement;
// const localFiles = this.injectLocalFiles();
// console.trace('renderSketch()', this.props);
const changedFiles = this.props.files.filter(file => file.changed);
const filesRequiringReload = changedFiles.filter(file => !file.content.startsWith('// liveUpdate'));
const filesToHotSwap = changedFiles.filter(file => filesRequiringReload.indexOf(file) === -1);
// console.log('changed and requiring reload:', changedFiles, filesRequiringReload, filesToHotSwap);
let saving;
console.log('is saving...', this.props);
if (changedFiles.length > 0 || this.props.project.updatedAt === '') {
saving = this.props.saveProject();
const localFiles = this.injectLocalFiles();
if (this.props.isPlaying) {
this.props.clearConsole();
srcDoc.set(doc, localFiles);
if (this.props.endSketchRefresh) {
this.props.endSketchRefresh();
}
} else {
// can be done pretier: a promise that always resolves
saving = new Promise((resolve, err) => resolve());
}
console.log('saving:', saving);
if (saving === null) {
console.log('Error saving... not authenticated?');
this.props.stopSketch(); // prevent crazy loop of renderSketch() through componentDidUpdate()
} else {
saving.catch(() => {
console.log('Error when saving... not authenticated? Redirect!');
this.props.stopSketch(); // prevent crazy loop of renderSketch() through componentDidUpdate()
// this.props.clearConsole(); // introduces bug: causes nested compnonentDidUpdate()
});
saving.then(() => {
console.log('play!', this.props.isPlaying);
if (this.props.isPlaying) {
doc.removeAttribute('srcdoc');
if (typeof this.props.project.owner === 'undefined') {
console.error('no owner for project?', this.props.project);
} else {
const source = `${window.location.origin}/${this.props.project.owner.username}/sketches/${this.props.project.id}/index.html`;
// console.log('FILES', this.props.files, doc.src, source, lastUpdate);
if (doc.src === source) {
// const newFiles = this.props.files.filter(file => new Date(file.updatedAt) > lastUpdate);
if (this.props.unsavedChanges) {
// console.log('unsaved changes');
}
// console.log('doc', doc);
// we need a hard reload
if (filesRequiringReload.length > 0) {
doc.src = source; // for now...
} else {
// if (doc.contentWindow.document.querySelector('script[src="/assets/hotswap.js"]') == null) {
// const headEl = doc.contentWindow.document.querySelector('head');
// const srcEl = doc.contentWindow.document.createElement('script');
// srcEl.src = '/assets/hotswap.js';
// headEl.appendChild(srcEl);
// }
console.log('Hot swap (..append):', filesToHotSwap);
const headEl = doc.contentWindow.document.querySelector('head');
const updatevar = Date.now();
filesToHotSwap.forEach((file) => {
// doc.contentWindow.postMessage({ 'action': 'code', 'contents': file.content }, '*');
const srcEl = doc.contentWindow.document.createElement('script');
srcEl.src = `${file.name}?changed=${updatevar}`;
srcEl.onload = 'setupAssets();'; // (re)load assets
headEl.appendChild(srcEl);
});
}
// if ( this.props.htmlFile.content === doc.contentWindow)
// TODO: don't set, but update only (will be hard... :-P)
} else {
doc.src = source;
}
}
} else {
doc.removeAttribute('src');
doc.srcdoc = '';
srcDoc.set(doc, ' ');
}
// prevent looping
if (this.props.endSketchRefresh) {
this.props.endSketchRefresh();
}
});
doc.srcdoc = '';
srcDoc.set(doc, ' ');
}
}
@ -464,18 +384,7 @@ PreviewFrame.propTypes = {
setBlobUrl: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired,
expandConsole: PropTypes.func.isRequired,
saveProject: PropTypes.func.isRequired,
project: PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string.isRequired,
owner: PropTypes.shape({
username: PropTypes.string,
id: PropTypes.string,
}),
updatedAt: PropTypes.string,
}).isRequired,
// clearConsole: PropTypes.func,
unsavedChanges: PropTypes.bool,
clearConsole: PropTypes.func.isRequired,
cmController: PropTypes.shape({
getContent: PropTypes.func
}),
@ -483,8 +392,7 @@ PreviewFrame.propTypes = {
PreviewFrame.defaultProps = {
fullView: false,
unsavedChanges: false,
cmController: {},
cmController: {}
};
export default PreviewFrame;

View File

@ -58,14 +58,9 @@ class Toolbar extends React.Component {
}
render() {
const canEditProjectName = this.canEditProjectName();
const playButtonClass = classNames({
'toolbar__play-button': true,
'toolbar__play-button--selected': this.props.isPlaying,
'toolbar__play-button--saved': this.props.isSaved,
'toolbar__play-button--unsaved': !this.props.isSaved,
'toolbar__play-button--clone': this.props.unsavedChanges && !canEditProjectName
'toolbar__play-button--selected': this.props.isPlaying
});
const stopButtonClass = classNames({
'toolbar__stop-button': true,
@ -80,6 +75,8 @@ class Toolbar extends React.Component {
'toolbar__project-name-container--editing': this.props.project.isEditingName
});
const canEditProjectName = this.canEditProjectName();
return (
<div className="toolbar">
<button
@ -179,8 +176,6 @@ class Toolbar extends React.Component {
Toolbar.propTypes = {
isPlaying: PropTypes.bool.isRequired,
unsavedChanges: PropTypes.bool.isRequired,
isSaved: PropTypes.bool.isRequired,
preferencesIsVisible: PropTypes.bool.isRequired,
stopSketch: PropTypes.func.isRequired,
setProjectName: PropTypes.func.isRequired,
@ -220,10 +215,8 @@ function mapStateToProps(state) {
infiniteLoop: state.ide.infiniteLoop,
isPlaying: state.ide.isPlaying,
owner: state.project.owner,
isSaved: state.project.updatedAt !== '',
preferencesIsVisible: state.ide.preferencesIsVisible,
project: state.project,
unsavedChanges: state.ide.unsavedChanges,
};
}

View File

@ -173,23 +173,6 @@ class IDEView extends React.Component {
clearTimeout(this.autosaveInterval);
this.autosaveInterval = null;
}
saveProject() {
console.trace('saving!');
// return a Promise to save or null
if (
isUserOwner(this.props) ||
(this.props.user.authenticated && !this.props.project.owner)
) {
console.trace('project to save:', this.props.project);
return this.props.saveProject(this.cmController.getContent());
} else if (this.props.user.authenticated) {
console.log('cloning!');
return this.props.cloneProject();
}
this.props.showErrorModal('forceAuthentication');
return null;
}
handleGlobalKeydown(e) {
// 83 === s
if (
@ -198,7 +181,16 @@ class IDEView extends React.Component {
) {
e.preventDefault();
e.stopPropagation();
this.saveProject();
if (
isUserOwner(this.props) ||
(this.props.user.authenticated && !this.props.project.owner)
) {
this.props.saveProject(this.cmController.getContent());
} else if (this.props.user.authenticated) {
this.props.cloneProject();
} else {
this.props.showErrorModal('forceAuthentication');
}
// 13 === enter
} else if (
e.keyCode === 13 &&
@ -395,11 +387,8 @@ class IDEView extends React.Component {
setBlobUrl={this.props.setBlobUrl}
expandConsole={this.props.expandConsole}
clearConsole={this.props.clearConsole}
saveProject={this.saveProject.bind(this)}
project={this.props.project}
cmController={this.cmController}
language={this.props.preferences.language}
unsavedChanges={this.props.ide.unsavedChanges}
/>
</div>
</section>

View File

@ -1,22 +1,20 @@
import objectID from 'bson-objectid';
import * as ActionTypes from '../../../constants';
const defaultSketch = `// liveUpdate
const defaultSketch = `function setup() {
createCanvas(400, 400);
}
function drawMask(detection) {
stroke('red')
strokeWeight(2)
rect(detection.x, detection.y, detection.width, detection.height)
function draw() {
background(220);
}`;
const defaultHTML =
`<!DOCTYPE html>
<html>
<html lang="en">
<head>
<script src="/assets/p5.js"></script>
<script src="/assets/ml5.js"></script>
<script src="/previewScripts.js"></script>
<script src="/assets/webcam.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
@ -34,8 +32,6 @@ const defaultCSS =
}
canvas {
display: block;
width: 100% !important;
height: auto !important;
}
`;
@ -151,21 +147,8 @@ const files = (state, action) => {
return file;
}
return Object.assign({}, file, { content: action.content, changed: true });
return Object.assign({}, file, { content: action.content });
});
case ActionTypes.SET_UNSAVED_CHANGES:
if (action.value) {
// ignore.
return state;
}
return state.map((file) => {
if (file.changed) {
return Object.assign({}, file, { changed: false });
}
return file;
});
// return Object.assign({}, state, { unsavedChanges: action.value });
case ActionTypes.SET_BLOB_URL:
return state.map((file) => {
if (file.id !== action.id) {
@ -190,8 +173,7 @@ const files = (state, action) => {
content: action.content,
url: action.url,
children: action.children,
fileType: action.fileType || 'file',
changed: false,
fileType: action.fileType || 'file'
}];
return newState.map((file) => {
if (file.id === action.parentId) {

View File

@ -22,7 +22,7 @@ import FooterTabSwitcher from '../../components/mobile/TabSwitcher';
import FooterTab from '../../components/mobile/Tab';
import Loader from '../App/components/loader';
const EXAMPLE_USERNAME = process.env.EXAMPLE_USERNAME || 'p5';
const EXAMPLE_USERNAME = 'p5';
// @ghalestrilo 08/13/2020: I'm sorry
const ContentWrapper = styled(Content)`

View File

@ -60,7 +60,7 @@ class AccountView extends React.Component {
</div>
</TabList>
<TabPanel>
{/* <SocialLoginPanel {...this.props} /> */}
<SocialLoginPanel {...this.props} />
</TabPanel>
<TabPanel>
<APIKeyForm {...this.props} />

View File

@ -42,11 +42,11 @@ class LoginView extends React.Component {
<div className="form-container__content">
<h2 className="form-container__title">{this.props.t('LoginView.Login')}</h2>
<LoginForm {...this.props} />
{/* <h2 className="form-container__divider">{this.props.t('LoginView.LoginOr')}</h2>
<h2 className="form-container__divider">{this.props.t('LoginView.LoginOr')}</h2>
<div className="form-container__stack">
<SocialAuthButton service={SocialAuthButton.services.github} />
<SocialAuthButton service={SocialAuthButton.services.google} />
</div> */}
</div>
<p className="form__navigation-options">
{this.props.t('LoginView.DontHaveAccount')}
<Link className="form__signup-button" to="/signup">{this.props.t('LoginView.SignUp')}</Link>

View File

@ -34,11 +34,11 @@ class SignupView extends React.Component {
<div className="form-container__content">
<h2 className="form-container__title">{this.props.t('SignupView.Description')}</h2>
<SignupForm {...this.props} />
{/* <h2 className="form-container__divider">{this.props.t('SignupView.Or')}</h2>
<h2 className="form-container__divider">{this.props.t('SignupView.Or')}</h2>
<div className="form-container__stack">
<SocialAuthButton service={SocialAuthButton.services.github} />
<SocialAuthButton service={SocialAuthButton.services.google} />
</div> */}
</div>
<p className="form__navigation-options">
{this.props.t('SignupView.AlreadyHave')}
<Link className="form__login-button" to="/login">{this.props.t('SignupView.Login')}</Link>

View File

@ -1,7 +1,7 @@
$base-font-size: 12;
//colors
$p5js-pink: #FFE117; /*DP Colours*/
$p5js-pink: #ed225d;
$processing-blue: #007BBB;
$p5js-active-pink: #f10046;
$white: #fff;

View File

@ -166,23 +166,12 @@
.nav__item-logo {
position: relative;
height: #{(42 - 1) / $base-font-size}rem;
height: #{42 / $base-font-size}rem;
width: #{56 / $base-font-size}rem;
background:$p5js-pink;
& span {
position: absolute;
}
svg{
width: 100%;
height: 90%;
padding-top: 5%;
path{
stroke: $dark;
}
}
}
.svg__logo g > path {
@include themify() {

View File

@ -1,16 +1,4 @@
.toolbar__play-button {
&--unsaved, &--clone {
// for some reason, I cannot manage to have PreviewFrame save _and_ actuall yload the project (it stops the playback)
// pressing twice works. So just show a floppy the first time around :-)
svg {
display: none;
}
&::after{
content: '\1F4BE'; // save floppy
}
// background-color: blue !important;
}
@include themify() {
@extend %toolbar-button;
display: flex;

View File

@ -7,7 +7,7 @@ export const Theme = {
};
export const colors = {
p5jsPink: '#FFE117', // DP Colours
p5jsPink: '#ed225d',
processingBlue: '#007BBB',
p5jsActivePink: '#f10046',
white: '#fff',

View File

@ -7,29 +7,30 @@ Follow these instructions to set up your development environment, which you need
_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`.
1. Install Node.js. The recommended way is to Node through [nvm](https://github.com/nvm-sh/nvm). You can also install [node.js](https://nodejs.org/download/release/v12.16.1/) version 12.16.1 directly from the Node.js website.
3. [Clone](https://help.github.com/articles/cloning-a-repository/) this repository using git onto your local computer.
2. [Fork](https://help.github.com/articles/fork-a-repo) the [p5.js Web Editor repository](https://github.com/processing/p5.js-web-editor) into your own GitHub account.
3. [Clone](https://help.github.com/articles/cloning-a-repository/) your new fork of the repository from GitHub onto your local computer.
```
$ git clone https://git.rubenvandeven.com/r/p5.js-web-editor/
$ git clone https://github.com/YOUR_USERNAME/p5.js-web-editor.git
```
4. If you are using nvm, run `$ nvm use` to set your Node version to 12.16.1
5. Navigate into the project folder and install all its necessary dependencies with npm.
```
$ cd p5.js-web-editor
$ npm install
```
6. Install MongoDB and make sure it is running
* For Mac OSX with [homebrew](http://brew.sh/): `brew tap mongodb/brew` then `brew install mongodb-community` and finally start the server with `brew services start mongodb-community` or you can visit the installation guide here [Installation Guide For MacOS](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/)
* For Windows and Linux: [MongoDB Installation](https://docs.mongodb.com/manual/installation/)
7. `$ cp .env.example .env`
8. (For DigitalPlayground) use provided .env file
9. Run `npm run build:server && npm run build:client` to make sure all script are built and bundled.
11. use the provided service file to start the server through systemd
- edit `p5.js-web-editor.service` so it has the right files & folders according to you installation
- `cp /home/dp/p5.js-web-editor/p5.js-web-editor.service /etc/systemd/system/`
- `systemctl deamon-reload`
- `systemctl start p5.js-web-editor.service`
12. (Optionally) Set the `PORT` and `NODE_PORT` variables in the p5.js-web-editor.service and .env files to the desired port OR use an apache server (or something else) as https-proxy server. For an example see `digitalplayground-apache.conf`.
8. (Optional) Update `.env` with necessary keys to enable certain app behaviors, i.e. add Github ID and Github Secret if you want to be able to log in with Github.
9. `$ npm run fetch-examples` - this downloads the example sketches into a user called 'p5'
10. `$ npm start`
11. Navigate to [http://localhost:8000](http://localhost:8000) in your browser
12. Install the [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)
13. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w`
## Docker Installation

View File

@ -1,26 +0,0 @@
# Forward to IPv6-only host
<VirtualHost *:80>
Servername dp.rubenvandeven.com
RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
Servername dp.rubenvandeven.com
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://digitalplayground.rubenvandeven.com:8000/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://digitalplayground.rubenvandeven.com:8000/$1 [P,L]
ProxyPass / http://digitalplayground.rubenvandeven.com:8000/
ProxyPassReverse / http://digitalplayground.rubenvandeven.com:8000/
ProxyPreserveHost On
SSLCertificateFile /etc/letsencrypt/live/dp.rubenvandeven.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/dp.rubenvandeven.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

View File

@ -1 +0,0 @@
Model weights from https://github.com/justadudewhohacks/face-api.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
[{"weights":[{"name":"dense0/conv0/filters","shape":[3,3,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008194216092427571,"min":-0.9423348506291708}},{"name":"dense0/conv0/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006839508168837603,"min":-0.8412595047670252}},{"name":"dense0/conv1/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009194007106855804,"min":-1.2779669878529567}},{"name":"dense0/conv1/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0036026100317637128,"min":-0.3170296827952067}},{"name":"dense0/conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.000740380117706224,"min":-0.06367269012273527}},{"name":"dense0/conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":1,"min":0}},{"name":"dense0/conv2/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":1,"min":0}},{"name":"dense0/conv2/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0037702228508743585,"min":-0.6220867703942692}},{"name":"dense1/conv0/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0033707996209462483,"min":-0.421349952618281}},{"name":"dense1/conv0/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014611541991140328,"min":-1.8556658328748217}},{"name":"dense1/conv0/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002832523046755323,"min":-0.30307996600281956}},{"name":"dense1/conv1/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006593170586754294,"min":-0.6329443763284123}},{"name":"dense1/conv1/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.012215249211180444,"min":-1.6001976466646382}},{"name":"dense1/conv1/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002384825547536214,"min":-0.3028728445370992}},{"name":"dense1/conv2/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005859645441466687,"min":-0.7617539073906693}},{"name":"dense1/conv2/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013121426806730382,"min":-1.7845140457153321}},{"name":"dense1/conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0032247188044529336,"min":-0.46435950784122243}},{"name":"dense2/conv0/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002659512618008782,"min":-0.32977956463308894}},{"name":"dense2/conv0/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015499923743453681,"min":-1.9839902391620712}},{"name":"dense2/conv0/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0032450980999890497,"min":-0.522460794098237}},{"name":"dense2/conv1/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005911862382701799,"min":-0.792189559282041}},{"name":"dense2/conv1/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021025861478319356,"min":-2.2077154552235325}},{"name":"dense2/conv1/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00349616945958605,"min":-0.46149436866535865}},{"name":"dense2/conv2/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008104994250278847,"min":-1.013124281284856}},{"name":"dense2/conv2/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.029337059282789044,"min":-3.5791212325002633}},{"name":"dense2/conv2/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0038808938334969913,"min":-0.4230174278511721}},{"name":"fc/weights","shape":[128,136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014016061670639936,"min":-1.8921683255363912}},{"name":"fc/bias","shape":[136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0029505149698724935,"min":0.088760145008564}}],"paths":["face_landmark_68_tiny_model-shard1"]}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1 +0,0 @@
[{"paths":["mtcnn_model-shard1"],"weights":[{"dtype":"float32","name":"pnet/conv1/weights","shape":[3,3,3,10]},{"dtype":"float32","name":"pnet/conv1/bias","shape":[10]},{"dtype":"float32","name":"pnet/prelu1_alpha","shape":[10]},{"dtype":"float32","name":"pnet/conv2/weights","shape":[3,3,10,16]},{"dtype":"float32","name":"pnet/conv2/bias","shape":[16]},{"dtype":"float32","name":"pnet/prelu2_alpha","shape":[16]},{"dtype":"float32","name":"pnet/conv3/weights","shape":[3,3,16,32]},{"dtype":"float32","name":"pnet/conv3/bias","shape":[32]},{"dtype":"float32","name":"pnet/prelu3_alpha","shape":[32]},{"dtype":"float32","name":"pnet/conv4_1/weights","shape":[1,1,32,2]},{"dtype":"float32","name":"pnet/conv4_1/bias","shape":[2]},{"dtype":"float32","name":"pnet/conv4_2/weights","shape":[1,1,32,4]},{"dtype":"float32","name":"pnet/conv4_2/bias","shape":[4]},{"dtype":"float32","name":"rnet/conv1/weights","shape":[3,3,3,28]},{"dtype":"float32","name":"rnet/conv1/bias","shape":[28]},{"dtype":"float32","name":"rnet/prelu1_alpha","shape":[28]},{"dtype":"float32","name":"rnet/conv2/weights","shape":[3,3,28,48]},{"dtype":"float32","name":"rnet/conv2/bias","shape":[48]},{"dtype":"float32","name":"rnet/prelu2_alpha","shape":[48]},{"dtype":"float32","name":"rnet/conv3/weights","shape":[2,2,48,64]},{"dtype":"float32","name":"rnet/conv3/bias","shape":[64]},{"dtype":"float32","name":"rnet/prelu3_alpha","shape":[64]},{"dtype":"float32","name":"rnet/fc1/weights","shape":[576,128]},{"dtype":"float32","name":"rnet/fc1/bias","shape":[128]},{"dtype":"float32","name":"rnet/prelu4_alpha","shape":[128]},{"dtype":"float32","name":"rnet/fc2_1/weights","shape":[128,2]},{"dtype":"float32","name":"rnet/fc2_1/bias","shape":[2]},{"dtype":"float32","name":"rnet/fc2_2/weights","shape":[128,4]},{"dtype":"float32","name":"rnet/fc2_2/bias","shape":[4]},{"dtype":"float32","name":"onet/conv1/weights","shape":[3,3,3,32]},{"dtype":"float32","name":"onet/conv1/bias","shape":[32]},{"dtype":"float32","name":"onet/prelu1_alpha","shape":[32]},{"dtype":"float32","name":"onet/conv2/weights","shape":[3,3,32,64]},{"dtype":"float32","name":"onet/conv2/bias","shape":[64]},{"dtype":"float32","name":"onet/prelu2_alpha","shape":[64]},{"dtype":"float32","name":"onet/conv3/weights","shape":[3,3,64,64]},{"dtype":"float32","name":"onet/conv3/bias","shape":[64]},{"dtype":"float32","name":"onet/prelu3_alpha","shape":[64]},{"dtype":"float32","name":"onet/conv4/weights","shape":[2,2,64,128]},{"dtype":"float32","name":"onet/conv4/bias","shape":[128]},{"dtype":"float32","name":"onet/prelu4_alpha","shape":[128]},{"dtype":"float32","name":"onet/fc1/weights","shape":[1152,256]},{"dtype":"float32","name":"onet/fc1/bias","shape":[256]},{"dtype":"float32","name":"onet/prelu5_alpha","shape":[256]},{"dtype":"float32","name":"onet/fc2_1/weights","shape":[256,2]},{"dtype":"float32","name":"onet/fc2_1/bias","shape":[2]},{"dtype":"float32","name":"onet/fc2_2/weights","shape":[256,4]},{"dtype":"float32","name":"onet/fc2_2/bias","shape":[4]},{"dtype":"float32","name":"onet/fc2_3/weights","shape":[256,10]},{"dtype":"float32","name":"onet/fc2_3/bias","shape":[10]}]}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
[{"weights":[{"name":"conv0/filters","shape":[3,3,3,16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009007044399485869,"min":-1.2069439495311063}},{"name":"conv0/bias","shape":[16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005263455241334205,"min":-0.9211046672334858}},{"name":"conv1/depthwise_filter","shape":[3,3,16,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004001977630690033,"min":-0.5042491814669441}},{"name":"conv1/pointwise_filter","shape":[1,1,16,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013836609615999109,"min":-1.411334180831909}},{"name":"conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0015159862590771096,"min":-0.30926119685173037}},{"name":"conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002666276225856706,"min":-0.317286870876948}},{"name":"conv2/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015265831292844286,"min":-1.6792414422128714}},{"name":"conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0020280554598453,"min":-0.37113414915168985}},{"name":"conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006100742489683862,"min":-0.8907084034938438}},{"name":"conv3/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016276211832083907,"min":-2.0508026908425725}},{"name":"conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394414279975143,"min":-0.7637432129944072}},{"name":"conv4/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006716050119961009,"min":-0.8059260143953211}},{"name":"conv4/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021875603993733724,"min":-2.8875797271728514}},{"name":"conv4/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0041141652009066415,"min":-0.8187188749804216}},{"name":"conv5/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008423839597141042,"min":-0.9013508368940915}},{"name":"conv5/pointwise_filter","shape":[1,1,256,512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.030007277283014035,"min":-3.8709387695088107}},{"name":"conv5/bias","shape":[512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008402082966823203,"min":-1.4871686851277068}},{"name":"conv8/filters","shape":[1,1,512,25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.028336129469030042,"min":-4.675461362389957}},{"name":"conv8/bias","shape":[25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002268134028303857,"min":-0.41053225912299807}}],"paths":["tiny_face_detector_model-shard1"]}]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,25 +0,0 @@
{
"game1": "/assets/images/game1.png",
"game10": "/assets/images/game10.png",
"game11": "/assets/images/game11.png",
"game12": "/assets/images/game12.png",
"game2": "/assets/images/game2.png",
"game3": "/assets/images/game3.png",
"game4": "/assets/images/game4.png",
"game5": "/assets/images/game5.png",
"game6": "/assets/images/game6.png",
"game7": "/assets/images/game7.png",
"game8": "/assets/images/game8.png",
"game9": "/assets/images/game9.png",
"glasses1": "/assets/images/glasses1.png",
"glasses2": "/assets/images/glasses2.png",
"glasses3": "/assets/images/glasses3.png",
"hat1": "/assets/images/hat1.png",
"hat2": "/assets/images/hat2.png",
"mustache1": "/assets/images/mustache1.png",
"mustache2": "/assets/images/mustache2.png",
"mustache3": "/assets/images/mustache3.png",
"mustache4": "/assets/images/mustache4.png",
"mustache5": "/assets/images/mustache5.png",
"dp": "/assets/images/dp.png"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

125828
dist/static/assets/ml5.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,595 +0,0 @@
// console.log('p5 version:', p5);
// console.log('ml5 version:', ml5);
// console.log(location.origin);
let assets = {};
var draw = function () {
// Begin met het tekenen van de video
// plaats hem op position x = 0, y = 0.
// vul de hele breedte en hoogte
image(lastFrame, 0,0, width, height);
for(let detection of detections) {
push();
let transformed = transformDetection(detection);
translate(transformed.origin.x, transformed.origin.y);
rotate(transformed.angle);
try {
if( overlayGrid ) {
drawGridOverlay(transformed)
}
if( overlayLandmarks ) {
drawLandmarkOverlay(transformed);
}
drawMask(transformed);
} catch (error) {
console.error(error);
}
pop();
}
};
function drawLandmarkOverlay(detection) {
for(let nr in detection.points) {
p = detection.points[nr]
stroke('red')
strokeWeight(5)
point(p.x, p.y)
noStroke();
textSize(12);
fill('white');
text(nr, p.x, p.y);
}
noFill();
}
function drawGridOverlay(detection) {
textSize(20)
stroke(100,100,100)
strokeWeight(1)
for (let y = 0; y < detection.height; y+=10) {
if(y%100 === 0) {
strokeWeight(3)
text(y, detection.x - 10, y);
} else {
strokeWeight(1)
}
line(detection.x, detection.y + y, detection.x+detection.width, detection.y + y);
}
for (let x = 0; x < detection.width; x+=10) {
if(x != 0 && x%100 === 0) { // 0 already drawn for y
strokeWeight(3)
text(x, x, detection.y - 10);
} else {
strokeWeight(1)
}
line(detection.x + x, detection.y, detection.x + x, detection.y + detection.height);
}
}
var drawMask = function(detection) {
};
// var gotResults = function(err, result) {
// if (err) {
// console.log(err)
// return
// }
// };
// function code_error(type, error) {
// window.parent.postMessage({
// 'type': type,
// 'error': error.message,
// 'name': error.name,
// 'line': error.lineNumber - 2, // seems it giveswrong line numbers
// 'column': error.columnNumber
// }, '*');
// }
// function no_code_error(type){
// window.parent.postMessage({
// 'type': type,
// 'error': null
// }, '*');
// }
// window.addEventListener("message", function (e) {
// if (event.origin !== window.location.origin) {
// console.error("Invalid origin of message. Ignored");
// return;
// }
// console.debug("receive", e.data);
// switch (e.data.action) {
// case 'asset':
// if(e.data.content === null){
// delete assets[e.data.id];
// } else {
// assets[e.data.id] = loadImage(e.data.content);
// }
// break;
// case 'code':
// let f = new Function("");
// try {
// f = new Function(e.data.draw);
// no_code_error('syntax');
// } catch (error) {
// code_error('syntax', error);
// // window.parent.postMessage({'syntax': error.lineNumber});
// }
// handleResults = f;
// break;
// default:
// console.error("Invalid action", e.data.action);
// break;
// }
// });
let faceapi;
var video;
var lastFrame;
var frameToDetect;
var detections = [];
var factor_x, factor_y;
var flip = false; // mirror mode, disabled, because it's tricky to enable with faceApi
// function pause() {
// if (running)
// running = false;
// else {
// running = true;
// faceapi.detect(gotResults);
// }
// }
// by default all options are set to true
const detection_options = {
withLandmarks: true,
withDescriptors: false,
minConfidence: 0.5,
Mobilenetv1Model: window.parent.location.origin + '/assets/faceapi',
FaceLandmarkModel: window.parent.location.origin + '/assets/faceapi',
FaceLandmark68TinyNet: window.parent.location.origin + '/assets/faceapi',
FaceRecognitionModel: window.parent.location.origin + '/assets/faceapi',
TinyFaceDetectorModel: window.parent.location.origin + '/assets/faceapi',
}
function setupAssets(){
// placeholder. Override in patch...
}
let images = {};
function preload() {
const req = new Request('/assets/images.json');
fetch(req).then(
response => response.json()
).then(data => {
for(let id in data) {
images[id] = loadImage(data[id]);
}
// console.log('images', data, images);
});
// console.log(images);
}
var overlayLandmarks = false;
var overlayGrid = false;
function setup() {
// createCanvas(1280,720, WEBGL);
createCanvas(540,420);
smooth();
noFill();
push();
translate(-width/2, -height/2);
let constraints = {
video: {
width: { min: 720 },
height: { min: 540 }
},
audio: false
};
video = createCapture(constraints);
lastFrame = createGraphics(video.width, video.height);
frameToDetect = createGraphics(video.width, video.height);
// console.log(video);
// HeadGazeSetup(video);
// video.size(width, height);
video.hide(); // Hide the video element, and just show the canvas
faceapi = ml5.faceApi(video, detection_options, modelReady);
textAlign(RIGHT);
setupAssets();
let controlEl = document.createElement('div');
controlEl.classList.add('controls');
let label1El = document.createElement('label');
let check1El = document.createElement('input');
check1El.type = 'checkbox';
check1El.addEventListener('change', (e) => overlayLandmarks = e.target.checked);
let text1Node = document.createTextNode("Show points")
label1El.appendChild(check1El);
label1El.appendChild(text1Node);
controlEl.appendChild(label1El);
let label2El = document.createElement('label');
let check2El = document.createElement('input');
check2El.type = 'checkbox';
check2El.addEventListener('change', (e) => overlayGrid = e.target.checked);
let text2Node = document.createTextNode("Show coordinates")
label2El.appendChild(check2El);
label2El.appendChild(text2Node);
controlEl.appendChild(label2El);
let downloadBtn = document.createElement('button');
downloadBtn.innerHTML = 'screenshot';
downloadBtn.style.float = 'right';
// Convert canvas to image
downloadBtn.addEventListener("click", function(e) {
const canvas = document.querySelector('canvas');
const dataURL = canvas.toDataURL("image/png", 1.0);
let a = document.createElement('a');
a.href = dataURL;
a.download = 'screenshot.png';
document.body.appendChild(a);
a.click();
});
controlEl.appendChild(downloadBtn);
document.body.appendChild(controlEl);
}
function modelReady() {
frameToDetect.image(video, 0,0, video.width, video.height);
faceapi.detect(gotResults);
}
// var handleResults = function(){
// // background(parseInt(Math.random()*255),parseInt(Math.random()*255),parseInt(Math.random()*255));
// background((millis()/100)%255,0,0);
// image(video, -width/2 + 10, -height/2 + 10, width - 20, height -20);
// };
gotResults = function(err, result) {
if (err) {
console.error(err)
return
}
// store data for async draw function
// TODO results to more compatible format
// translate(width,0); // move to far corner
if (flip) {
lastFrame.push();
lastFrame.scale(-1.0,1.0); // flip x-axis backwards
lastFrame.image(frameToDetect, -lastFrame.width, 0, lastFrame.width, lastFrame.height);
lastFrame.pop();
} else {
lastFrame.image(frameToDetect, 0, 0, lastFrame.width, lastFrame.height);
}
detections = parseDetectionResults(result, flip, width);
// size of video becomes known only after camera approval
if(lastFrame.width != video.width || lastFrame.height != video.height){
// console.log('Resizing canvas');
lastFrame.resizeCanvas(video.width, video.height);
frameToDetect.resizeCanvas(video.width, video.height);
}
// lastFrame.background('red');
frameToDetect.image(video, 0,0, video.width, video.height);
factor_x = width / video.width;
factor_y = height / video.height;
faceapi.detect(gotResults);
}
function drawBox(detections) {
for (let i = 0; i < detections.length; i++) {
const alignedRect = detections[i].alignedRect;
const x = alignedRect._box._x
const y = alignedRect._box._y
const boxWidth = alignedRect._box._width
const boxHeight = alignedRect._box._height
noFill();
stroke(161, 95, 251);
strokeWeight(2);
rect(x, y, boxWidth, boxHeight);
}
}
function drawLandmarks(detection) {
// for (let i = 0; i < detections.length; i++) {
const mouth = detection.parts.mouth;
const nose = detection.parts.nose;
const leftEye = detection.parts.leftEye;
const rightEye = detection.parts.rightEye;
const rightEyeBrow = detection.parts.rightEyeBrow;
const leftEyeBrow = detection.parts.leftEyeBrow;
const jawOutline = detection.parts.jawOutline;
strokePoints(mouth, CLOSE);
strokePoints(nose, CLOSE);
strokePoints(leftEye, CLOSE);
strokePoints(leftEyeBrow, OPEN);
strokePoints(rightEye, CLOSE);
strokePoints(rightEyeBrow, OPEN);
strokePoints(jawOutline, OPEN);
// }
}
function strokePoints(points, closed) {
beginShape();
for (let i = 0; i < points.length; i++) {
const x = points[i].x;
const y = points[i].y;
vertex(x, y)
}
if(typeof closed === 'undefined') {
closed = CLOSE;
}
endShape(closed)
}
function drawPoints(points, radius) {
if(typeof radius === 'undefined') {
radius = 2;
}
for (let i = 0; i < points.length; i++) {
const x = points[i].x;
const y = points[i].y;
circle(x, y, radius);
}
}
function faceDistance(face1, face2){
// distance between faces, in pixels, not meters.. for now
// we cheat a little: take centers, visualise circle with r = max(width, height)
// and find distance between these circles
box1 = (face1.x, face1.x + face1.width)
box2 = (face2.x, face2.x + face2.width)
c1 = {
x: face1.x + face1.width / 2,
y: face1.y + face1.height / 2,
}
c2 = {
x: face2.x + face2.width / 2,
y: face2.y + face2.height / 2,
}
r1 = Math.max(face1.width, face1.height) / 2;
r2 = Math.max(face2.width, face2.height) / 2;
dx = c1.x - c2.x;
dy = c1.y - c2.y;
return Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) ) - r1 - r2;
}
function mergePoints() {
// a points should be {x: , y: }
// collect all points in the arguments:
let points = [];
for(let arg of arguments) {
if(Array.isArray(arg)) {
points.push(...arg);
} else {
points.push(arg);
}
}
return points;
}
function getBoundingBox(){
// arguments contains points, or sets of points. Find bbox
const points = mergePoints(...arguments);
const xs = points.map((point) => point.x);
const ys = points.map((point) => point.y);
const minx = Math.min(...xs);
const miny = Math.min(...ys);
return {
x: minx,
y: miny,
width: Math.max(...xs) - minx,
height: Math.max(...ys) - miny,
}
}
function parseDetectionResults(results, flip, frameWidth) {
let detections = [];
for(let result of results) {
const landmarks = result.landmarks._positions.map((pos) => parseCoordinate(pos, flip, frameWidth));
let x = result.alignedRect._box._x * factor_x;
if(flip) {
x *= -1;
x += frameWidth;
}
let detection = {
'points': landmarks,
// TODO: rotation
'parts': {},
x: x,
y: result.alignedRect._box._y * factor_y,
width: result.alignedRect._box._width * factor_x,
height: result.alignedRect._box._height * factor_y,
}
// for(let idx in result.parts) {
// detection.parts[idx] = result.parts[idx].map((pos) => parseCoordinate(pos));
// }
detection['center'] = {
x: detection.x + detection.width / 2,
y: detection.y + detection.height / 2,
}
detections.push(detection);
}
return detections;
}
/**
* face api detector returns coordinates with _x and _y attributes.
* We convert this to the canvas's coordinates
* @param Object {_x: , _y: }
*/
function parseCoordinate(position, flip, frameWidth) {
let x = position._x * factor_x;
if (flip) {
x *= -1;
x += frameWidth;
}
return {
x: x,
y: position._y * factor_y,
}
}
function transformDetection(original) {
const b = original.points[36]; // outer point on left eye
const a = original.points[45]; // outer point on right eye
const angle = atan2(a.y - b.y, a.x - b.x);
// let cx =a.x/2 + b.x/2
// let cy = a.y/2 + b.y/2
const cx = original.x
const cy = original.y
let detection = {
'points': original.points.map(p => transformPoint(p, cx, cy, angle)),
'origin': {x:cx, y:cy},
'angle': angle,
'original': original
}
const bbox = getBoundingBox(detection.points);
padding_x = bbox.width * .1;
padding_y = bbox.height * .1;
detection['x'] = bbox.x - padding_x,
detection['y'] = bbox.y - padding_y,
detection['width'] = bbox.width * 1.2,
detection['height'] = bbox.height * 1.2
// detection['x'] = original.x - cx
// detection['y'] = original.y - cy
// detection['width'] = original.width
// detection['height'] = original.height
return detection;
}
function transformPoint(p, cx, cy, angle) {
const px = p.x-cx;
const py = p.y-cy;
return {
x: px * cos(-angle) - py * sin(-angle),
y: px * sin(-angle) + py * cos(-angle)
}
}
// error handling from consoleUtils.js::hijackConsoleErrorsScript
function getScriptOff(line) {
var offs = 0;
var l = 0;
var file = '';
for (var i=0; i<offs.length; i++) {
var n = offs[i][0];
if (n < line && n > l) {
l = n;
file = offs[i][1];
}
}
return [line - l, file];
}
// catch reference errors, via http://stackoverflow.com/a/12747364/2994108
window.onerror = function (msg, url, lineNumber, columnNo, error) {
var string = msg.toLowerCase();
var substring = "script error";
var data = {};
// if (url.match(${EXTERNAL_LINK_REGEX}) !== null && error.stack){
// var errorNum = error.stack.split('about:srcdoc:')[1].split(':')[0];
// var fileInfo = getScriptOff(errorNum);
// data = msg + ' (' + fileInfo[1] + ': line ' + fileInfo[0] + ')';
// } else {
// var fileInfo = getScriptOff(lineNumber);
data = msg + ' (' + error.fileName + ': line ' + error.lineNumber + ')';
// }
window.parent.postMessage([{
log: [{
method: 'error',
data: [data],
id: Date.now().toString()
}],
source: error.fileName
}], '*');
return false;
};
// catch rejected promises
window.onunhandledrejection = function (event) {
if (event.reason && event.reason.message && event.reason.stack){
// var errorNum = event.reason.stack.split('about:srcdoc:')[1].split(':')[0];
// var fileInfo = getScriptOff(errorNum);
var data = event.reason.message + ' (' + event.reason.stack + ': line ' + event.reason.stack.split("\n")[0] + ')';
window.parent.postMessage([{
log: [{
method: 'error',
data: [data],
id: Date.now().toString()
}],
source: event.reason.stack.split("\n")[0]
}], '*');
}
};

View File

@ -1,157 +0,0 @@
---
title: Cheatsheet
documentclass: scrartcl
classoption:
- twocolumn
# - landscape
---
## Punten op het gezicht
![](dlib_face_points.png)
detection.points
: Alle bovenstaande punten. Ieder punt bestaat uit een `x` en een `y` coördinaat. Bijvoorbeeld `detection.points[33].x` is het x-coördinaat van het puntje van de neus.
: `x` is de afstand vanaf links. De horizontale positie.
: `y` is de afstand vanaf boven. De verticale positie.
detection.x
: x coordinaat van de bounding box van het gezicht. Dit is het punt linkboven.
detection.y
: y coordinaat van de bounding box van het gezicht. Dit is het punt linkboven.
detection.width
: Breedte van de bounding box van het gezicht.
detection.height
: Hoogte van de bounding box van het gezicht.
detection.angle
: Hoeveel het gezicht gedraaid is (oftewel de 'roll').
## Tekenen
drawPoints(\[punt1, punt2, ...])
: Teken punten
strokePoints(\[punt1, punt2, ...])
: Teken lijnen tussen een set punten
strokePoints(\[punt1, punt2, ...], OPEN)
: Teken lijnen tussen een set punten, maar de eindpunten worden _niet_ met elkaar verbonden.
rect(x, y, breedte, hoogte)
: teken een rechthoek. _x_ en _y_ bepalen de linker bovenhoek.
circle(cx, cy, radius)
: Teken een cirkel. _cx_ en _cy_ bepalen het midden. Radius is de straal.
image(naam, x, y, breedte, hoogte)
: Toon een ingeladen afbeelding. _x_ en _y_ bepalen waar de linker bovenhoek wordt geplaatst.
: Zie hieronder voor alle beschikbare afbeeldingen
let box = getBoundingBox(punten)
: Bepaald het rechthoek waarbinnen de punten vallen.
: Te gebruiken als `box.x`, `box.y`, `box.width`, `box.height`.
## Opmaak
fill(kleur)
: de kleur van de vulling van wat je gaat tekenen.
noFill()
: geen vulling
stroke(kleur)
: de kleur van de omlijning.
strokeWeight(dikte)
: de dikte van de omlijning
noStroke()
: geen omlijning
### Kleuren
Kleuren kunnen op verschillende manieren worden opgeven:
* als engelse naam. Bijvoorbeeld `fill('orange')`, `fill('darkblue')`.
* Op basis van drie getallen tussen 0 en 255. Dit zijn de rood, groen, en blauwe componenten van de kleur. Bijvoorbeeld `fill(0, 0, 255)` geeft een blauwe vulling. `fill(100,100,100)` is grijs.
* Op basis van _vier_ getallen. Dit werkt hetzelfde als met drie. Het vierde getal bepaald transparantie. 0 is volledig tranparant, 255 helemaal geen transparantie. Dus `fill(0, 0, 255, 130)` geeft een half transparante blauwe vulling.
https://genekogan.com/code/p5js-transformations/
## Afbeeldingen
Deze afbeeldingen kun je gebruiken met `image()`.
Bijvoorbeeld: `image(images.game1, 10, 10, 50, 50)` tekent het plaatje `images.game1`, waarbij de linker bovenhoek 10 pixels van links en 10 pixels vanaf rechts wordt geplaatst. En het plaatje 50 bij 50 pixels is.
images.game1
![](../dist/static/assets/images/game1.png)
images.game10
![](../dist/static/assets/images/game10.png)
images.game11
![](../dist/static/assets/images/game11.png)
images.game12
![](../dist/static/assets/images/game12.png)
images.game2
![](../dist/static/assets/images/game2.png)
images.game3
![](../dist/static/assets/images/game3.png)
images.game4
![](../dist/static/assets/images/game4.png)
images.game5
![](../dist/static/assets/images/game5.png)
images.game6
![](../dist/static/assets/images/game6.png)
images.game7
![](../dist/static/assets/images/game7.png)
images.game8
![](../dist/static/assets/images/game8.png)
images.game9
![](../dist/static/assets/images/game9.png)
images.glasses1
![](../dist/static/assets/images/glasses1.png)
images.glasses2
![](../dist/static/assets/images/glasses2.png)
images.glasses3
![](../dist/static/assets/images/glasses3.png)
images.hat1
![](../dist/static/assets/images/hat1.png)
images.hat2
![](../dist/static/assets/images/hat2.png)
images.mustache1
![](../dist/static/assets/images/mustache1.png)
images.mustache3
![](../dist/static/assets/images/mustache3.png)
images.mustache4
![](../dist/static/assets/images/mustache4.png)
images.mustache5
![](../dist/static/assets/images/mustache5.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -6,53 +6,47 @@ services:
- dbdata:/data/db
app:
build:
context: ./
context: .
dockerfile: Dockerfile
target: production
# uncomment the following line to pull the image from docker hub
# image: index.docker.io/catarak/p5.js-web-editor:latest
# uncomment the following lines if you don't want export all of the variables
# defined in your .env file for testing
env_file:
- "$PWD/.env.production"
# environment:
# - API_URL
# - AWS_ACCESS_KEY
# - AWS_REGION
# - AWS_SECRET_KEY
# - CORS_ALLOW_LOCALHOST
# - EMAIL_SENDER
# - EMAIL_VERIFY_SECRET_TOKEN
# - EXAMPLE_USERNAME
# - EXAMPLE_USER_EMAIL
# - EXAMPLE_USER_PASSWORD
# - GG_EXAMPLES_USERNAME
# - GG_EXAMPLES_EMAIL
# - GG_EXAMPLES_PASS
# - ML5_EXAMPLES_USERNAME
# - ML5_EXAMPLES_EMAIL
# - ML5_EXAMPLES_PASS
# - GITHUB_ID
# - GITHUB_SECRET
# - GOOGLE_ID
# - GOOGLE_SECRET
# - MAILGUN_DOMAIN
# - MAILGUN_KEY
# - MONGO_URL
# - PORT
# - S3_BUCKET
# - S3_BUCKET_URL_BASE
# - SESSION_SECRET
# - UI_ACCESS_TOKEN_ENABLED
# - UPLOAD_LIMIT
# - MOBILE_ENABLED
# env_file:
# - "$PWD/.env.production"
environment:
- API_URL
- MONGO_URL
- PORT
- SESSION_SECRET
- AWS_ACCESS_KEY
- AWS_SECRET_KEY
- S3_BUCKET
- AWS_REGION
- GITHUB_ID
- GITHUB_SECRET
- MAILGUN_DOMAIN
- MAILGUN_KEY
- EMAIL_SENDER
- EMAIL_VERIFY_SECRET_TOKEN
- S3_BUCKET_URL_BASE
- GG_EXAMPLES_USERNAME
- GG_EXAMPLES_PASS
- GG_EXAMPLES_EMAIL
- GOOGLE_ID
- GOOGLE_SECRET
- EXAMPLE_USER_EMAIL
- EXAMPLE_USER_PASSWORD
- ML5_EXAMPLES_USERNAME
- ML5_EXAMPLES_PASS
- ML5_EXAMPLES_EMAIL
# you can either set this in your .env or as an environment variables
# or here YOU CHOOSE
# - MONGO_URL=mongodb://mongo:27017/p5js-web-editor
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
- .:/opt/node/app
- /opt/node/app/node_modules
ports:
- '8000:8000'
depends_on:

View File

@ -1,5 +1,3 @@
console.log('environment:',process.env.NODE_ENV);
if (process.env.NODE_ENV === 'production') {
process.env.webpackAssets = JSON.stringify(require('./dist/static/manifest.json'));
require('./dist/server.bundle.js');

View File

@ -1,16 +0,0 @@
[Unit]
Description=p5.js web editor
After=network.target
[Service]
Environment=NODE_PORT=8000
Type=simple
User=YOUR_USER
EnvironmentFile=/home/YOUR_USER/p5.js-web-editor/.env
WorkingDirectory=/home/YOUR_USER/p5.js-web-editor
ExecStart=/usr/bin/npm run start:prod
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@ -3,9 +3,9 @@ import friendlyWords from 'friendly-words';
import lodash from 'lodash';
import passport from 'passport';
// import GitHubStrategy from 'passport-github';
import GitHubStrategy from 'passport-github';
import LocalStrategy from 'passport-local';
// import GoogleStrategy from 'passport-google-oauth20';
import GoogleStrategy from 'passport-google-oauth20';
import { BasicStrategy } from 'passport-http';
import User from '../models/user';
@ -82,101 +82,101 @@ const getPrimaryEmail = githubEmails => (
/**
* Sign in with GitHub.
*/
// passport.use(new GitHubStrategy({
// clientID: process.env.GITHUB_ID,
// clientSecret: process.env.GITHUB_SECRET,
// callbackURL: '/auth/github/callback',
// passReqToCallback: true,
// scope: ['user:email'],
// }, (req, accessToken, refreshToken, profile, done) => {
// User.findOne({ github: profile.id }, (findByGithubErr, existingUser) => {
// if (existingUser) {
// done(null, existingUser);
// return;
// }
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
callbackURL: '/auth/github/callback',
passReqToCallback: true,
scope: ['user:email'],
}, (req, accessToken, refreshToken, profile, done) => {
User.findOne({ github: profile.id }, (findByGithubErr, existingUser) => {
if (existingUser) {
done(null, existingUser);
return;
}
// const emails = getVerifiedEmails(profile.emails);
// const primaryEmail = getPrimaryEmail(profile.emails);
const emails = getVerifiedEmails(profile.emails);
const primaryEmail = getPrimaryEmail(profile.emails);
// User.findByEmail(emails, (findByEmailErr, existingEmailUser) => {
// if (existingEmailUser) {
// existingEmailUser.email = existingEmailUser.email || primaryEmail;
// existingEmailUser.github = profile.id;
// existingEmailUser.username = existingEmailUser.username || profile.username;
// existingEmailUser.tokens.push({ kind: 'github', accessToken });
// existingEmailUser.name = existingEmailUser.name || profile.displayName;
// existingEmailUser.verified = User.EmailConfirmation.Verified;
// existingEmailUser.save(saveErr => done(null, existingEmailUser));
// } else {
// const user = new User();
// user.email = primaryEmail;
// user.github = profile.id;
// user.username = profile.username;
// user.tokens.push({ kind: 'github', accessToken });
// user.name = profile.displayName;
// user.verified = User.EmailConfirmation.Verified;
// user.save(saveErr => done(null, user));
// }
// });
// });
// }));
User.findByEmail(emails, (findByEmailErr, existingEmailUser) => {
if (existingEmailUser) {
existingEmailUser.email = existingEmailUser.email || primaryEmail;
existingEmailUser.github = profile.id;
existingEmailUser.username = existingEmailUser.username || profile.username;
existingEmailUser.tokens.push({ kind: 'github', accessToken });
existingEmailUser.name = existingEmailUser.name || profile.displayName;
existingEmailUser.verified = User.EmailConfirmation.Verified;
existingEmailUser.save(saveErr => done(null, existingEmailUser));
} else {
const user = new User();
user.email = primaryEmail;
user.github = profile.id;
user.username = profile.username;
user.tokens.push({ kind: 'github', accessToken });
user.name = profile.displayName;
user.verified = User.EmailConfirmation.Verified;
user.save(saveErr => done(null, user));
}
});
});
}));
// /**
// * Sign in with Google.
// */
// passport.use(new GoogleStrategy({
// clientID: process.env.GOOGLE_ID,
// clientSecret: process.env.GOOGLE_SECRET,
// callbackURL: '/auth/google/callback',
// passReqToCallback: true,
// scope: ['openid email'],
// }, (req, accessToken, refreshToken, profile, done) => {
// User.findOne({ google: profile._json.emails[0].value }, (findByGoogleErr, existingUser) => {
// if (existingUser) {
// done(null, existingUser);
// return;
// }
/**
* Sign in with Google.
*/
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: '/auth/google/callback',
passReqToCallback: true,
scope: ['openid email'],
}, (req, accessToken, refreshToken, profile, done) => {
User.findOne({ google: profile._json.emails[0].value }, (findByGoogleErr, existingUser) => {
if (existingUser) {
done(null, existingUser);
return;
}
// const primaryEmail = profile._json.emails[0].value;
const primaryEmail = profile._json.emails[0].value;
// User.findByEmail(primaryEmail, (findByEmailErr, existingEmailUser) => {
// let username = profile._json.emails[0].value.split('@')[0];
// User.findByUsername(username, (findByUsernameErr, existingUsernameUser) => {
// if (existingUsernameUser) {
// const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
// username = slugify(`${username} ${adj}`);
// }
// // what if a username is already taken from the display name too?
// // then, append a random friendly word?
// if (existingEmailUser) {
// existingEmailUser.email = existingEmailUser.email || primaryEmail;
// existingEmailUser.google = profile._json.emails[0].value;
// existingEmailUser.username = existingEmailUser.username || username;
// existingEmailUser.tokens.push({ kind: 'google', accessToken });
// existingEmailUser.name = existingEmailUser.name || profile._json.displayName;
// existingEmailUser.verified = User.EmailConfirmation.Verified;
// existingEmailUser.save((saveErr) => {
// if (saveErr) {
// console.log(saveErr);
// }
// done(null, existingEmailUser);
// });
// } else {
// const user = new User();
// user.email = primaryEmail;
// user.google = profile._json.emails[0].value;
// user.username = username;
// user.tokens.push({ kind: 'google', accessToken });
// user.name = profile._json.displayName;
// user.verified = User.EmailConfirmation.Verified;
// user.save((saveErr) => {
// if (saveErr) {
// console.log(saveErr);
// }
// done(null, user);
// });
// }
// });
// });
// });
// }));
User.findByEmail(primaryEmail, (findByEmailErr, existingEmailUser) => {
let username = profile._json.emails[0].value.split('@')[0];
User.findByUsername(username, (findByUsernameErr, existingUsernameUser) => {
if (existingUsernameUser) {
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
username = slugify(`${username} ${adj}`);
}
// what if a username is already taken from the display name too?
// then, append a random friendly word?
if (existingEmailUser) {
existingEmailUser.email = existingEmailUser.email || primaryEmail;
existingEmailUser.google = profile._json.emails[0].value;
existingEmailUser.username = existingEmailUser.username || username;
existingEmailUser.tokens.push({ kind: 'google', accessToken });
existingEmailUser.name = existingEmailUser.name || profile._json.displayName;
existingEmailUser.verified = User.EmailConfirmation.Verified;
existingEmailUser.save((saveErr) => {
if (saveErr) {
console.log(saveErr);
}
done(null, existingEmailUser);
});
} else {
const user = new User();
user.email = primaryEmail;
user.google = profile._json.emails[0].value;
user.username = username;
user.tokens.push({ kind: 'google', accessToken });
user.name = profile._json.displayName;
user.verified = User.EmailConfirmation.Verified;
user.save((saveErr) => {
if (saveErr) {
console.log(saveErr);
}
done(null, user);
});
}
});
});
});
}));

View File

@ -9,7 +9,6 @@ import Project from '../models/project';
import User from '../models/user';
import { resolvePathToFile } from '../utils/filePath';
import generateFileSystemSafeName from '../utils/generateFileSystemSafeName';
import mime from 'mime-types';
export { default as createProject, apiCreateProject } from './project.controller/createProject';
export { default as deleteProject } from './project.controller/deleteProject';
@ -113,11 +112,6 @@ export function getProjectAsset(req, res) {
return res.status(404).send({ message: 'Asset does not exist' });
}
if (!resolvedFile.url) {
// set the content type based on the filename
const mimetype = mime.lookup(resolvedFile.name);
if(mimetype) {
res.type(mimetype);
}
return res.send(resolvedFile.content);
}
request({ method: 'GET', url: resolvedFile.url, encoding: null }, (innerErr, response, body) => {

View File

@ -1,19 +1,17 @@
const defaultSketch = `// liveUpdate
function drawMask(detection) {
stroke('red')
strokeWeight(2)
rect(detection.x, detection.y, detection.width, detection.height)
const defaultSketch = `function setup() {
createCanvas(400, 400);
}
`;
function draw() {
background(220);
}`;
const defaultHTML =
`<!DOCTYPE html>
<html>
<head>
<script src="/assets/p5.js"></script>
<script src="/assets/ml5.js"></script>
<script src="/assets/webcam.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
@ -31,8 +29,6 @@ const defaultCSS =
}
canvas {
display: block;
width: 100% !important;
height: auto !important;
}
`;

View File

@ -28,8 +28,6 @@ import embedRoutes from './routes/embed.routes';
import assetRoutes from './routes/asset.routes';
import { requestsOfTypeJSON } from './utils/requestsOfType';
import User from './models/user';
import { renderIndex } from './views/index';
import { get404Sketch } from './views/404Page';
@ -50,8 +48,6 @@ if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) {
const allowedCorsOrigins = [
/p5js\.org$/,
/digitalplayground\.nl$/,
/rubenvandeven\.com$/
];
// to allow client-only development
@ -94,18 +90,6 @@ app.use(
app.use(Express.static(path.resolve(__dirname, '../dist/static'), {
maxAge: process.env.STATIC_MAX_AGE || (process.env.NODE_ENV === 'production' ? '1d' : '0')
}));
app.use(
'/assets',
Express.static(
path.resolve(__dirname, '../dist/static/assets'),
{
// Browsers must revalidate for changes to the locale files
// It doesn't actually mean "don't cache this file"
// See: https://jakearchibald.com/2016/caching-best-practices/
setHeaders: res => res.setHeader('Cache-Control', 'no-cache')
}
)
);
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use(bodyParser.json({ limit: '50mb' }));
@ -151,15 +135,15 @@ app.use('/', serverRoutes);
app.use(assetRoutes);
app.use('/', embedRoutes);
// app.get('/auth/github', passport.authenticate('github'));
// app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req, res) => {
// res.redirect('/');
// });
app.get('/auth/github', passport.authenticate('github'));
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req, res) => {
res.redirect('/');
});
// app.get('/auth/google', passport.authenticate('google'));
// app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
// res.redirect('/');
// });
app.get('/auth/google', passport.authenticate('google'));
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
res.redirect('/');
});
// configure passport
require('./config/passport');
@ -189,22 +173,6 @@ app.use('/api', (error, req, res, next) => {
});
// overview of users:
app.get('/users', (req, res) => {
// let results = [];
User.find({}).sort({ createdAt: -1 }).exec()
.then((users) => {
const usernames = users.map((user) => user.username);
let names = "<ul>";
usernames.forEach((username) => names += `<li><a href="/${username}/sketches">${username}</a></li>`);
names += "</ul>";
res.send(names);
});
// User.find()
// res.send({'testing': true});
});
// Handle missing routes.
app.get('*', (req, res) => {
res.status(404);

View File

@ -3,7 +3,7 @@
*/
import nodemailer from 'nodemailer';
// import mg from 'nodemailer-mailgun-transport';
import mg from 'nodemailer-mailgun-transport';
const auth = {
api_key: process.env.MAILGUN_KEY,
@ -12,10 +12,7 @@ const auth = {
class Mail {
constructor() {
this.client = nodemailer.createTransport({
streamTransport: true,
// newline: 'windows'
});
this.client = nodemailer.createTransport(mg({ auth }));
this.sendOptions = {
from: process.env.EMAIL_SENDER,
};

View File

@ -12,7 +12,7 @@ export function renderIndex() {
${process.env.NODE_ENV === 'production' ? `<link rel='stylesheet' href='${assetsManifest['/app.css']}' />` : ''}
<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='icon' href='/assets/favicon.png' type='image/png' / >
<link rel='shortcut icon' href='https://raw.githubusercontent.com/processing/p5.js-website-OLD/master/favicon.ico' type='image/x-icon' / >
<script>
if (!window.process) {
window.process = {};
@ -29,7 +29,6 @@ export function renderIndex() {
window.process.env.CLIENT = 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};
window.process.env.EXAMPLE_USERNAME = '${process.env.EXAMPLE_USERNAME || 'p5'}';
window.process.env.UI_ACCESS_TOKEN_ENABLED = ${process.env.UI_ACCESS_TOKEN_ENABLED === 'false' ? false : true};
window.process.env.UI_COLLECTIONS_ENABLED = ${process.env.UI_COLLECTIONS_ENABLED === 'false' ? false : true};
window.process.env.UPLOAD_LIMIT = ${process.env.UPLOAD_LIMIT ? `${process.env.UPLOAD_LIMIT}` : undefined};
@ -43,6 +42,13 @@ export function renderIndex() {
</div>
<script src='${process.env.NODE_ENV === 'production' ? `${assetsManifest['/app.js']}` : '/app.js'}'></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-53383000-1', 'auto');
ga('send', 'pageview');
</script>
</body>