-
Notifications
You must be signed in to change notification settings - Fork 20
/
index.js
158 lines (156 loc) · 5.3 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
*
* inject script to facilitate iframe resizing
* https://github.com/davidjbradshaw/iframe-resizer
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { iframeResizer as iframeResizerLib } from 'iframe-resizer';
class IframeResizer extends React.Component {
componentDidMount() {
// can't update until we have a mounted iframe
this.updateIframe(this.props);
this.resizeIframe(this.props);
}
componentWillReceiveProps(nextProps) {
// can replace content if we got new props
this.updateIframe(nextProps);
this.resizeIframe(nextProps);
}
updateIframe = (props) => {
// has src - no injected content
if (props.src) return;
// do we have content to inject (content or children)
const content = props.content || props.children;
if (!content) return;
// get frame to inject into
const frame = this.refs.frame;
if (!frame) return;
// verify frame document access
// Due to browser security, this will fail with the following error
// Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
// Blocked a frame with origin "http://<hostname>" from accessing a cross-origin frame.
// resolve this by loading documents from the same domain name,
// or injecting HTML `content` vs. loading via `src`
const doc = frame.contentDocument;
if (!doc) return;
// replace iframe document content
if (typeof content === 'string') {
// assume this is a HTML block
// we could send this in via REACT dangerously set HTML
// but we are in an iframe anyway, already a red-headed step-child.
doc.open();
doc.write(content);
doc.close();
} else {
// assume this is a REACT component
doc.open();
doc.write('<div id="iframe-root"></div>');
doc.close();
ReactDOM.render(content, doc.getElementById('iframe-root'))
}
}
// inject the iframe resizer "content window" script
injectIframeResizerUrl = () => {
if (!this.props.iframeResizerUrl) return;
const frame = this.refs.frame;
if (!frame) return;
// verify frame document access
// Due to browser security, this will fail with the following error
// Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
// Blocked a frame with origin "http://<hostname>" from accessing a cross-origin frame.
// resolve this by loading documents from the same domain name,
// or injecting HTML `content` vs. loading via `src`
const doc = frame.contentDocument;
if (!doc) return;
// where can we insert into? (fail into whatever we can find)
let injectTarget = null;
['head', 'HEAD', 'body', 'BODY', 'div', 'DIV'].forEach(tagName => {
if (injectTarget) return;
const found = doc.getElementsByTagName(tagName);
if (!(found && found.length)) return;
injectTarget = found[0];
});
if (!injectTarget) {
console.error('Unable to inject iframe resizer script');
return;
}
const resizerScriptElement = document.createElement('script');
resizerScriptElement.type = 'text/javascript';
resizerScriptElement.src = this.props.iframeResizerUrl;
injectTarget.appendChild(resizerScriptElement);
}
onLoad = () => {
this.injectIframeResizerUrl();
// DISABLED because it's causing a loading loop :(
// if (this.props.onIframeLoaded) this.props.onIframeLoaded();
}
resizeIframe = (props) => {
const frame = this.refs.frame;
if (!frame) return;
if (props.iframeResizerEnable) {
iframeResizerLib(props.iframeResizerOptions, frame);
}
}
render() {
const { src, id, frameBorder, className, style } = this.props;
return (
<iframe
ref="frame"
src={src}
id={id}
frameBorder={frameBorder}
className={className}
style={style}
onLoad={this.onLoad}
/>
);
}
}
IframeResizer.propTypes = {
// iframe content/document
// option 1. content of HTML to load in the iframe
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
]),
// option 2. src to a URL to load in the iframe
src: PropTypes.string,
// iframe-resizer controls and helpers
iframeResizerEnable: PropTypes.bool,
iframeResizerOptions: PropTypes.object,
iframeResizerUrl: PropTypes.oneOfType([
PropTypes.string, // URL to inject
PropTypes.bool, // false = disable inject
]),
// misc props to pass through to iframe
id: PropTypes.string,
frameBorder: PropTypes.number,
className: PropTypes.string,
style: PropTypes.object,
// optional extra callback when iframe is loaded
// onIframeLoaded: PropTypes.func,
};
IframeResizer.defaultProps = {
// resize iframe
iframeResizerEnable: true,
iframeResizerOptions: {
// log: true,
// autoResize: true,
// checkOrigin: false,
// resizeFrom: 'parent',
// heightCalculationMethod: 'max',
// initCallback: () => { console.log('ready!'); },
// resizedCallback: () => { console.log('resized!'); },
},
iframeResizerUrl: 'https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/3.5.8/iframeResizer.contentWindow.min.js',
// misc props to pass through to iframe
frameBorder: 0,
style: {
width: '100%',
minHeight: 20,
},
};
export default IframeResizer;