Skip to content

JS error when loading embedded HTML output on Anywidget #369

@kylebarron

Description

@kylebarron

It's not clear that the ultimate issue here is in Anywidget, but I figured it would be ok to start with an issue here. I'm making lonboard as a fast map rendering library and widget. It would be nice to support standalone HTML export and nbconvert conversion to HTML. When I create a minimal map, e.g. with:

import geopandas as gpd
from lonboard import viz
import ipywidgets.embed

gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))

map_ = viz(gdf, radius_min_pixels=10)
map_

ipywidgets.embed.embed_minimal_html('minimal.html', views=[map_], drop_defaults=False)

The map renders fine within Jupyter Notebook:
image

But loading the exported HTML fails with

TypeError: Cannot read properties of undefined (reading 'prototype')
    at enhancePointerEventInput (cd357310-fde6-48b7-a3a9-c8ad15fa6425:65439:41)
    at cd357310-fde6-48b7-a3a9-c8ad15fa6425:65475:1

That's specifically coming from a bundled library that's a dependency of mine (via deckgl).

// node_modules/mjolnir.js/dist/esm/utils/hammer.browser.js
var hammerjs = __toESM(require_hammer());

...

// node_modules/mjolnir.js/dist/esm/utils/hammer.browser.js
enhancePointerEventInput(hammerjs.PointerEventInput);

...

function enhancePointerEventInput(PointerEventInput2) {
  const oldHandler = PointerEventInput2.prototype.handler;
  PointerEventInput2.prototype.handler = function handler(ev) {
    const store = this.store;
    if (ev.button > 0 && ev.type === "pointerdown") {
      if (!some(store, (e) => e.pointerId === ev.pointerId)) {
        store.push(ev);
      }
    }
    oldHandler.call(this, ev);
  };
}

It's weird that this works fine in the notebook but not standalone. My hunch is that there's a difference in how the JS gets run inside the notebook vs as a standalone file.

If we look at what ipywidgets.embed.embed_minimal_html does, it's described here and essentially creates:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>IPyWidget export</title>
</head>
<body>


<!-- Load require.js. Delete this if your page already loads require.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager@^1.0.1/dist/embed-amd.js" crossorigin="anonymous"></script>

<script type="application/vnd.jupyter.widget-state+json">
{
  "version_major": 2,
  "version_minor": 0,
...

So it uses standard script tags for require.js and @jupyter-widgets/html-manager as well as for the custom widget state. I imagine there must be some difference in the above generated HTML that means the anywidget code isn't getting run as ESM...? (I'm definitely not an expert on this part of JS).

As repro, I'm attaching a zipfile with minimal.ipynb that has the same Python code as above, and minimal.html with the export. This is using lonboard 0.2.0 but with minify set to false so the exported HTML can be somewhat readable.

minimal.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions