1+
12<!DOCTYPE html>
23< html >
34< head >
1920 </ style >
2021</ head >
2122< body >
22- < iframe id ="explorerFrame " sandbox ="allow-scripts "> </ iframe >
23+ < iframe id ="explorerFrame " sandbox ="allow-scripts allow-forms "> </ iframe >
2324
2425 < script >
2526 // Create the HTML content for the iframe
@@ -81,16 +82,76 @@ <h1>API Explorer</h1>
8182 <form onsubmit="makeRequest(); return false;">
8283 <div class="input-group">
8384 <input type="text" id="urlInput" placeholder="Enter API URL" value="https://datasette.io/content/stats.json?_size=1">
84- <button onclick="makeRequest() ">Submit</button>
85+ <button type="submit ">Submit</button>
8586 </div>
8687 </form>
8788 <pre id="output">// Response will appear here</pre>
8889 </div>
8990
9091 <script>
92+ class APIClient {
93+ constructor(targetOrigin = '*') {
94+ this.targetOrigin = targetOrigin;
95+ this.pendingRequests = new Map();
96+ this.requestId = 0;
97+
98+ // Set up message listener
99+ window.addEventListener('message', this.handleMessage.bind(this));
100+ }
101+
102+ handleMessage(event) {
103+ if (event.data?.type !== 'api-response' || !event.data?.requestId) {
104+ return;
105+ }
106+
107+ const { requestId, error, response } = event.data;
108+ const pendingRequest = this.pendingRequests.get(requestId);
109+
110+ if (!pendingRequest) {
111+ return;
112+ }
113+
114+ this.pendingRequests.delete(requestId);
115+
116+ if (error) {
117+ pendingRequest.reject(new Error(error));
118+ } else {
119+ pendingRequest.resolve(response);
120+ }
121+ }
122+
123+ async callAPI(url) {
124+ const requestId = ++this.requestId;
125+
126+ const promise = new Promise((resolve, reject) => {
127+ this.pendingRequests.set(requestId, { resolve, reject });
128+
129+ // Set a timeout to prevent hanging promises
130+ setTimeout(() => {
131+ if (this.pendingRequests.has(requestId)) {
132+ this.pendingRequests.delete(requestId);
133+ reject(new Error('Request timed out'));
134+ }
135+ }, 30000); // 30 second timeout
136+ });
137+
138+ // Send the request to parent
139+ window.parent.postMessage({
140+ type: 'api-request',
141+ requestId,
142+ url,
143+ method: 'GET'
144+ }, this.targetOrigin);
145+
146+ return promise;
147+ }
148+ }
149+
150+ const api = new APIClient();
151+
152+ // Height management
91153 let currentHeight = null;
92154 function updateParentHeight() {
93- // Borrowed from https://github.com/FaiblUG/setIframeHeight/blob/5509feb0f281251d03065991b1c5f451f40fa8bd/dist/set-iframe-height-child.js#L31C1-L31C220
94155 let newHeight = Math.min(
95156 Math.max(
96157 document.body.offsetHeight, document.documentElement.offsetHeight
@@ -103,7 +164,7 @@ <h1>API Explorer</h1>
103164 document.body.clientHeight, document.documentElement.clientHeight
104165 )
105166 )
106- );
167+ ) + 10 ;
107168 if (currentHeight === newHeight) {
108169 return;
109170 }
@@ -115,41 +176,26 @@ <h1>API Explorer</h1>
115176 }
116177
117178 updateParentHeight();
118-
119179 setInterval(updateParentHeight, 1000);
120180
121- function makeRequest() {
181+ async function makeRequest() {
122182 const url = document.getElementById('urlInput').value;
123183 const output = document.getElementById('output');
124184
125- // Send message to parent window
126- window.parent.postMessage({
127- type: 'api-request',
128- url: url,
129- method: 'GET'
130- }, '*');
131-
132185 output.textContent = 'Loading...';
133- }
134-
135- // Listen for response from parent
136- window.addEventListener('message', function(event) {
137- const output = document.getElementById('output');
138186
139- if (event.data.type === 'api-response') {
140- if (event.data.error) {
141- output.textContent = 'Error: ' + event.data.error;
142- output.classList.add('error');
143- } else {
144- output.textContent = JSON.stringify(event.data.response, null, 2);
145- output.classList.remove('error');
146- }
147- // Content has changed, update height
148- setTimeout(() => {
149- updateParentHeight();
150- }, 100);
187+ try {
188+ const response = await api.callAPI(url);
189+ output.textContent = JSON.stringify(response, null, 2);
190+ output.classList.remove('error');
191+ } catch (error) {
192+ output.textContent = 'Error: ' + error.message;
193+ output.classList.add('error');
151194 }
152- });
195+
196+ // Content has changed, update height
197+ setTimeout(updateParentHeight, 100);
198+ }
153199 SCUB
154200 </body>
155201 </html>
@@ -172,12 +218,14 @@ <h1>API Explorer</h1>
172218 // Send response back to iframe
173219 iframe . contentWindow . postMessage ( {
174220 type : 'api-response' ,
221+ requestId : event . data . requestId ,
175222 response : data
176223 } , '*' ) ;
177224 } catch ( error ) {
178225 // Send error back to iframe
179226 iframe . contentWindow . postMessage ( {
180227 type : 'api-response' ,
228+ requestId : event . data . requestId ,
181229 error : error . message
182230 } , '*' ) ;
183231 }
0 commit comments