diff --git a/files_reader/vendor/pdfjs/.pdf.reader.js.swp b/files_reader/vendor/pdfjs/.pdf.reader.js.swp
new file mode 100644
index 0000000..bf2f21d
Binary files /dev/null and b/files_reader/vendor/pdfjs/.pdf.reader.js.swp differ
diff --git a/files_reader/vendor/pdfjs/controllers/.controls_controller.js.swp b/files_reader/vendor/pdfjs/controllers/.controls_controller.js.swp
new file mode 100644
index 0000000..b5febc9
Binary files /dev/null and b/files_reader/vendor/pdfjs/controllers/.controls_controller.js.swp differ
diff --git a/files_reader/vendor/pdfjs/controllers/.reader_controller.js.swp b/files_reader/vendor/pdfjs/controllers/.reader_controller.js.swp
new file mode 100644
index 0000000..ee76dc4
Binary files /dev/null and b/files_reader/vendor/pdfjs/controllers/.reader_controller.js.swp differ
diff --git a/files_reader/vendor/pdfjs/controllers/controls_controller.js b/files_reader/vendor/pdfjs/controllers/controls_controller.js
index 321dae1..5fbc4c1 100644
--- a/files_reader/vendor/pdfjs/controllers/controls_controller.js
+++ b/files_reader/vendor/pdfjs/controllers/controls_controller.js
@@ -1,27 +1,45 @@
PDFJS.reader.ControlsController = function(book) {
- var reader = this;
+ var reader = this,
+ settings = reader.settings;
var $store = $("#store"),
+ $viewer = $("#viewer"),
$fullscreen = $("#fullscreen"),
$fullscreenicon = $("#fullscreenicon"),
$cancelfullscreenicon = $("#cancelfullscreenicon"),
$slider = $("#slider"),
$main = $("#main"),
$sidebar = $("#sidebar"),
+ $titlebar = $("#titlebar"),
$settings = $("#setting"),
$bookmark = $("#bookmark"),
- $note = $("#note");
+ $note = $("#note"),
+ $togglelayout = $("#toggle-layout"),
+ $zoom_icon = $("#zoom_icon"),
+ $zoom_options = $("#zoom_options"),
+ $zoom_option = $(".zoom_option"),
+ $page_num = $("#page_num");
- var goOnline = function() {
- reader.offline = false;
- // $store.attr("src", $icon.data("save"));
+ if (reader.isMobile() === true) {
+ $titlebar.addClass("background_visible");
};
- var goOffline = function() {
- reader.offline = true;
- // $store.attr("src", $icon.data("saved"));
+ var show = function () {
+ $titlebar.removeClass("hide");
};
+ var hide = function () {
+ $titlebar.addClass("hide");
+ };
+
+ var toggle = function () {
+ $titlebar.toggleClass("hide");
+ };
+
+ $viewer.on("touchstart", function(e) {
+ reader.ControlsController.toggle();
+ });
+
var fullscreen = false;
$slider.on("click", function () {
@@ -81,6 +99,94 @@ PDFJS.reader.ControlsController = function(book) {
});
+ /* select works fine on most browsers, but - of course - apple mobile has 'special needs'
+ * in that it does not support any styling in the drop-down list, and as such can not display
+ * icons there. Due to the unfortunate fact that many still buy these apple-encumbered devices
+ * a custom select is needed...
+ *
+ $zoomlevel.val(settings.zoomLevel);
+
+ $zoomlevel.on("change", function () {
+ reader.setZoom($(this).val());
+ var $option = $zoomlevel.find(":selected");
+ if ($option.data("icon") !== undefined)
+ $("#zoom_icon")[0].textContent = $option.data("icon");
+ });
+
+ *
+ */
+
+ /* custom select, supports icons in drop-down list */
+ $zoom_icon.on("click", function () {
+ var offset = $(this).offset();
+ console.log(offset);
+ $zoom_options.css({
+ 'left': parseInt(offset.left) + "px",
+ 'top' : parseInt(parseInt(offset.top) + parseInt($zoom_icon.height())) + "px"
+ });
+
+ $zoom_options.toggleClass("hide");
+ });
+
+ $zoom_icon[0].className="";
+ var $current_zoom_option = $zoom_options.find("[data-value='" + settings.zoomLevel + "']");
+ if ($current_zoom_option.data("class")) {
+ $zoom_icon.addClass($current_zoom_option.data("class"));
+ } else {
+ $zoom_icon[0].textContent = $current_zoom_option.data("text");
+ }
+
+ $zoom_option.on("click", function () {
+ var $this = $(this);
+ reader.setZoom($this.data("value"));
+ $zoom_icon[0].className="";
+ $zoom_icon[0].textContent = "";
+ if ($this.data("class")) {
+ $zoom_icon.addClass($this.data("class"));
+ } else {
+ $zoom_icon[0].textContent = $this.data("text");
+ }
+ $zoom_options.addClass("hide");
+ });
+ /* end custom select */
+
+ var enterPageNum = function(e) {
+ var text = e.target,
+ page;
+
+ switch (e.keyCode) {
+ case 27: // escape - cancel, discard changes
+ $page_num[0].removeEventListener("keydown", enterPageNum, false);
+ $page_num.removeClass("editable");
+ $page_num.prop("contenteditable",false);
+ $page_num.text($page_num.data("content"));
+ break;
+ case 13: // enter - accept changes
+ $page_num[0].removeEventListener("keydown", enterPageNum, false);
+ $page_num.removeClass("editable");
+ $page_num.attr("contenteditable", false);
+ page = parseInt($page_num.text());
+ if (page > 0 && page <= reader.settings.numPages) {
+ reader.queuePage(page);
+ } else {
+ $page_num.text($page_num.data("content"));
+ }
+ break;
+ }
+
+ e.stopPropagation;
+ };
+
+
+ $page_num.on("click", function() {
+ $page_num.data("content", $page_num.text());
+ $page_num.text("");
+ $page_num.prop("contenteditable", true);
+ $page_num.addClass("editable");
+ $page_num[0].addEventListener("keydown", enterPageNum, false);
+ });
+
+
/*
book.on('renderer:locationChanged', function(cfi){
var cfiFragment = "#" + cfi;
@@ -113,6 +219,8 @@ PDFJS.reader.ControlsController = function(book) {
*/
return {
-
+ "show": show,
+ "hide": hide,
+ "toggle": toggle
};
};
diff --git a/files_reader/vendor/pdfjs/controllers/reader_controller.js b/files_reader/vendor/pdfjs/controllers/reader_controller.js
index bd7e488..9895e50 100644
--- a/files_reader/vendor/pdfjs/controllers/reader_controller.js
+++ b/files_reader/vendor/pdfjs/controllers/reader_controller.js
@@ -1,5 +1,6 @@
PDFJS.reader.ReaderController = function(book) {
var $main = $("#main"),
+ $viewer = $("#viewer"),
$divider = $("#divider"),
$loader = $("#loader"),
$next = $("#next"),
@@ -84,7 +85,7 @@ PDFJS.reader.ReaderController = function(book) {
page_no = 1;
break;
case 'last':
- // TODO
+ page_no = reader.settings.numPages;
break;
case 'annotate':
$note.click();
@@ -95,6 +96,9 @@ PDFJS.reader.ReaderController = function(book) {
case 'reflow':
$sidebarReflow.click();
break;
+ case 'toggleTitlebar':
+ reader.ControlsController.toggle();
+ break;
case 'toggleSidebar':
reader.SidebarController.toggle();
break;
@@ -115,8 +119,7 @@ PDFJS.reader.ReaderController = function(book) {
}
if (page_no) {
-
- // TODO
+ reader.queuePage(page_no);
}
}
diff --git a/files_reader/vendor/pdfjs/controllers/textlayer_controller.js b/files_reader/vendor/pdfjs/controllers/textlayer_controller.js
new file mode 100644
index 0000000..df2b81c
--- /dev/null
+++ b/files_reader/vendor/pdfjs/controllers/textlayer_controller.js
@@ -0,0 +1,379 @@
+/* Copyright 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @typedef {Object} TextLayerBuilderOptions
+ * @property {HTMLDivElement} textLayerDiv - The text layer container.
+ * @property {EventBus} eventBus - The application event bus.
+ * @property {number} pageIndex - The page index.
+ * @property {PageViewport} viewport - The viewport of the text layer.
+ * @property {PDFFindController} findController
+ * @property {boolean} enhanceTextSelection - Option to turn on improved
+ * text selection.
+ */
+
+/**
+ * TextLayerBuilder provides text-selection functionality for the PDF.
+ * It does this by creating overlay divs over the PDF text. These divs
+ * contain text that matches the PDF text they are overlaying. This object
+ * also provides a way to highlight text that is being searched for.
+ * @class
+ */
+PDFJS.Reader.TextLayerController = function (options) {
+
+ var EXPAND_DIVS_TIMEOUT = 300; // ms
+
+ this.textLayerDiv = options.textLayerDiv;
+ this.eventBus = options.eventBus || null;
+ this.textContent = null;
+ this.renderingDone = false;
+ this.pageIdx = options.pageIndex;
+ this.pageNumber = this.pageIdx + 1;
+ this.matches = [];
+ this.viewport = options.viewport;
+ this.textDivs = [];
+ this.findController = options.findController || null;
+ this.textLayerRenderTask = null;
+ this.enhanceTextSelection = options.enhanceTextSelection;
+ this._bindMouse();
+
+ return this;
+};
+
+PDFJS.Reader.TextLayerController.prototype._finishRendering = function () {
+ this.renderingDone = true;
+
+ if (!this.enhanceTextSelection) {
+ var endOfContent = document.createElement('div');
+ endOfContent.className = 'endOfContent';
+ this.textLayerDiv.appendChild(endOfContent);
+ }
+
+ if (this.eventBus !== null) {
+ this.eventBus.dispatch('textlayerrendered', {
+ source: this,
+ pageNumber: this.pageNumber,
+ numTextDivs: this.textDivs.length,
+ });
+ }
+};
+
+/**
+ * Renders the text layer.
+ * @param {number} timeout (optional) if specified, the rendering waits
+ * for specified amount of ms.
+ */
+PDFJS.Reader.TextLayerController.prototype.render = function(timeout) {
+ if (!this.textContent || this.renderingDone) {
+ return;
+ }
+ this.cancel();
+
+ this.textDivs = [];
+ var textLayerFrag = document.createDocumentFragment();
+ this.textLayerRenderTask = PDFJS.renderTextLayer({
+ textContent: this.textContent,
+ container: textLayerFrag,
+ viewport: this.viewport,
+ textDivs: this.textDivs,
+ timeout: timeout,
+ enhanceTextSelection: this.enhanceTextSelection,
+ });
+ this.textLayerRenderTask.promise.then(function () {
+ this.textLayerDiv.appendChild(textLayerFrag);
+ this._finishRendering();
+ this.updateMatches();
+ }.bind(this), function (reason) {
+ // cancelled or failed to render text layer -- skipping errors
+ });
+};
+
+/**
+ * Cancels rendering of the text layer.
+ */
+PDFJS.Reader.TextLayerController.prototype.cancel = function () {
+ if (this.textLayerRenderTask) {
+ this.textLayerRenderTask.cancel();
+ this.textLayerRenderTask = null;
+ }
+};
+
+PDFJS.Reader.TextLayerController.prototype.setTextContent = function (textContent) {
+ this.cancel();
+ this.textContent = textContent;
+};
+
+PDFJS.Reader.TextLayerController.prototype.convertMatches = function(matches, matchesLength) {
+ var i = 0;
+ var iIndex = 0;
+ var bidiTexts = this.textContent.items;
+ var end = bidiTexts.length - 1;
+ var queryLen = (this.findController === null ?
+ 0 : this.findController.state.query.length);
+ var ret = [];
+ if (!matches) {
+ return ret;
+ }
+ for (var m = 0, len = matches.length; m < len; m++) {
+ // Calculate the start position.
+ var matchIdx = matches[m];
+
+ // Loop over the divIdxs.
+ while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
+ iIndex += bidiTexts[i].str.length;
+ i++;
+ }
+
+ if (i === bidiTexts.length) {
+ console.error('Could not find a matching mapping');
+ }
+
+ var match = {
+ begin: {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ }
+ };
+
+ // Calculate the end position.
+ if (matchesLength) { // multiterm search
+ matchIdx += matchesLength[m];
+ } else { // phrase search
+ matchIdx += queryLen;
+ }
+
+ // Somewhat the same array as above, but use > instead of >= to get
+ // the end position right.
+ while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
+ iIndex += bidiTexts[i].str.length;
+ i++;
+ }
+
+ match.end = {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ };
+ ret.push(match);
+ }
+
+ return ret;
+};
+
+PDFJS.Reader.TextLayerController.prototype.renderMatches = function (matches) {
+ // Early exit if there is nothing to render.
+ if (matches.length === 0) {
+ return;
+ }
+
+ var bidiTexts = this.textContent.items;
+ var textDivs = this.textDivs;
+ var prevEnd = null;
+ var pageIdx = this.pageIdx;
+ var isSelectedPage = (this.findController === null ?
+ false : (pageIdx === this.findController.selected.pageIdx));
+ var selectedMatchIdx = (this.findController === null ?
+ -1 : this.findController.selected.matchIdx);
+ var highlightAll = (this.findController === null ?
+ false : this.findController.state.highlightAll);
+ var infinity = {
+ divIdx: -1,
+ offset: undefined
+ };
+
+ function beginText(begin, className) {
+ var divIdx = begin.divIdx;
+ textDivs[divIdx].textContent = '';
+ appendTextToDiv(divIdx, 0, begin.offset, className);
+ }
+
+ function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
+ var div = textDivs[divIdx];
+ var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
+ var node = document.createTextNode(content);
+ if (className) {
+ var span = document.createElement('span');
+ span.className = className;
+ span.appendChild(node);
+ div.appendChild(span);
+ return;
+ }
+ div.appendChild(node);
+ }
+
+ var i0 = selectedMatchIdx, i1 = i0 + 1;
+ if (highlightAll) {
+ i0 = 0;
+ i1 = matches.length;
+ } else if (!isSelectedPage) {
+ // Not highlighting all and this isn't the selected page, so do nothing.
+ return;
+ }
+
+ for (var i = i0; i < i1; i++) {
+ var match = matches[i];
+ var begin = match.begin;
+ var end = match.end;
+ var isSelected = (isSelectedPage && i === selectedMatchIdx);
+ var highlightSuffix = (isSelected ? ' selected' : '');
+
+ if (this.findController) {
+ this.findController.updateMatchPosition(pageIdx, i, textDivs,
+ begin.divIdx);
+ }
+
+ // Match inside new div.
+ if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+ // If there was a previous div, then add the text at the end.
+ if (prevEnd !== null) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+ // Clear the divs and set the content until the starting point.
+ beginText(begin);
+ } else {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
+ }
+
+ if (begin.divIdx === end.divIdx) {
+ appendTextToDiv(begin.divIdx, begin.offset, end.offset,
+ 'highlight' + highlightSuffix);
+ } else {
+ appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
+ 'highlight begin' + highlightSuffix);
+ for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
+ textDivs[n0].className = 'highlight middle' + highlightSuffix;
+ }
+ beginText(end, 'highlight end' + highlightSuffix);
+ }
+ prevEnd = end;
+ }
+
+ if (prevEnd) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+};
+
+PDFJS.Reader.TextLayerController.prototype.updateMatches = function () {
+ // Only show matches when all rendering is done.
+ if (!this.renderingDone) {
+ return;
+ }
+
+ // Clear all matches.
+ var matches = this.matches;
+ var textDivs = this.textDivs;
+ var bidiTexts = this.textContent.items;
+ var clearedUntilDivIdx = -1;
+
+ // Clear all current matches.
+ for (var i = 0, len = matches.length; i < len; i++) {
+ var match = matches[i];
+ var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+ for (var n = begin, end = match.end.divIdx; n <= end; n++) {
+ var div = textDivs[n];
+ div.textContent = bidiTexts[n].str;
+ div.className = '';
+ }
+ clearedUntilDivIdx = match.end.divIdx + 1;
+ }
+
+ if (this.findController === null || !this.findController.active) {
+ return;
+ }
+
+ // Convert the matches on the page controller into the match format
+ // used for the textLayer.
+ var pageMatches, pageMatchesLength;
+ if (this.findController !== null) {
+ pageMatches = this.findController.pageMatches[this.pageIdx] || null;
+ pageMatchesLength = (this.findController.pageMatchesLength) ?
+ this.findController.pageMatchesLength[this.pageIdx] || null : null;
+ }
+
+ this.matches = this.convertMatches(pageMatches, pageMatchesLength);
+ this.renderMatches(this.matches);
+};
+
+/**
+ * Fixes text selection: adds additional div where mouse was clicked.
+ * This reduces flickering of the content if mouse slowly dragged down/up.
+ * @private
+ */
+PDFJS.Reader.TextLayerController.prototype._bindMouse = function () {
+ var div = this.textLayerDiv;
+ var self = this;
+ var expandDivsTimer = null;
+
+ div.addEventListener('mousedown', function (e) {
+ if (self.enhanceTextSelection && self.textLayerRenderTask) {
+ self.textLayerRenderTask.expandTextDivs(true);
+ if ((typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) &&
+ expandDivsTimer) {
+ clearTimeout(expandDivsTimer);
+ expandDivsTimer = null;
+ }
+ return;
+ }
+ var end = div.querySelector('.endOfContent');
+ if (!end) {
+ return;
+ }
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ // On non-Firefox browsers, the selection will feel better if the height
+ // of the endOfContent div will be adjusted to start at mouse click
+ // location -- this will avoid flickering when selections moves up.
+ // However it does not work when selection started on empty space.
+ var adjustTop = e.target !== div;
+ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
+ adjustTop = adjustTop && window.getComputedStyle(end).
+ getPropertyValue('-moz-user-select') !== 'none';
+ }
+ if (adjustTop) {
+ var divBounds = div.getBoundingClientRect();
+ var r = Math.max(0, (e.pageY - divBounds.top) / divBounds.height);
+ end.style.top = (r * 100).toFixed(2) + '%';
+ }
+ }
+ end.classList.add('active');
+ });
+
+ div.addEventListener('mouseup', function (e) {
+ if (self.enhanceTextSelection && self.textLayerRenderTask) {
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ expandDivsTimer = setTimeout(function() {
+ if (self.textLayerRenderTask) {
+ self.textLayerRenderTask.expandTextDivs(false);
+ }
+ expandDivsTimer = null;
+ }, 300);
+ } else {
+ self.textLayerRenderTask.expandTextDivs(false);
+ }
+ return;
+ }
+ var end = div.querySelector('.endOfContent');
+ if (!end) {
+ return;
+ }
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ end.style.top = '';
+ }
+ end.classList.remove('active');
+ });
+};
+
diff --git a/files_reader/vendor/pdfjs/controllers/textlayer_controller.js.simple b/files_reader/vendor/pdfjs/controllers/textlayer_controller.js.simple
new file mode 100644
index 0000000..087f2d5
--- /dev/null
+++ b/files_reader/vendor/pdfjs/controllers/textlayer_controller.js.simple
@@ -0,0 +1,185 @@
+PDFJS.reader.TextLayerController = function() {
+
+ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
+ var textLayerFrag = document.createDocumentFragment();
+
+ this.textLayerDiv = textLayerDiv;
+ this.layoutDone = false;
+ this.divContentDone = false;
+ this.pageIdx = pageIdx;
+ this.matches = [];
+
+ this.beginLayout = function textLayerBuilderBeginLayout() {
+ this.textDivs = [];
+ this.renderingDone = false;
+ };
+
+ this.endLayout = function textLayerBuilderEndLayout() {
+ this.layoutDone = true;
+ this.insertDivContent();
+ };
+
+ this.renderLayer = function textLayerBuilderRenderLayer() {
+ var textDivs = this.textDivs;
+ var bidiTexts = this.textContent.bidiTexts;
+ var textLayerDiv = this.textLayerDiv;
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+
+ // No point in rendering so many divs as it'd make the browser unusable
+ // even after the divs are rendered
+ var MAX_TEXT_DIVS_TO_RENDER = 100000;
+ if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
+ return;
+
+ for (var i = 0, ii = textDivs.length; i < ii; i++) {
+ var textDiv = textDivs[i];
+ if ('isWhitespace' in textDiv.dataset) {
+ continue;
+ }
+ textLayerFrag.appendChild(textDiv);
+
+ ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
+ var width = ctx.measureText(textDiv.textContent).width;
+
+ if (width > 0) {
+ var textScale = textDiv.dataset.canvasWidth / width;
+
+ var transform = 'scale(' + textScale + ', 1)';
+ if (bidiTexts[i].dir === 'ttb') {
+ transform = 'rotate(90deg) ' + transform;
+ }
+ CustomStyle.setProp('transform', textDiv, transform);
+ CustomStyle.setProp('transformOrigin', textDiv, '0% 0%');
+
+ textLayerDiv.appendChild(textDiv);
+ }
+ }
+
+ this.renderingDone = true;
+
+ textLayerDiv.appendChild(textLayerFrag);
+ };
+
+ this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
+ // Schedule renderLayout() if user has been scrolling, otherwise
+ // run it right away
+ var RENDER_DELAY = 200; // in ms
+ var self = this;
+ //0 was originally PDFView.lastScroll
+ if (Date.now() - 0 > RENDER_DELAY) {
+ // Render right away
+ this.renderLayer();
+ } else {
+ // Schedule
+ if (this.renderTimer)
+ clearTimeout(this.renderTimer);
+ this.renderTimer = setTimeout(function () {
+ self.setupRenderLayoutTimer();
+ }, RENDER_DELAY);
+ }
+ };
+
+ this.appendText = function textLayerBuilderAppendText(geom) {
+ var textDiv = document.createElement('div');
+
+ // vScale and hScale already contain the scaling to pixel units
+ var fontHeight = geom.fontSize * Math.abs(geom.vScale);
+ textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
+ textDiv.dataset.fontName = geom.fontName;
+
+ textDiv.style.fontSize = fontHeight + 'px';
+ textDiv.style.fontFamily = geom.fontFamily;
+ textDiv.style.left = geom.x + 'px';
+ textDiv.style.top = (geom.y - fontHeight) + 'px';
+
+ // The content of the div is set in the `setTextContent` function.
+
+ this.textDivs.push(textDiv);
+ };
+
+ this.insertDivContent = function textLayerUpdateTextContent() {
+ // Only set the content of the divs once layout has finished, the content
+ // for the divs is available and content is not yet set on the divs.
+ if (!this.layoutDone || this.divContentDone || !this.textContent)
+ return;
+
+ this.divContentDone = true;
+
+ var textDivs = this.textDivs;
+ var bidiTexts = this.textContent.bidiTexts;
+
+ for (var i = 0; i < bidiTexts.length; i++) {
+ var bidiText = bidiTexts[i];
+ var textDiv = textDivs[i];
+ if (!/\S/.test(bidiText.str)) {
+ textDiv.dataset.isWhitespace = true;
+ continue;
+ }
+
+ textDiv.textContent = bidiText.str;
+ // bidiText.dir may be 'ttb' for vertical texts.
+ textDiv.dir = bidiText.dir === 'rtl' ? 'rtl' : 'ltr';
+ }
+
+ this.setupRenderLayoutTimer();
+ };
+
+ this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
+ this.textContent = textContent;
+ this.insertDivContent();
+ };
+ };
+};
+
+var CustomStyle = (function CustomStyleClosure() {
+
+ // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
+ // animate-css-transforms-firefox-webkit.html
+ // in some versions of IE9 it is critical that ms appear in this list
+ // before Moz
+ var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
+ var _cache = { };
+
+ function CustomStyle() {
+ }
+
+ CustomStyle.getProp = function get(propName, element) {
+ // check cache only when no element is given
+ if (arguments.length == 1 && typeof _cache[propName] == 'string') {
+ return _cache[propName];
+ }
+
+ element = element || document.documentElement;
+ var style = element.style, prefixed, uPropName;
+
+ // test standard property first
+ if (typeof style[propName] == 'string') {
+ return (_cache[propName] = propName);
+ }
+
+ // capitalize
+ uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
+
+ // test vendor specific properties
+ for (var i = 0, l = prefixes.length; i < l; i++) {
+ prefixed = prefixes[i] + uPropName;
+ if (typeof style[prefixed] == 'string') {
+ return (_cache[propName] = prefixed);
+ }
+ }
+
+ //if all fails then set to undefined
+ return (_cache[propName] = 'undefined');
+ };
+
+ CustomStyle.setProp = function set(propName, element, str) {
+ var prop = this.getProp(propName);
+ if (prop != 'undefined')
+ element.style[prop] = str;
+ };
+
+ return CustomStyle;
+})();
+
+
diff --git a/files_reader/vendor/pdfjs/css/.main.css.swp b/files_reader/vendor/pdfjs/css/.main.css.swp
index 2dc7fb2..a769684 100644
Binary files a/files_reader/vendor/pdfjs/css/.main.css.swp and b/files_reader/vendor/pdfjs/css/.main.css.swp differ
diff --git a/files_reader/vendor/pdfjs/css/main.css b/files_reader/vendor/pdfjs/css/main.css
index 50bb4c9..9ec1b45 100644
--- a/files_reader/vendor/pdfjs/css/main.css
+++ b/files_reader/vendor/pdfjs/css/main.css
@@ -41,6 +41,7 @@ body {
#titlebar {
padding: 0.5em;
+ height: 1.4em;
color: #4f4f4f;
font-weight: 100;
font-family: Georgia, "Times New Roman", Times, serif;
@@ -54,7 +55,15 @@ body {
}
#titlebar:hover {
- opacity: 1;
+ opacity: 1;
+ background-color: white;
+ box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4);
+}
+
+.background_visible {
+ background-color: white;
+ box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4);
+ opacity: 1 !important;
}
#titlebar a {
@@ -91,12 +100,24 @@ body {
display: none;
}
+.controls-separator {
+ width: 1em;
+}
+
+.editable {
+ display: inline-block;
+ min-width: 2em;
+ box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.4);
+}
+
#title-controls,
#opener {
margin-left: 0.3em;
margin-right: 0.3em;
position: fixed;
top: 0.3em;
+ display: flex;
+ align-items: center;
}
#opener {
@@ -121,25 +142,61 @@ body {
right: 0;
}
+#zioom_icon {
+ text-align: center;
+ font-family: 'icomoon', 'Helvetica' !important;
+ font-size: 1.2em;
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ border: none;
+}
+
+#zoom_icon {
+ padding: 0 0.5em;
+}
+
+#zoom_options {
+ font-family: "Lucida Console", "Monospace", "Courier New";
+ position: fixed;
+ box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.4);
+ background-color: white;
+ font-size: 1em;
+ padding: 0.2em 0.5em;
+}
+
+.zoom_option:hover {
+ background: black;
+ color: white;
+}
+
#viewer {
- display: flex;
- justify-content: center;
- /* width: 100%; */
- /* height: 80%; */
- height: calc(100% - 2em);
- /* top: 10%; */
- top: 2em;
+ height: 100%;
+ padding: 0;
+ margin: 0;
z-index: 2;
position: relative;
- overflow: hidden;
+ overflow: scroll;
+}
+
+#viewer.flex {
+ /* top: 2em; */
+ display: -webkit-box;
+ display: -moz-box;
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
#viewer canvas {
- position: relative;
-}
-
-#viewer iframe {
- border: none;
+ /* position: relative; */
+ /* height: auto; */
+ /* width: auto; */
}
#prev, #next {
@@ -150,7 +207,7 @@ body {
}
.touch_nav {
- width: 35%;
+ width: 25%;
}
.arrow div {
@@ -295,11 +352,11 @@ body {
width: 80%;
margin-left: 10%;
}
- */
#viewer {
width: 95%;
}
+ */
#prev {
padding-left: 0;
}
diff --git a/files_reader/vendor/pdfjs/css/text_layer_builder.css b/files_reader/vendor/pdfjs/css/text_layer_builder.css
new file mode 100644
index 0000000..ff5e7c8
--- /dev/null
+++ b/files_reader/vendor/pdfjs/css/text_layer_builder.css
@@ -0,0 +1,82 @@
+/* Copyright 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.textLayer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ overflow: hidden;
+ opacity: 0.2;
+ line-height: 1.0;
+}
+
+.textLayer > div {
+ color: transparent;
+ position: absolute;
+ white-space: pre;
+ cursor: text;
+ -webkit-transform-origin: 0% 0%;
+ -moz-transform-origin: 0% 0%;
+ -o-transform-origin: 0% 0%;
+ -ms-transform-origin: 0% 0%;
+ transform-origin: 0% 0%;
+}
+
+.textLayer .highlight {
+ margin: -1px;
+ padding: 1px;
+
+ background-color: rgb(180, 0, 170);
+ border-radius: 4px;
+}
+
+.textLayer .highlight.begin {
+ border-radius: 4px 0px 0px 4px;
+}
+
+.textLayer .highlight.end {
+ border-radius: 0px 4px 4px 0px;
+}
+
+.textLayer .highlight.middle {
+ border-radius: 0px;
+}
+
+.textLayer .highlight.selected {
+ background-color: rgb(0, 100, 0);
+}
+
+.textLayer ::selection { background: rgb(0,0,255); }
+.textLayer ::-moz-selection { background: rgb(0,0,255); }
+
+.textLayer .endOfContent {
+ display: block;
+ position: absolute;
+ left: 0px;
+ top: 100%;
+ right: 0px;
+ bottom: 0px;
+ z-index: -1;
+ cursor: default;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ -moz-user-select: none;
+}
+
+.textLayer .endOfContent.active {
+ top: 0px;
+}
diff --git a/files_reader/vendor/pdfjs/lib/text_layer_builder.js b/files_reader/vendor/pdfjs/lib/text_layer_builder.js
new file mode 100644
index 0000000..110f75b
--- /dev/null
+++ b/files_reader/vendor/pdfjs/lib/text_layer_builder.js
@@ -0,0 +1,424 @@
+/* Copyright 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define('pdfjs-web/text_layer_builder', ['exports', 'pdfjs-web/dom_events',
+ 'pdfjs-web/pdfjs'], factory);
+ } else if (typeof exports !== 'undefined') {
+ factory(exports, require('./dom_events.js'), require('./pdfjs.js'));
+ } else {
+ factory((root.pdfjsWebTextLayerBuilder = {}), root.pdfjsWebDOMEvents,
+ root.pdfjsWebPDFJS);
+ }
+}(this, function (exports, domEvents, pdfjsLib) {
+
+var EXPAND_DIVS_TIMEOUT = 300; // ms
+
+/**
+ * @typedef {Object} TextLayerBuilderOptions
+ * @property {HTMLDivElement} textLayerDiv - The text layer container.
+ * @property {EventBus} eventBus - The application event bus.
+ * @property {number} pageIndex - The page index.
+ * @property {PageViewport} viewport - The viewport of the text layer.
+ * @property {PDFFindController} findController
+ * @property {boolean} enhanceTextSelection - Option to turn on improved
+ * text selection.
+ */
+
+/**
+ * TextLayerBuilder provides text-selection functionality for the PDF.
+ * It does this by creating overlay divs over the PDF text. These divs
+ * contain text that matches the PDF text they are overlaying. This object
+ * also provides a way to highlight text that is being searched for.
+ * @class
+ */
+var TextLayerBuilder = (function TextLayerBuilderClosure() {
+ function TextLayerBuilder(options) {
+ this.textLayerDiv = options.textLayerDiv;
+ this.eventBus = options.eventBus || domEvents.getGlobalEventBus();
+ this.textContent = null;
+ this.renderingDone = false;
+ this.pageIdx = options.pageIndex;
+ this.pageNumber = this.pageIdx + 1;
+ this.matches = [];
+ this.viewport = options.viewport;
+ this.textDivs = [];
+ this.findController = options.findController || null;
+ this.textLayerRenderTask = null;
+ this.enhanceTextSelection = options.enhanceTextSelection;
+ this._bindMouse();
+ }
+
+ TextLayerBuilder.prototype = {
+ /**
+ * @private
+ */
+ _finishRendering: function TextLayerBuilder_finishRendering() {
+ this.renderingDone = true;
+
+ if (!this.enhanceTextSelection) {
+ var endOfContent = document.createElement('div');
+ endOfContent.className = 'endOfContent';
+ this.textLayerDiv.appendChild(endOfContent);
+ }
+
+ this.eventBus.dispatch('textlayerrendered', {
+ source: this,
+ pageNumber: this.pageNumber,
+ numTextDivs: this.textDivs.length,
+ });
+ },
+
+ /**
+ * Renders the text layer.
+ * @param {number} timeout (optional) if specified, the rendering waits
+ * for specified amount of ms.
+ */
+ render: function TextLayerBuilder_render(timeout) {
+ if (!this.textContent || this.renderingDone) {
+ return;
+ }
+ this.cancel();
+
+ this.textDivs = [];
+ var textLayerFrag = document.createDocumentFragment();
+ this.textLayerRenderTask = pdfjsLib.renderTextLayer({
+ textContent: this.textContent,
+ container: textLayerFrag,
+ viewport: this.viewport,
+ textDivs: this.textDivs,
+ timeout: timeout,
+ enhanceTextSelection: this.enhanceTextSelection,
+ });
+ this.textLayerRenderTask.promise.then(function () {
+ this.textLayerDiv.appendChild(textLayerFrag);
+ this._finishRendering();
+ this.updateMatches();
+ }.bind(this), function (reason) {
+ // cancelled or failed to render text layer -- skipping errors
+ });
+ },
+
+ /**
+ * Cancels rendering of the text layer.
+ */
+ cancel: function TextLayerBuilder_cancel() {
+ if (this.textLayerRenderTask) {
+ this.textLayerRenderTask.cancel();
+ this.textLayerRenderTask = null;
+ }
+ },
+
+ setTextContent: function TextLayerBuilder_setTextContent(textContent) {
+ this.cancel();
+ this.textContent = textContent;
+ },
+
+ convertMatches: function TextLayerBuilder_convertMatches(matches,
+ matchesLength) {
+ var i = 0;
+ var iIndex = 0;
+ var bidiTexts = this.textContent.items;
+ var end = bidiTexts.length - 1;
+ var queryLen = (this.findController === null ?
+ 0 : this.findController.state.query.length);
+ var ret = [];
+ if (!matches) {
+ return ret;
+ }
+ for (var m = 0, len = matches.length; m < len; m++) {
+ // Calculate the start position.
+ var matchIdx = matches[m];
+
+ // Loop over the divIdxs.
+ while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
+ iIndex += bidiTexts[i].str.length;
+ i++;
+ }
+
+ if (i === bidiTexts.length) {
+ console.error('Could not find a matching mapping');
+ }
+
+ var match = {
+ begin: {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ }
+ };
+
+ // Calculate the end position.
+ if (matchesLength) { // multiterm search
+ matchIdx += matchesLength[m];
+ } else { // phrase search
+ matchIdx += queryLen;
+ }
+
+ // Somewhat the same array as above, but use > instead of >= to get
+ // the end position right.
+ while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
+ iIndex += bidiTexts[i].str.length;
+ i++;
+ }
+
+ match.end = {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ };
+ ret.push(match);
+ }
+
+ return ret;
+ },
+
+ renderMatches: function TextLayerBuilder_renderMatches(matches) {
+ // Early exit if there is nothing to render.
+ if (matches.length === 0) {
+ return;
+ }
+
+ var bidiTexts = this.textContent.items;
+ var textDivs = this.textDivs;
+ var prevEnd = null;
+ var pageIdx = this.pageIdx;
+ var isSelectedPage = (this.findController === null ?
+ false : (pageIdx === this.findController.selected.pageIdx));
+ var selectedMatchIdx = (this.findController === null ?
+ -1 : this.findController.selected.matchIdx);
+ var highlightAll = (this.findController === null ?
+ false : this.findController.state.highlightAll);
+ var infinity = {
+ divIdx: -1,
+ offset: undefined
+ };
+
+ function beginText(begin, className) {
+ var divIdx = begin.divIdx;
+ textDivs[divIdx].textContent = '';
+ appendTextToDiv(divIdx, 0, begin.offset, className);
+ }
+
+ function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
+ var div = textDivs[divIdx];
+ var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
+ var node = document.createTextNode(content);
+ if (className) {
+ var span = document.createElement('span');
+ span.className = className;
+ span.appendChild(node);
+ div.appendChild(span);
+ return;
+ }
+ div.appendChild(node);
+ }
+
+ var i0 = selectedMatchIdx, i1 = i0 + 1;
+ if (highlightAll) {
+ i0 = 0;
+ i1 = matches.length;
+ } else if (!isSelectedPage) {
+ // Not highlighting all and this isn't the selected page, so do nothing.
+ return;
+ }
+
+ for (var i = i0; i < i1; i++) {
+ var match = matches[i];
+ var begin = match.begin;
+ var end = match.end;
+ var isSelected = (isSelectedPage && i === selectedMatchIdx);
+ var highlightSuffix = (isSelected ? ' selected' : '');
+
+ if (this.findController) {
+ this.findController.updateMatchPosition(pageIdx, i, textDivs,
+ begin.divIdx);
+ }
+
+ // Match inside new div.
+ if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+ // If there was a previous div, then add the text at the end.
+ if (prevEnd !== null) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+ // Clear the divs and set the content until the starting point.
+ beginText(begin);
+ } else {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
+ }
+
+ if (begin.divIdx === end.divIdx) {
+ appendTextToDiv(begin.divIdx, begin.offset, end.offset,
+ 'highlight' + highlightSuffix);
+ } else {
+ appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
+ 'highlight begin' + highlightSuffix);
+ for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
+ textDivs[n0].className = 'highlight middle' + highlightSuffix;
+ }
+ beginText(end, 'highlight end' + highlightSuffix);
+ }
+ prevEnd = end;
+ }
+
+ if (prevEnd) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+ },
+
+ updateMatches: function TextLayerBuilder_updateMatches() {
+ // Only show matches when all rendering is done.
+ if (!this.renderingDone) {
+ return;
+ }
+
+ // Clear all matches.
+ var matches = this.matches;
+ var textDivs = this.textDivs;
+ var bidiTexts = this.textContent.items;
+ var clearedUntilDivIdx = -1;
+
+ // Clear all current matches.
+ for (var i = 0, len = matches.length; i < len; i++) {
+ var match = matches[i];
+ var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+ for (var n = begin, end = match.end.divIdx; n <= end; n++) {
+ var div = textDivs[n];
+ div.textContent = bidiTexts[n].str;
+ div.className = '';
+ }
+ clearedUntilDivIdx = match.end.divIdx + 1;
+ }
+
+ if (this.findController === null || !this.findController.active) {
+ return;
+ }
+
+ // Convert the matches on the page controller into the match format
+ // used for the textLayer.
+ var pageMatches, pageMatchesLength;
+ if (this.findController !== null) {
+ pageMatches = this.findController.pageMatches[this.pageIdx] || null;
+ pageMatchesLength = (this.findController.pageMatchesLength) ?
+ this.findController.pageMatchesLength[this.pageIdx] || null : null;
+ }
+
+ this.matches = this.convertMatches(pageMatches, pageMatchesLength);
+ this.renderMatches(this.matches);
+ },
+
+ /**
+ * Fixes text selection: adds additional div where mouse was clicked.
+ * This reduces flickering of the content if mouse slowly dragged down/up.
+ * @private
+ */
+ _bindMouse: function TextLayerBuilder_bindMouse() {
+ var div = this.textLayerDiv;
+ var self = this;
+ var expandDivsTimer = null;
+
+ div.addEventListener('mousedown', function (e) {
+ if (self.enhanceTextSelection && self.textLayerRenderTask) {
+ self.textLayerRenderTask.expandTextDivs(true);
+ if ((typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) &&
+ expandDivsTimer) {
+ clearTimeout(expandDivsTimer);
+ expandDivsTimer = null;
+ }
+ return;
+ }
+ var end = div.querySelector('.endOfContent');
+ if (!end) {
+ return;
+ }
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ // On non-Firefox browsers, the selection will feel better if the height
+ // of the endOfContent div will be adjusted to start at mouse click
+ // location -- this will avoid flickering when selections moves up.
+ // However it does not work when selection started on empty space.
+ var adjustTop = e.target !== div;
+ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
+ adjustTop = adjustTop && window.getComputedStyle(end).
+ getPropertyValue('-moz-user-select') !== 'none';
+ }
+ if (adjustTop) {
+ var divBounds = div.getBoundingClientRect();
+ var r = Math.max(0, (e.pageY - divBounds.top) / divBounds.height);
+ end.style.top = (r * 100).toFixed(2) + '%';
+ }
+ }
+ end.classList.add('active');
+ });
+
+ div.addEventListener('mouseup', function (e) {
+ if (self.enhanceTextSelection && self.textLayerRenderTask) {
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ expandDivsTimer = setTimeout(function() {
+ if (self.textLayerRenderTask) {
+ self.textLayerRenderTask.expandTextDivs(false);
+ }
+ expandDivsTimer = null;
+ }, EXPAND_DIVS_TIMEOUT);
+ } else {
+ self.textLayerRenderTask.expandTextDivs(false);
+ }
+ return;
+ }
+ var end = div.querySelector('.endOfContent');
+ if (!end) {
+ return;
+ }
+ if (typeof PDFJSDev === 'undefined' ||
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
+ end.style.top = '';
+ }
+ end.classList.remove('active');
+ });
+ },
+ };
+ return TextLayerBuilder;
+})();
+
+/**
+ * @constructor
+ * @implements IPDFTextLayerFactory
+ */
+function DefaultTextLayerFactory() {}
+DefaultTextLayerFactory.prototype = {
+ /**
+ * @param {HTMLDivElement} textLayerDiv
+ * @param {number} pageIndex
+ * @param {PageViewport} viewport
+ * @param {boolean} enhanceTextSelection
+ * @returns {TextLayerBuilder}
+ */
+ createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport,
+ enhanceTextSelection) {
+ return new TextLayerBuilder({
+ textLayerDiv: textLayerDiv,
+ pageIndex: pageIndex,
+ viewport: viewport,
+ enhanceTextSelection: enhanceTextSelection
+ });
+ }
+};
+
+exports.TextLayerBuilder = TextLayerBuilder;
+exports.DefaultTextLayerFactory = DefaultTextLayerFactory;
+}));
diff --git a/files_reader/vendor/pdfjs/pdf.reader.js b/files_reader/vendor/pdfjs/pdf.reader.js
index cbf7675..08624ca 100644
--- a/files_reader/vendor/pdfjs/pdf.reader.js
+++ b/files_reader/vendor/pdfjs/pdf.reader.js
@@ -1,6 +1,8 @@
PDFJS.reader = {};
PDFJS.reader.plugins = {};
+PDFJS.TextLayerBuilder = {};
+
(function(root, $) {
var previousReader = root.pdfReader || {};
@@ -18,13 +20,20 @@ PDFJS.Reader = function(bookPath, _options) {
$viewer = $("#viewer"),
search = window.location.search;
+ var TEXT_RENDER_DELAY = 200, // ms
+ PAGE_RENDER_DELAY = 200; // ms
+
this.settings = this.defaults(_options || {}, {
bookPath: bookPath,
+ textRenderDelay: TEXT_RENDER_DELAY,
+ pageRenderDelay: PAGE_RENDER_DELAY,
+ textSelect: true, // true || false, add selectable text layer
+ doubleBuffer: true, // true || false, draw to off-screen canvas
numPages: 0,
currentPage: 1,
scale: 1,
- sideBySide: true, // when true, render 2 pages side-by-side
oddPageRight: true, // when true, odd pages render on the right side
+ zoomLevel: window.outerWidth > window.outerHeight ? "spread" : "fit_page", // spread, fit_page, fit_width, percentage
history: true,
keyboard: {
32: 'next', // space
@@ -38,7 +47,7 @@ PDFJS.Reader = function(bookPath, _options) {
66: 'bookmark', // b
82: 'reflow', // r
83: 'toggleSidebar', // s
- 84: 'toolbar', // t
+ 84: 'toggleTitlebar', // t
68: 'toggleDay', // d
78: 'toggleNight', // n
70: 'toggleFullscreen', // f
@@ -72,15 +81,32 @@ PDFJS.Reader = function(bookPath, _options) {
this.extra = extra || null;
};
- this.canvaslst = [
- document.getElementById("left"),
- document.getElementById("right")
+ this.resourcelst = [
+ {
+ canvas: document.getElementById("left"),
+ ctx: document.getElementById("left").getContext('2d'),
+ textdiv: document.getElementById("text_left"),
+ textLayer: null,
+ renderTask: null,
+ oscanvas: null,
+ osctx: null,
+ pageNum: null
+ },
+ {
+ canvas: document.getElementById("right"),
+ ctx: document.getElementById("right").getContext('2d'),
+ textdiv: document.getElementById("text_right"),
+ textLayer: null,
+ renderTask: null,
+ oscanvas: null,
+ osctx: null,
+ pageNum: null
+ }
];
- this.contextlst = [
- document.getElementById("left").getContext('2d'),
- document.getElementById("right").getContext('2d')
- ];
+ this.cancelPage = {};
+
+ this.renderQueue = false;
// Overide options with search parameters
if(search) {
@@ -93,13 +119,12 @@ PDFJS.Reader = function(bookPath, _options) {
});
}
+
//this.restoreDefaults(this.settings.session.defaults);
//this.restorePreferences(this.settings.session.preferences);
//this.restoreAnnotations(this.settings.session.annotations);
this.sideBarOpen = false;
this.viewerResized = false;
- //this.sideBySide = window.outerWidth > window.outerHeight ? true : false;
- this.sideBySide = false;
this.pageNumPending = null;
PDFJS.getDocument(reader.settings.bookPath).then(function(_book) {
@@ -121,9 +146,11 @@ PDFJS.Reader = function(bookPath, _options) {
//reader.SearchController = PDFJS.reader.SearchController.call(reader, book);
//reader.MetaController = EPUBJS.reader.MetaController.call(reader, meta);
//reader.TocController = EPUBJS.reader.TocController.call(reader, toc);
+ //reader.TextLayerController = PDFJS.reader.TextLayerController();
//reader.queuePage(reader.settings.currentPage);
- var startPage = reader.settings.oddPageRight ? 0 : 1;
+ console.log(reader.settings);
+ var startPage = (reader.settings.zoomLevel === "spread" && reader.settings.oddPageRight) ? 0 : 1;
reader.queuePage(startPage);
reader.ReaderController.hideLoader();
});
@@ -131,114 +158,273 @@ PDFJS.Reader = function(bookPath, _options) {
return this;
};
+PDFJS.Reader.prototype.setZoom = function(zoom) {
+
+ var reader = this,
+ page = reader.settings.currentPage;
+
+ reader.settings.zoomLevel = zoom;
+ reader.queuePage(page);
+};
+
+PDFJS.Reader.prototype.cancelRender = function (index) {
+
+ var reader = this,
+ resourcelst = reader.resourcelst[index];
+
+ if (resourcelst.renderTask) {
+ console.log("cancel render on canvas " + index + ", pageNum " + resourcelst.pageNum);
+ resourcelst.renderTask.cancel();
+ resourcelst.renderTask = resourcelst.pageNum = null;
+ resourcelst.oscanvas = resourcelst.osctx = null;
+ }
+
+ if (resourcelst.textLayer) {
+ resourcelst.textLayer.cancel();
+ resourcelst.textLayer = null;
+ }
+};
+
PDFJS.Reader.prototype.renderPage = function(pageNum) {
- var reader = this,
- pageShift = this.settings.sideBySide ? 2 : 1,
- oddPageShift = this.settings.oddPageRight ? 0 : 1,
- i = (pageNum - oddPageShift) % pageShift,
- canvas = this.canvaslst[i],
- ctx = this.contextlst[i],
- pixelratio = window.devicePixelRatio,
- max_view_height = parseInt(window.outerHeight * 0.95),
- max_view_width = reader.settings.sideBySide
- ? parseInt((window.outerWidth / 2) * 1)
- : parseInt(window.outerWidth * 1),
- scale,
- transform,
+ var reader = this,
+ $viewer = $("#viewer"),
$page_num = document.getElementById('page_num');
- if (this.settings.sideBySide) {
- this.canvaslst[1].style.display = "block";
+ var index,
+ canvas, // actual canvas
+ ctx, // actual context
+ oscanvas, // off-screen canvas
+ osctx, // off-screen context
+ textdiv,
+ textLayer,
+ outputscale,
+ max_view_width,
+ max_view_height,
+ scale_width,
+ scale_height,
+ view_aspect,
+ document_aspect,
+ scale,
+ viewport,
+ zoom,
+ fraction,
+ offset,
+ renderContext,
+ renderTask,
+ resourcelst;
+
+ max_view_width = window.innerWidth;
+ max_view_height = window.innerHeight;
+
+ if (this.settings.zoomLevel === "spread") {
+
+ // show second canvas
+ reader.resourcelst[1].canvas.style.display = "block";
+ max_view_width /= 2;
+ // select canvas and ctx based on pageNum, pageShift and oddPageRight
+ pageShift = 2;
+ oddPageShift = reader.settings.oddPageRight ? 0 : 1;
+ index = (pageNum - oddPageShift) % pageShift;
+
} else {
- this.canvaslst[1].style.display = "none";
+
+ index = 0;
+ // hide second canvas
+ reader.resourcelst[1].canvas.style.display = "none";
+
+ // don't try to render non-existing page 0 (which is used
+ // to indicate the empty left page when oddPageRight === true)
+ if (pageNum === 0)
+ pageNum++;
+
}
+ resourcelst = reader.resourcelst[index];
+
+ canvas = resourcelst.canvas;
+ ctx = resourcelst.ctx;
+ textdiv = resourcelst.textdiv;
+ outputscale = reader.getOutputScale(resourcelst.ctx);
+ fraction = reader.approximateFraction(outputscale);
+
if (pageNum <= this.settings.numPages && pageNum >= 1) {
- this.pageRendering = true;
+ if (resourcelst.renderTask) {
+ resourcelst.renderTask.cancel();
+ resourcelst.renderTask = null;
+ }
+
+ if (resourcelst.textLayer) {
+ resourcelst.textLayer.cancel();
+ resourcelst.textLayer = null;
+ }
+
+ resourcelst.pageNum = pageNum;
+
+ if (reader.cancelPage[pageNum])
+ delete reader.cancelPage[pageNum];
this.book.getPage(pageNum).then(function(page) {
- console.log(page);
+ //console.log(page);
var page_width = page.pageInfo.view[2];
var page_height = page.pageInfo.view[3];
+ document_aspect = parseFloat(page_width / page_height);
+ view_aspect = parseFloat(max_view_width / max_view_height);
- var scale_height = parseFloat(max_view_height / page_height);
- var scale_width = parseFloat(max_view_width / page_width);
- var document_aspect = parseFloat(page_width / page_height);
- var view_aspect = parseFloat(max_view_width / max_view_height);
+ scale_height = parseFloat(max_view_height / page_height);
+ scale_width = parseFloat(max_view_width / page_width);
- console.log(max_view_width
- + " " + max_view_height
- + " " + page_width
- + " " + page_height
- + " " + document_aspect
- + " " + view_aspect
- + " " + scale_width
- + " " + scale_height
- + " " + pixelratio);
+ /*
+ console.log("m_v_w: " + max_view_width
+ + " m_v_h: " + max_view_height
+ + " p_w: " + page_width
+ + " p_h: " + page_height
+ + " d_a: " + document_aspect
+ + " v_a: " + view_aspect
+ + " s_w: " + scale_width
+ + " s_h: " + scale_height
+ + " o: " + outputscale);
+ console.log("fraction: ");
+ console.log(fraction);
+ */
- scale = Math.min(scale_width, scale_height) / pixelratio;
+ switch (reader.settings.zoomLevel) {
- if (scale_width < scale_height) {
- canvas.width = max_view_width;
- canvas.height = parseInt(page_height * scale_width);
- canvas.style.width = parseInt(max_view_width / pixelratio);
- scale = scale_width;
- } else {
- canvas.height = max_view_height;
- canvas.width = parseInt(page_width * scale_height);
- canvas.style.width = parseInt(max_view_width / pixelratio);
- scale = scale_height;
+ case "spread":
+
+ // INTENTIONAL FALL-THROUGH
+
+ case "fit_page":
+
+ $viewer.addClass("flex");
+
+ if (scale_width > scale_height) {
+ scale = scale_height;
+ canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
+ canvas.width = reader.roundToDivide(parseInt(canvas.height * document_aspect), fraction[0]);
+ } else {
+ scale = scale_width;
+ canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0])
+ canvas.height = reader.roundToDivide(parseInt(canvas.width / document_aspect), fraction[0]);
+ }
+
+ break;
+
+ case "fit_width":
+
+ $viewer.removeClass("flex");
+
+ if (scale_width < scale_height) {
+ scale = scale_height;
+ canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
+ canvas.width = reader.roundToDivide(parseInt(canvas.height * document_aspect), fraction[0]);
+ } else {
+ scale = scale_width;
+ canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0])
+ canvas.height = reader.roundToDivide(parseInt(canvas.width / document_aspect), fraction[0]);
+ }
+
+ break;
+
+ default:
+
+ $viewer.removeClass("flex");
+ scale = parseFloat(reader.settings.zoomLevel);
+ canvas.width = reader.roundToDivide(parseInt(page_width * scale * outputscale), fraction[0]); ;
+ canvas.height = reader.roundToDivide(parseInt(page_height * scale * outputscale), fraction[0]);
+ break;
}
- if (document_aspect < view_aspect) {
- console.log("document aspect < view aspect, aspect ratio " + document_aspect);
- //canvas.height = parseInt(max_view_height / pixelratio);
- //canvas.width = parseInt(canvas.height * document_aspect);
- //scale = parseFloat(scale_height / pixelratio);
- transform = [ 1, 0, 0, 1, parseInt((canvas.width - (page_width*scale)) / 2), 0 ];
+ //console.log(canvas.width + " " + canvas.height);
+
+ transform = (outputscale === 1)
+ ? null
+ : [outputscale, 0, 0, outputscale, 0, 0];
+
+ viewport = page.getViewport(scale);
+
+ if (outputscale !== 1) {
+ canvas.style.width = reader.roundToDivide(viewport.width, fraction[1]) + 'px';
+ canvas.style.height = reader.roundToDivide(viewport.height, fraction[1]) + 'px';
} else {
- console.log("document aspect > view_aspect, aspect ratio " + document_aspect);
-
- //canvas.height = parseInt(max_view_height / pixelratio);
- //canvas.width = parseInt(canvas.height * document_aspect);
- //canvas.width = parseInt(max_view_width / pixelratio);
- //canvas.height = parseInt(canvas.width * document_aspect);
- //scale = parseFloat(scale_width / pixelratio);
- canvas.style.top = parseInt((max_view_height - canvas.height) / 2);
- transform = [ 1, 0, 0, 1, 0, parseInt((canvas.height - (page_height*scale)) / 2) ];
+ canvas.style.width = "";
+ canvas.style.height = "";
}
- console.log(canvas.width + " " + canvas.height);
- var viewport = page.getViewport(scale);
- console.log(viewport);
+ /* textlayer */
+ if (reader.settings.textSelect) {
+ textdiv.style.width = reader.roundToDivide(viewport.width, fraction[1]) + 'px';
+ textdiv.style.height = reader.roundToDivide(viewport.height, fraction[1]) + 'px';
+ offset = $(canvas).offset();
+ $(textdiv).offset({
+ top: offset.top,
+ left: offset.left
+ });
+ page.getTextContent().then(function (textContent) {
+ resourcelst.textLayer = textLayer = new PDFJS.Reader.TextLayerController({
+ textLayerDiv: textdiv,
+ pageIdx: pageNum - 1,
+ viewport: viewport,
+ enhanceTextSelection: true
+ });
+ textLayer.setTextContent(textContent);
+ });
+ } else {
+ resourcelst.textLayer = textLayer = null;
+ }
- var renderContext = {
- canvasContext: ctx,
- viewport: viewport
- //transform: transform
+ /* /textLayer */
+
+ if (reader.settings.doubleBuffer) {
+ resourcelst.oscanvas = oscanvas = document.createElement("canvas");
+ resourcelst.osctx = context = osctx = oscanvas.getContext('2d');
+ oscanvas.width = canvas.width;
+ oscanvas.height = canvas.height;
+ } else {
+ context = ctx;
+ }
+
+ renderContext = {
+ canvasContext: context,
+ viewport: viewport,
+ transform: transform,
+ textLayer: textLayer
};
- var renderTask = page.render(renderContext);
- renderTask.promise.then(function() {
- reader.pageRendering = false;
- if (reader.pageNumPending !== null) {
- reader.renderPage(reader.pageNumPending);
- reader.pageNumPending = null;
+ resourcelst.renderTask = renderTask = page.render(renderContext);
+
+ renderTask.promise.then(
+ function pdfPageRenderCallback (something) {
+ if (reader.cancelPage[pageNum] === undefined) {
+ console.log("finished rendering page " + pageNum);
+ if (reader.settings.doubleBuffer)
+ ctx.drawImage(oscanvas, 0, 0);
+ if (textLayer)
+ textLayer.render(reader.settings.textRenderDelay);
+ } else {
+ console.log("rendering page " + pageNum + " finished but cancelled");
+ }
+ },
+ function pdfPageRenderError(error) {
+ console.log("pdfPageRenderError: " + error);
}
- });
+ );
});
} else {
-
- canvas.width = max_view_width;
- canvas.height = max_view_height;
+ // clear canvas, use maximum size
+ canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0]);
+ canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
+ if (outputscale !== 1) {
+ canvas.style.width = reader.roundToDivide(max_view_width, fraction[1]) + 'px';
+ canvas.style.height = reader.roundToDivide(max_view_height, fraction[1]) + 'px';
+ }
ctx.clearRect(0, 0, canvas.width, canvas.height);
-
}
- if (i === 0) {
+ /*
+ if (index === 0) {
if (pageNum > 0) {
$page_num.textContent = pageNum.toString();
@@ -251,7 +437,7 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
$page_num.textContent = pageNum.toString();
- } else {
+ } else if (pageNum <= reader.settings.numPages) {
var text = $page_num.textContent;
text += "-" + pageNum.toString();
@@ -259,40 +445,92 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
}
}
+ */
};
-PDFJS.Reader.prototype.queuePage = function(pageNum) {
+PDFJS.Reader.prototype.queuePage = function(page) {
- var pageShift = this.settings.sideBySide ? 2 : 1;
+ //var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
+ //
+ var reader = this,
+ zoom = reader.settings.zoomLevel,
+ oddPageRight = reader.settings.oddPageRight,
+ pageShift,
+ $page_num = document.getElementById('page_num'),
+ text;
- if (this.pageRendering) {
- this.pageNumPending = pageNum;
- } else {
- for (var i = 0; i < pageShift; i++) {
- this.renderPage(pageNum + i);
+ if (zoom === "spread") {
+ pageShift = 2;
+ if (oddPageRight === true) {
+ page -= page % 2;
+ } else {
+ page -= (page + 1) % 2;
}
+
+ console.log("page: " + page);
+
+ if (page >= 0 && page <= reader.settings.numPages) {
+ if (page === reader.settings.numPages) {
+ text = page.toString();
+ } else if (page === 0) {
+ text = "1";
+ } else {
+ text = page.toString() + "-" + parseInt(page+1).toString();
+ }
+
+ $page_num.textContent = text;
+ }
+
+ } else {
+ pageShift = 1;
+ if (page >= 1 && page <= reader.settings.numPages)
+ $page_num.textContent = page.toString();
}
+
+ reader.settings.currentPage = page;
+
+ if (typeof reader.renderQueue === 'number') {
+ window.clearTimeout(reader.renderQueue);
+ reader.renderQueue = false;
+ }
+
+ reader.renderQueue = window.setTimeout(function queuePages() {
+ for (var i = 0; i < pageShift; i++) {
+ reader.renderPage(page + i);
+ }
+ }, reader.settings.pageRenderDelay);
};
PDFJS.Reader.prototype.prevPage = function() {
- var pageShift = this.settings.sideBySide ? 2 : 1;
+ var reader = this;
+
+ var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
+
var oddPageShift = this.settings.oddPageRight ? 0 : 1;
if (this.settings.currentPage - pageShift < oddPageShift) {
return;
} else {
+ for (var i = 0; i < pageShift; i++) {
+ reader.cancelPage[this.settings.currentPage - i] = true;
+ }
this.queuePage(this.settings.currentPage - pageShift);
}
};
PDFJS.Reader.prototype.nextPage = function() {
- var pageShift = this.settings.sideBySide ? 2 : 1;
+ var reader = this;
+
+ var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
if (this.settings.currentPage + pageShift > this.settings.numPages) {
return;
} else {
+ for (var i = 0; i < pageShift; i++) {
+ reader.cancelPage[this.settings.currentPage + i] = true;
+ }
this.queuePage(this.settings.currentPage + pageShift);
}
};
@@ -310,3 +548,76 @@ PDFJS.Reader.prototype.defaults = function (obj) {
PDFJS.Reader.prototype.setScale = function (scale) {
};
+
+PDFJS.Reader.prototype.getOutputScale = function (ctx) {
+ var devicePixelRatio = window.devicePixelRatio || 1,
+ backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
+ ctx.mozBackingStorePixelRatio ||
+ ctx.msBackingStorePixelRatio ||
+ ctx.oBackingStorePixelRatio ||
+ ctx.backingStorePixelRatio || 1,
+ pixelRatio = devicePixelRatio / backingStoreRatio;
+
+ return pixelRatio;
+};
+
+PDFJS.Reader.prototype.roundToDivide = function (x, div) {
+
+ var r = x % div;
+
+ return r === 0
+ ? x
+ : Math.round(x - r + div);
+};
+
+
+/**
+ * Approximates float number as a fraction using Farey sequence (max order
+ * of 8).
+ * @param {number} x - Positive float number.
+ * @returns {Array} Estimated fraction: the first array item is a numerator,
+ * the second one is a denominator.
+ */
+PDFJS.Reader.prototype.approximateFraction = function (x) {
+
+ // Fast paths for int numbers or their inversions.
+ if (Math.floor(x) === x) {
+ return [x, 1];
+ }
+ var xinv = 1 / x;
+ var limit = 8;
+ if (xinv > limit) {
+ return [1, limit];
+ } else if (Math.floor(xinv) === xinv) {
+ return [1, xinv];
+ }
+
+ var x_ = x > 1 ? xinv : x;
+ // a/b and c/d are neighbours in Farey sequence.
+ var a = 0, b = 1, c = 1, d = 1;
+ // Limiting search to order 8.
+ while (true) {
+ // Generating next term in sequence (order of q).
+ var p = a + c, q = b + d;
+ if (q > limit) {
+ break;
+ }
+ if (x_ <= p / q) {
+ c = p; d = q;
+ } else {
+ a = p; b = q;
+ }
+ }
+ var result;
+ // Select closest of the neighbours to x.
+ if (x_ - a / b < c / d - x_) {
+ result = x_ === x ? [a, b] : [b, a];
+ } else {
+ result = x_ === x ? [c, d] : [d, c];
+ }
+ return result;
+};
+
+PDFJS.Reader.prototype.isMobile = function () {
+ return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
+};