1
0
mirror of https://github.com/pdf2htmlEX/pdf2htmlEX.git synced 2024-07-16 13:58:51 +00:00
pdf2htmlEX/share/pdf2htmlEX.js.in

734 lines
22 KiB
JavaScript
Raw Normal View History

2013-03-31 09:41:38 +00:00
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab filetype=javascript : */
2013-10-30 07:50:07 +00:00
/**
* @license
2013-11-05 15:46:23 +00:00
* pdf2htmlEX.js: Core UI functions for pdf2htmlEX
2013-10-30 07:50:07 +00:00
* Copyright 2012,2013 Lu Wang <coolwanglu@gmail.com> and other contributors
* https://github.com/coolwanglu/pdf2htmlEX/blob/master/share/LICENSE
*/
2013-10-19 07:06:32 +00:00
/*
* Dependencies:
2013-11-05 15:46:23 +00:00
* jQuery - use it only when necessary
2012-09-22 14:47:44 +00:00
*/
2013-10-20 08:35:31 +00:00
/*
* Attention:
* This files is to be optimized by closure-compiler,
* so pay attention to the forms of property names:
*
* string/bracket form is safe, won't be optimized:
* var obj={ 'a':'b' }; obj['a'] = 'b';
* name/dot form will be optimized, the name is likely to be modified:
* var obj={ a:'b' }; obj.a = 'b';
*
* Either form can be used for internal objects,
* but must be consistent for each one respectively.
*
* string/bracket form must be used for external objects
* e.g. DEFAULT_CONFIG, object stored in page-data
* property names are part of the `protocol` in these cases.
*
*/
2013-05-29 18:31:48 +00:00
'use strict';
2013-11-06 08:33:15 +00:00
(function(pdf2htmlEX, document){
/**
* @const
* @struct
*/
2013-10-22 07:03:07 +00:00
var CSS_CLASS_NAMES = {
page_frame : '@CSS_PAGE_FRAME_CN@',
page_content_box : '@CSS_PAGE_CONTENT_BOX_CN@',
page_data : '@CSS_PAGE_DATA_CN@',
background_image : '@CSS_BACKGROUND_IMAGE_CN@',
link : '@CSS_LINK_CN@',
__dummy__ : 'no comma'
};
2013-09-30 07:50:15 +00:00
2013-10-22 13:32:02 +00:00
/**
* @const
* @dict
*/
2013-10-05 08:11:03 +00:00
var DEFAULT_CONFIG = {
// id of the element to put the pages in
2013-10-19 07:06:32 +00:00
'container_id' : 'page-container',
2013-10-05 08:11:03 +00:00
// id of the element for sidebar (to open and close)
2013-10-19 07:06:32 +00:00
'sidebar_id' : 'sidebar',
2013-10-05 08:11:03 +00:00
// id of the element for outline
2013-10-19 07:06:32 +00:00
'outline_id' : 'outline',
2013-10-05 08:11:03 +00:00
// class for the loading indicator
2013-10-20 08:35:31 +00:00
'loading_indicator_cls' : 'loading-indicator',
2013-10-05 08:11:03 +00:00
// How many page shall we preload that are below the last visible page
2013-10-19 07:06:32 +00:00
'preload_pages' : 3,
// how many ms should we wait before actually rendering the pages and after a scroll event
2013-10-19 07:06:32 +00:00
'render_timeout' : 100,
// zoom ratio step for each zoom in/out event
2013-10-19 07:06:32 +00:00
'scale_step' : 0.9,
// register global key handler
'register_key_handler' : true,
2013-09-30 07:50:15 +00:00
2013-10-22 13:32:02 +00:00
'__dummy__' : 'no comma'
2013-10-05 08:11:03 +00:00
};
2012-09-25 16:27:58 +00:00
2013-10-22 07:03:07 +00:00
/** @const */
2012-09-23 09:26:12 +00:00
var EPS = 1e-6;
2013-10-22 13:32:02 +00:00
2013-11-06 08:33:15 +00:00
/************************************/
/* utility function */
2013-10-22 13:32:02 +00:00
function invert(ctm) {
2012-09-25 11:29:59 +00:00
var det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2013-10-22 13:32:02 +00:00
return [ ctm[3] / det
,-ctm[1] / det
,-ctm[2] / det
,ctm[0] / det
,(ctm[2] * ctm[5] - ctm[3] * ctm[4]) / det
,(ctm[1] * ctm[4] - ctm[0] * ctm[5]) / det
];
2012-09-25 11:29:59 +00:00
};
2013-10-22 13:32:02 +00:00
function transform(ctm, pos) {
2012-09-25 11:29:59 +00:00
return [ctm[0] * pos[0] + ctm[2] * pos[1] + ctm[4]
,ctm[1] * pos[0] + ctm[3] * pos[1] + ctm[5]];
};
2013-11-06 08:33:15 +00:00
2013-11-06 10:02:36 +00:00
/**
* @param{Element} ele
*/
function get_page_number(ele) {
return parseInt(ele.getAttribute('data-page-no'), 16);
};
2013-11-06 08:33:15 +00:00
2013-11-06 10:35:51 +00:00
function disable_dragstart(eles) {
for(var i = 0, l = eles.length; i < l; ++i) {
eles[i].addEventListener('dragstart', function() {
return false;
}, false);
}
};
2013-10-22 07:03:07 +00:00
/**
* @constructor
2013-11-06 08:33:15 +00:00
* @param{Element} page The element for the page
2013-10-22 07:03:07 +00:00
* @struct
*/
2013-11-05 11:42:41 +00:00
function Page(page) {
2013-10-22 13:32:02 +00:00
if(!page) return;
2012-09-25 11:29:59 +00:00
2013-06-12 15:53:14 +00:00
this.loaded = false;
2013-07-16 15:42:02 +00:00
this.shown = false;
2013-11-05 11:42:41 +00:00
this.p = page; // page frame element
2013-06-12 15:53:14 +00:00
2013-11-06 10:02:36 +00:00
this.n = get_page_number(page);
2013-09-30 07:50:15 +00:00
// page size
// Need to make rescale work when page_content_box is not loaded, yet
2013-11-06 10:02:36 +00:00
this.original_height = page.clientHeight;
this.original_width = page.clientWidth;
2012-09-25 11:29:59 +00:00
2013-11-06 08:33:15 +00:00
// content box
2013-11-06 10:35:51 +00:00
var content_box = page.getElementsByClassName(CSS_CLASS_NAMES.page_content_box)[0];
2013-11-06 08:33:15 +00:00
2013-06-12 15:53:14 +00:00
// if page is loaded
2013-11-06 08:33:15 +00:00
if (content_box) {
this.content_box = content_box;
2013-06-12 15:53:14 +00:00
/*
* scale ratios
*
* default_r : the first one
* set_r : last set
* cur_r : currently using
*/
2013-11-06 08:33:15 +00:00
this.default_r = this.set_r = this.cur_r = this.original_height / content_box.clientHeight;
2013-11-06 10:35:51 +00:00
this.page_data = JSON.parse(page.getElementsByClassName(CSS_CLASS_NAMES.page_data)[0].getAttribute('data-data'));
2012-09-25 11:29:59 +00:00
2013-10-20 08:35:31 +00:00
this.ctm = this.page_data['ctm'];
2013-06-12 15:53:14 +00:00
this.ictm = invert(this.ctm);
this.loaded = true;
}
2012-09-23 09:26:12 +00:00
};
2013-10-22 07:03:07 +00:00
$.extend(Page.prototype, /** @lends {Page.prototype} */ {
2013-02-06 11:52:34 +00:00
/* hide & show are for contents, the page frame is still there */
2012-09-25 16:27:58 +00:00
hide : function(){
2013-11-06 08:33:15 +00:00
if(this.loaded) {
this.content_box.classList.remove('opened');
this.shown = false;
}
2012-09-25 16:27:58 +00:00
},
show : function(){
if (this.loaded) {
if (! this.shown) {
2013-11-06 08:33:15 +00:00
this.content_box.classList.add('opened');
this.shown = true;
}
2012-09-25 16:27:58 +00:00
}
},
rescale : function(ratio) {
2012-09-25 16:27:58 +00:00
if(ratio == 0) {
this.set_r = this.default_r;
} else {
this.set_r = ratio;
}
2012-09-23 05:04:29 +00:00
if(Math.abs(this.set_r - this.cur_r) > EPS) {
this.cur_r = this.set_r;
2013-11-06 08:33:15 +00:00
var s = this.content_box.style;
s.msTransform = s.webkitTransform = s.transform = 'scale('+this.cur_r.toFixed(3)+')';
}
2013-11-05 11:42:41 +00:00
var s = this.p.style;
2013-11-05 15:46:23 +00:00
s.height = (this.original_height * this.set_r) + 'px';
s.width = (this.original_width * this.set_r) + 'px';
2012-09-25 16:27:58 +00:00
},
/* return if any part of this page is shown in the container */
2012-09-25 16:27:58 +00:00
is_visible : function() {
2013-11-05 15:46:23 +00:00
var p = this.p;
var pos = this.position();
return !((pos[1] > p.clientHeight) || (pos[1] + p.parentNode.clientHeight < 0));
2012-09-25 16:27:58 +00:00
},
/* return if this page or any neighbor of it is visible */
is_nearly_visible : function() {
2013-11-05 15:46:23 +00:00
var p = this.p;
var off = this.position();
2013-11-01 07:34:18 +00:00
/*
* I should use the height of the previous page or the next page here
* but since they are not easily available, just use '*2', which should be a good estimate in most cases
*/
2013-11-05 15:46:23 +00:00
return !((off[1] > p.clientHeight * 2) || (off[1] + p.parentNode.clientHeight * 2 < 0));
},
2013-11-01 07:34:18 +00:00
/*
* return the coordinate of the top-left corner of container
2013-08-08 22:53:44 +00:00
* in our coordinate system
2013-11-05 11:08:15 +00:00
* may or may not work if there are nodes between p.parentNode and p.offsetParent
2012-09-25 16:27:58 +00:00
*/
position : function () {
2013-11-05 11:42:41 +00:00
var p = this.p;
2013-11-05 11:08:15 +00:00
var c = p.parentNode;
2013-11-05 15:46:23 +00:00
return [ c.scrollLeft - p.offsetLeft - p.clientLeft
, c.scrollTop - p.offsetTop - p.clientTop];
},
height : function () {
return this.p.clientHeight;
},
width : function () {
return this.p.clientWidth;
2012-09-25 16:27:58 +00:00
}
});
2012-09-23 05:04:29 +00:00
2013-10-22 07:03:07 +00:00
/**
2013-11-05 15:46:23 +00:00
* export pdf2htmlEX.Viewer
2013-10-22 07:03:07 +00:00
* @constructor
*/
2013-10-22 13:32:02 +00:00
function Viewer(config) {
/* do nothing if jQuery is not ready */
2013-10-28 08:29:58 +00:00
if(!jQuery) return;
2013-10-05 08:11:03 +00:00
this.config = $.extend({}, DEFAULT_CONFIG, config);
2013-10-28 08:05:24 +00:00
this.pages_loading = [];
2012-09-25 14:40:21 +00:00
this.init_before_loading_content();
2012-09-23 05:04:29 +00:00
2012-09-25 14:40:21 +00:00
var _ = this;
2013-11-06 10:35:51 +00:00
document.addEventListener('DOMContentLoaded', function(){
_.init_after_loading_content();
}, false);
2012-09-25 14:40:21 +00:00
};
2013-10-22 07:03:07 +00:00
$.extend(Viewer.prototype, /** @lends {Viewer.prototype} */ {
2013-07-16 15:42:02 +00:00
scale : 1,
2012-09-23 05:04:29 +00:00
2012-09-23 09:26:12 +00:00
init_before_loading_content : function() {
/*hide all pages before loading, will reveal only visible ones later */
this.pre_hide_pages();
},
2013-06-13 15:00:42 +00:00
2012-09-23 09:26:12 +00:00
init_after_loading_content : function() {
2013-11-06 10:02:36 +00:00
this.sidebar = document.getElementById(this.config['sidebar_id']);
this.outline = document.getElementById(this.config['outline_id']);
2013-11-06 08:33:15 +00:00
this.container = document.getElementById(this.config['container_id']);
2013-11-06 10:02:36 +00:00
this.loading_indicator = document.getElementsByClassName(this.config['loading_indicator_cls'])[0];
2012-09-23 05:04:29 +00:00
2013-11-06 11:00:23 +00:00
{
// Open the outline if nonempty
var empty = true;
var nodes = this.outline.childNodes;
for(var i = 0, l = nodes.length; i < l; ++i) {
var cur_node = nodes[i];
if(cur_node.nodeName == 'UL') {
empty = false;
break;
}
}
if(!empty)
this.sidebar.classList.add('opened');
2013-01-28 14:11:42 +00:00
}
2013-06-12 15:53:14 +00:00
this.find_pages();
2012-09-23 05:04:29 +00:00
2013-11-06 11:00:23 +00:00
// disable dragging of background images
disable_dragstart(document.getElementsByClassName(CSS_CLASS_NAMES.background_image));
if(this.config['register_key_handler'])
this.register_key_handler();
// register schedule rendering
2013-11-05 15:46:23 +00:00
// renew old schedules since scroll() may be called frequently
2012-09-23 09:26:12 +00:00
var _ = this;
2013-11-06 10:35:51 +00:00
this.container.addEventListener('scroll', function() {
_.schedule_render(true);
}, false);
// handle links
2013-11-06 10:35:51 +00:00
this.container.addEventListener('click', function(e) {
_.link_handler(e);
}, false);
this.outline.addEventListener('click', function(e) {
_.link_handler(e);
}, false);
2012-09-23 05:04:29 +00:00
2012-09-23 09:26:12 +00:00
this.render();
},
2013-07-16 15:42:02 +00:00
2013-10-28 08:05:24 +00:00
/*
* set up this.pages and this.page_map
* pages is an array holding all the Page objects
* page-Map maps an original page number (in PDF) to the corresponding index in page
*/
find_pages : function() {
2013-10-28 08:05:24 +00:00
var new_pages = [];
var new_page_map = {};
2013-11-06 08:33:15 +00:00
var nodes = this.container.childNodes;
for(var i = 0, l = nodes.length; i < l; ++i) {
var cur_node = nodes[i];
if((cur_node.nodeType == Node.ELEMENT_NODE)
&& cur_node.classList.contains(CSS_CLASS_NAMES.page_frame)) {
var p = new Page(cur_node);
new_pages.push(p);
new_page_map[p.n] = new_pages.length - 1;
}
}
this.pages = new_pages;
2013-10-28 08:05:24 +00:00
this.page_map = new_page_map;
},
2013-06-13 15:00:42 +00:00
load_page : function(idx, pages_to_preload, successCallback, errorCallback) {
2013-10-28 08:29:58 +00:00
var pages = this.pages;
if (idx >= pages.length)
return; // Page does not exist
2013-11-01 07:34:18 +00:00
var cur_page = pages[idx];
if (cur_page.loaded)
2013-06-12 15:53:14 +00:00
return; // Page is loaded
if (this.pages_loading[idx])
2013-06-12 15:53:14 +00:00
return; // Page is already loading
2013-11-06 10:02:36 +00:00
var new_loading_indicator = this.loading_indicator.cloneNode();
new_loading_indicator.classList.add('active');
2013-11-05 15:46:23 +00:00
var cur_page_ele = cur_page.p;
2013-11-06 10:02:36 +00:00
cur_page_ele.appendChild(new_loading_indicator);
2013-11-05 15:46:23 +00:00
var url = cur_page_ele.getAttribute('data-page-url');
2013-11-01 07:34:18 +00:00
if (url) {
this.pages_loading[idx] = true; // Set semaphore
2013-06-12 15:53:14 +00:00
2013-11-01 07:34:18 +00:00
/* closure variables */
var _ = this;
var _idx = idx;
$.ajax({
2013-06-12 15:53:14 +00:00
url: url,
dataType: 'text'
}).done(function(data){
2013-11-06 08:33:15 +00:00
// find the page element in the data
var div = document.createElement('div');
div.innerHTML = data;
var new_page = null;
var nodes = div.childNodes;
for(var i = 0, l = nodes.length; i < l; ++i) {
var cur_node = nodes[i];
2013-11-06 08:38:11 +00:00
if((cur_node.nodeType == Node.ELEMENT_NODE)
&& cur_node.classList.contains(CSS_CLASS_NAMES.page_frame)) {
2013-11-06 08:33:15 +00:00
new_page = cur_node;
break;
}
}
2013-10-07 07:25:47 +00:00
// replace the old page with loaded data
// the loading indicator on this page should also be destroyed
2013-11-06 08:33:15 +00:00
var p = _.pages[_idx];
_.container.replaceChild(new_page, p.p);
p = new Page(new_page);
_.pages[_idx] = p;
2013-06-12 15:53:14 +00:00
2013-11-01 07:34:18 +00:00
p.hide();
p.rescale(_.scale);
2013-06-12 15:53:14 +00:00
2013-10-06 11:53:37 +00:00
// disable background image dragging
2013-11-06 10:35:51 +00:00
disable_dragstart(new_page.getElementsByClassName(CSS_CLASS_NAMES.background_image));
2013-06-12 15:53:14 +00:00
2013-11-05 15:46:23 +00:00
_.schedule_render(false);
2013-11-01 07:34:18 +00:00
2013-10-06 11:53:37 +00:00
// Reset loading token
delete _.pages_loading[idx];
2013-11-06 08:33:15 +00:00
if (successCallback) successCallback(_.pages[_idx]);
2013-11-01 07:34:18 +00:00
}).fail(function(jqXHR, textStatus, errorThrown){
// Reset loading token
2013-11-06 08:33:15 +00:00
delete _.pages_loading[_idx];
if (errorCallback) errorCallback();
});
}
2013-06-12 15:53:14 +00:00
// Concurrent prefetch of the next pages
if (pages_to_preload === undefined)
2013-10-05 08:11:03 +00:00
pages_to_preload = this.config['preload_pages'];
2013-06-12 15:53:14 +00:00
if (--pages_to_preload > 0)
2013-11-01 07:34:18 +00:00
this.load_page(idx+1, pages_to_preload);
2012-09-23 09:26:12 +00:00
},
2013-06-13 15:00:42 +00:00
2012-09-23 09:26:12 +00:00
pre_hide_pages : function() {
/* pages might have not been loaded yet, so add a CSS rule */
2013-10-20 08:35:31 +00:00
var s = '@media screen{.'+CSS_CLASS_NAMES.page_content_box+'{display:none;}}';
2012-09-25 11:29:59 +00:00
var n = document.createElement('style');
n.type = 'text/css';
2012-09-23 09:26:12 +00:00
if (n.styleSheet) {
n.styleSheet.cssText = s;
} else {
n.appendChild(document.createTextNode(s));
}
2012-09-25 11:29:59 +00:00
document.getElementsByTagName('head')[0].appendChild(n);
2012-09-23 09:26:12 +00:00
},
render : function () {
/* hide (positional) invisible pages */
var pl = this.pages;
2013-10-28 08:29:58 +00:00
var pm = this.page_map;
2013-10-28 08:05:24 +00:00
for(var i = 0, l = pl.length; i < l; ++i) {
2012-09-23 09:26:12 +00:00
var p = pl[i];
if(p.is_nearly_visible()){
2013-06-12 15:53:14 +00:00
if (p.loaded) {
p.show();
2013-11-06 08:33:15 +00:00
} else {
this.load_page(i);
}
} else {
2012-09-25 11:29:59 +00:00
p.hide();
}
2012-09-22 14:47:44 +00:00
}
2012-09-23 09:26:12 +00:00
},
2013-11-05 15:46:23 +00:00
/*
* renew: renew the existing schedule instead of using the old one
*/
schedule_render : function(renew) {
2013-11-06 08:33:15 +00:00
if(this.render_timer !== undefined) {
2013-11-05 15:46:23 +00:00
if(!renew) return;
2012-09-23 09:26:12 +00:00
clearTimeout(this.render_timer);
2013-11-05 15:46:23 +00:00
}
2012-09-23 09:26:12 +00:00
var _ = this;
this.render_timer = setTimeout(function () {
_.render();
2013-11-05 15:46:23 +00:00
delete _.render_timer;
}, this.config['render_timeout']);
2012-09-23 09:26:12 +00:00
},
2013-10-06 11:53:37 +00:00
/*
* Handling key events, zooming, scrolling etc.
*/
register_key_handler: function () {
2012-09-23 09:26:12 +00:00
/*
* When user try to zoom in/out using ctrl + +/- or mouse wheel
* handle this and prevent the default behaviours
*
* Code credit to PDF.js
*/
var _ = this;
// Firefox specific event, so that we can prevent browser from zooming
2013-11-06 10:35:51 +00:00
window.addEventListener('DOMMouseScroll', function(e) {
2012-09-23 09:26:12 +00:00
if (e.ctrlKey) {
e.preventDefault();
_.rescale(Math.pow(_.config['scale_step'], e.detail), true);
2012-09-23 09:26:12 +00:00
}
2013-11-06 10:35:51 +00:00
}, false);
2012-09-23 09:26:12 +00:00
2013-11-06 10:35:51 +00:00
window.addEventListener('keydown', function(e) {
2013-10-06 11:53:37 +00:00
var handled = false;
/*
2013-10-22 13:32:02 +00:00
var cmd = (e.ctrlKey ? 1 : 0)
| (e.altKey ? 2 : 0)
| (e.shiftKey ? 4 : 0)
| (e.metaKey ? 8 : 0)
;
*/
var with_ctrl = e.ctrlKey || e.metaKey;
var with_alt = e.altKey;
2013-10-06 11:53:37 +00:00
switch (e.keyCode) {
case 61: // FF/Mac '='
case 107: // FF '+' and '='
case 187: // Chrome '+'
if(with_ctrl){
_.rescale(1.0 / _.config['scale_step'], true);
2013-10-06 11:53:37 +00:00
handled = true;
}
break;
case 173: // FF/Mac '-'
case 109: // FF '-'
case 189: // Chrome '-'
if(with_ctrl){
_.rescale(_.config['scale_step'], true);
2013-10-06 11:53:37 +00:00
handled = true;
}
break;
case 48: // '0'
if(with_ctrl){
2012-09-23 09:26:12 +00:00
_.rescale(0, false);
2013-10-06 11:53:37 +00:00
handled = true;
}
break;
case 33: // Page UP:
if (with_alt) { // alt-pageup -> scroll one page up
_.scroll_to(_.get_prev_page());
} else { // pageup -> scroll one screen up
2013-11-06 08:33:15 +00:00
_.container.scrollTop -= _.container.clientHeight;
2013-10-06 11:53:37 +00:00
}
handled = true;
break;
case 34: // Page DOWN
if (with_alt) { // alt-pagedown -> scroll one page down
_.scroll_to(_.get_next_page());
} else { // pagedown -> scroll one screen down
2013-11-06 08:33:15 +00:00
_.container.scrollTop += _.container.clientHeight;
2013-10-06 11:53:37 +00:00
}
handled = true;
break;
case 35: // End
if (with_ctrl) {
2013-11-06 08:33:15 +00:00
_.container.scrollTop = _.container.scrollHeight;
2013-10-06 11:53:37 +00:00
handled = true;
}
break;
case 36: // Home
if (e.with_ctrl) {
2013-11-06 08:33:15 +00:00
_.container.scrollTop = 0;
2013-10-06 11:53:37 +00:00
handled = true;
}
break;
2012-09-22 16:17:30 +00:00
}
2013-10-06 11:53:37 +00:00
if(handled) {
2012-09-24 12:20:34 +00:00
e.preventDefault();
return;
2012-09-22 16:17:30 +00:00
}
2013-11-06 10:35:51 +00:00
}, false);
},
2013-10-22 13:32:02 +00:00
// TODO
get_next_page : function() { return undefined; },
get_prev_page : function() { return undefined; },
2012-09-23 09:26:12 +00:00
// TODO: offsetX/Y is by default the center of container
2013-11-05 11:42:41 +00:00
// TODO consider scale on offsetX/Y
2013-07-16 15:42:02 +00:00
rescale : function (ratio, is_relative, offsetX, offsetY) {
if (! offsetX)
offsetX = 0;
if (! offsetY)
offsetY = 0;
2013-07-16 15:42:02 +00:00
// Save offset of the active page
var active_page = this.get_active_page();
2013-09-30 07:50:15 +00:00
if(!active_page) return;
2013-11-05 11:42:41 +00:00
var active_page_ele = active_page.p;
var prev_offset = [ active_page_ele.offsetLeft, active_page_ele.offsetTop ];
2013-07-16 15:42:02 +00:00
var old_scale = this.scale;
2013-09-30 07:50:15 +00:00
var pl = this.pages;
2013-07-16 15:42:02 +00:00
// Set new scale
if (ratio == 0) {
this.scale = 1;
is_relative = false;
}
else if (is_relative)
2013-07-16 15:42:02 +00:00
this.scale *= ratio;
else
this.scale = ratio;
// Rescale pages
for(var i = 0, l = pl.length; i < l; ++i)
pl[i].rescale(this.scale);
2012-09-22 16:17:30 +00:00
2013-11-06 08:33:15 +00:00
var container = this.container;
2013-07-16 15:42:02 +00:00
// Correct container scroll to keep view aligned while zooming
2013-11-05 11:42:41 +00:00
var correction_top = active_page_ele.offsetTop - prev_offset[1];
2013-11-06 08:33:15 +00:00
container.scrollTop += correction_top + offsetY;
2013-07-16 15:42:02 +00:00
// Take the center of the view as a reference
2013-11-06 08:33:15 +00:00
var prev_center_x = container.clientWidth / 2 - prev_offset[0];
2013-07-16 15:42:02 +00:00
// Calculate the difference respect the center of the view after the zooming
2013-11-05 11:42:41 +00:00
var correction_left = prev_center_x * (this.scale/old_scale - 1) + active_page_ele.offsetLeft - prev_offset[0];
2013-07-16 15:42:02 +00:00
// Scroll the container accordingly to keep alignment to the initial reference
2013-11-06 08:33:15 +00:00
container.scrollLeft += correction_left + offsetX;
2013-07-16 15:42:02 +00:00
// some pages' visibility may be toggled, wait for next render()
2013-11-05 15:46:23 +00:00
// renew old schedules since rescale() may be called frequently
this.schedule_render(true);
2012-09-23 09:26:12 +00:00
},
2012-09-22 16:17:30 +00:00
2013-07-16 15:42:02 +00:00
fit_width : function () {
var active_page = this.get_active_page();
2013-09-30 07:50:15 +00:00
if(!active_page) return;
2013-07-16 15:42:02 +00:00
2013-11-06 08:33:15 +00:00
this.rescale(this.container.clientWidth / active_page.width(), false);
2013-10-28 08:29:58 +00:00
this.scroll_to(this.page_map[active_page.n], [0,0]);
2013-07-16 15:42:02 +00:00
},
fit_height : function () {
var active_page = this.get_active_page();
2013-09-30 07:50:15 +00:00
if(!active_page) return;
2013-07-16 15:42:02 +00:00
2013-11-06 08:33:15 +00:00
this.rescale(this.container.clientHeight / active_page.height(), false);
2013-10-28 08:29:58 +00:00
this.scroll_to(this.page_map[active_page.n], [0,0]);
2013-07-16 15:42:02 +00:00
},
// TODO: preserve active_page idx after rescaling
2013-07-16 15:42:02 +00:00
get_active_page : function () {
2013-11-01 08:11:44 +00:00
var pl = this.pages;
2013-10-28 08:05:24 +00:00
for(var i = 0, l = pl.length; i < l; ++i) {
if (pl[i].is_visible())
return pl[i];
2013-10-06 11:53:37 +00:00
}
return;
2013-07-16 15:42:02 +00:00
},
2013-11-06 10:02:36 +00:00
/**
2013-11-06 10:35:51 +00:00
* @param{Node} ele
2013-11-06 10:02:36 +00:00
*/
get_containing_page : function(ele) {
2012-09-25 11:29:59 +00:00
/* get the page obj containing obj */
2013-11-06 10:02:36 +00:00
while(ele) {
if((ele.nodeType == Node.ELEMENT_NODE)
&& ele.classList.contains(CSS_CLASS_NAMES.page_frame)) {
/*
* Get original page number and map it to index of pages
* TODO: store the index on the dom element
*/
2013-11-06 10:35:51 +00:00
var pn = get_page_number(/** @type{Element} */(ele));
2013-11-06 10:02:36 +00:00
var pm = this.page_map;
return (pn in pm) ? this.pages[pm[pn]] : null;
}
ele = ele.parentNode;
}
return null;
2012-09-25 11:29:59 +00:00
},
2013-01-28 13:01:02 +00:00
link_handler : function (e) {
2013-11-06 08:33:15 +00:00
var target = e.currentTarget;
2013-01-28 15:58:00 +00:00
var cur_pos = [0,0];
// cur_page might be undefined, e.g. from Outline
2013-11-06 11:00:23 +00:00
var cur_page = this.get_containing_page(target);
if(cur_page)
2013-01-28 15:58:00 +00:00
{
cur_pos = cur_page.position();
//get the coordinates in default user system
2013-11-05 15:46:23 +00:00
cur_pos = transform(cur_page.ictm, [cur_pos[0], cur_page.height()-cur_pos[1]]);
2013-01-28 15:58:00 +00:00
}
2012-09-25 11:29:59 +00:00
2013-11-06 08:38:11 +00:00
var detail_str = /** @type{string} */ (target.getAttribute('data-dest-detail'));
2013-11-06 11:00:23 +00:00
if(!detail_str) return;
2012-09-25 11:29:59 +00:00
var ok = false;
2012-09-26 08:00:55 +00:00
var detail = JSON.parse(detail_str);
2012-09-25 11:29:59 +00:00
2013-10-28 08:05:24 +00:00
var target_page_no = detail[0];
var page_map = this.page_map;
if(!(target_page_no in page_map)) return;
var target_page_idx = page_map[target_page_no];
2013-11-06 11:00:23 +00:00
var target_page = this.pages[target_page_idx];
2012-09-26 08:00:55 +00:00
var pos = [0,0];
var upside_down = true;
// TODO: zoom
// TODO: BBox
switch(detail[1]) {
case 'XYZ':
pos = [(detail[2] == null) ? cur_pos[0] : detail[2]
2013-10-06 11:53:37 +00:00
,(detail[3] == null) ? cur_pos[1] : detail[3]];
2012-09-26 08:00:55 +00:00
ok = true;
break;
case 'Fit':
case 'FitB':
pos = [0,0];
ok = true;
break;
case 'FitH':
case 'FitBH':
pos = [0, (detail[2] == null) ? cur_pos[1] : detail[2]];
2012-09-26 08:00:55 +00:00
ok = true;
break;
case 'FitV':
case 'FitBV':
pos = [(detail[2] == null) ? cur_pos[0] : detail[2], 0];
ok = true;
break;
case 'FitR':
/* locate the top-left corner of the rectangle */
pos = [detail[2], detail[5]];
upside_down = false;
ok = true;
break;
2012-09-25 11:29:59 +00:00
default:
2012-09-26 08:00:55 +00:00
ok = false;
2012-09-25 11:29:59 +00:00
break;
}
2012-09-26 08:00:55 +00:00
if(ok) {
2013-10-28 08:05:24 +00:00
/* page should of type Page */
var transform_and_scroll = function(page) {
pos = transform(page.ctm, pos);
if(upside_down) {
2013-11-05 15:46:23 +00:00
pos[1] = page.height() - pos[1];
2013-06-12 15:53:14 +00:00
}
2013-11-06 11:00:23 +00:00
this.scroll_to(target_page_idx, pos);
};
if (target_page.loaded) {
2013-10-28 08:05:24 +00:00
transform_and_scroll(target_page);
2013-06-12 15:53:14 +00:00
} else {
2013-11-05 07:24:50 +00:00
// TODO: scroll_to may finish before load_page
2013-06-12 15:53:14 +00:00
// Scroll to the exact position once loaded.
2013-11-06 11:00:23 +00:00
this.load_page(target_page_idx, undefined, transform_and_scroll);
2013-06-12 15:53:14 +00:00
// In the meantime page gets loaded, scroll approximately position for maximum responsiveness.
2013-11-06 11:00:23 +00:00
this.scroll_to(target_page_idx, [0,0]);
2012-09-26 08:00:55 +00:00
}
2012-09-25 11:29:59 +00:00
e.preventDefault();
2012-09-26 08:00:55 +00:00
}
2012-09-25 14:24:36 +00:00
},
2012-09-26 08:00:55 +00:00
/* pos=[x,y], where (0,0) is the top-left corner */
2013-10-28 08:05:24 +00:00
scroll_to : function(page_idx, pos) {
var target_page = this.pages[page_idx];
2013-10-22 11:42:56 +00:00
if(target_page === undefined) return;
2012-09-25 14:24:36 +00:00
2013-10-22 11:42:56 +00:00
if(pos === undefined)
2013-10-06 11:53:37 +00:00
pos = [0,0];
2012-09-25 14:24:36 +00:00
var cur_target_pos = target_page.position();
2013-11-06 08:33:15 +00:00
var container = this.container;
container.scrollLeft += pos[0] - cur_target_pos[0];
container.scrollTop += pos[1] - cur_target_pos[1];
2012-09-25 14:24:36 +00:00
},
__last_member__ : 'no comma' /*,*/
2012-09-25 14:40:21 +00:00
});
2013-10-22 13:32:02 +00:00
pdf2htmlEX['Viewer'] = Viewer;
2013-11-06 08:33:15 +00:00
})((window['pdf2htmlEX'] = window['pdf2htmlEX'] || {}), document);