Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local Storage is not shared between pages

I have an epub3 book with 2 pages as well as a Table of Contents Page. I am viewing this book in Apple's Books, their inbuilt epub3 reader, on Mac OSX. The two pages appear side by side. The first page is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=500, height=600"/>
    </head>
    <body>

<p id="result"></p>

<script>
//<![CDATA[
  var current_page = "1";
  var other_page = "2";

  var t = 0;

  setInterval(function() {

    var d = new Date();
    var storage = localStorage; 

    storage.setItem("t"+ current_page, d.toLocaleString());

    document.getElementById("result").innerHTML = storage.getItem("t"+ current_page) +" "+storage.getItem("t"+ other_page);    
  }, 1000);

//]]>
</script>

    </body>
</html>

and the only thing different in my second page is:

  var current_page = "2";
  var other_page = "1";

So every second, Page 1 saves the current time to Local Storage as t1, and Page 2 does the same for the value t2. At the same time, both pages are reading both t1 and t2 from Local Storage, before their values are displayed to screen. However in ibooks, Page 1 only manages to display the current value for t2 when the page is reloaded - like when I flip to the Table of Contents and then back to Page 1 and 2 again. With something similar happening for Page 2 with regard to t1.

So at time 21:10:00, Page 1 might display:

08/09/19, 21:09:18 08/09/19, 21:08:58

and Page 2:

08/09/19, 21:09:22 08/09/19, 21:08:01

I also tried using Session Data but Page 1 can't ever read t2 and Page 2 can't read t1. So, this would be displayed instead:

08/09/19, 21:09:18 null

I can think of several applications where it would be very useful for Pages to communicate with each other.

For example, if a video is playing on one page, it would be useful to stop it if a video on another page is started. This would normally be done using Session Storage. This is related to my own use case and the reason I started exploring this problem.

Likewise, if the user is asked on Page 1 to enters the name of the main character of the story, then that entry should appear immediately on Page 2 once it is entered.

Is there any other way for Pages to communicate with each other in epub3 other than Local or Session Storage?

like image 312
Baz Avatar asked Mar 04 '23 01:03

Baz


1 Answers

I dont know epub3 and dont have a MAC to test, but here are four possible solutions that come to my mind:

Cookies

It is not as performant as localStorage for that use-case, but if you dont have many options, better that than nothing.

Functions to create, read and delete cookies (Credits to https://stackoverflow.com/a/28230846/9150652):

function setCookie(name,value,days) {
    var expires = "";
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days*24*60*60*1000));
        expires = "; expires=" + date.toUTCString();
    }
    document.cookie = name + "=" + (value || "")  + expires + "; path=/";
}
function getCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}
function eraseCookie(name) {   
    document.cookie = name+'=; Max-Age=-99999999;';  
}

Usage for your example:

<script>
//<![CDATA[
  var current_page = "1";
  var other_page = "2";

  var t = 0;

  setInterval(function() {

    var d = new Date();

    setCookie("t"+ current_page, d.toLocaleString(), 100); // 100 days

    document.getElementById("result").innerHTML = getCookie("t"+ current_page) +" "+getCookie("t"+ other_page);    
  }, 1000);

//]]>
</script>

BroadcastChannel

BroadcastChannel is a very new functionality, so it might not be supported by the "Books" app. But here is a concept:

<script>
//<![CDATA[
  var broadcaster = new BroadcastChannel('test');
  var current_page = "1";
  var other_page = "2";

  var t = 0;

  setInterval(function() {

    var d = new Date();

    // Send message to all other tabs with BroadcastChannel('test')
    bc.postMessage({
        senderPage: "t"+ current_page,
        date: d.toLocaleString()
    });
  }, 1000);

  broadcaster.onmessage = (result) => {
      if(result.senderPage == "t"+ other_page) { // If the message is from the other page
        // Set HTML to current date + sent Date from other page
        var d = new Date();
        document.getElementById("result").innerHTML = d.toLocaleString() +" "+result.date;    
      }
  };

//]]>
</script>

Some sort of Backend

If none of the above works, you probably have no other option, than to use some sort of backend, to provide and save the data

If it is just for you, I suggest you to use a free tier of Firebase or MongoDB Atlas, as they both provide quite some value on their free tier.

If you do it with a Backend, it could be done with something like this:

<script>
//<![CDATA[
  var current_page = "1";
  var other_page = "2";
  var lastLocalDate = new Date();
  const serverUrl = "http://someUrl.com/endpoint/"

  // Gets the latest date of the other page via ajax
  function getUpdate() {
    var xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == XMLHttpRequest.DONE) {
            // If successful, update HTML
            if (xmlhttp.status == 200) {
                document.getElementById("result").innerHTML = lastLocalDate.toLocaleString() +" "+xhr.responseText;
            }

            // Update the date of this page anyways
            sendUpdate();
        }
    };

    // GET request with parameter requestingPage, which is the other page
    xmlhttp.open("GET", serverUrl, true);
    xmlhttp.send(`requestingPage=${other_page}`);
  }

  // Sends the current date of this page to the webserver
  function sendUpdate() {
    var xmlhttp = new XMLHttpRequest();

    // No need to check if successful, just update the page again
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == XMLHttpRequest.DONE) {
            getUpdate();
        }
    };

    lastLocalDate = new Date();

    // POST request with parameters page and date
    xmlhttp.open("POST", serverUrl, true);
    xmlhttp.send(`sendingPage=${current_page}&data=${lastLocalDate.toLocaleString()}`);
  }

  // Start with sending an update (so that lastLocalDate is at least sent once to the server)
  sendUpdate();
//]]>
</script>

And some methods in your backend that need to look something like this (note that this is not valid code in any language):

@GET
function getDate(requestingPageId)
    find latest entry with page.id == requestingPageId
    return page.date

@POST
function saveDate(savingPage, savingDate)
    store new page element with 
        page.id = savingPage
        page.date = savingDate

And a collection in your database looking like this:

[
    {
        id: 1,
        date: "date"
    },{
        id: 2,
        date: "date"
    },{
        id: 2,
        date: "date"
    },{
        id: 1,
        date: "date"
    },

    // ...
]

Window References

If the Books app opens the second tab from the first tab, it might be worth to look into:

like image 96
MauriceNino Avatar answered Mar 09 '23 00:03

MauriceNino