|
2 | 2 | <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
3 | 3 |
|
4 | 4 | <meta charset="utf-8">
|
5 |
| -<meta name="generator" content="quarto-1.6.4"> |
| 5 | +<meta name="generator" content="quarto-1.6.25"> |
6 | 6 |
|
7 | 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
8 | 8 |
|
|
69 | 69 | <script src="../site_libs/quarto-html/tippy.umd.min.js"></script>
|
70 | 70 | <script src="../site_libs/quarto-html/anchor.min.js"></script>
|
71 | 71 | <link href="../site_libs/quarto-html/tippy.css" rel="stylesheet">
|
72 |
| -<link href="../site_libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles"> |
73 |
| -<link href="../site_libs/quarto-html/quarto-syntax-highlighting-dark.css" rel="prefetch" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles"> |
| 72 | +<link href="../site_libs/quarto-html/quarto-syntax-highlighting-018089954d508eae8a473f0b7f0491f0.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles"> |
| 73 | +<link href="../site_libs/quarto-html/quarto-syntax-highlighting-dark-5223d57514b7f3c9e8f675d86406bf62.css" rel="prefetch" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles"> |
74 | 74 | <script src="../site_libs/bootstrap/bootstrap.min.js"></script>
|
75 | 75 | <link href="../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
76 |
| -<link href="../site_libs/bootstrap/bootstrap.min.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light"> |
| 76 | +<link href="../site_libs/bootstrap/bootstrap-e127080b54728d29fe72abdaeabf2e9f.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light"> |
77 | 77 | <link href="../site_libs/bootstrap/bootstrap-dark.min.css" rel="prefetch" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
|
78 | 78 | <script id="quarto-search-options" type="application/json">{
|
79 | 79 | "location": "sidebar",
|
|
329 | 329 | max-height: 100%;
|
330 | 330 | margin: 0;
|
331 | 331 | padding: 0;
|
332 |
| -} |
| 332 | +} |
333 | 333 |
|
334 | 334 | /* Provide space to entries */
|
335 | 335 | .reveal div.qwebr-output-code-area pre div {
|
|
374 | 374 | overflow: scroll;
|
375 | 375 | }
|
376 | 376 |
|
| 377 | +iframe.qwebr-output-code-browse { |
| 378 | + width: 100%; |
| 379 | + |
| 380 | + /* |
| 381 | + TODO: How to make the height automatic according to the widget size, |
| 382 | + or respect the quarto code block options? |
| 383 | + */ |
| 384 | + min-height: 500px; |
| 385 | +} |
| 386 | + |
377 | 387 | </style>
|
378 | 388 | <script type="module">
|
379 | 389 | // Document level settings ----
|
|
401 | 411 | };
|
402 | 412 |
|
403 | 413 | // Store cell data
|
404 |
| -globalThis.qwebrCellDetails = [{"id":1,"options":{"message":"true","editor-word-wrap":"true","output":"true","classes":"","editor-font-scale":"1","dpi":72,"out-width":"700px","read-only":"false","editor-max-height":"","context":"interactive","fig-height":5,"warning":"true","out-height":"","results":"markup","comment":"","fig-width":7,"label":"unnamed-chunk-1","fig-cap":"","editor-quick-suggestions":"false","autorun":"false"},"code":"# Check to see if the function works\ndemorwasmbinary::in_webr()\n\n# View help documentation\n?demorwasmbinary::in_webr"}]; |
| 414 | +globalThis.qwebrCellDetails = [{"id":1,"code":"# Check to see if the function works\ndemorwasmbinary::in_webr()\n\n# View help documentation\n?demorwasmbinary::in_webr","options":{"fig-cap":"","message":"true","autorun":"false","editor-word-wrap":"true","fig-width":7,"out-width":"700px","classes":"","read-only":"false","results":"markup","label":"unnamed-chunk-1","dpi":72,"comment":"","context":"interactive","warning":"true","editor-quick-suggestions":"false","output":"true","editor-font-scale":"1","fig-height":5,"editor-max-height":"","out-height":""}}]; |
405 | 415 |
|
406 | 416 | </script>
|
407 | 417 | <script type="module">
|
|
1223 | 1233 | // Setup a shelter
|
1224 | 1234 | globalThis.mainWebRCodeShelter = await new mainWebR.Shelter();
|
1225 | 1235 |
|
1226 |
| - // Setup a pager to allow processing help documentation |
1227 |
| - await mainWebR.evalRVoid('webr::pager_install()'); |
| 1236 | + // Setup a pager to allow processing help documentation |
| 1237 | + await mainWebR.evalRVoid('webr::pager_install()'); |
| 1238 | + |
| 1239 | + // Setup a viewer to allow processing htmlwidgets. |
| 1240 | + // This might not be available in old webr version |
| 1241 | + await mainWebR.evalRVoid('try({ webr::viewer_install() })'); |
1228 | 1242 |
|
1229 | 1243 | // Override the existing install.packages() to use webr::install()
|
1230 |
| - await mainWebR.evalRVoid('webr::shim_install()'); |
| 1244 | + await mainWebR.evalRVoid('webr::shim_install()'); |
1231 | 1245 |
|
1232 | 1246 | // Specify the repositories to pull from
|
1233 | 1247 | // Note: webR does not use the `repos` option, but instead uses `webr_pkg_repos`
|
|
1265 | 1279 | return Object.keys(arr).length === 0;
|
1266 | 1280 | }
|
1267 | 1281 |
|
1268 |
| -// Global version of the Escape HTML function that converts HTML |
| 1282 | +// Global version of the Escape HTML function that converts HTML |
1269 | 1283 | // characters to their HTML entities.
|
1270 | 1284 | globalThis.qwebrEscapeHTMLCharacters = function(unsafe) {
|
1271 | 1285 | return unsafe
|
|
1274 | 1288 | .replace(/>/g, ">")
|
1275 | 1289 | .replace(/"/g, """)
|
1276 | 1290 | .replace(/'/g, "'");
|
1277 |
| - }; |
| 1291 | +}; |
1278 | 1292 |
|
1279 | 1293 | // Passthrough results
|
1280 | 1294 | globalThis.qwebrIdentity = function(x) {
|
|
1291 | 1305 | qwebrRCommandHistory.push(
|
1292 | 1306 | `# Ran code in ${options.label} at ${new Date().toLocaleString()} ----\n${codeToRun}`
|
1293 | 1307 | );
|
1294 |
| -} |
| 1308 | +}; |
1295 | 1309 |
|
1296 | 1310 | // Function to attach a download button onto the canvas
|
1297 | 1311 | // allowing the user to download the image.
|
|
1311 | 1325 | link.download = 'qwebr-canvas-image.png';
|
1312 | 1326 | link.click();
|
1313 | 1327 | });
|
1314 |
| - } |
1315 |
| - |
| 1328 | +} |
| 1329 | + |
1316 | 1330 |
|
1317 | 1331 | // Function to parse the pager results
|
1318 |
| -globalThis.qwebrParseTypePager = async function (msg) { |
| 1332 | +globalThis.qwebrParseTypePager = async function (msg) { |
1319 | 1333 |
|
1320 | 1334 | // Split out the event data
|
1321 |
| - const { path, title, deleteFile } = msg.data; |
| 1335 | + const { path, title, deleteFile } = msg.data; |
1322 | 1336 |
|
1323 | 1337 | // Process the pager data by reading the information from disk
|
1324 | 1338 | const paged_data = await mainWebR.FS.readFile(path).then((data) => {
|
|
1327 | 1341 |
|
1328 | 1342 | // Remove excessive backspace characters until none remain
|
1329 | 1343 | while(content.match(/.[\b]/)){
|
1330 |
| - content = content.replace(/.[\b]/g, ''); |
| 1344 | + content = content.replace(/.[\b]/g, ''); |
1331 | 1345 | }
|
1332 | 1346 |
|
1333 | 1347 | // Returned cleaned data
|
1334 | 1348 | return content;
|
1335 | 1349 | });
|
1336 | 1350 |
|
1337 | 1351 | // Unlink file if needed
|
1338 |
| - if (deleteFile) { |
1339 |
| - await mainWebR.FS.unlink(path); |
1340 |
| - } |
| 1352 | + if (deleteFile) { |
| 1353 | + await mainWebR.FS.unlink(path); |
| 1354 | + } |
1341 | 1355 |
|
1342 | 1356 | // Return extracted data with spaces
|
1343 | 1357 | return paged_data;
|
1344 |
| -} |
| 1358 | +}; |
| 1359 | + |
| 1360 | + |
| 1361 | +// Function to parse the browse results |
| 1362 | +globalThis.qwebrParseTypeBrowse = async function (msg) { |
| 1363 | + |
| 1364 | + // msg.type === "browse" |
| 1365 | + const path = msg.data.url; |
| 1366 | + |
| 1367 | + // Process the browse data by reading the information from disk |
| 1368 | + const browse_data = await mainWebR.FS.readFile(path).then((data) => { |
| 1369 | + // Obtain the file content |
| 1370 | + let content = new TextDecoder().decode(data); |
| 1371 | + |
| 1372 | + return content; |
| 1373 | + }); |
| 1374 | + |
| 1375 | + // Return extracted data as-is |
| 1376 | + return browse_data; |
| 1377 | +}; |
1345 | 1378 |
|
1346 | 1379 | // Function to run the code using webR and parse the output
|
1347 | 1380 | globalThis.qwebrComputeEngine = async function(
|
1348 |
| - codeToRun, |
1349 |
| - elements, |
| 1381 | + codeToRun, |
| 1382 | + elements, |
1350 | 1383 | options) {
|
1351 | 1384 |
|
1352 | 1385 | // Call into the R compute engine that persists within the document scope.
|
1353 |
| - // To be prepared for all scenarios, the following happens: |
| 1386 | + // To be prepared for all scenarios, the following happens: |
1354 | 1387 | // 1. We setup a canvas device to write to by making a namespace call into the {webr} package
|
1355 | 1388 | // 2. We use values inside of the options array to set the figure size.
|
1356 | 1389 | // 3. We capture the output stream information (STDOUT and STERR)
|
|
1370 | 1403 | processOutput = qwebrIdentity;
|
1371 | 1404 | }
|
1372 | 1405 |
|
1373 |
| - // ---- |
| 1406 | + // ---- |
1374 | 1407 | // Convert from Inches to Pixels by using DPI (dots per inch)
|
1375 | 1408 | // for bitmap devices (dpi * inches = pixels)
|
1376 |
| - let fig_width = options["fig-width"] * options["dpi"] |
1377 |
| - let fig_height = options["fig-height"] * options["dpi"] |
| 1409 | + let fig_width = options["fig-width"] * options["dpi"]; |
| 1410 | + let fig_height = options["fig-height"] * options["dpi"]; |
1378 | 1411 |
|
1379 | 1412 | // Initialize webR
|
1380 | 1413 | await mainWebR.init();
|
|
1386 | 1419 | captureConditions: false,
|
1387 | 1420 | // env: webR.objs.emptyEnv, // maintain a global environment for webR v0.2.0
|
1388 | 1421 | };
|
1389 |
| - |
| 1422 | + |
1390 | 1423 | // Determine if the browser supports OffScreen
|
1391 | 1424 | if (qwebrOffScreenCanvasSupport()) {
|
1392 | 1425 | // Mirror default options of webr::canvas()
|
|
1418 | 1451 |
|
1419 | 1452 | // Start attempting to parse the result data
|
1420 | 1453 | processResultOutput:try {
|
1421 |
| - |
| 1454 | + |
1422 | 1455 | // Avoid running through output processing
|
1423 |
| - if (options.results === "hide" || options.output === "false") { |
1424 |
| - break processResultOutput; |
| 1456 | + if (options.results === "hide" || options.output === "false") { |
| 1457 | + break processResultOutput; |
1425 | 1458 | }
|
1426 | 1459 |
|
1427 | 1460 | // Merge output streams of STDOUT and STDErr (messages and errors are combined.)
|
1428 |
| - // Require both `warning` and `message` to be true to display `STDErr`. |
| 1461 | + // Require both `warning` and `message` to be true to display `STDErr`. |
1429 | 1462 | const out = result.output
|
1430 | 1463 | .filter(
|
1431 |
| - evt => evt.type === "stdout" || |
1432 |
| - ( evt.type === "stderr" && (options.warning === "true" && options.message === "true")) |
| 1464 | + evt => evt.type === "stdout" || |
| 1465 | + ( evt.type === "stderr" && (options.warning === "true" && options.message === "true")) |
1433 | 1466 | )
|
1434 | 1467 | .map((evt, index) => {
|
1435 | 1468 | const className = `qwebr-output-code-${evt.type}`;
|
|
1441 | 1474 |
|
1442 | 1475 | // Clean the state
|
1443 | 1476 | // We're now able to process pager events.
|
1444 |
| - // As a result, we cannot maintain a true 1-to-1 output order |
| 1477 | + // As a result, we cannot maintain a true 1-to-1 output order |
1445 | 1478 | // without individually feeding each line
|
1446 | 1479 | const msgs = await mainWebR.flush();
|
1447 | 1480 |
|
1448 | 1481 | // Use `map` to process the filtered "pager" events asynchronously
|
1449 |
| - const pager = await Promise.all( |
1450 |
| - msgs.filter(msg => msg.type === 'pager').map( |
| 1482 | + const pager = []; |
| 1483 | + const browse = []; |
| 1484 | + |
| 1485 | + await Promise.all( |
| 1486 | + msgs.map( |
1451 | 1487 | async (msg) => {
|
1452 |
| - return await qwebrParseTypePager(msg); |
| 1488 | + |
| 1489 | + const msgType = msg.type || "unknown"; |
| 1490 | + |
| 1491 | + switch(msgType) { |
| 1492 | + case 'pager': |
| 1493 | + const pager_data = await qwebrParseTypePager(msg); |
| 1494 | + pager.push(pager_data); |
| 1495 | + break; |
| 1496 | + case 'browse': |
| 1497 | + const browse_data = await qwebrParseTypeBrowse(msg); |
| 1498 | + browse.push(browse_data); |
| 1499 | + break; |
| 1500 | + } |
| 1501 | + return; |
1453 | 1502 | }
|
1454 | 1503 | )
|
1455 | 1504 | );
|
|
1512 | 1561 | // Draw image onto Canvas
|
1513 | 1562 | const ctx = canvas.getContext("2d");
|
1514 | 1563 | ctx.drawImage(img, 0, 0, img.width, img.height);
|
1515 |
| - |
| 1564 | + |
1516 | 1565 | // Append canvas to figure output area
|
1517 | 1566 | figureElement.appendChild(canvas);
|
1518 | 1567 |
|
1519 | 1568 | });
|
1520 |
| - |
| 1569 | + |
1521 | 1570 | if (options['fig-cap']) {
|
1522 | 1571 | // Create figcaption element
|
1523 | 1572 | const figcaptionElement = document.createElement('figcaption');
|
1524 | 1573 | figcaptionElement.innerText = options['fig-cap'];
|
1525 | 1574 | // Append figcaption to figure
|
1526 |
| - figureElement.appendChild(figcaptionElement); |
| 1575 | + figureElement.appendChild(figcaptionElement); |
1527 | 1576 | }
|
1528 |
| - |
| 1577 | + |
1529 | 1578 | elements.outputGraphDiv.appendChild(figureElement);
|
1530 | 1579 |
|
1531 | 1580 | }
|
1532 | 1581 |
|
1533 | 1582 | // Display the pager data
|
1534 |
| - if (pager) { |
1535 |
| - // Use the `pre` element to preserve whitespace. |
1536 |
| - pager.forEach((paged_data, index) => { |
1537 |
| - let pre_pager = document.createElement("pre"); |
1538 |
| - pre_pager.innerText = paged_data; |
1539 |
| - pre_pager.classList.add("qwebr-output-code-pager"); |
1540 |
| - pre_pager.setAttribute("id", `qwebr-output-code-pager-editor-${elements.id}-result-${index + 1}`); |
1541 |
| - elements.outputCodeDiv.appendChild(pre_pager); |
1542 |
| - }); |
| 1583 | + if (pager.length > 0) { |
| 1584 | + // Use the `pre` element to preserve whitespace. |
| 1585 | + pager.forEach((paged_data, index) => { |
| 1586 | + const pre_pager = document.createElement("pre"); |
| 1587 | + pre_pager.innerText = paged_data; |
| 1588 | + pre_pager.classList.add("qwebr-output-code-pager"); |
| 1589 | + pre_pager.setAttribute("id", `qwebr-output-code-pager-editor-${elements.id}-result-${index + 1}`); |
| 1590 | + elements.outputCodeDiv.appendChild(pre_pager); |
| 1591 | + }); |
| 1592 | + } |
| 1593 | + |
| 1594 | + // Display the browse data |
| 1595 | + if (browse.length > 0) { |
| 1596 | + // Use the `pre` element to preserve whitespace. |
| 1597 | + browse.forEach((browse_data, index) => { |
| 1598 | + const iframe_browse = document.createElement('iframe'); |
| 1599 | + iframe_browse.classList.add("qwebr-output-code-browse"); |
| 1600 | + iframe_browse.setAttribute("id", `qwebr-output-code-browse-editor-${elements.id}-result-${index + 1}`); |
| 1601 | + iframe_browse.style.width = "100%"; |
| 1602 | + iframe_browse.style.minHeight = "500px"; |
| 1603 | + elements.outputCodeDiv.appendChild(iframe_browse); |
| 1604 | + |
| 1605 | + iframe_browse.contentWindow.document.open(); |
| 1606 | + iframe_browse.contentWindow.document.write(browse_data); |
| 1607 | + iframe_browse.contentWindow.document.close(); |
| 1608 | + }); |
1543 | 1609 | }
|
1544 | 1610 | } finally {
|
1545 | 1611 | // Clean up the remaining code
|
1546 | 1612 | mainWebRCodeShelter.purge();
|
1547 | 1613 | }
|
1548 |
| -} |
| 1614 | +}; |
1549 | 1615 |
|
1550 | 1616 | // Function to execute the code (accepts code as an argument)
|
1551 | 1617 | globalThis.qwebrExecuteCode = async function (
|
|
1555 | 1621 |
|
1556 | 1622 | // If options are not passed, we fall back on the bare minimum to handle the computation
|
1557 | 1623 | if (qwebrIsObjectEmpty(options)) {
|
1558 |
| - options = { |
1559 |
| - "context": "interactive", |
1560 |
| - "fig-width": 7, "fig-height": 5, |
1561 |
| - "out-width": "700px", "out-height": "", |
| 1624 | + options = { |
| 1625 | + "context": "interactive", |
| 1626 | + "fig-width": 7, "fig-height": 5, |
| 1627 | + "out-width": "700px", "out-height": "", |
1562 | 1628 | "dpi": 72,
|
1563 |
| - "results": "markup", |
| 1629 | + "results": "markup", |
1564 | 1630 | "warning": "true", "message": "true",
|
1565 | 1631 | };
|
1566 | 1632 | }
|
|
0 commit comments