Unpkg integration of mirador viewer not working within existDB app

161 views Asked by At

I have an app I'm translating from xslt-generated html to existDB, and I'd like to include a Mirador viewer.The example (modified for my purposes as shown below) works fine with a static html page, and if I take the html generated from my exist application and save it as a static html file then and put it in there it works fine as well. But if I attempt to use it within my exist app I get a very unstable version of the viewer, wherein any attempt to interact with it results in it eventually throwing an error (which I'll show below under the code). The minified code is not particularly helpful for troubleshooting purposes, so I was wondering if this is a situation others have run into and if so what they did to get around it. The germane bit of code is as follows, using the default Harvard manifest from the github example for testing:

<div id="viewer" allowfullscreen="allowfullscreen">
<script src="https://unpkg.com/mirador@latest/dist/mirador.min.js"></script>
<div id="mirador"></div>
<script type="text/javascript">
          const mirador = Mirador.viewer({
      "id": "mirador",
        "manifests": {
        "https://iiif.lib.harvard.edu/manifests/drs:48309543": {
          "provider": "Harvard University"
        }
      },
      "windows": [
        {
          "loadedManifest": "https://iiif.lib.harvard.edu/manifests/drs:48309543",
          "canvasIndex": 2,
          "thumbnailNavigationPosition": 'far-bottom'
        }
      ]
    });
        </script>
</div>

The error I'm getting on load is as follows:

TypeError: this.gridRef.current is null
    value ThumbnailNavigation.js:35
    React 2
    unstable_runWithPriority scheduler.production.min.js:19
    React 4
    unstable_runWithPriority scheduler.production.min.js:19
    React 4
    Redux 68
    Ks createPluggableStore.js:22
    e MiradorViewer.js:20
    viewer init.js:15
    <anonymous> index:15
react-dom.production.min.js:209:194
    React 9
    unstable_runWithPriority scheduler.production.min.js:19
    React 4
    Redux 68
    Ks createPluggableStore.js:22
    e MiradorViewer.js:20
    viewer init.js:15
    <anonymous> index:15

And any attempt to interact with the canvas results in a "too much recursion" error:

InternalError: too much recursion
    c getScrollParent.js:27
    G setupEventListeners.js:11
    G mirador.min.js:2  -- THIS REPEATS 123 TIMES --
react-dom.production.min.js:209:194
    React 9
        os
        payload
        gi
        Fa
        Es
        vc
        gc
        sc
        Xo
    unstable_runWithPriority scheduler.production.min.js:19
    React 5
        qo
        Xo
        Yo
        nc
        ya
    o useControlled.js:38
    we Tooltip.js:273
    current Tooltip.js:306
    (Async: setTimeout handler)
    Oe Tooltip.js:305
    React 12
        s
        p
        v
        v
        st
        it
        ct
        ht
        L
        F
        Jt
        Qt
    unstable_runWithPriority scheduler.production.min.js:19
    React 11
        Xt
        Zt
        Kt
        gt
        un
        es
        bc
        vc
        gc
        sc
        Xo
    unstable_runWithPriority scheduler.production.min.js:19
    React 2
        qo
        Xo
    W scheduler.production.min.js:17
    onmessage scheduler.production.min.js:14
    (Async: EventHandlerNonNull)
    <anonymous> scheduler.production.min.js:13
    Webpack 15
        o
        <anonymous>
        o
        <anonymous>
        o
        <anonymous>
        o
        <anonymous>
        o
        <anonymous>
        o
        <anonymous>
        <anonymous>
        <anonymous>
        <anonymous>
1

There are 1 answers

1
awagner On

Perhaps I have run into the same problem. IMO, the (my) problem is (was) a race condition and you have to make sure that you are addressing mirador only once it has finished initializing. The following works (for me) with mirador 2.7.0.

After some other approaches, I have now settled with loading mirador in an iframe and sending messages around:

my main reading view page, work.html, calls an exist-db template, template_work.html, that listens for a message about completion of mirador init and then calls a function miradorLoaded (line 564ff.):

<div id="parent">
    <iframe title="Mirador" id="Mirador" src="viewer.html" allowfullscreen="" style="min-width: 99%;height:99%;"/>
</div>
<script type="text/javascript">
    window.addEventListener('message', function(message){ if(message.data.type=="mirador.loaded"){ miradorLoaded(); } });
    function miradorLoaded() {
        /* here comes all the init stuff (see below) */
    };
</script>

viewer.html analogously does nothing but call template_viewer.html where the mirador js files are imported and the actual initialization happens in a $(document).ready() function: The Mirador object is created and configured and a message is sent back to the parent (line 95ff.):

$(document).ready(function(){
    MyObjects.myMirador = Mirador({
        /* set mirador config according to docs */
    });
    // Binding to events happens in the parent html file (which includes the present file in an iframe).
    MyObjects.myMirador.eventEmitter.subscribe('windowAdded', function(){
        window.parent.postMessage({ type:'mirador.loaded' },"*");
    });
});

That's the main approach. The miradorLoaded function in the main/parent template mentioned above then has a lot of things to do:

  • bind click event (on elements with pageNo class, that's my page numbers, and they include an attribute data-canvas holding the canvas for this page) for:
    • opening the popup holding the mirador iframe
    • pointing mirador to the correct image/canvas
    • update some elements of the viewer popup like title, buttons
  • bind click event on the popup's close icon to close the popup again
function miradorLoaded() {
    // Bind click event for opening viewer popup
    $(document).on('click', ".pageNo", function(event){
        event.preventDefault();                                                 
        $(this).blur();
        var $myMirador = document.getElementById('Mirador').contentWindow.MyObjects.myMirador;
        var $windowID = $myMirador.saveController.slots[0].window.id;

        // Configure viewer to go to the correct canvas
        var $targetCanvasID =  $(this).attr('data-canvas');
        $myMirador.eventEmitter.publish('SET_CURRENT_CANVAS_ID.'+ $windowID, $targetCanvasID);

        // Update some values of the dialog popup window
        $("#parent small").text($(this).attr('href'));         // update Viewer Heading
        $("#parent div").attr('title', $(this).attr('href'));  // update Viewer Title

        // Open the dialog window
        $("#parent").dialog('open');                           // show Viewer with jquery-ui Dialog method
    });

    // Bind click event for closing the popup
    $('#Mirador').contents().find("#close").on('click', function(){
        $("#parent").dialog('close');
    });
});

In the case of our application, there is more going on, leading to much more code in the respective functions than what is listed above: We keep track of viewer state with a viewer query parameter (updated when viewer opens or goes to a different canvas, triggers opening the viewer when a URL with such a parameter is called), I have added a "Sync" button (that navigates the full text to the viewer's current canvas) and a "Download pdf" button. But I hope that what is listed is sufficient to get you going...

This approach can be seen in action at, e.g. https://id.salamanca.school/texts/W0034?format=html (but we may soon launch an updated version which probably no longer relies on mirador but the (more lightweight) tify viewer).