Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript / JQuery save location in epub

I'm trying to build an epub reader for android, every thing is fine and working as expected, BUT the problem I cannot solve is saving last seen page (or save position for bookmark).

A little background:

I use css multi column to show epub, columnWidth is set to windowWidth and columnHeight is set to windowHeight. To ensure each column will fill the entire screen.

Currently for saving position I pre process the html and wrap each element with a div including a specific id that represents the section number and tag position. For example a <p> tag after process would be like this:

<div id="id__1__8"><p>some text</p></div>

The id__1__8 represents that this text belongs to section 1 and it is the 8th element in that body.

I have a full list of these ids, and for saving position I use jQuery to compare left of the current column with left of every id so the nearest id will be found and I know that this page belongs to where in epub.

The next step is to find offset (suppose a p tag that fills 7 pages). With offset I know that I must load the 8th element of section 1 and go to page 5.

Look at the function in jQuery: (for finding nearest element and offset)

jqGetLastPosition = function(ids)
    {
        var tempColumn = _column; // _column is current page that is showing
        if(tempColumn < 0)
        {
            tempColumn = -1 * tempColumn;
        }
        var realIds = ids.split("|");
        var columnLeft = (tempColumn * (_windowWidth + _columnGap));
        var currentLeft;
        var currId = "#" + realIds[0];
        var nearestId = realIds[0] + "__0";
        var minDistance = 1000000;
        var tempDistance = 0;
        var exactColumn = 0;
        for(i=0; i<realIds.length; i++)
        {
            try
            {
                currId = "#" + realIds[i];
                currentLeft = $(currId).position().left;
                if(currentLeft < 0)
                {
                    currentLeft = -1 * currentLeft;
                }
                tempDistance = columnLeft - currentLeft;
                if(tempDistance < 0)
                {
                    //this id is after this page
                    continue;
                }
                else if(tempDistance < minDistance)
                {
                    minDistance = tempDistance;
                    exactColumn = Math.floor(minDistance/(_windowWidth + _columnGap)); //this must compute the offset pages after nearest element
                    nearestId = realIds[i] + "__" + exactColumn;
                }
            }
            catch(e)
            {
            }
        }

        jsSaveLastLocation(nearestId);
    };

This code works fine for most of situations where page offset is zero, like id__1__8__0.

The problem arises when there is offset, the offset page cannot be computed correctly, I can see that there is one page offset, but this code gives me 0 offset, or when there is 9 page offset it gives me 4.

So what is the problem with this code?

Or am I wrong doing this for saving location?

Is there any better method?

UPDATE:

If i add div before any tag like <div id="id__1__8"></div><p>some text</p> the result will be accurate in 90% of the time. so the updated question will be How achieve this goal (saving position in epub) with 100% accuracy?

UPDATE 2:

I'm putting the div for every elements eg. head, p, link, img ....

Is there any possibility that this makes the problem??

UPDATE 3:

I finally find what is causing the problem. consider a situation where the nearest element to the current page start at the middle of the previous page, I save the id of this element and the offset would be 1. when i want to load the saved location, the element load at top of the page, SO a little shift in text would happen, in the below image i show what is happening.

any idea would be appreciated

enter image description here

UPDATE 4:

CSS:

#container {
    width: 100%;
    height: 98%;
    overflow: hidden;
    }
    #content {
    position: relative;
    height: 98%;
    -moz-column-width: 200px;
    -webkit-column-width: 200px;
    column-width: 200px;
    -moz-column-gap: 1px;
    -webkit-column-gap: 1px;
    column-gap: 1px;
    }
    img {
    max-width: 100%;
    max-height: 100%;
    display:inline-block;
    -webkit-column-break-inside : avoid;
    }

the <span id=\"endMarker\"></span> will add to the end of body so i have a marker at the end of html content.

the Jquery:

var _column = 0;
var _columnCount = 0;
var _windowWidth;
var _windowHeight;
var rtl = 0;

