numbl 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  <title>numbl project</title>
8
8
  <!-- numbl:base -->
9
9
  <script src="./coi-serviceworker.js"></script>
10
- <script type="module" crossorigin src="./assets/index-CgKjTQT7.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-USrK1-DZ.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="./assets/index-D5YY8PKx.css">
12
12
  </head>
13
13
  <body>
@@ -15,7 +15,32 @@ You can also specify the MATLAB script URL via attribute:
15
15
  <numbl-embed script="relative-or-absolute-url-to-matlab-script">
16
16
  <iframe width="100%" height="500" frameborder="0"></iframe>
17
17
  </numbl-embed>
18
+
19
+ Lazy mode (recommended when many embeds share a page, e.g. documentation):
20
+ add the `lazy` attribute and the iframe is not loaded until the reader clicks
21
+ an "Edit & run" button. This keeps the page light — no worker, editor, or
22
+ package download boots until the reader asks for it. Customize the button text
23
+ with the `label` attribute.
24
+
25
+ <numbl-embed lazy label="▶ Edit & run this example"
26
+ script="https://example.com/snippet.m">
27
+ <iframe width="100%" height="560" frameborder="0"></iframe>
28
+ </numbl-embed>
18
29
  */
30
+ // Base64-encode a string as UTF-8. Plain btoa() throws on any character
31
+ // outside Latin-1 (e.g. an em dash or a Greek letter in a comment/title), so
32
+ // we go through the UTF-8 bytes. For pure-ASCII input this is byte-identical
33
+ // to btoa(), so it stays compatible with older /embed decoders.
34
+ function utf8ToBase64(text) {
35
+ const bytes = new TextEncoder().encode(text);
36
+ let binary = "";
37
+ const chunk = 0x8000;
38
+ for (let i = 0; i < bytes.length; i += chunk) {
39
+ binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
40
+ }
41
+ return btoa(binary);
42
+ }
43
+
19
44
  class NumblEmbed extends HTMLElement {
20
45
  constructor() {
21
46
  super();
@@ -23,19 +48,65 @@ class NumblEmbed extends HTMLElement {
23
48
 
24
49
  connectedCallback() {
25
50
  if (document.readyState === "loading") {
26
- document.addEventListener("DOMContentLoaded", () => this.initialize());
51
+ document.addEventListener("DOMContentLoaded", () => this.setup());
27
52
  } else {
28
- this.initialize();
53
+ this.setup();
29
54
  }
30
55
  }
31
56
 
32
- initialize() {
57
+ setup() {
33
58
  this.iframe = this.querySelector("iframe");
34
59
  if (!this.iframe) {
35
60
  console.error("Missing iframe element in numbl-embed");
36
61
  return;
37
62
  }
38
63
 
64
+ // Lazy mode: defer all loading until the reader opts in by clicking.
65
+ if (this.hasAttribute("lazy")) {
66
+ this.renderActivateButton();
67
+ } else {
68
+ this.activate();
69
+ }
70
+ }
71
+
72
+ renderActivateButton() {
73
+ this.iframe.style.display = "none";
74
+
75
+ const button = document.createElement("button");
76
+ button.type = "button";
77
+ button.className = "numbl-embed-activate";
78
+ button.textContent = this.getAttribute("label") || "▶ Edit & run in numbl";
79
+ // Inline styles so the button looks reasonable even without external CSS;
80
+ // a page may override via the .numbl-embed-activate class.
81
+ button.style.cssText = [
82
+ "display:inline-flex",
83
+ "align-items:center",
84
+ "gap:0.4em",
85
+ "padding:0.45em 0.9em",
86
+ "font-size:0.85rem",
87
+ "font-family:inherit",
88
+ "color:#fff",
89
+ "background:#2e7d32",
90
+ "border:none",
91
+ "border-radius:4px",
92
+ "cursor:pointer",
93
+ ].join(";");
94
+
95
+ button.addEventListener(
96
+ "click",
97
+ () => {
98
+ button.remove();
99
+ this.iframe.style.display = "";
100
+ this.activate();
101
+ },
102
+ { once: true }
103
+ );
104
+
105
+ this.activateButton = button;
106
+ this.insertBefore(button, this.iframe);
107
+ }
108
+
109
+ activate() {
39
110
  const defaultNumblUrl = "https://numbl.org";
40
111
  const numblUrl = this.attributes["numbl-url"]?.value || defaultNumblUrl;
41
112
  const cacheBust = `_cb=${Date.now()}`;
@@ -49,7 +120,7 @@ class NumblEmbed extends HTMLElement {
49
120
 
50
121
  const encodePlain = text => {
51
122
  const text2 = text.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
52
- return btoa(text2);
123
+ return utf8ToBase64(text2);
53
124
  };
54
125
 
55
126
  const scriptElement = this.querySelector("script.matlab-script");
@@ -87,7 +158,7 @@ class NumblEmbed extends HTMLElement {
87
158
  }
88
159
 
89
160
  const scriptContent = await response.text();
90
- const scriptBase64 = btoa(scriptContent);
161
+ const scriptBase64 = utf8ToBase64(scriptContent);
91
162
 
92
163
  const defaultNumblUrl = "https://numbl.org";
93
164
  const numblUrl = this.attributes["numbl-url"]?.value || defaultNumblUrl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numbl",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Run .m source files in the browser and on the command line by compiling to JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",