|
2 | 2 | <html lang="en"> |
3 | 3 | <head> |
4 | 4 | <meta charset="UTF-8"> |
| 5 | + <meta content="zpkJm8KWRDtbMaW0OX3DHdY1eYqJabj5V7ER9K0xKbA" name="google-site-verification"/> |
5 | 6 | <title>Plotly</title> |
6 | 7 | <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> |
7 | 8 | <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet"> |
8 | 9 | <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> |
9 | 10 | <script src="https://cdn.plot.ly/plotly-2.32.0.min.js"></script> |
| 11 | + <script src=" https://cdn.jsdelivr.net/npm/[email protected]/dist/moveable.min.js" ></script> |
| 12 | + <link href=" https://cdn.jsdelivr.net/npm/[email protected]/dist/moveable.min.css" rel=" stylesheet" > |
10 | 13 | <style> |
11 | 14 | body { |
12 | 15 | height: 100vh; |
|
26 | 29 | position: absolute; |
27 | 30 | top: 50px; |
28 | 31 | left: 50px; |
29 | | - resize: both; |
| 32 | + /*resize: both;*/ |
30 | 33 | overflow: hidden; |
31 | 34 | z-index: 0; |
32 | 35 | } |
|
70 | 73 | background-color: #f0f0f0; |
71 | 74 | } |
72 | 75 |
|
| 76 | + .btn:active { |
| 77 | + background-color: #e0e0e0; |
| 78 | + } |
| 79 | + |
73 | 80 | #add-box-btn { |
74 | 81 | position: absolute; |
75 | 82 | top: 10px; |
|
96 | 103 | .hidden { |
97 | 104 | display: none; |
98 | 105 | } |
| 106 | + |
| 107 | + .moveable-control { |
| 108 | + opacity: 0; |
| 109 | + } |
| 110 | + |
| 111 | + .moveable-line { |
| 112 | + opacity: 0; |
| 113 | + } |
| 114 | + |
| 115 | + .input-label-required { |
| 116 | + font-weight: bold; |
| 117 | + } |
| 118 | + |
| 119 | + .view-chart { |
| 120 | + box-shadow: 0 0 15px darkgrey; |
| 121 | + } |
99 | 122 | </style> |
100 | 123 | </head> |
101 | 124 | <body> |
102 | 125 | <i id="add-box-btn" class="fas fa-plus-circle fa-2x"></i> |
103 | 126 | <div id="overlay-text">Plotly JSON Visualizer</div> |
104 | 127 |
|
| 128 | +<script src="https://unpkg.com/moveable@latest/dist/moveable.min.js"></script> |
105 | 129 | <script> |
106 | 130 | let highestZIndex = 1000; |
107 | 131 |
|
|
123 | 147 | </div> |
124 | 148 | <div class="box-body"> |
125 | 149 | <div class="view-input-type"> |
126 | | - <label for="input-type">Input Type:</label> |
| 150 | + <label for="input-type" class="input-label-required">Input Type:</label> |
127 | 151 | <select id="input-type" class="form-control"> |
128 | 152 | <option value="json">JSON Text</option> |
129 | 153 | <option value="http">HTTP Request</option> |
130 | 154 | </select><br> |
131 | 155 | </div> |
132 | 156 | <div class="view-json"> |
133 | | - <label for="json-input">JSON Data:</label><br> |
134 | | - <textarea class="json-input form-control" rows="10"></textarea><br> |
| 157 | + <label for="form-json-input" class="input-label-required">JSON Data:</label><br> |
| 158 | + <textarea id="form-json-input" class="json-input form-control" rows="10"></textarea><br> |
135 | 159 | <button class="btn btn-beautify">Beautify JSON</button><br><br> |
136 | | - <label for="json-path-json">JSON Path (optional):</label><br> |
137 | | - <input type="text" class="json-path-json form-control" placeholder="e.g., data.chart"><br> |
| 160 | + <label for="form-json-path">JSON Path</label><br> |
| 161 | + <input id="form-json-path" type="text" class="json-path-json form-control" placeholder="data.chart"><br> |
138 | 162 | </div> |
139 | 163 | <div class="view-http hidden"> |
140 | 164 | <form id="requestForm"> |
141 | | - <label for="url">URL:</label> |
142 | | - <input type="text" class="http-url form-control" required><br> |
143 | | - <label for="method">HTTP Method:</label> |
144 | | - <select class="http-method form-control"> |
| 165 | + <label for="form-http-url" class="input-label-required">URL:</label> |
| 166 | + <input id="form-http-url" type="text" class="http-url form-control" required placeholder="http://localhost:8080/generate?key1=value1&key2=value2"><br> |
| 167 | + <label for="form-http-method" class="input-label-required">HTTP Method:</label> |
| 168 | + <select id="form-http-method" class="http-method form-control"> |
145 | 169 | <option value="GET">GET</option> |
146 | 170 | <option value="POST">POST</option> |
147 | 171 | </select><br> |
148 | | - <div class="paramsContainer"> |
149 | | - <label for="params">Query Parameters</label><br> |
150 | | - <textarea class="http-params form-control" placeholder="key1=value1&key2=value2"></textarea><br> |
| 172 | + <div class="headersContainer"> |
| 173 | + <label for="form-http-headers">Headers</label><br> |
| 174 | + <textarea id="form-http-headers" class="http-headers form-control" placeholder='Content-Type=application/json\nAccept=*/*'></textarea><br> |
151 | 175 | </div> |
152 | 176 | <div class="bodyContainer"> |
153 | | - <label for="body">Body</label><br> |
154 | | - <textarea class="http-body form-control" placeholder='{"key1": "value1", "key2": "value2"}'></textarea><br> |
| 177 | + <label for="form-http-body">Body</label><br> |
| 178 | + <textarea id="form-http-body" class="http-body form-control" placeholder='{"key1": "value1", "key2": "value2"}'></textarea><br> |
155 | 179 | </div> |
156 | 180 | <div class="jsonPathContainer"> |
157 | | - <label for="json-path-http">JSON Path (optional):</label><br> |
158 | | - <input type="text" class="json-path-http form-control" placeholder="e.g., data.chart"><br> |
| 181 | + <label for="form-http-json-path">JSON Path</label><br> |
| 182 | + <input id="form-http-json-path" type="text" class="json-path-http form-control" placeholder="data.chart"><br> |
159 | 183 | </div> |
160 | 184 | </form> |
161 | 185 | </div> |
|
166 | 190 | `; |
167 | 191 | document.body.appendChild(box); |
168 | 192 |
|
169 | | - setupDraggable(box); |
170 | | - setupResizable(box); |
| 193 | + const headerEl = box.querySelector('[data-drag-handle]'); |
| 194 | + |
| 195 | + const moveable = new Moveable(document.body, { |
| 196 | + target: box, |
| 197 | + resizable: true, |
| 198 | + keepRatio: false, |
| 199 | + edge: true, |
| 200 | + draggable: true, |
| 201 | + checkInput: true, |
| 202 | + checkSelect: true, |
| 203 | + }); |
| 204 | + |
| 205 | + box.querySelector('#input-type').addEventListener("mousedown", (e) => { |
| 206 | + e.stopPropagation(); |
| 207 | + }); |
| 208 | + |
| 209 | + box.querySelector('#form-http-method').addEventListener("mousedown", (e) => { |
| 210 | + e.stopPropagation(); |
| 211 | + }); |
| 212 | + |
| 213 | + moveable.on("drag", ({target, left, top}) => { |
| 214 | + target.style.left = `${left}px`; |
| 215 | + target.style.top = `${top}px`; |
| 216 | + }); |
| 217 | + |
| 218 | + moveable.on("resize", ({target, width, height, delta, direction}) => { |
| 219 | + const minWidth = 100; |
| 220 | + const minHeight = 200; |
| 221 | + |
| 222 | + if (width >= minWidth && height >= minHeight) { |
| 223 | + target.style.width = `${width}px`; |
| 224 | + target.style.height = `${height}px`; |
| 225 | + |
| 226 | + if (direction[0] === -1) { |
| 227 | + target.style.left = `${target.offsetLeft - delta[0]}px`; |
| 228 | + } |
| 229 | + if (direction[1] === -1) { |
| 230 | + target.style.top = `${target.offsetTop - delta[1]}px`; |
| 231 | + } |
| 232 | + } |
| 233 | + updatePlotSize(box); |
| 234 | + }); |
| 235 | + |
171 | 236 | setupZIndex(box); |
172 | 237 |
|
173 | 238 | box.querySelector('.btn-generate').addEventListener('click', () => reloadPlot(box)); |
174 | 239 | box.querySelector('.btn-back').addEventListener('click', () => switchToJson(box)); |
175 | | - box.querySelector('.btn-close').addEventListener('click', () => box.remove()); |
| 240 | + box.querySelector('.btn-close').addEventListener('click', () => { |
| 241 | + moveable.destroy(); // Clean up movable instance |
| 242 | + box.remove(); |
| 243 | + }); |
176 | 244 | box.querySelector('.btn-beautify').addEventListener('click', () => beautifyJson(box)); |
177 | 245 |
|
178 | 246 | box.querySelector('#input-type').addEventListener('change', (event) => switchInputType(box, event.target.value)); |
|
247 | 315 | } else { |
248 | 316 | const url = box.querySelector('.http-url').value; |
249 | 317 | const method = box.querySelector('.http-method').value; |
250 | | - const params = box.querySelector('.http-params').value; |
| 318 | + const headers = box.querySelector('.http-headers').value; |
251 | 319 | const body = box.querySelector('.http-body').value; |
252 | 320 |
|
253 | | - let fullUrl = url; |
254 | | - if (method === 'GET' && params) { |
255 | | - fullUrl += '?' + params; |
256 | | - } |
| 321 | + let parsedHeaders = parseHeaders(headers); |
257 | 322 |
|
258 | | - const response = await fetch(fullUrl, { |
| 323 | + const response = await fetch(url, { |
259 | 324 | method: method, |
260 | | - headers: { |
261 | | - 'Content-Type': 'application/json' |
262 | | - }, |
| 325 | + headers: parsedHeaders, |
263 | 326 | body: method === 'POST' ? body : null |
264 | 327 | }); |
265 | 328 | const jsonData = await response.json(); |
|
292 | 355 | } |
293 | 356 | } |
294 | 357 |
|
| 358 | + function parseHeaders(headerString) { |
| 359 | + const headers = {}; |
| 360 | + const lines = headerString.split('\n'); // Split the string by new lines |
| 361 | + |
| 362 | + lines.forEach(line => { |
| 363 | + const [key, value] = line.split('='); // Split each line by '=' |
| 364 | + if (key && value) { |
| 365 | + headers[key.trim()] = value.trim(); // Trim and add to headers object |
| 366 | + } |
| 367 | + }); |
| 368 | + |
| 369 | + return headers; |
| 370 | + } |
| 371 | + |
295 | 372 | function beautifyJson(box) { |
296 | 373 | const jsonInput = box.querySelector('.json-input'); |
297 | 374 | try { |
|
303 | 380 | } |
304 | 381 | } |
305 | 382 |
|
306 | | - function setupDraggable(box) { |
307 | | - const dragHandleEl = box.querySelector('[data-drag-handle]'); |
308 | | - let dragEl; |
309 | | - let lastPosition = {}; |
310 | | - |
311 | | - dragHandleEl.addEventListener('mousedown', dragStart); |
312 | | - dragHandleEl.addEventListener('mouseup', dragEnd); |
313 | | - dragHandleEl.addEventListener('mouseout', dragEnd); |
314 | | - |
315 | | - function dragStart(event) { |
316 | | - dragEl = box; |
317 | | - dragEl.style.setProperty('position', 'absolute'); |
318 | | - dragEl.style.setProperty('z-index', ++highestZIndex); // Bring to front on drag |
319 | | - lastPosition.left = event.clientX; |
320 | | - lastPosition.top = event.clientY; |
321 | | - dragHandleEl.classList.add('dragging'); |
322 | | - dragHandleEl.addEventListener('mousemove', dragMove); |
323 | | - } |
324 | | - |
325 | | - function dragMove(event) { |
326 | | - const dragElRect = dragEl.getBoundingClientRect(); |
327 | | - const newLeft = dragElRect.left + event.clientX - lastPosition.left; |
328 | | - const newTop = dragElRect.top + event.clientY - lastPosition.top; |
329 | | - dragEl.style.setProperty('left', `${newLeft}px`); |
330 | | - dragEl.style.setProperty('top', `${newTop}px`); |
331 | | - lastPosition.left = event.clientX; |
332 | | - lastPosition.top = event.clientY; |
333 | | - window.getSelection().removeAllRanges(); |
334 | | - updatePlotSize(box); |
335 | | - } |
336 | | - |
337 | | - function dragEnd() { |
338 | | - dragHandleEl.classList.remove('dragging'); |
339 | | - dragHandleEl.removeEventListener('mousemove', dragMove); |
340 | | - dragEl = null; |
341 | | - } |
342 | | - } |
343 | | - |
344 | | - function setupResizable(box) { |
345 | | - box.style.setProperty('resize', 'both'); |
346 | | - box.style.setProperty('overflow', 'hidden'); |
347 | | - |
348 | | - new ResizeObserver(() => { |
349 | | - updatePlotSize(box); |
350 | | - }).observe(box); |
351 | | - } |
352 | | - |
353 | 383 | function setupZIndex(box) { |
354 | 384 | box.addEventListener('mousedown', () => { |
355 | 385 | box.style.zIndex = ++highestZIndex; |
|
363 | 393 | } |
364 | 394 | } |
365 | 395 |
|
366 | | - // Add initial box on load |
367 | 396 | addNewBox(); |
368 | 397 | </script> |
369 | 398 | </body> |
|
0 commit comments