Advanced iBooks Author HTML Widgets: Widget Events, Storing/Sending Data, and more

Widget Events when Opening / Closing Widgets

Using a Tumult Hype animation within a HTML Widget (in iBooks Author)
is very different from displaying it within a web page. Here are a few
things to consider when producing a widget:

  • When you play audio within a widget, it will keep playing even if the widget is closed.
  • You cannot pinch and drag on the widget. Pinching and dragging moves the widget area.
  • If you open a widget and go to a different scene, the document will
    remain right where you left if you close and reopen it. It will reset
    if the book is close, iBooks is closed (and shut down) or if the book
    has been closed for a certain amount of time.

To address some of these behaviors, Apple has exposed Widget
events
(also known as listeners) that are triggered when opening and
closing widgets. When exporting from Tumult Hype, AppleWidget.js is included in the head. The sample code included below can be added to the ‘head’ of your document to perform actions when closing or reopening
your widget:

<script>
    function myCallback(hypeDocument, element, event) {
    // Get the name of the document. This is also on line 5 of the generated javascript file. 
    window.DocumentName = hypeDocument.documentName();
    }

    if("HYPE_eventListeners" in window === false) {
    window.HYPE_eventListeners = Array();
    }
    window.HYPE_eventListeners.push({"type":"HypeDocumentLoad", "callback":myCallback});
</script>

<script type="text/javascript">
    widget.pauseAudioVisual = function() {
    // Go to the Quiet scene when closing a widget
    HYPE.documents[DocumentName].showSceneNamed("quietscene");
    };

    widget.didEnterWidgetMode = function() {
    // When reopening the widget, go back to the first scene
    HYPE.documents[DocumentName].showSceneNamed("firstscene");
    };
</script>

An example document can be downloaded here: advanced-widget-ibooks.zip (73.4 KB)

Updated June 9, 2016: Another demo document demonstrating when widget.pauseAudioVisual, widget.didEnterWidgetMode, and widget.willEnterWidgetMode trigger: ibookstest.zip (56.1 KB)

View this iBooks to see it in action: booktest.zip (1.0 MB)

Here’s an example book which contains a video showing how the ‘didEnterWidgetMode’ behaves as of May 12, 2016 in iBooks for iPad and iBooks for mac.

Override the Pinch Gesture

By default, the pinch gesture closes an open HTML Widget. To restrict
this behavior, add the following JavaScript in the head of your
document. You can edit the contents of the <head>...</head> of
your exported .html file by clicking on ‘Edit HTML Head’ in the
Document Inspector:

<script type="text/javascript"> 
  document.addEventListener('touchmove', function (e) {e.preventDefault();}, false);
  document.addEventListener('touchstart', function (e) {e.preventDefault();}, false);
  document.addEventListener('touchend', function (e) {e.preventDefault();}, false);
</script>

Pre-made Templates

Here’s a full screen template you can use for working around the X icon in the top left hand corner of the widget.

If you’re looking for more advanced widget creation tutorials, please check out this set of tutorials by Marielle Lange on Github. You’ll find code samples and documentation for widgets that
can store data that can be shared across widgets, widgets that can send data to a Google Form, and more. Another great resource is Jim McKeeth’s tutorials here.

Hi Daniel,

This was a very interesting post. I’m new to Hype and I don’t know anything about java script, but the sample code for “widget events opening and closing” may be what I’m looking for.

I’ve created a children’s book for iBooks using Hype to create the pages, which are exported as widgets. Each widget (page) has audio. In iBooks, if I swipe from one page to another before the audio from the previous page has stopped playing, the audio from the previous page will continue to play along with the audio of the current page. Since I know nothing about java, this code looks a bit intimidating. In iBooks, is it possible to have the audio of one widget (page) stop playing by selecting, in Hype, “On Scene Unload - Stop Sound”?

Hi Daniel and Jlam,

I’m also wondering if this code can be used to pause/stop audio from one widget when swiping to another Ibooks page containing another widget. It looks to me like this code only works when closing a widget (as opposed to swiping to another page) but any help would be appreciated.

For me audio is automatically stopped; perhaps you could provide a .hype and .iba file that reproduces it continuing to be played?

Regardless, I would expect this technique could be used to pause the audio. Step one would be to create a custom behavior that has actions to pause your audio. Then you’d probably modify pauseAudioVisual block to look like this to run the custom behavior:


widget.pauseAudioVisual = function() {
    HYPE.documents[DocumentName].triggerCustomBehaviorNamed("customBehaviorName");
};

Thanks so much for your reply Jonathan.

i’ve created a simple example that I’ve uploaded to show what i’m talking about - an iba file and 2 hype docs .

I didn’t include your suggested code yet but it might be able to be triggered by the page 2 ‘play audio’ button effectively stopping the page 1 audio when you trigger the page 2 audio. Ideally the page 1 audio would stop automatically when you swipe across to page 2, but I don’t know if this is possible.

iBookSoundBetweenPages.iba.zip (1.5 MB)
Page1.hype.zip (372.6 KB)
Page2.hype.zip (615.3 KB)

Ah, it appears the audio only plays through on macOS iBooks; iBooks on my iPad is what paused it.

The technique I described works - here’s page1 that will do the stopping of audio:

Page1-fixed.hype.zip (380.3 KB)

You’re totally correct - I test both on an iPad and macOS since I need to cater for both, but hadn’t noticed the audio only continues on macOS.

Your code in the head HTML combined with a custom pauseAudio behavior totally works - now audio automatically stops as soon as I swipe to another page in macOS - THANK YOU!

1 Like

Hi again, I have a new question. I have an iBook with many widgets, and on each page users can change which language is displayed using a local variable that determine which text box in which language is visible. The default language on each page is English.

I'd like to pass the variable from one widget to another so that if a user has set the language to be French on one widget on the iBook, on the next page of the iBook (the book is made up of single full screen widgets on each page) it remembers the language chosen.

This is the code I have which isn't working in the head, under the myCallback function. The variable value is based on the opacity of a button used to switch languages. I'm not sure how to pass what a variable's value is and check it when another widget reopens - any thoughts would be appreciated (and is this even possible)? Thank you. Happy to provide a simple example too if this helps.

HYPE.documents[DocumentName].var englishOn;

widget.languageSelected = function() {
   englishOn = HYPE.documents[DocumentName].getElementProperty(switchEN, 'opacity');
  // HYPE.documents[DocumentName].showSceneNamed("firstscene");
  	};

I haven't tried it, but looking online it appears that people state that localStorage is available between HTML Widgets in iBooks. Please see the docs:

Basically using this API you can set data that would be able to be read by any other book.

Example setter when a button is clicked that has a "data-language" : "en" pair in Additional HTML Attributes in the Identity Inspector:

function languageButtonClicked(hypeDocument, element, event) {
    var myLanguage = element.getAttribute("data-language"); // something like "en" which would be set in the Additional HTML Attributes under the language key
    window.localStorage.setItem("language", myLanguage);
}

Example fetch/setter:

function onSceneLoad(hypeDocument, element, event) {
    var myLanguage = window.localStorage.getItem("language");
    if(myLanguage == "en") {
        // do something to switch to english
    } else if(myLanguage == "de") {
        // do something to switch to german 
    }
}

You method works Jonathan - thank you so much! I'll have a play to see if I can get it to work with my setup which doesn't involve an Additional HTML Attribute - but have just made a quick example with your code and method that works perfectly!

1 Like

Oh, that's great to hear!

The HTML attribute isn't really required; I just thought it would be a simple way that didn't involve using/abusing an element's ID or Class Name but still tie what might be a button in your UI to a language choice.