1
- import { useRef , useLayoutEffect } from ' preact/hooks' ;
1
+ import { useRef , useLayoutEffect , useEffect } from " preact/hooks" ;
2
2
import { p5VersionForEmbeds } from "@/src/globals/globals" ;
3
3
4
4
/*
@@ -33,7 +33,20 @@ ${code.css || ""}
33
33
</style>
34
34
<body>${ code . htmlBody || "" } </body>
35
35
<script id="code" type="text/javascript">${ code . js || "" } </script>
36
- <script src="${ p5LibraryUrl } "></script>
36
+ <script type="text/javascript">
37
+ // Listen for p5.min.js text content and include in iframe's head as script
38
+ window.addEventListener("message", event => {
39
+ // Include check to prevent p5.min.js from being loaded twice
40
+ const scriptExists = !!document.getElementById("p5ScriptTagInIframe");
41
+ if (!scriptExists && event.data?.sender === '${ p5LibraryUrl } ') {
42
+ const p5ScriptElement = document.createElement('script');
43
+ p5ScriptElement.id = "p5ScriptTagInIframe";
44
+ p5ScriptElement.type = 'text/javascript';
45
+ p5ScriptElement.textContent = event.data.message;
46
+ document.head.appendChild(p5ScriptElement);
47
+ }
48
+ })
49
+ </script>
37
50
` . replace ( / \u00A0 / g, " " ) ;
38
51
39
52
export interface CodeFrameProps {
@@ -52,6 +65,7 @@ export interface CodeFrameProps {
52
65
export const CodeFrame = ( props : CodeFrameProps ) => {
53
66
const iframeRef = useRef < HTMLIFrameElement > ( null ) ;
54
67
const containerRef = useRef < HTMLDivElement > ( null ) ;
68
+ const p5ScriptTag = document . getElementById ( 'p5ScriptTag' ) as HTMLScriptElement ;
55
69
56
70
// For performance, set the iframe to display:none when
57
71
// not visible on the page. This will stop the browser
@@ -78,6 +92,31 @@ export const CodeFrame = (props: CodeFrameProps) => {
78
92
return ( ) => observer . disconnect ( ) ;
79
93
} , [ ] ) ;
80
94
95
+ useEffect ( ( ) => {
96
+ ( async ( ) => {
97
+ if ( ! p5ScriptTag || ! iframeRef . current ) return ;
98
+
99
+ /*
100
+ * Uses postMessage to receive the text content of p5.min.js, to be included
101
+ * in a script so p5.js functions can be called.
102
+ *
103
+ * Rather than including the script with <script src=p5LibraryUrl>, this had
104
+ * to be done because caching this resource with the service worker or browser
105
+ * cache, so the cached version could be used by an iframe isn't currently
106
+ * supported on all major browsers.
107
+ * It would instead, cause multiple downloads of p5.min.js on page load for
108
+ * each example, and on re-running a CodeFrame.
109
+ *
110
+ * See https://github.com/w3c/ServiceWorker/issues/765.
111
+ */
112
+ const p5ScriptText = await fetch ( p5ScriptTag . src ) . then ( ( res ) => res . text ( ) ) ;
113
+ iframeRef . current . contentWindow ?. postMessage ( {
114
+ sender : p5LibraryUrl ,
115
+ message : p5ScriptText
116
+ } , '*' ) ;
117
+ } ) ( )
118
+ } , [ props . jsCode ] )
119
+
81
120
return (
82
121
< div ref = { containerRef } style = { { width : props . width , height : props . height } } >
83
122
< iframe
0 commit comments