From d5ade42d5a11d6511f4a96353727646d4fef9d32 Mon Sep 17 00:00:00 2001 From: Michele Redolfi Date: Wed, 12 Jun 2013 17:53:14 +0200 Subject: [PATCH 1/3] lazy load with deferrered scroll_to --- ChangeLog | 1 + share/manifest | 2 +- share/pdf2htmlEX.js.in | 136 ++++++++++++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 37 deletions(-) mode change 100644 => 100755 share/pdf2htmlEX.js.in 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(); } }, From 51dd4fdecd75977a7638212a5bfeb2082e45b1a5 Mon Sep 17 00:00:00 2001 From: Michele Redolfi Date: Thu, 13 Jun 2013 12:39:52 +0200 Subject: [PATCH 2/3] load_page idx now starts from 1; pages_loading move to Viewer instance; fixes; pdf2htmlEX.js.in executable bit removed; --- share/pdf2htmlEX.js.in | 69 ++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 29 deletions(-) mode change 100755 => 100644 share/pdf2htmlEX.js.in diff --git a/share/pdf2htmlEX.js.in b/share/pdf2htmlEX.js.in old mode 100755 new mode 100644 index 3eb2e05..fe4d92e --- a/share/pdf2htmlEX.js.in +++ b/share/pdf2htmlEX.js.in @@ -18,9 +18,7 @@ var pdf2htmlEX = (function(){ link : '@CSS_LINK_CN@', __dummy__ : 'no comma' }; - var PAGES_TO_PRELOAD = 3; - - var pages_loading = {}; + var DEFAULT_PAGES_TO_PRELOAD = 3; var pdf2htmlEX = new Object(); @@ -132,6 +130,8 @@ var pdf2htmlEX = (function(){ this.container_id = config['container_id']; this.sidebar_id = config['sidebar_id']; this.outline_id = config['outline_id']; + this.pages_to_preload = config['pages_to_preload'] || DEFAULT_PAGES_TO_PRELOAD; + this.pages_loading = {}; this.init_before_loading_content(); var _ = this; @@ -163,7 +163,7 @@ var pdf2htmlEX = (function(){ 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); @@ -171,7 +171,7 @@ var pdf2htmlEX = (function(){ this.render(); // Trigger documentLoaded event - $(this.container).trigger("documentLoaded"); + this.container.trigger("documentLoaded"); }, find_pages : function() { var new_pages = new Array(); @@ -183,15 +183,18 @@ var pdf2htmlEX = (function(){ } this.pages = new_pages; }, - load_page : function(idx, pages_to_preload, successCallback) { - if (this.pages[idx+1].loaded) + load_page : function(idx, pages_to_preload, successCallback, errorCallback) { + if (idx >= this.pages.length) + return; // Page does not exist + + if (this.pages[idx].loaded) return; // Page is loaded - if (pages_loading[idx]) + if (this.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); + var page_no_hex = idx.toString(16); + var $pf = this.container.find('#' + CSS_CLASS_NAMES['page_frame'] + page_no_hex); if($pf.length == 0) return; // Page does not exist @@ -199,34 +202,42 @@ var pdf2htmlEX = (function(){ var url = $pf.data('page-url'); if (url && url.length > 0) { - pages_loading[idx] = true; // Set semaphore + this.pages_loading[idx] = true; // Set semaphore $.ajax({ url: url, dataType: 'text' }).done(function(data){ - _.pages[idx+1].p.parent().replaceWith(data); // pages index starts from 1 + _.pages[idx].p.parent().replaceWith(data); - 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); + var $new_pf = _.container.find('#' + CSS_CLASS_NAMES['page_frame'] + page_no_hex); + _.pages[idx] = new Page($new_pf, _.container); + _.pages[idx].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); + _.container.trigger("pageLoaded", idx); // 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; + delete _.pages_loading[idx]; if (successCallback) successCallback(); - }); + } + ).fail(function(jqXHR, textStatus, errorThrown){ + console.error('error loading page ' + idx + ': ' + textStatus); + + // Reset loading token + delete _.pages_loading[idx]; + + if (errorCallback) errorCallback(); + }); } // Concurrent prefetch of the next pages if (pages_to_preload === undefined) - pages_to_preload = PAGES_TO_PRELOAD; + pages_to_preload = this.pages_to_preload; if (--pages_to_preload > 0) _.load_page(idx+1, pages_to_preload); @@ -261,7 +272,7 @@ var pdf2htmlEX = (function(){ p.show(); this.container.trigger("pageShown", i); } else - this.load_page(p.n - 1); // load_page index starts from 0 + this.load_page(p.n); } else { p.hide(); } @@ -394,21 +405,21 @@ var pdf2htmlEX = (function(){ } if(ok) { - if (target_page.loaded) { + var transform_and_scroll = function() { pos = transform(target_page.ctm, pos); - if(upside_down) { + if(upside_down) { pos[1] = target_page.height() - pos[1]; } _.scroll_to(detail[0], pos); + } + + if (target_page.loaded) { + transform_and_scroll(); } 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); + _.load_page(target_page.n, 1, function() { + target_page = _.pages[target_page.n]; // Refresh reference + transform_and_scroll(); }); // In the meantime page gets loaded, scroll approximately position for maximum responsiveness. From 25a3273bac9b8e4b1cd236f56cb2df1fdddfe1ba Mon Sep 17 00:00:00 2001 From: Michele Redolfi Date: Thu, 13 Jun 2013 14:40:50 +0200 Subject: [PATCH 3/3] event triggering removed --- share/pdf2htmlEX.js.in | 7 ------- 1 file changed, 7 deletions(-) diff --git a/share/pdf2htmlEX.js.in b/share/pdf2htmlEX.js.in index fe4d92e..7b5d5c5 100644 --- a/share/pdf2htmlEX.js.in +++ b/share/pdf2htmlEX.js.in @@ -169,9 +169,6 @@ var pdf2htmlEX = (function(){ this.container.add(this.outline).on('click', '.'+CSS_CLASS_NAMES['link'], this, this.link_handler); this.render(); - - // Trigger documentLoaded event - this.container.trigger("documentLoaded"); }, find_pages : function() { var new_pages = new Array(); @@ -215,9 +212,6 @@ var pdf2htmlEX = (function(){ _.pages[idx].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); - // disable background image dragging $new_pf.find('.'+CSS_CLASS_NAMES['background_image']).on('dragstart', function(e){return false;}); @@ -270,7 +264,6 @@ var pdf2htmlEX = (function(){ if(p.is_nearly_visible()){ if (p.loaded) { p.show(); - this.container.trigger("pageShown", i); } else this.load_page(p.n); } else {