diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 19cb940a..7526e72c 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -183,9 +183,10 @@ export function setUnsavedChanges(value) { }; } -export function detectInfiniteLoops() { +export function detectInfiniteLoops(message) { return { - type: ActionTypes.DETECT_INFINITE_LOOPS + type: ActionTypes.DETECT_INFINITE_LOOPS, + message }; } diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js index 07cd4633..390afd51 100644 --- a/client/modules/IDE/components/Console.js +++ b/client/modules/IDE/components/Console.js @@ -20,30 +20,23 @@ class Console extends React.Component { * @type {Array} */ this.children = []; + this.appendConsoleEvent = this.appendConsoleEvent.bind(this); } componentWillReceiveProps(nextProps) { if (nextProps.isPlaying && !this.props.isPlaying) { this.children = []; - } else if (nextProps.consoleEvent !== this.props.consoleEvent) { + } else if (nextProps.consoleEvent !== this.props.consoleEvent && this.props.isPlaying) { const args = nextProps.consoleEvent.arguments; Object.keys(args).forEach((key) => { if (args[key].includes('Exiting potential infinite loop')) { - // stop sketch - // dispatch infinite loop found this.props.stopSketch(); - this.props.detectInfiniteLoops(); + this.props.expandConsole(); + this.appendConsoleEvent(nextProps.consoleEvent); } }); if (nextProps.isExpanded) { - // const args = nextProps.consoleEvent.arguments; - const method = nextProps.consoleEvent.method; - const nextChild = ( -
- {Object.keys(args).map((key) => {args[key]})} -
- ); - this.children.push(nextChild); + this.appendConsoleEvent(nextProps.consoleEvent); } } } @@ -58,6 +51,17 @@ class Console extends React.Component { this.refs.console_messages.scrollTop = this.refs.console_messages.scrollHeight; } + appendConsoleEvent(consoleEvent) { + const args = consoleEvent.arguments; + const method = consoleEvent.method; + const nextChild = ( +
+ {Object.keys(args).map((key) => {args[key]})} +
+ ); + this.children.push(nextChild); + } + render() { const childrenToDisplay = this.children.slice(-consoleMax); const consoleClass = classNames({ @@ -92,7 +96,6 @@ Console.propTypes = { collapseConsole: PropTypes.func.isRequired, expandConsole: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired, - detectInfiniteLoops: PropTypes.func.isRequired }; export default Console; diff --git a/client/modules/IDE/components/Editor.js b/client/modules/IDE/components/Editor.js index 0dc1b7ea..1e08d2b6 100644 --- a/client/modules/IDE/components/Editor.js +++ b/client/modules/IDE/components/Editor.js @@ -28,7 +28,6 @@ const downArrowUrl = require('../../../images/down-arrow.svg'); import classNames from 'classnames'; import { debounce } from 'throttle-debounce'; -import loopProtect from 'loop-protect'; class Editor extends React.Component { constructor(props) { @@ -66,17 +65,12 @@ class Editor extends React.Component { } }); - this._cm.on('change', debounce(1000, () => { + this._cm.on('change', debounce(200, () => { this.props.setUnsavedChanges(true); this.props.updateFileContent(this.props.file.name, this._cm.getValue()); if (this.props.autorefresh && this.props.isPlaying) { this.props.startRefreshSketch(); } - // this.checkForInfiniteLoop((infiniteLoop, prevs) => { - // if (!infiniteLoop && prevs && this.props.autorefresh) { - // this.props.startRefreshSketch(); - // } - // }); })); this._cm.on('keyup', () => { @@ -123,14 +117,6 @@ class Editor extends React.Component { if (this.props.theme !== prevProps.theme) { this._cm.setOption('theme', `p5-${this.props.theme}`); } - - if (this.props.infiniteLoop && this.props.infiniteLoop !== prevProps.infiniteLoop) { - const msg = document.createElement('div'); - const loopError = 'Loop is taking too long to run. This might be an infinite loop.'; - msg.appendChild(document.createTextNode(loopError)); - msg.className = 'lint-error'; - this.widgets.push(this._cm.addLineWidget(1, msg, { coverGutter: false, noHScroll: true })); - } } componentWillUnmount() { @@ -153,77 +139,6 @@ class Editor extends React.Component { } } - checkForInfiniteLoop(callback) { - const prevIsplaying = this.props.isPlaying; - let infiniteLoop = false; - let prevLine; - this.props.resetInfiniteLoops(); - let iframe; - - for (let i = 0; i < this.widgets.length; ++i) { - this._cm.removeLineWidget(this.widgets[i]); - } - this.widgets.length = 0; - - loopProtect.alias = 'protect'; - - let foundInfiniteLoop = false; - loopProtect.hit = (line) => { - foundInfiniteLoop = true; - if (line !== prevLine) { - this.props.detectInfiniteLoops(); - this.props.stopSketch(); - infiniteLoop = true; - callback(infiniteLoop, prevIsplaying); - const msg = document.createElement('div'); - const loopError = `line ${line}: This loop is taking too long to run. This might be an infinite loop.`; - msg.appendChild(document.createTextNode(loopError)); - msg.className = 'lint-error'; - this.widgets.push(this._cm.addLineWidget(line - 1, msg, { coverGutter: false, noHScroll: true })); - prevLine = line; - } - }; - - const processed = loopProtect(this.props.file.content); - - let iframeForLoop = document.getElementById('iframeForLoop'); - if (iframeForLoop === null) { - iframe = document.createElement('iframe'); - iframe.id = 'iframeForLoop'; - iframe.style.display = 'none'; - document.body.appendChild(iframe); - iframeForLoop = iframe; - } else { - iframeForLoop.srcdoc = ''; - } - const win = iframeForLoop.contentWindow; - const doc = win.document; - doc.open(); - - win.protect = loopProtect; - - doc.write(` - - - - - - - - - `); - win.onerror = () => true; - doc.close(); - - setTimeout(() => { - if (!foundInfiniteLoop) { - callback(infiniteLoop, prevIsplaying, prevLine); - } - }, 200); - } - _cm: CodeMirror.Editor render() { @@ -289,14 +204,10 @@ Editor.propTypes = { closeEditorOptions: PropTypes.func.isRequired, showKeyboardShortcutModal: PropTypes.func.isRequired, setUnsavedChanges: PropTypes.func.isRequired, - infiniteLoop: PropTypes.bool.isRequired, - detectInfiniteLoops: PropTypes.func.isRequired, - resetInfiniteLoops: PropTypes.func.isRequired, startRefreshSketch: PropTypes.func.isRequired, autorefresh: PropTypes.bool.isRequired, isPlaying: PropTypes.bool.isRequired, theme: PropTypes.string.isRequired, - stopSketch: PropTypes.func.isRequired }; export default Editor; diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js index 22f46fdd..2fc40003 100644 --- a/client/modules/IDE/components/PreviewFrame.js +++ b/client/modules/IDE/components/PreviewFrame.js @@ -217,20 +217,16 @@ class PreviewFrame extends React.Component { let htmlHeadContents = htmlHead[0].split(headRegex)[1]; htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2); htmlHeadContents += '\n'; - htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); if (this.props.textOutput || this.props.isTextOutputPlaying) { - const htmlHead = htmlFile.match(/(?:)([\s\S]*?)(?:<\/head>)/gmi); - const headRegex = new RegExp('head', 'i'); - let htmlHeadContents = htmlHead[0].split(headRegex)[1]; - htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2); htmlHeadContents += '\n'; htmlHeadContents += '\n'; htmlHeadContents += '\n'; htmlHeadContents += ''; - htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); } + htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); + scriptOffs = getAllScriptOffsets(htmlFile); htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs)); @@ -238,9 +234,8 @@ class PreviewFrame extends React.Component { } renderSketch() { - this.props.resetInfiniteLoops(); const doc = ReactDOM.findDOMNode(this); - if (this.props.isPlaying && !this.props.infiniteLoop) { + if (this.props.isPlaying) { srcDoc.set(doc, this.injectLocalFiles()); this.props.endSketchRefresh(); } else { @@ -288,8 +283,6 @@ PreviewFrame.propTypes = { files: PropTypes.array.isRequired, dispatchConsoleEvent: PropTypes.func, children: PropTypes.element, - infiniteLoop: PropTypes.bool.isRequired, - resetInfiniteLoops: PropTypes.func.isRequired, autorefresh: PropTypes.bool.isRequired, endSketchRefresh: PropTypes.func.isRequired, previewIsRefreshing: PropTypes.bool.isRequired, diff --git a/client/modules/IDE/components/Toolbar.js b/client/modules/IDE/components/Toolbar.js index 4804c981..831edb34 100644 --- a/client/modules/IDE/components/Toolbar.js +++ b/client/modules/IDE/components/Toolbar.js @@ -72,7 +72,7 @@ class Toolbar extends React.Component { > - {/*
+
Auto-refresh -
*/} +
@@ -322,8 +318,6 @@ class IDEView extends React.Component { isTextOutputPlaying={this.props.ide.isTextOutputPlaying} textOutput={this.props.preferences.textOutput} dispatchConsoleEvent={this.props.dispatchConsoleEvent} - infiniteLoop={this.props.ide.infiniteLoop} - resetInfiniteLoops={this.props.resetInfiniteLoops} autorefresh={this.props.preferences.autorefresh} previewIsRefreshing={this.props.ide.previewIsRefreshing} endSketchRefresh={this.props.endSketchRefresh} @@ -433,7 +427,8 @@ IDEView.propTypes = { keyboardShortcutVisible: PropTypes.bool.isRequired, unsavedChanges: PropTypes.bool.isRequired, infiniteLoop: PropTypes.bool.isRequired, - previewIsRefreshing: PropTypes.bool.isRequired + previewIsRefreshing: PropTypes.bool.isRequired, + infiniteLoopMessage: PropTypes.string.isRequired }).isRequired, startSketch: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired, diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 20509c6d..0abde94c 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -18,7 +18,8 @@ const initialState = { keyboardShortcutVisible: false, unsavedChanges: false, infiniteLoop: false, - previewIsRefreshing: false + previewIsRefreshing: false, + infiniteLoopMessage: '' }; const ide = (state = initialState, action) => { @@ -74,9 +75,9 @@ const ide = (state = initialState, action) => { case ActionTypes.SET_UNSAVED_CHANGES: return Object.assign({}, state, { unsavedChanges: action.value }); case ActionTypes.DETECT_INFINITE_LOOPS: - return Object.assign({}, state, { infiniteLoop: true }); + return Object.assign({}, state, { infiniteLoop: true, infiniteLoopMessage: action.message }); case ActionTypes.RESET_INFINITE_LOOPS: - return Object.assign({}, state, { infiniteLoop: false }); + return Object.assign({}, state, { infiniteLoop: false, infiniteLoopMessage: '' }); case ActionTypes.START_SKETCH_REFRESH: return Object.assign({}, state, { previewIsRefreshing: true }); case ActionTypes.END_SKETCH_REFRESH: