-
Notifications
You must be signed in to change notification settings - Fork 50
Description
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:
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.