From 14ede1388be8d461ea8f96a45d43e740416f8f89 Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Fri, 8 Jul 2016 14:33:06 -0400 Subject: [PATCH 1/7] console: hijack iframe console messages --- client/modules/IDE/components/PreviewFrame.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js index 94d5d7ce..08b916c0 100644 --- a/client/modules/IDE/components/PreviewFrame.js +++ b/client/modules/IDE/components/PreviewFrame.js @@ -4,6 +4,8 @@ import ReactDOM from 'react-dom'; class PreviewFrame extends React.Component { componentDidMount() { + this.hijackConsole(); + if (this.props.isPlaying) { this.renderFrameContents(); } @@ -33,6 +35,25 @@ class PreviewFrame extends React.Component { doc.close(); } + hijackConsole() { + const iframeWindow = ReactDOM.findDOMNode(this).contentWindow; + const originalConsole = iframeWindow.console; + iframeWindow.console = {}; + + const methods = [ + 'debug', 'clear', 'error', 'info', 'log', 'warn' + ]; + + methods.forEach((method) => { + iframeWindow.console[method] = (...theArgs) => { + originalConsole[method].apply(originalConsole, theArgs); + + // TO DO: do something with the arguments + // window.alert(JSON.stringify(theArgs)); + }; + }); + } + renderSketch() { const doc = ReactDOM.findDOMNode(this).contentDocument; this.clearPreview(); From 7f0b7afac106c7bc4d4bdfb1f1ef9cd374444fc3 Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Sun, 17 Jul 2016 19:06:43 -0400 Subject: [PATCH 2/7] add Console component, gets postMessage from previewFrame --- client/constants.js | 2 + client/modules/IDE/actions/ide.js | 7 ++ client/modules/IDE/components/Console.js | 49 +++++++++++ client/modules/IDE/components/PreviewFrame.js | 84 ++++++++++++++----- client/modules/IDE/pages/IDEView.js | 13 ++- client/modules/IDE/reducers/ide.js | 8 +- client/styles/components/_console.scss | 14 ++++ 7 files changed, 151 insertions(+), 26 deletions(-) create mode 100644 client/modules/IDE/components/Console.js create mode 100644 client/styles/components/_console.scss diff --git a/client/constants.js b/client/constants.js index 967df39b..f6256cad 100644 --- a/client/constants.js +++ b/client/constants.js @@ -31,5 +31,7 @@ export const SET_PROJECTS = 'SET_PROJECTS'; export const SET_SELECTED_FILE = 'SET_SELECTED_FILE'; +export const CONSOLE_EVENT = 'CONSOLE_EVENT'; + // eventually, handle errors more specifically and better export const ERROR = 'ERROR'; diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 0bcbc414..eec73884 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -24,3 +24,10 @@ export function setSelectedFile(fileId) { selectedFile: fileId }; } + +export function dispatchConsoleEvent(...args) { + return { + type: ActionTypes.CONSOLE_EVENT, + event: args[0].data + }; +} diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js new file mode 100644 index 00000000..06b39c8c --- /dev/null +++ b/client/modules/IDE/components/Console.js @@ -0,0 +1,49 @@ +import React, { PropTypes } from 'react'; + +const consoleMax = 5; + +class Console extends React.Component { + + constructor(props) { + super(props); + this.children = []; + } + + shouldComponentUpdate(nextProps) { + // clear children if paused, but only update when new consoleEvent happens + if (!nextProps.isPlaying) { + this.children = []; + } + return nextProps.consoleEvent !== this.props.consoleEvent; + } + + render() { + const args = this.props.consoleEvent.arguments; + const method = this.props.consoleEvent.method; + + const nextChild = ( +
+ {Object.keys(args).map((key) => {args[key]})} +
+ ); + this.children.push(nextChild); + + if (this.children.length > consoleMax) { + this.children = this.children.slice(0, 1); + } + + return ( +
+ {this.children} +
+ ); + } + +} + +Console.propTypes = { + consoleEvent: PropTypes.object, + isPlaying: PropTypes.bool.isRequired +}; + +export default Console; diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js index b0313441..ac42e845 100644 --- a/client/modules/IDE/components/PreviewFrame.js +++ b/client/modules/IDE/components/PreviewFrame.js @@ -3,14 +3,68 @@ import ReactDOM from 'react-dom'; import escapeStringRegexp from 'escape-string-regexp'; import srcDoc from 'srcdoc-polyfill'; +const hijackConsoleScript = ``; + class PreviewFrame extends React.Component { componentDidMount() { - this.hijackConsole(); - if (this.props.isPlaying) { this.renderFrameContents(); } + + window.addEventListener('message', (msg) => { + if (msg.data.source === 'sketch') { + this.props.dispatchConsoleEvent(msg); + } + }); } componentDidUpdate(prevProps) { @@ -53,29 +107,11 @@ class PreviewFrame extends React.Component { // htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2); // htmlHeadContents += '\n'; // htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); + htmlFile += hijackConsoleScript; return htmlFile; } - hijackConsole() { - const iframeWindow = ReactDOM.findDOMNode(this).contentWindow; - const originalConsole = iframeWindow.console; - iframeWindow.console = {}; - - const methods = [ - 'debug', 'clear', 'error', 'info', 'log', 'warn' - ]; - - methods.forEach((method) => { - iframeWindow.console[method] = (...theArgs) => { - originalConsole[method].apply(originalConsole, theArgs); - - // TO DO: do something with the arguments - // window.alert(JSON.stringify(theArgs)); - }; - }); - } - renderSketch() { const doc = ReactDOM.findDOMNode(this); if (this.props.isPlaying) { @@ -107,7 +143,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" - > + /> ); } } @@ -120,7 +156,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; diff --git a/client/modules/IDE/pages/IDEView.js b/client/modules/IDE/pages/IDEView.js index c7308fa9..62f5956e 100644 --- a/client/modules/IDE/pages/IDEView.js +++ b/client/modules/IDE/pages/IDEView.js @@ -5,6 +5,7 @@ import PreviewFrame from '../components/PreviewFrame'; import Toolbar from '../components/Toolbar'; import Preferences from '../components/Preferences'; 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'; @@ -77,8 +78,14 @@ class IDEView extends React.Component { } isPlaying={this.props.ide.isPlaying} + dispatchConsoleEvent={this.props.dispatchConsoleEvent} + /> + + ); } } @@ -92,7 +99,8 @@ IDEView.propTypes = { createProject: PropTypes.func.isRequired, saveProject: PropTypes.func.isRequired, ide: PropTypes.shape({ - isPlaying: PropTypes.bool.isRequired + isPlaying: PropTypes.bool.isRequired, + consoleEvent: PropTypes.object }).isRequired, startSketch: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired, @@ -125,7 +133,8 @@ IDEView.propTypes = { setSelectedFile: PropTypes.func.isRequired, htmlFile: PropTypes.object.isRequired, jsFiles: PropTypes.array.isRequired, - cssFiles: PropTypes.array.isRequired + cssFiles: PropTypes.array.isRequired, + dispatchConsoleEvent: PropTypes.func.isRequired }; function mapStateToProps(state) { diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 9c4c5b88..7fc1d033 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -2,7 +2,11 @@ import * as ActionTypes from '../../../constants'; const initialState = { isPlaying: false, - selectedFile: '1' + selectedFile: '1', + consoleEvent: { + method: undefined, + arguments: [] + } }; const ide = (state = initialState, action) => { @@ -17,6 +21,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 }); default: return state; } diff --git a/client/styles/components/_console.scss b/client/styles/components/_console.scss new file mode 100644 index 00000000..f1502c46 --- /dev/null +++ b/client/styles/components/_console.scss @@ -0,0 +1,14 @@ +.preview-console { + position: fixed; + width:100%; + height:60px; + right:0px; + bottom: 0px; + background:grey; + z-index:1000; + + & > { + position:relative; + text-align:left; + } +} \ No newline at end of file From e7ea35fa60e9640966817096d719107e215b23fc Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Sun, 17 Jul 2016 20:05:20 -0400 Subject: [PATCH 3/7] add comments --- client/modules/IDE/components/Console.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js index 06b39c8c..362856a0 100644 --- a/client/modules/IDE/components/Console.js +++ b/client/modules/IDE/components/Console.js @@ -1,11 +1,20 @@ 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 = []; } From 8b69ab7fdd27f9faa7dd68f68077ff870c58db92 Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Sun, 17 Jul 2016 20:49:10 -0400 Subject: [PATCH 4/7] really clear sketch when sketch is stopped --- client/modules/IDE/components/PreviewFrame.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js index ac42e845..8d119e25 100644 --- a/client/modules/IDE/components/PreviewFrame.js +++ b/client/modules/IDE/components/PreviewFrame.js @@ -115,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, ' '); } } From 227b562ad980cbd9255ca4cb06cb6c9866514f42 Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Sun, 17 Jul 2016 20:49:38 -0400 Subject: [PATCH 5/7] console: color-coded error messages --- client/styles/components/_console.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/styles/components/_console.scss b/client/styles/components/_console.scss index f1502c46..0d4b43f7 100644 --- a/client/styles/components/_console.scss +++ b/client/styles/components/_console.scss @@ -11,4 +11,17 @@ position:relative; text-align:left; } + + // assign styles to different types of console messages + .log { + color: black; + } + + .error { + color: red; + } + + .warn { + color: yellow; + } } \ No newline at end of file From 40704b4b004f50c74acffc9657ed6ea315b422a7 Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Sun, 17 Jul 2016 20:49:57 -0400 Subject: [PATCH 6/7] console: shouldComponentUpdate and componentWilLReceiveProps tweaks --- client/modules/IDE/components/Console.js | 35 ++++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js index 362856a0..c604cc41 100644 --- a/client/modules/IDE/components/Console.js +++ b/client/modules/IDE/components/Console.js @@ -18,32 +18,31 @@ class Console extends React.Component { this.children = []; } - shouldComponentUpdate(nextProps) { - // clear children if paused, but only update when new consoleEvent happens - if (!nextProps.isPlaying) { + 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 = ( +
+ {Object.keys(args).map((key) => {args[key]})} +
+ ); + this.children.push(nextChild); } - return nextProps.consoleEvent !== this.props.consoleEvent; + } + + shouldComponentUpdate(nextProps) { + return (nextProps.consoleEvent !== this.props.consoleEvent) || (nextProps.isPlaying && !this.props.isPlaying); } render() { - const args = this.props.consoleEvent.arguments; - const method = this.props.consoleEvent.method; - - const nextChild = ( -
- {Object.keys(args).map((key) => {args[key]})} -
- ); - this.children.push(nextChild); - - if (this.children.length > consoleMax) { - this.children = this.children.slice(0, 1); - } + const childrenToDisplay = this.children.slice(-consoleMax); return (
- {this.children} + {childrenToDisplay}
); } From 0c7f713887292a5fd023ded86ee1d00a146b2f6d Mon Sep 17 00:00:00 2001 From: therewasaguy Date: Mon, 18 Jul 2016 19:10:42 -0400 Subject: [PATCH 7/7] console BEM style --- client/modules/IDE/components/Console.js | 2 +- client/styles/abstracts/_variables.scss | 3 +++ client/styles/components/_console.scss | 14 +++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js index c604cc41..e05809db 100644 --- a/client/modules/IDE/components/Console.js +++ b/client/modules/IDE/components/Console.js @@ -25,7 +25,7 @@ class Console extends React.Component { const args = nextProps.consoleEvent.arguments; const method = nextProps.consoleEvent.method; const nextChild = ( -
+
{Object.keys(args).map((key) => {args[key]})}
); diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss index aea3f2fb..a006ff62 100644 --- a/client/styles/abstracts/_variables.scss +++ b/client/styles/abstracts/_variables.scss @@ -39,3 +39,6 @@ $dark-button-active-color: $white; $ide-border-color: #f4f4f4; $editor-selected-line-color: #f3f3f3; $input-border-color: #979797; + +$console-warn-color: #ffbe05; +$console-error-color: #ff5f52; diff --git a/client/styles/components/_console.scss b/client/styles/components/_console.scss index 0d4b43f7..8dd0e6ba 100644 --- a/client/styles/components/_console.scss +++ b/client/styles/components/_console.scss @@ -4,7 +4,7 @@ height:60px; right:0px; bottom: 0px; - background:grey; + background:$dark-background-color; z-index:1000; & > { @@ -13,15 +13,15 @@ } // assign styles to different types of console messages - .log { - color: black; + .preview-console__log { + color: $dark-secondary-text-color; } - .error { - color: red; + .preview-console__error { + color: $console-error-color; } - .warn { - color: yellow; + .preview-console__warn { + color: $console-warn-color; } } \ No newline at end of file