2016-06-27 19:47:48 +00:00
|
|
|
import React, { PropTypes } from 'react';
|
2016-06-23 22:29:55 +00:00
|
|
|
import ReactDOM from 'react-dom';
|
2016-07-11 19:22:29 +00:00
|
|
|
import escapeStringRegexp from 'escape-string-regexp';
|
2016-07-11 21:21:20 +00:00
|
|
|
import srcDoc from 'srcdoc-polyfill';
|
2016-06-23 22:29:55 +00:00
|
|
|
|
|
|
|
class PreviewFrame extends React.Component {
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
if (this.props.isPlaying) {
|
|
|
|
this.renderFrameContents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-27 19:47:48 +00:00
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
if (this.props.isPlaying !== prevProps.isPlaying) {
|
2016-07-11 19:22:29 +00:00
|
|
|
this.renderSketch();
|
2016-06-27 19:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.props.isPlaying && this.props.content !== prevProps.content) {
|
|
|
|
this.renderSketch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).contentDocument.body);
|
|
|
|
}
|
|
|
|
|
2016-06-23 22:29:55 +00:00
|
|
|
clearPreview() {
|
2016-07-11 19:22:29 +00:00
|
|
|
const doc = ReactDOM.findDOMNode(this);
|
|
|
|
doc.srcDoc = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
injectLocalFiles() {
|
|
|
|
let htmlFile = this.props.htmlFile.content;
|
2016-07-20 01:36:21 +00:00
|
|
|
|
|
|
|
// have to build the array manually because the spread operator is only
|
|
|
|
// one level down...
|
|
|
|
const jsFiles = [];
|
|
|
|
this.props.jsFiles.forEach(jsFile => {
|
|
|
|
const newJSFile = { ...jsFile };
|
|
|
|
const jsFileStrings = newJSFile.content.match(/(['"])((\\\1|.)*?)\1/gm);
|
2016-07-19 23:36:50 +00:00
|
|
|
jsFileStrings.forEach(jsFileString => {
|
|
|
|
if (jsFileString.match(/^('|")(?!(http:\/\/|https:\/\/)).*\.(png|jpg|jpeg|gif|bmp)('|")$/)) {
|
2016-07-20 01:36:21 +00:00
|
|
|
const filePath = jsFileString.substr(1, jsFileString.length - 2);
|
|
|
|
let fileName = filePath;
|
|
|
|
if (fileName.match(/^\.\//)) {
|
|
|
|
fileName = fileName.substr(2, fileName.length - 1);
|
|
|
|
} else if (fileName.match(/^\//)) {
|
|
|
|
fileName = fileName.substr(1, fileName.length - 1);
|
|
|
|
}
|
2016-07-19 23:36:50 +00:00
|
|
|
this.props.files.forEach(file => {
|
|
|
|
if (file.name === fileName) {
|
2016-07-20 01:36:21 +00:00
|
|
|
newJSFile.content = newJSFile.content.replace(filePath, file.blobURL); // eslint-disable-line
|
2016-07-19 23:36:50 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2016-07-20 01:36:21 +00:00
|
|
|
jsFiles.push(newJSFile);
|
2016-07-19 23:36:50 +00:00
|
|
|
});
|
2016-07-11 19:22:29 +00:00
|
|
|
|
2016-07-19 23:36:50 +00:00
|
|
|
jsFiles.forEach(jsFile => {
|
2016-07-11 19:22:29 +00:00
|
|
|
const fileName = escapeStringRegexp(jsFile.name);
|
|
|
|
const fileRegex = new RegExp(`<script.*?src=('|")((\.\/)|\/)?${fileName}('|").*?>([\s\S]*?)<\/script>`, 'gmi');
|
|
|
|
htmlFile = htmlFile.replace(fileRegex, `<script>\n${jsFile.content}\n</script>`);
|
|
|
|
});
|
|
|
|
|
2016-07-12 01:54:08 +00:00
|
|
|
this.props.cssFiles.forEach(cssFile => {
|
|
|
|
const fileName = escapeStringRegexp(cssFile.name);
|
|
|
|
const fileRegex = new RegExp(`<link.*?href=('|")((\.\/)|\/)?${fileName}('|").*?>`, 'gmi');
|
|
|
|
htmlFile = htmlFile.replace(fileRegex, `<style>\n${cssFile.content}\n</style>`);
|
|
|
|
});
|
|
|
|
|
2016-07-11 19:22:29 +00:00
|
|
|
return htmlFile;
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
renderSketch() {
|
2016-07-11 19:22:29 +00:00
|
|
|
const doc = ReactDOM.findDOMNode(this);
|
|
|
|
if (this.props.isPlaying) {
|
2016-07-11 19:27:00 +00:00
|
|
|
// TODO add polyfill for this
|
2016-07-11 21:21:20 +00:00
|
|
|
// doc.srcdoc = this.injectLocalFiles();
|
|
|
|
srcDoc.set(doc, this.injectLocalFiles());
|
2016-07-11 19:22:29 +00:00
|
|
|
} else {
|
2016-07-11 21:21:20 +00:00
|
|
|
// doc.srcdoc = '';
|
|
|
|
srcDoc.set(doc, '');
|
|
|
|
doc.contentWindow.document.open();
|
|
|
|
doc.contentWindow.document.write('');
|
|
|
|
doc.contentWindow.document.close();
|
2016-07-11 19:22:29 +00:00
|
|
|
}
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-27 19:47:48 +00:00
|
|
|
renderFrameContents() {
|
|
|
|
const doc = ReactDOM.findDOMNode(this).contentDocument;
|
|
|
|
if (doc.readyState === 'complete') {
|
2016-06-27 21:22:54 +00:00
|
|
|
this.renderSketch();
|
2016-06-27 19:47:48 +00:00
|
|
|
} else {
|
|
|
|
setTimeout(this.renderFrameContents, 0);
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2016-06-27 19:47:48 +00:00
|
|
|
return (
|
|
|
|
<iframe
|
|
|
|
className="preview-frame"
|
2016-07-13 19:23:48 +00:00
|
|
|
role="region"
|
|
|
|
tabIndex="0"
|
2016-06-27 19:47:48 +00:00
|
|
|
frameBorder="0"
|
|
|
|
title="sketch output"
|
2016-07-11 21:32:13 +00:00
|
|
|
sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms"
|
2016-06-27 19:47:48 +00:00
|
|
|
></iframe>
|
|
|
|
);
|
2016-06-23 22:29:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-27 19:47:48 +00:00
|
|
|
PreviewFrame.propTypes = {
|
|
|
|
isPlaying: PropTypes.bool.isRequired,
|
2016-06-27 19:57:36 +00:00
|
|
|
head: PropTypes.object.isRequired,
|
2016-07-11 19:22:29 +00:00
|
|
|
content: PropTypes.string.isRequired,
|
|
|
|
htmlFile: PropTypes.shape({
|
|
|
|
content: PropTypes.string.isRequired
|
|
|
|
}),
|
2016-07-12 01:54:08 +00:00
|
|
|
jsFiles: PropTypes.array.isRequired,
|
2016-07-19 23:36:50 +00:00
|
|
|
cssFiles: PropTypes.array.isRequired,
|
|
|
|
files: PropTypes.array.isRequired
|
2016-06-27 19:47:48 +00:00
|
|
|
};
|
|
|
|
|
2016-06-23 22:29:55 +00:00
|
|
|
export default PreviewFrame;
|