readium / SDKLauncher-iOS

A small iOS application to serve as a launcher/testbed for the Readium SDK.
BSD 3-Clause "New" or "Revised" License
71 stars 47 forks source link

How to detect when page is loaded #53

Open rkwright opened 9 years ago

rkwright commented 9 years ago

From Tom Feldman: Hi all, I'm wondering if there is an api somewhere in the readium-shared-js or maybe even some standard javascript I can execute to know when javascript is done figuring out its paging routine. I was previously under the impression that a call to ReadiumSDK.reader.openPageNext() would result in a single callback request of pageDidChange when it was finished. If the spine item changes however, this callback is being requested more than once.

Here's a log of the requests:

2015-03-24 10:44:48.837 SDKLauncher-iOS[2135:209217] Request: file:///private/var/mobile/Containers/Bundle/Application/09A8C37D-532B-43BE-A752-EABE857331CB/SDKLauncher-iOS.app/reader.html
2015-03-24 10:44:49.114 SDKLauncher-iOS[2135:209217] Request: epubobjc:readerDidInitialize
2015-03-24 10:44:49.130 SDKLauncher-iOS[2135:209217] Request: about:blank
2015-03-24 10:44:49.210 SDKLauncher-iOS[2135:209217] Request: epubobjc:settingsDidApply
2015-03-24 10:44:49.233 SDKLauncher-iOS[2135:209217] Request: about:blank
2015-03-24 10:44:49.254 SDKLauncher-iOS[2135:209217] Request: http://127.0.0.1:55661/spi-ad.xhtml
2015-03-24 10:44:49.490 SDKLauncher-iOS[2135:209217] Request: epubobjc:pageDidChange?q={"isRightToLeft":false,"isFixedLayout":false,"spineItemCount":22,"openPages":[{"spineItemPageIndex":0,"spineItemPageCount":1,"idref":"spi_ad","spineItemIndex":1}],"canGoLeft_":false,"canGoRight_":true}
2015-03-24 10:44:54.911 SDKLauncher-iOS[2135:209217] Running open next script
2015-03-24 10:44:54.931 SDKLauncher-iOS[2135:209217] Request: about:blank
2015-03-24 10:44:54.937 SDKLauncher-iOS[2135:209217] Ran open next script
2015-03-24 10:44:54.965 SDKLauncher-iOS[2135:209217] Request: epubobjc:pageDidChange?q={"isRightToLeft":false,"isFixedLayout":false,"spineItemCount":22,"openPages":[{"spineItemPageIndex":0,"spineItemPageCount":1,"idref":"spi_ad","spineItemIndex":1}],"canGoLeft_":false,"canGoRight_":true}
2015-03-24 10:44:55.004 SDKLauncher-iOS[2135:209217] Request: http://127.0.0.1:55661/index.xhtml
2015-03-24 10:44:55.230 SDKLauncher-iOS[2135:209217] Request: epubobjc:pageDidChange?q={"isRightToLeft":false,"isFixedLayout":false,"spineItemCount":22,"openPages":[{"spineItemPageIndex":0,"spineItemPageCount":1,"idref":"id-id2442754","spineItemIndex":2}],"canGoLeft_":true,"canGoRight_":true}

You'll notice one call to the open next script and two pageDidChange events.

danielweck commented 9 years ago

@nodehack (Tom Feldman), this looks like the first event is a "ghost" of the first one (see the different idref). @JCCR (Juan Corona) I seem to remember you mentioning having come across the same issue recently, but maybe I am mistaken?

nodehack commented 9 years ago

Looks like readium fires a pageDidChange event when setting view settings. The code shows that each time you open a page..if the the view type did not change then we set the view settings. I think this is incorrect. We should only be setting view settings if the desired view type changes.

In reader_view.js :

function openPage(pageRequest, dir) {

        initViewForItem(pageRequest.spineItem, function(isViewChanged){

            if(!isViewChanged) {
                _currentView.setViewSettings(_viewerSettings);
            }

            _currentView.openPage(pageRequest, dir);
        });
    }

I think you only want to setViewSettings if the desired view type changed..so it should read:

function openPage(pageRequest, dir) {

        initViewForItem(pageRequest.spineItem, function(isViewChanged){

            if(isViewChanged) {
                _currentView.setViewSettings(_viewerSettings);
            }

            _currentView.openPage(pageRequest, dir);
        });
    }

Thoughts?

Edit: Actually..upon further investigation I'm not sure why we need that if statement. initViewForItem already calls setViewSettings if the view type did change.

jdempcy commented 9 years ago

I had to code around this issue. Here is my workaround, which I do not advise as a permanent solution, but may be of assistance.

My goal was to register an onclick event on particular elements like audio and video tags. I need to register the event handler any time the DOM is overwritten and I assume it has to be triggered by a PAGINATION_CHANGED ("pageDidChange") event.

I was getting the problem where hooking into this event resulted in multiple registrations of my event handlers, so a single click would fire a function multiple times.

My solution was to set a flag on the HTML root node itself, knowing that I only need to re-register the event handlers when the HTML node was destroyed. You can see my code here:

// Register event handlers inside the iframe when it is loaded
ReadiumSDK.on(ReadiumSDK.Events.READER_INITIALIZED, function () {
  ReadiumSDK.reader.on(ReadiumSDK.Events.PAGINATION_CHANGED, function () {
    var $iframe = $('iframe');
    var $htmlElement = $iframe.contents().find('html');
    if ($htmlElement.attr('isEventHandlingRegistered')) {
      // Do nothing
    } else {
      $htmlElement.attr('isEventHandlingRegistered', 'true');
      $iframe.contents().find('audio, video')
          .on('click', function (e) {
            // Event handler code
          });
    }
  }); // End PAGINATION_CHANGED
}); // End READER_INITIALIZED

If I could rely on PAGINATION_CHANGED to only be triggered when the DOM is overwritten, I could remove the code which checks for a flag. But the solution outlined above seems to be working okay for me for now, so it isn't a blocking issue for me.

danielweck commented 9 years ago

FYI, I filed an issue here about the duplicated event: https://github.com/readium/readium-shared-js/issues/172

@jdempcy note that PAGINATION_CHANGED is triggered every time a reflowable document's page layout is potentially (but strictly-speaking: not necessarily) altered, for example due to changes in font size, line spacing, margins, viewport resizing, etc. The CONTENT_DOCUMENT_LOADED event is probably the one to use, if you need to inject behaviours only once per HTML DOM.

nodehack commented 9 years ago

@danielweck I added ReadiumSDK.reader.on(ReadiumSDK.Events.CONTENT_DOCUMENT_LOADED, this.onDocumentLoaded, this); to host_app_feedback.js and it does fire however I don't get any feedback to the native side via the request.

My onDocumentLoaded looks like:

    this.onDocumentLoaded = function() {
        window.location.href = "epubobjc:documentLoaded";
    };

Am I missing a step somewhere?

danielweck commented 9 years ago

@nodehack to wire the event into a native behaviour, add some handling code here: https://github.com/readium/SDKLauncher-iOS/blob/master/Classes/EPubViewController.m#L593

NSString *s = @"epubobjc:";

    if ([url hasPrefix:s]) {
        url = [url substringFromIndex:s.length];

        if ([url isEqualToString:@"documentLoaded"]) {