$(function() {
    _columnWidth = $('#container').width();
    _windowWidth = $('#container').width();
    _windowHeight = $('#container').height();
    $('#content').css('-webkit-column-width', _windowWidth);
    $('#content').css('-moz-column-width', _windowWidth);
    $('#content').css('column-width', _windowWidth);

    $(document).ready(function(){
    $(window).load(function(){
        _columnCount = Math.floor($('#endMarker').position().left/(_windowWidth + _columnGap));
        if(_columnCount < 0)
        {
            rtl = 1;
            _columnCount = (_columnCount * -1);// + 2;
            informRTL(rtl); //inform the java part that this doc is right to left
        }
        else
        {
            informRTL(rtl);
        }
        reportNumberOfPage(_columnCount); // this will report to java part
    });
    });

    setColumn = function(i) {
        if(rtl == 1)
        {
            _column = (i * -1);
        }
        else
        {
            _column = i;
        }

        $('#content').css({"-webkit-transform":"translate(" + (-1 * _column * (_windowWidth + _columnGap)) + "px,0px)"});
    }    

    setColumn(0); //set the showing column to first

    nextPage = function() {
        if (_column==_columnCount -1 || (-1*_column)==_columnCount -1)
            informEndPage();
        else
            {           
                if(rtl == 1)
                {
                    _column = _column-1;
                    $('#content').css({"-webkit-transform":"translate(" + (-1 * _column * (_windowWidth + _columnGap)) + "px,0px)"});                   
                }
                else
                {
                    _column = _column+1;
                    $('#content').css({"-webkit-transform":"translate(" + (-1 * _column * (_windowWidth + _columnGap)) + "px,0px)"});                                                   
                }           
            }           
    };

    prevPage = function() {
        if (0==_column) 
            informStartPage();
        else
            {           
                if(rtl == 1)
                {
                    _column = _column+1;                    
                    $('#content').css({"-webkit-transform":"translate(" + (-1 * _column * (_windowWidth + _columnGap)) + "px,0px)"});
                    updateCurrentPageText((_column * -1));
                }
                else
                {
                    _column = _column-1;                    
                    $('#content').css({"-webkit-transform":"translate(" + (-1 * _column * (_windowWidth + _columnGap)) + "px,0px)"});
                    updateCurrentPageText(_column);
                }                           
            }        
    };

    //this function add more html content to the end of current body
    addString = function(s)
    {
        $(s).insertBefore('#endMarker');
        $(window).load(addStringReport());
    };

    addStringReport = function()
    {
        _columnCount = Math.floor($('#endMarker').position().left/(_windowWidth + _columnGap));
        if(_columnCount == 0)
        {
            requestMorePage();
        }
        if(_columnCount < 0)
        {
            rtl = 1;
            _columnCount = (_columnCount * -1);
        }
        nextPage();
        reportNumberOfPage(_columnCount);
    }

    //this function add more html content to the first of body
    addStringToFirst = function(s)
    {
        $('#content').prepend(s);
        $(window).load(addStringToFirstReport());
    }

    addStringToFirstReport = function()
    {
        maxColumn = Math.floor($('#endMarker').position().left/(_windowWidth + _columnGap));
        if(maxColumn < 0)
        {
            rtl = 1;
            maxColumn = (maxColumn * -1);
            _column = (maxColumn - _columnCount + _column);
        }
        else
        {
            _column = maxColumn - _columnCount + _column;
        }

        _columnCount = maxColumn;
        setColumn(_column);
        reportNumberOfPage(_columnCount);
    }

this is almost all of my code, if you need more please let me know.

like image 356
mehdok Avatar asked Apr 07 '15 07:04

mehdok


1 Answers

I think I understand what you are asking. Are you trying to create a bunch of "fake" pages that are all one HTML file? I created a working example of what I think you want. It works by updating location.hash with the current section as you scroll. When you reload it should appear on the same section ("fake" page). You need to download this for it to run properly so I put it in a gist.

This is because Stackoverflow and JSFiddle fun the code in a sandbox where location.hash can not be updated.

This will update your browser history every time you scroll to a new page. I figured this was annoying so I have HTML5 push state setup. This will only work on a web server but every time you scroll to a new page it will replace the current url (and history) with the "fake" page in view so you do not have an infinite history. The code I posted will run locally on your computer or on a webserver but it will run better on a web server.

Gist: https://gist.github.com/Christianjuth/a4e6fad50da54818bbc3

Code:

$(document).scroll(function() {

  //get current section
  var $currentPage = $('.pages > section:onScreen').first();
  var $title = $('.pages > section:onScreen').first().find('h1').first();

  //update url
  if (history && history.pushState && location.protocol !== "file:") {
    history.pushState({}, $title.text(), "/#" + $currentPage.attr('id'));
  } else {
    location.hash = $currentPage.attr('id');
  }
});
html,
body {
  margin: 0;
  padding: 0;
  height: 101%;
  width: 100%;
}
*,
*:before,
*:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
.pages {
  display: inline-block;
  width: 100%;
  height: 100%;
}
.pages > section {
  display: inline-block;
  min-height: 100%;
  padding: 10px;
  font-family: Georgia;
}
.pages > section:nth-child(even) {
  background-color: #000;
  color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://benpickles.github.io/onScreen/jquery.onscreen.js"></script>

<div class="pages">
  <section id="html5">
    <h1>HTML5</h1>

    <p>HTML5 is a core technology markup language of the Internet used for structuring and presenting content for the World Wide Web. As of October 2014 this is the final and complete fifth revision of the HTML standard of the World Wide Web Consortium (W3C).[3]
      The previous version, HTML 4, was standardised in 1997.</p>
    <p>Its core aims have been to improve the language with support for the latest multimedia while keeping it easily readable by humans and consistently understood by computers and devices (web browsers, parsers, etc.). HTML5 is intended to subsume not
      only HTML 4, but also XHTML 1 and DOM Level 2 HTML.</p>
  </section>
  <section id="css3">
    <h1>CSS3</h1>

    <p>Cascading Style Sheets (CSS) is a style sheet language used for describing the look and formatting of a document written in a markup language. While most often used to change the style of web pages and user interfaces written in HTML and XHTML, the
      language can be applied to any kind of XML document, including plain XML, SVG and XUL. Along with HTML and JavaScript, CSS is a cornerstone technology used by most websites to create visually engaging webpages, user interfaces for web applications,
      and user interfaces for many mobile applications.</p>
  </section>
  <section id="bootstrap">
    <h1>Bootstrap</h1>

    <p>Bootstrap is a free and open-source collection of tools for creating websites and web applications. It contains HTML- and CSS-based design templates for typography, forms, buttons, navigation and other interface components, as well as optional JavaScript
      extensions. The bootstrap framework aims to ease web development.</p>
    <p>Bootstrap is a front end, that is an interface between the user and the server-side code which resides on the "back end" or server. And it is a web application framework, that is a software framework which is designed to support the development of
      dynamic websites and web applications.</p>
  </section>
</div>
like image 116
Christian J Avatar answered Sep 28 '22 12:09

Christian J