Skip to content

Commit 1ada9cf

Browse files
committed
Address #213 by update how iframes created by CodeFrame make use of p5.min.js.
Avoids requiring a cached version of script by passing the text content of the script directly to the iframe.
1 parent 64be27a commit 1ada9cf

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

src/components/CodeEmbed/frame.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef, useLayoutEffect } from 'preact/hooks';
1+
import { useRef, useLayoutEffect, useEffect } from "preact/hooks";
22
import { p5VersionForEmbeds } from "@/src/globals/globals";
33

44
/*
@@ -33,7 +33,20 @@ ${code.css || ""}
3333
</style>
3434
<body>${code.htmlBody || ""}</body>
3535
<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>
3750
`.replace(/\u00A0/g, " ");
3851

3952
export interface CodeFrameProps {
@@ -52,6 +65,7 @@ export interface CodeFrameProps {
5265
export const CodeFrame = (props: CodeFrameProps) => {
5366
const iframeRef = useRef<HTMLIFrameElement>(null);
5467
const containerRef = useRef<HTMLDivElement>(null);
68+
const p5ScriptTag = document.getElementById('p5ScriptTag') as HTMLScriptElement;
5569

5670
// For performance, set the iframe to display:none when
5771
// not visible on the page. This will stop the browser
@@ -78,6 +92,31 @@ export const CodeFrame = (props: CodeFrameProps) => {
7892
return () => observer.disconnect();
7993
}, []);
8094

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+
81120
return (
82121
<div ref={containerRef} style={{ width: props.width, height: props.height }}>
83122
<iframe

src/components/CodeEmbed/index.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useEffect, useRef } from "preact/hooks";
22
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
33
import { javascript } from "@codemirror/lang-javascript";
4+
import { p5VersionForEmbeds } from "@/src/globals/globals";
45

56
import { CodeFrame } from "./frame";
67
import { CopyCodeButton } from "../CopyCodeButton";
@@ -44,8 +45,19 @@ export const CodeEmbed = (props) => {
4445

4546
const [previewCodeString, setPreviewCodeString] = useState(codeString);
4647

48+
/*
49+
* Url to fetch the p5.js library from
50+
*/
51+
const p5LibraryUrl = `https://cdnjs.cloudflare.com/ajax/libs/p5.js/${p5VersionForEmbeds}/p5.min.js`;
52+
4753
useEffect(() => {
4854
setRendered(true);
55+
56+
// Includes p5.min.js script to be used by `CodeFrame` iframe(s)
57+
const p5ScriptElement = document.createElement('script');
58+
p5ScriptElement.id = "p5ScriptTag";
59+
p5ScriptElement.src = p5LibraryUrl;
60+
document.head.appendChild(p5ScriptElement);
4961
}, []);
5062

5163
if (!rendered) return <div className="code-placeholder" />;

0 commit comments

Comments
 (0)