diff --git a/ChangeLog b/ChangeLog index 9c4ba08..6309d52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ Developing v0.9 +* Lazy loading of entire pages * Licensed changed - Additional terms for usage in online services - Remove GPLv2 diff --git a/share/manifest b/share/manifest index a3fbe8c..8b4401d 100644 --- a/share/manifest +++ b/share/manifest @@ -49,7 +49,7 @@ pdf2htmlEX.defaultViewer = new pdf2htmlEX.Viewer({ """ -# The sidbar +# The sidebar # By default this is hidden, pdf2htmlEX.js will add the 'opened' class if it is not empty # You can add a class 'opened' here if you want it always opened or you don't use pdf2htmlEX.js # e.g. diff --git a/share/pdf2htmlEX.js.in b/share/pdf2htmlEX.js.in old mode 100644 new mode 100755 index e157418..3eb2e05 --- a/share/pdf2htmlEX.js.in +++ b/share/pdf2htmlEX.js.in @@ -18,6 +18,9 @@ var pdf2htmlEX = (function(){ link : '@CSS_LINK_CN@', __dummy__ : 'no comma' }; + var PAGES_TO_PRELOAD = 3; + + var pages_loading = {}; var pdf2htmlEX = new Object(); @@ -40,24 +43,36 @@ var pdf2htmlEX = (function(){ var Page = function(page, container) { if(page == undefined) return; + this.loaded = false; this.p = $(page); + + this.container = container; + this.n = parseInt(this.p.attr('data-page-no'), 16); this.b = $('.'+CSS_CLASS_NAMES['page_content_box'], this.p); + this.d = this.p.parents('.'+CSS_CLASS_NAMES['page_decoration']); - /* - * scale ratios - * - * default_r : the first one - * set_r : last set - * cur_r : currently using - */ - this.default_r = this.set_r = this.cur_r = this.p.height() / this.b.height(); + this.h = this.p.height(); // Need to make rescale work when page_content_box is not loaded, yet + this.w = this.p.width(); - this.data = JSON.parse($($('.'+CSS_CLASS_NAMES['page_data'], this.p)[0]).attr('data-data')); + // if page is loaded + if (this.b[0]) { + /* + * scale ratios + * + * default_r : the first one + * set_r : last set + * cur_r : currently using + */ + this.default_r = this.set_r = this.cur_r = this.p.height() / this.b.height(); - this.ctm = this.data.ctm; - this.ictm = invert(this.ctm); - this.container = container; + this.data = JSON.parse($($('.'+CSS_CLASS_NAMES['page_data'], this.p)[0]).attr('data-data')); + + this.ctm = this.data.ctm; + this.ictm = invert(this.ctm); + + this.loaded = true; + } }; $.extend(Page.prototype, { /* hide & show are for contents, the page frame is still there */ @@ -67,6 +82,7 @@ var pdf2htmlEX = (function(){ show : function(){ if(Math.abs(this.set_r - this.cur_r) > EPS) { this.cur_r = this.set_r; + //TODO make it cross-browser compliant this.b.css('transform', 'scale('+this.cur_r.toFixed(3)+')'); } this.b.addClass('opened'); @@ -116,7 +132,6 @@ var pdf2htmlEX = (function(){ this.container_id = config['container_id']; this.sidebar_id = config['sidebar_id']; this.outline_id = config['outline_id']; - this.page_urls = config['page_urls']; this.init_before_loading_content(); var _ = this; @@ -141,26 +156,22 @@ var pdf2htmlEX = (function(){ if(this.outline.children().length > 0) { this.sidebar.addClass('opened'); } - + this.find_pages(); // register schedule rendering var _ = this; this.container.scroll(function(){ _.schedule_render(); }); - //this.zoom_fixer(); + this.zoom_fixer(); // handle links this.container.add(this.outline).on('click', '.'+CSS_CLASS_NAMES['link'], this, this.link_handler); - // disable background image draging - $('img', this.container).on('dragstart', function(e){return false;}); - this.render(); - // load split pages - // has no effect if --split-pages is 0 - this.load_page(0); + // Trigger documentLoaded event + $(this.container).trigger("documentLoaded"); }, find_pages : function() { var new_pages = new Array(); @@ -172,19 +183,54 @@ var pdf2htmlEX = (function(){ } this.pages = new_pages; }, - load_page : function(idx) { - if(idx < this.page_urls.length){ - var _ = this; + load_page : function(idx, pages_to_preload, successCallback) { + if (this.pages[idx+1].loaded) + return; // Page is loaded + + if (pages_loading[idx]) + return; // Page is already loading + + var page_no_hex = (idx+1).toString(16); + var $pf = $(this.container).find('#' + CSS_CLASS_NAMES['page_frame'] + page_no_hex); + if($pf.length == 0) + return; // Page does not exist + + var _ = this; + + var url = $pf.data('page-url'); + if (url && url.length > 0) { + pages_loading[idx] = true; // Set semaphore + $.ajax({ - url: this.page_urls[idx], + url: url, dataType: 'text' }).done(function(data){ - $('#'+_.container_id).append(data); - _.find_pages(); - _.schedule_render(); - _.load_page(idx+1); - }); + _.pages[idx+1].p.parent().replaceWith(data); // pages index starts from 1 + + var $new_pf = $(_.container).find('#' + CSS_CLASS_NAMES['page_frame'] + page_no_hex); + _.pages[idx+1] = new Page($new_pf, _.container); + _.pages[idx+1].rescale(_.scale); + _.schedule_render(); + + // Event fired when page is loaded. Please notice that it's not necessarily rendered (see event pageShown). + _.container.trigger("pageLoaded", idx+1); + + // disable background image dragging + $new_pf.find('.'+CSS_CLASS_NAMES['background_image']).on('dragstart', function(e){return false;}); + + // Reset loading token + pages_loading[idx] = undefined; + + if (successCallback) successCallback(); + }); } + // Concurrent prefetch of the next pages + if (pages_to_preload === undefined) + pages_to_preload = PAGES_TO_PRELOAD; + + if (--pages_to_preload > 0) + _.load_page(idx+1, pages_to_preload); + }, pre_hide_pages : function() { /* pages might have not been loaded yet, so add a CSS rule */ @@ -211,7 +257,11 @@ var pdf2htmlEX = (function(){ for(var i in pl) { var p = pl[i]; if(p.is_nearly_visible()){ - p.show(); + if (p.loaded) { + p.show(); + this.container.trigger("pageShown", i); + } else + this.load_page(p.n - 1); // load_page index starts from 0 } else { p.hide(); } @@ -284,7 +334,6 @@ var pdf2htmlEX = (function(){ }, link_handler : function (e) { - console.log('here'); var _ = e.data; var t = $(e.currentTarget); @@ -345,11 +394,26 @@ var pdf2htmlEX = (function(){ } if(ok) { - pos = transform(target_page.ctm, pos); - if(upside_down) { - pos[1] = target_page.height() - pos[1]; + if (target_page.loaded) { + pos = transform(target_page.ctm, pos); + if(upside_down) { + pos[1] = target_page.height() - pos[1]; + } + _.scroll_to(detail[0], pos); + } else { + // Scroll to the exact position once loaded. + _.load_page(target_page.n - 1, 1, function() { // load_page index starts from 0 + target_page = _.pages[target_page.n]; + pos = transform(target_page.ctm, pos); + if(upside_down) { + pos[1] = target_page.height() - pos[1]; + } + _.scroll_to(detail[0], pos); + }); + + // In the meantime page gets loaded, scroll approximately position for maximum responsiveness. + _.scroll_to(detail[0], [0,0]); } - _.scroll_to(detail[0], pos); e.preventDefault(); } },