Merge branch 'console' of git://github.com/therewasaguy/p5.js-web-editor into therewasaguy-console
This commit is contained in:
commit
699735f8c4
8 changed files with 172 additions and 9 deletions
|
@ -37,5 +37,7 @@ export const CREATE_FILE = 'CREATE_FILE';
|
|||
export const EXPAND_SIDEBAR = 'EXPAND_SIDEBAR';
|
||||
export const COLLAPSE_SIDEBAR = 'COLLAPSE_SIDEBAR';
|
||||
|
||||
export const CONSOLE_EVENT = 'CONSOLE_EVENT';
|
||||
|
||||
// eventually, handle errors more specifically and better
|
||||
export const ERROR = 'ERROR';
|
||||
|
|
|
@ -25,6 +25,13 @@ export function setSelectedFile(fileId) {
|
|||
};
|
||||
}
|
||||
|
||||
export function dispatchConsoleEvent(...args) {
|
||||
return {
|
||||
type: ActionTypes.CONSOLE_EVENT,
|
||||
event: args[0].data
|
||||
};
|
||||
}
|
||||
|
||||
export function newFile() {
|
||||
return {
|
||||
type: ActionTypes.SHOW_MODAL
|
||||
|
|
57
client/modules/IDE/components/Console.js
Normal file
57
client/modules/IDE/components/Console.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
|
||||
/**
|
||||
* How many console messages to store
|
||||
* @type {Number}
|
||||
*/
|
||||
const consoleMax = 5;
|
||||
|
||||
class Console extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
/**
|
||||
* An array of React Elements that include previous console messages
|
||||
* @type {Array}
|
||||
*/
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isPlaying && !this.props.isPlaying) {
|
||||
this.children = [];
|
||||
} else if (nextProps.consoleEvent !== this.props.consoleEvent) {
|
||||
const args = nextProps.consoleEvent.arguments;
|
||||
const method = nextProps.consoleEvent.method;
|
||||
const nextChild = (
|
||||
<div key={this.children.length} className={method}>
|
||||
{Object.keys(args).map((key) => <span key={`${this.children.length}-${key}`}>{args[key]}</span>)}
|
||||
</div>
|
||||
);
|
||||
this.children.push(nextChild);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (nextProps.consoleEvent !== this.props.consoleEvent) || (nextProps.isPlaying && !this.props.isPlaying);
|
||||
}
|
||||
|
||||
render() {
|
||||
const childrenToDisplay = this.children.slice(-consoleMax);
|
||||
|
||||
return (
|
||||
<div ref="console" className="preview-console">
|
||||
{childrenToDisplay}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Console.propTypes = {
|
||||
consoleEvent: PropTypes.object,
|
||||
isPlaying: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default Console;
|
|
@ -3,12 +3,68 @@ import ReactDOM from 'react-dom';
|
|||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import srcDoc from 'srcdoc-polyfill';
|
||||
|
||||
const hijackConsoleScript = `<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var iframeWindow = window;
|
||||
var originalConsole = iframeWindow.console;
|
||||
iframeWindow.console = {};
|
||||
|
||||
var methods = [
|
||||
'debug', 'clear', 'error', 'info', 'log', 'warn'
|
||||
];
|
||||
|
||||
methods.forEach( function(method) {
|
||||
iframeWindow.console[method] = function() {
|
||||
originalConsole[method].apply(originalConsole, arguments);
|
||||
|
||||
var args = Array.from(arguments);
|
||||
args = args.map(function(i) {
|
||||
// catch objects
|
||||
return (typeof i === 'string') ? i : JSON.stringify(i);
|
||||
});
|
||||
|
||||
// post message to parent window
|
||||
window.parent.postMessage({
|
||||
method: method,
|
||||
arguments: args,
|
||||
source: 'sketch'
|
||||
}, '*');
|
||||
};
|
||||
});
|
||||
|
||||
// catch reference errors, via http://stackoverflow.com/a/12747364/2994108
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
var string = msg.toLowerCase();
|
||||
var substring = "script error";
|
||||
var data = {};
|
||||
|
||||
if (string.indexOf(substring) > -1){
|
||||
data = 'Script Error: See Browser Console for Detail';
|
||||
} else {
|
||||
data = msg + ' Line: ' + lineNo + 'column: ' + columnNo;
|
||||
}
|
||||
window.parent.postMessage({
|
||||
method: 'error',
|
||||
arguments: data,
|
||||
source: 'sketch'
|
||||
}, '*');
|
||||
return false;
|
||||
};
|
||||
});
|
||||
</script>`;
|
||||
|
||||
class PreviewFrame extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.isPlaying) {
|
||||
this.renderFrameContents();
|
||||
}
|
||||
|
||||
window.addEventListener('message', (msg) => {
|
||||
if (msg.data.source === 'sketch') {
|
||||
this.props.dispatchConsoleEvent(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
@ -51,6 +107,7 @@ class PreviewFrame extends React.Component {
|
|||
// htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2);
|
||||
// htmlHeadContents += '<link rel="stylesheet" type="text/css" href="/preview-styles.css" />\n';
|
||||
// htmlFile = htmlFile.replace(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi, `<head>\n${htmlHeadContents}\n</head>`);
|
||||
htmlFile += hijackConsoleScript;
|
||||
|
||||
return htmlFile;
|
||||
}
|
||||
|
@ -58,15 +115,10 @@ class PreviewFrame extends React.Component {
|
|||
renderSketch() {
|
||||
const doc = ReactDOM.findDOMNode(this);
|
||||
if (this.props.isPlaying) {
|
||||
// TODO add polyfill for this
|
||||
// doc.srcdoc = this.injectLocalFiles();
|
||||
srcDoc.set(doc, this.injectLocalFiles());
|
||||
} else {
|
||||
// doc.srcdoc = '';
|
||||
srcDoc.set(doc, '');
|
||||
doc.contentWindow.document.open();
|
||||
doc.contentWindow.document.write('');
|
||||
doc.contentWindow.document.close();
|
||||
doc.srcdoc = '';
|
||||
srcDoc.set(doc, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +140,7 @@ class PreviewFrame extends React.Component {
|
|||
frameBorder="0"
|
||||
title="sketch output"
|
||||
sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms"
|
||||
></iframe>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +153,9 @@ PreviewFrame.propTypes = {
|
|||
content: PropTypes.string.isRequired
|
||||
}),
|
||||
jsFiles: PropTypes.array.isRequired,
|
||||
cssFiles: PropTypes.array.isRequired
|
||||
cssFiles: PropTypes.array.isRequired,
|
||||
dispatchConsoleEvent: PropTypes.func.isRequired,
|
||||
children: PropTypes.element
|
||||
};
|
||||
|
||||
export default PreviewFrame;
|
||||
|
|
|
@ -6,6 +6,7 @@ import Toolbar from '../components/Toolbar';
|
|||
import Preferences from '../components/Preferences';
|
||||
import NewFileModal from '../components/NewFileModal';
|
||||
import Nav from '../../../components/Nav';
|
||||
import Console from '../components/Console';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import * as FileActions from '../actions/files';
|
||||
|
@ -85,12 +86,18 @@ class IDEView extends React.Component {
|
|||
<link type="text/css" rel="stylesheet" href="/preview-styles.css" />
|
||||
}
|
||||
isPlaying={this.props.ide.isPlaying}
|
||||
dispatchConsoleEvent={this.props.dispatchConsoleEvent}
|
||||
/>
|
||||
<Console
|
||||
consoleEvent={this.props.ide.consoleEvent}
|
||||
isPlaying={this.props.ide.isPlaying}
|
||||
/>
|
||||
<NewFileModal
|
||||
isVisible={this.props.ide.modalIsVisible}
|
||||
closeModal={this.props.closeNewFileModal}
|
||||
/>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +112,7 @@ IDEView.propTypes = {
|
|||
saveProject: PropTypes.func.isRequired,
|
||||
ide: PropTypes.shape({
|
||||
isPlaying: PropTypes.bool.isRequired,
|
||||
consoleEvent: PropTypes.object,
|
||||
modalIsVisible: PropTypes.bool.isRequired,
|
||||
sidebarIsExpanded: PropTypes.bool.isRequired
|
||||
}).isRequired,
|
||||
|
@ -143,6 +151,7 @@ IDEView.propTypes = {
|
|||
htmlFile: PropTypes.object.isRequired,
|
||||
jsFiles: PropTypes.array.isRequired,
|
||||
cssFiles: PropTypes.array.isRequired,
|
||||
dispatchConsoleEvent: PropTypes.func.isRequired,
|
||||
newFile: PropTypes.func.isRequired,
|
||||
closeNewFileModal: PropTypes.func.isRequired,
|
||||
expandSidebar: PropTypes.func.isRequired,
|
||||
|
|
|
@ -3,6 +3,10 @@ import * as ActionTypes from '../../../constants';
|
|||
const initialState = {
|
||||
isPlaying: false,
|
||||
selectedFile: '1',
|
||||
consoleEvent: {
|
||||
method: undefined,
|
||||
arguments: []
|
||||
},
|
||||
modalIsVisible: false,
|
||||
sidebarIsExpanded: true
|
||||
};
|
||||
|
@ -19,6 +23,8 @@ const ide = (state = initialState, action) => {
|
|||
case ActionTypes.SET_PROJECT:
|
||||
case ActionTypes.NEW_PROJECT:
|
||||
return Object.assign({}, state, { selectedFile: action.selectedFile });
|
||||
case ActionTypes.CONSOLE_EVENT:
|
||||
return Object.assign({}, state, { consoleEvent: action.event });
|
||||
case ActionTypes.SHOW_MODAL:
|
||||
return Object.assign({}, state, { modalIsVisible: true });
|
||||
case ActionTypes.HIDE_MODAL:
|
||||
|
|
27
client/styles/components/_console.scss
Normal file
27
client/styles/components/_console.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
.preview-console {
|
||||
position: fixed;
|
||||
width:100%;
|
||||
height:60px;
|
||||
right:0px;
|
||||
bottom: 0px;
|
||||
background:grey;
|
||||
z-index:1000;
|
||||
|
||||
& > {
|
||||
position:relative;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
// assign styles to different types of console messages
|
||||
.log {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.warn {
|
||||
color: yellow;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
@import 'components/sketch-list';
|
||||
@import 'components/sidebar';
|
||||
@import 'components/modal';
|
||||
@import 'components/console';
|
||||
|
||||
@import 'layout/ide';
|
||||
@import 'layout/sketch-list';
|
||||
|
|
Loading…
Reference in a new issue