2016-06-27 20:03:22 +00:00
|
|
|
import React, { PropTypes } from 'react';
|
2016-08-12 01:26:49 +00:00
|
|
|
import EditorAccessibility from '../components/EditorAccessibility';
|
2016-06-23 22:29:55 +00:00
|
|
|
import CodeMirror from 'codemirror';
|
|
|
|
import 'codemirror/mode/javascript/javascript';
|
2016-07-13 23:52:50 +00:00
|
|
|
import 'codemirror/mode/css/css';
|
|
|
|
import 'codemirror/mode/htmlmixed/htmlmixed';
|
2016-06-23 22:29:55 +00:00
|
|
|
import 'codemirror/addon/selection/active-line';
|
2016-07-12 21:38:24 +00:00
|
|
|
import 'codemirror/addon/lint/lint';
|
|
|
|
import 'codemirror/addon/lint/javascript-lint';
|
2016-07-13 23:52:50 +00:00
|
|
|
import 'codemirror/addon/lint/css-lint';
|
|
|
|
import 'codemirror/addon/lint/html-lint';
|
2016-07-21 18:33:23 +00:00
|
|
|
import 'codemirror/addon/comment/comment';
|
|
|
|
import 'codemirror/keymap/sublime';
|
2016-08-05 20:58:59 +00:00
|
|
|
import 'codemirror/addon/search/jump-to-line';
|
2016-07-12 21:38:24 +00:00
|
|
|
import { JSHINT } from 'jshint';
|
|
|
|
window.JSHINT = JSHINT;
|
2016-07-13 23:52:50 +00:00
|
|
|
import { CSSLint } from 'csslint';
|
|
|
|
window.CSSLint = CSSLint;
|
|
|
|
import { HTMLHint } from 'htmlhint';
|
|
|
|
window.HTMLHint = HTMLHint;
|
2016-06-23 22:29:55 +00:00
|
|
|
|
2016-07-14 01:50:59 +00:00
|
|
|
import { debounce } from 'throttle-debounce';
|
2016-06-23 22:29:55 +00:00
|
|
|
|
|
|
|
class Editor extends React.Component {
|
|
|
|
|
|
|
|
componentDidMount() {
|
2016-08-12 00:26:14 +00:00
|
|
|
// TODO: replace with wav file and loader
|
2016-08-11 20:56:39 +00:00
|
|
|
this.beep = new Audio('data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3
|
2016-06-23 22:29:55 +00:00
|
|
|
this._cm = CodeMirror(this.refs.container, { // eslint-disable-line
|
|
|
|
theme: 'p5-widget',
|
2016-07-08 19:58:49 +00:00
|
|
|
value: this.props.file.content,
|
2016-06-23 22:29:55 +00:00
|
|
|
lineNumbers: true,
|
|
|
|
styleActiveLine: true,
|
2016-07-13 15:59:47 +00:00
|
|
|
inputStyle: 'contenteditable',
|
2016-07-12 19:58:11 +00:00
|
|
|
mode: 'javascript',
|
2016-07-12 21:38:24 +00:00
|
|
|
lineWrapping: true,
|
|
|
|
gutters: ['CodeMirror-lint-markers'],
|
2016-08-05 20:58:59 +00:00
|
|
|
keyMap: 'sublime',
|
|
|
|
lint: {
|
2016-08-08 17:49:45 +00:00
|
|
|
onUpdateLinting: debounce(2000, (annotations) => {
|
2016-08-11 17:24:02 +00:00
|
|
|
this.props.clearLintMessage();
|
2016-08-06 03:08:44 +00:00
|
|
|
annotations.forEach((x) => {
|
|
|
|
if (x.from.line > -1) {
|
2016-08-11 17:24:02 +00:00
|
|
|
this.props.updateLintMessage(x.severity, (x.from.line + 1), x.message);
|
2016-08-06 03:08:44 +00:00
|
|
|
}
|
|
|
|
});
|
2016-08-11 18:09:59 +00:00
|
|
|
if (this.props.lintMessages.length > 0 && this.props.lintWarning) {
|
2016-08-06 03:08:44 +00:00
|
|
|
this.beep.play();
|
|
|
|
}
|
2016-08-08 17:49:45 +00:00
|
|
|
})
|
2016-08-05 20:58:59 +00:00
|
|
|
}
|
2016-06-23 22:29:55 +00:00
|
|
|
});
|
2016-07-14 01:50:59 +00:00
|
|
|
this._cm.on('change', debounce(200, () => {
|
2016-07-08 19:58:49 +00:00
|
|
|
this.props.updateFileContent(this.props.file.name, this._cm.getValue());
|
2016-07-14 01:50:59 +00:00
|
|
|
}));
|
2016-08-05 20:58:59 +00:00
|
|
|
this._cm.on('keyup', () => {
|
2016-08-11 17:24:02 +00:00
|
|
|
this.props.updateLineNumber(parseInt((this._cm.getCursor().line) + 1, 10));
|
2016-08-05 20:58:59 +00:00
|
|
|
});
|
2016-07-14 01:50:59 +00:00
|
|
|
// this._cm.on('change', () => { // eslint-disable-line
|
|
|
|
// // this.props.updateFileContent('sketch.js', this._cm.getValue());
|
|
|
|
// throttle(1000, () => console.log('debounce is working!'));
|
|
|
|
// this.props.updateFileContent(this.props.file.name, this._cm.getValue());
|
|
|
|
// });
|
2016-06-23 22:29:55 +00:00
|
|
|
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
|
2016-07-11 03:11:06 +00:00
|
|
|
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
|
2016-07-11 13:00:44 +00:00
|
|
|
this._cm.setOption('tabSize', this.props.indentationAmount);
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps) {
|
2016-07-08 19:58:49 +00:00
|
|
|
if (this.props.file.content !== prevProps.file.content &&
|
|
|
|
this.props.file.content !== this._cm.getValue()) {
|
|
|
|
this._cm.setValue(this.props.file.content); // eslint-disable-line no-underscore-dangle
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
if (this.props.fontSize !== prevProps.fontSize) {
|
|
|
|
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
|
|
|
|
}
|
2016-07-06 15:27:39 +00:00
|
|
|
if (this.props.indentationAmount !== prevProps.indentationAmount) {
|
|
|
|
this._cm.setOption('tabSize', this.props.indentationAmount);
|
|
|
|
}
|
2016-07-11 03:11:06 +00:00
|
|
|
if (this.props.isTabIndent !== prevProps.isTabIndent) {
|
|
|
|
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
|
|
|
|
}
|
2016-07-13 23:52:50 +00:00
|
|
|
if (this.props.file.name !== prevProps.name) {
|
|
|
|
if (this.props.file.name.match(/.+\.js$/)) {
|
|
|
|
this._cm.setOption('mode', 'javascript');
|
|
|
|
} else if (this.props.file.name.match(/.+\.css$/)) {
|
|
|
|
this._cm.setOption('mode', 'css');
|
|
|
|
} else if (this.props.file.name.match(/.+\.html$/)) {
|
|
|
|
this._cm.setOption('mode', 'htmlmixed');
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._cm = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
_cm: CodeMirror.Editor
|
|
|
|
|
|
|
|
render() {
|
2016-08-05 20:58:59 +00:00
|
|
|
return (
|
2016-08-12 01:26:49 +00:00
|
|
|
<div>
|
|
|
|
<div ref="container" className="editor-holder" tabIndex="0" title="code editor" role="main">
|
|
|
|
</div>
|
|
|
|
<EditorAccessibility
|
|
|
|
lintMessages={this.props.lintMessages}
|
|
|
|
lineNo={this.props.lineNo}
|
|
|
|
/>
|
2016-08-05 20:58:59 +00:00
|
|
|
</div>
|
|
|
|
);
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-27 20:03:22 +00:00
|
|
|
Editor.propTypes = {
|
2016-08-11 18:09:59 +00:00
|
|
|
lintWarning: PropTypes.bool.isRequired,
|
2016-08-12 01:26:49 +00:00
|
|
|
lineNo: PropTypes.string.isRequired,
|
2016-08-11 17:24:02 +00:00
|
|
|
lintMessages: PropTypes.array.isRequired,
|
|
|
|
updateLintMessage: PropTypes.func.isRequired,
|
|
|
|
clearLintMessage: PropTypes.func.isRequired,
|
|
|
|
updateLineNumber: PropTypes.func.isRequired,
|
2016-07-11 02:52:48 +00:00
|
|
|
indentationAmount: PropTypes.number.isRequired,
|
2016-07-12 03:40:30 +00:00
|
|
|
isTabIndent: PropTypes.bool.isRequired,
|
|
|
|
updateFileContent: PropTypes.func.isRequired,
|
|
|
|
fontSize: PropTypes.number.isRequired,
|
2016-07-08 19:58:49 +00:00
|
|
|
file: PropTypes.shape({
|
|
|
|
name: PropTypes.string.isRequired,
|
|
|
|
content: PropTypes.string.isRequired
|
2016-07-12 03:40:30 +00:00
|
|
|
})
|
2016-06-27 20:03:22 +00:00
|
|
|
};
|
|
|
|
|
2016-06-23 22:29:55 +00:00
|
|
|
export default Editor;
|