mirror of
https://github.com/Yetangitu/owncloud-apps.git
synced 2025-10-02 14:49:17 +02:00
files_reader: More PDF madness, next/previous search hit navigation w/highlighting, etc...
This commit is contained in:
parent
640256c49b
commit
f909cd0f1b
11 changed files with 448 additions and 458 deletions
Binary file not shown.
|
@ -1,4 +1,3 @@
|
||||||
- search
|
|
||||||
- bookmarks
|
- bookmarks
|
||||||
- annotations
|
- annotations
|
||||||
- settings
|
- settings
|
||||||
|
@ -6,5 +5,7 @@
|
||||||
- rtl and ltr
|
- rtl and ltr
|
||||||
|
|
||||||
- test canvas size restriction
|
- test canvas size restriction
|
||||||
|
- test internal links
|
||||||
|
|
||||||
|
- add IDs to highlights so they can be marked when hovered - or when related list item is hovered
|
||||||
|
|
||||||
- add preload/postload of X pages around current position (optional), store resulting rendered pages in off-screen canvas?
|
|
||||||
|
|
|
@ -258,6 +258,8 @@
|
||||||
<a id="slider" class="icon-menu">
|
<a id="slider" class="icon-menu">
|
||||||
<?php p($l->t("menu")); ?>
|
<?php p($l->t("menu")); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<div id="status_message_left">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="metainfo" class="nightshift">
|
<div id="metainfo" class="nightshift">
|
||||||
<span id="book-title">
|
<span id="book-title">
|
||||||
|
@ -273,23 +275,11 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="title-controls">
|
<div id="title-controls">
|
||||||
<!-- select works fine, except for the fact that - as usual - apple mobile does not support icons...
|
<div id="status_message_right">
|
||||||
<label for="zoomlevel">zoom: </label>
|
</div>
|
||||||
<select id="zoomlevel">
|
<div id="match_count">
|
||||||
<option value="spread" data-icon="" data-text="2-page"></option>
|
</div>
|
||||||
<option value="fit_page" data-icon="" data-text="fit page"></option>
|
<!-- select works fine, except for the fact that - as usual - apple mobile acts up... -->
|
||||||
<option value="fit_width" data-icon="" data-text="fit width"></option>
|
|
||||||
<option value="0.25" class="text">25%</option>
|
|
||||||
<option value="0.5" class="text">50%</option>
|
|
||||||
<option value="0.75" class="text">75%</option>
|
|
||||||
<option value="1" class="text">100%</option>
|
|
||||||
<option value="1.25" class="text">125%</option>
|
|
||||||
<option value="1.5" class="text">150%</option>
|
|
||||||
<option value="2" class="text">200%</option>
|
|
||||||
<option value="3" class="text">300%</option>
|
|
||||||
<option value="4" class="text">400%</option>
|
|
||||||
</select>
|
|
||||||
-->
|
|
||||||
<div id="zoom_options" class="hide">
|
<div id="zoom_options" class="hide">
|
||||||
<div class="zoom_option icon-double_page_mode" data-value="spread" data-class="icon-double_page_mode" data-text=""></div>
|
<div class="zoom_option icon-double_page_mode" data-value="spread" data-class="icon-double_page_mode" data-text=""></div>
|
||||||
<div class="zoom_option icon-single_page_mode" data-value="fit_page" data-class="icon-single_page_mode" data-text=""></div>
|
<div class="zoom_option icon-single_page_mode" data-value="fit_page" data-class="icon-single_page_mode" data-text=""></div>
|
||||||
|
|
|
@ -24,11 +24,18 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
$rotate_left = $("#rotate_left"),
|
$rotate_left = $("#rotate_left"),
|
||||||
$rotate_right = $("#rotate_right"),
|
$rotate_right = $("#rotate_right"),
|
||||||
$page_num = $("#page_num"),
|
$page_num = $("#page_num"),
|
||||||
$total_pages = $("#total_pages");
|
$total_pages = $("#total_pages"),
|
||||||
|
$status_message_left = $("#status_message_left"),
|
||||||
|
$status_message_right = $("#status_message_right");
|
||||||
|
|
||||||
|
var STATUS_MESSAGE_LENGTH = 30,
|
||||||
|
STATUS_MESSAGE_TIMEOUT = 3000,
|
||||||
|
status_timeout_left,
|
||||||
|
status_timeout_right;
|
||||||
|
|
||||||
if (reader.isMobile() === true) {
|
if (reader.isMobile() === true) {
|
||||||
$titlebar.addClass("background_visible");
|
$titlebar.addClass("background_visible");
|
||||||
};
|
}
|
||||||
|
|
||||||
var show = function () {
|
var show = function () {
|
||||||
$titlebar.removeClass("hide");
|
$titlebar.removeClass("hide");
|
||||||
|
@ -46,6 +53,31 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
reader.ControlsController.toggle();
|
reader.ControlsController.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var setStatus = function (message, right) {
|
||||||
|
|
||||||
|
$status_message = (right) ? $status_message_right : $status_message_left;
|
||||||
|
status_timeout = (right) ? status_timeout_right : status_timeout_left;
|
||||||
|
|
||||||
|
$status_message[0].textContent = reader.ellipsize(message, STATUS_MESSAGE_LENGTH);
|
||||||
|
//$status_message[0].textContent = message;
|
||||||
|
|
||||||
|
if (typeof status_timeout === "number") {
|
||||||
|
clearTimeout(status_timeout);
|
||||||
|
status_timeout = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_timeout = setTimeout(function () {
|
||||||
|
$status_message[0].textContent = "";
|
||||||
|
}, STATUS_MESSAGE_TIMEOUT);
|
||||||
|
|
||||||
|
if (right) {
|
||||||
|
status_timeout_right = status_timeout;
|
||||||
|
} else {
|
||||||
|
status_timeout_left = status_timeout;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
var fullscreen = false;
|
var fullscreen = false;
|
||||||
|
|
||||||
$slider.on("click", function () {
|
$slider.on("click", function () {
|
||||||
|
@ -105,21 +137,8 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* select works fine on most browsers, but - of course - apple mobile has 'special needs'
|
/* select works fine on most browsers, but - of course - apple mobile has 'special needs' so
|
||||||
* 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...
|
* 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 */
|
/* custom select, supports icons in drop-down list */
|
||||||
|
@ -245,7 +264,7 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.stopPropagation;
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,21 +292,23 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
text;
|
text;
|
||||||
|
|
||||||
if (zoom === "spread") {
|
if (zoom === "spread") {
|
||||||
if (oddPageRight === true) {
|
if (oddPageRight === true) {
|
||||||
page -= page % 2;
|
page -= page % 2;
|
||||||
} else {
|
|
||||||
page -= (page + 1) % 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page >= 0 && page <= total_pages) {
|
|
||||||
if (page === total_pages) {
|
|
||||||
text = reader.getPageLabel(page);
|
|
||||||
} else if (page === 0) {
|
|
||||||
text = reader.getPageLabel(page + 1);
|
|
||||||
} else {
|
} else {
|
||||||
text = reader.getPageLabel(page) + "-" + reader.getPageLabel(page + 1);
|
page -= (page + 1) % 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (page >= 0 && page <= total_pages) {
|
||||||
|
if (page === total_pages) {
|
||||||
|
text = reader.getPageLabel(page);
|
||||||
|
} else if (page === 0) {
|
||||||
|
text = reader.getPageLabel(page + 1);
|
||||||
|
} else {
|
||||||
|
text = reader.getPageLabel(page) + "-" + reader.getPageLabel(page + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = reader.getPageLabel(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
$page_num[0].textContent = text;
|
$page_num[0].textContent = text;
|
||||||
|
@ -332,6 +353,7 @@ PDFJS.reader.ControlsController = function(book) {
|
||||||
"setZoomIcon": setZoomIcon,
|
"setZoomIcon": setZoomIcon,
|
||||||
"setRotateIcon": setRotateIcon,
|
"setRotateIcon": setRotateIcon,
|
||||||
"setCurrentPage": setCurrentPage,
|
"setCurrentPage": setCurrentPage,
|
||||||
"setPageCount": setPageCount
|
"setPageCount": setPageCount,
|
||||||
|
"setStatus": setStatus
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
PDFJS.reader.ReaderController = function(book) {
|
PDFJS.reader.ReaderController = function() {
|
||||||
var $main = $("#main"),
|
var $main = $("#main"),
|
||||||
$viewer = $("#viewer"),
|
$viewer = $("#viewer"),
|
||||||
$divider = $("#divider"),
|
$divider = $("#divider"),
|
||||||
|
@ -13,7 +13,8 @@ PDFJS.reader.ReaderController = function(book) {
|
||||||
$bookmark = $("#bookmark"),
|
$bookmark = $("#bookmark"),
|
||||||
$note = $("#note"),
|
$note = $("#note"),
|
||||||
$rotate_left = $("#rotate_left"),
|
$rotate_left = $("#rotate_left"),
|
||||||
$rotate_right = $("#rotate_right");
|
$rotate_right = $("#rotate_right"),
|
||||||
|
$clear_search = $("#clear_search");
|
||||||
|
|
||||||
var reader = this,
|
var reader = this,
|
||||||
book = this.book,
|
book = this.book,
|
||||||
|
@ -76,6 +77,8 @@ PDFJS.reader.ReaderController = function(book) {
|
||||||
|
|
||||||
var page_no = false;
|
var page_no = false;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
switch (settings.keyboard[e.keyCode]) {
|
switch (settings.keyboard[e.keyCode]) {
|
||||||
case 'previous':
|
case 'previous':
|
||||||
$prev.click();
|
$prev.click();
|
||||||
|
@ -121,6 +124,22 @@ PDFJS.reader.ReaderController = function(book) {
|
||||||
break;
|
break;
|
||||||
case 'cycleZoom':
|
case 'cycleZoom':
|
||||||
reader.cycleZoom();
|
reader.cycleZoom();
|
||||||
|
break;
|
||||||
|
case 'previousMatch':
|
||||||
|
reader.SearchController.nextMatch(true);
|
||||||
|
break;
|
||||||
|
case 'nextMatch':
|
||||||
|
reader.SearchController.nextMatch(false);
|
||||||
|
break;
|
||||||
|
case 'clearSearch':
|
||||||
|
$clear_search.click();
|
||||||
|
break;
|
||||||
|
case 'search':
|
||||||
|
if (e.shiftKey) {
|
||||||
|
reader.SidebarController.changePanelTo("Search");
|
||||||
|
reader.SearchController.show();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("unsupported keyCode: " + e.keyCode);
|
console.log("unsupported keyCode: " + e.keyCode);
|
||||||
|
@ -129,7 +148,7 @@ PDFJS.reader.ReaderController = function(book) {
|
||||||
if (page_no) {
|
if (page_no) {
|
||||||
reader.queuePage(page_no);
|
reader.queuePage(page_no);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', keyCommands, false);
|
document.addEventListener('keydown', keyCommands, false);
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,7 @@
|
||||||
var FindStates = {
|
|
||||||
FIND_FOUND: 0,
|
|
||||||
FIND_NOTFOUND: 1,
|
|
||||||
FIND_WRAPPED: 2,
|
|
||||||
FIND_PENDING: 3
|
|
||||||
};
|
|
||||||
|
|
||||||
var FIND_SCROLL_OFFSET_TOP = -50;
|
|
||||||
var FIND_SCROLL_OFFSET_LEFT = -400;
|
|
||||||
|
|
||||||
var CHARACTERS_TO_NORMALIZE = {
|
|
||||||
'\u2018': '\'', // Left single quotation mark
|
|
||||||
'\u2019': '\'', // Right single quotation mark
|
|
||||||
'\u201A': '\'', // Single low-9 quotation mark
|
|
||||||
'\u201B': '\'', // Single high-reversed-9 quotation mark
|
|
||||||
'\u201C': '"', // Left double quotation mark
|
|
||||||
'\u201D': '"', // Right double quotation mark
|
|
||||||
'\u201E': '"', // Double low-9 quotation mark
|
|
||||||
'\u201F': '"', // Double high-reversed-9 quotation mark
|
|
||||||
'\u00BC': '1/4', // Vulgar fraction one quarter
|
|
||||||
'\u00BD': '1/2', // Vulgar fraction one half
|
|
||||||
'\u00BE': '3/4', // Vulgar fraction three quarters
|
|
||||||
};
|
|
||||||
|
|
||||||
PDFJS.reader.SearchController = function () {
|
PDFJS.reader.SearchController = function () {
|
||||||
|
|
||||||
var reader = this,
|
var reader = this,
|
||||||
book = this.book,
|
book = this.book;
|
||||||
query = "";
|
|
||||||
|
|
||||||
var $searchBox = $("#searchBox"),
|
var $searchBox = $("#searchBox"),
|
||||||
$clearBtn = $("#searchBox").next(),
|
$clearBtn = $("#searchBox").next(),
|
||||||
|
@ -34,54 +9,53 @@ PDFJS.reader.SearchController = function () {
|
||||||
$searchResults = $("#searchResults"),
|
$searchResults = $("#searchResults"),
|
||||||
$searchView = $("#searchView"),
|
$searchView = $("#searchView"),
|
||||||
$body = $("#viewer iframe").contents().find('body'),
|
$body = $("#viewer iframe").contents().find('body'),
|
||||||
$sidebar = $("#sidebar");
|
$sidebar = $("#sidebar"),
|
||||||
|
$match_count = $("#match_count");
|
||||||
|
|
||||||
var onShow = function() {
|
/* search logic, partly from Mozilla pdfViewer */
|
||||||
$searchView.addClass("open");
|
var CHARACTERS_TO_NORMALIZE = {
|
||||||
$searchBox.focus();
|
'\u2018': '\'', // Left single quotation mark
|
||||||
|
'\u2019': '\'', // Right single quotation mark
|
||||||
|
'\u201A': '\'', // Single low-9 quotation mark
|
||||||
|
'\u201B': '\'', // Single high-reversed-9 quotation mark
|
||||||
|
'\u201C': '"', // Left double quotation mark
|
||||||
|
'\u201D': '"', // Right double quotation mark
|
||||||
|
'\u201E': '"', // Double low-9 quotation mark
|
||||||
|
'\u201F': '"', // Double high-reversed-9 quotation mark
|
||||||
|
'\u00BC': '1/4', // Vulgar fraction one quarter
|
||||||
|
'\u00BD': '1/2', // Vulgar fraction one half
|
||||||
|
'\u00BE': '3/4', // Vulgar fraction three quarters
|
||||||
};
|
};
|
||||||
|
|
||||||
var onHide = function() {
|
var startedTextExtraction = false,
|
||||||
unhighlight();
|
extractTextPromises = [],
|
||||||
$searchView.removeClass("open");
|
matchCount = 0,
|
||||||
};
|
pendingFindMatches = Object.create(null);
|
||||||
|
|
||||||
this.onUpdateResultsCount = null;
|
|
||||||
this.onUpdateState = null;
|
|
||||||
|
|
||||||
// Compile the regular expression for text normalization once.
|
// Compile the regular expression for text normalization once.
|
||||||
var replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
|
var replace = Object.keys(CHARACTERS_TO_NORMALIZE).join(''),
|
||||||
this.normalizationRegex = new RegExp('[' + replace + ']', 'g');
|
normalizationRegex = new RegExp('[' + replace + ']', 'g');
|
||||||
|
|
||||||
var reset = function () {
|
var reset = function () {
|
||||||
this.startedTextExtraction = false;
|
|
||||||
this.extractTextPromises = [];
|
pendingFindMatches = Object.create(null);
|
||||||
this.pendingFindMatches = Object.create(null);
|
reader.search_active = false; // If active, find results will be highlighted.
|
||||||
this.active = false; // If active, find results will be highlighted.
|
reader.pageMatches.length = 0;
|
||||||
this.pageContents = []; // Stores the text for each page.
|
reader.pageMatchesLength = null;
|
||||||
this.pageMatches = [];
|
reader.search_state = null;
|
||||||
this.pageMatchesLength = null;
|
matchCount = 0;
|
||||||
this.matchCount = 0;
|
resetMatchCounter();
|
||||||
this.selected = { // Currently selected match.
|
reader.selected = { // Currently selected match.
|
||||||
pageIdx: -1,
|
pageIdx: -1,
|
||||||
matchIdx: -1
|
matchIdx: -1,
|
||||||
|
at_start: false,
|
||||||
|
at_end: false
|
||||||
};
|
};
|
||||||
this.offset = { // Where the find algorithm currently is in the document.
|
updatePage();
|
||||||
pageIdx: null,
|
|
||||||
matchIdx: null
|
|
||||||
};
|
|
||||||
this.pagesToSearch = null;
|
|
||||||
this.resumePageIdx = null;
|
|
||||||
this.state = null;
|
|
||||||
this.dirtyMatch = false;
|
|
||||||
this.findTimeout = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
|
|
||||||
var normalize = function (text) {
|
var normalize = function (text) {
|
||||||
return text.replace(this.normalizationRegex, function (ch) {
|
return text.replace(normalizationRegex, function (ch) {
|
||||||
return CHARACTERS_TO_NORMALIZE[ch];
|
return CHARACTERS_TO_NORMALIZE[ch];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -151,7 +125,7 @@ PDFJS.reader.SearchController = function () {
|
||||||
}
|
}
|
||||||
matches.push(matchIdx);
|
matches.push(matchIdx);
|
||||||
}
|
}
|
||||||
this.pageMatches[pageIndex] = matches;
|
reader.pageMatches[pageIndex] = matches;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,21 +153,22 @@ PDFJS.reader.SearchController = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Prepare arrays for store the matches.
|
// Prepare arrays for store the matches.
|
||||||
if (!this.pageMatchesLength) {
|
if (!reader.pageMatchesLength) {
|
||||||
this.pageMatchesLength = [];
|
reader.pageMatchesLength = [];
|
||||||
}
|
}
|
||||||
this.pageMatchesLength[pageIndex] = [];
|
reader.pageMatchesLength[pageIndex] = [];
|
||||||
this.pageMatches[pageIndex] = [];
|
reader.pageMatches[pageIndex] = [];
|
||||||
// Sort matchesWithLength, clean up intersecting terms
|
// Sort matchesWithLength, clean up intersecting terms
|
||||||
// and put the result into the two arrays.
|
// and put the result into the two arrays.
|
||||||
_prepareMatches(matchesWithLength, this.pageMatches[pageIndex],
|
_prepareMatches(matchesWithLength, reader.pageMatches[pageIndex],
|
||||||
this.pageMatchesLength[pageIndex]);
|
reader.pageMatchesLength[pageIndex]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var getSnippet = function (pageIndex, position) {
|
var getSnippet = function (pageIndex, position) {
|
||||||
|
|
||||||
var ellipse = '…',
|
var ellipse = '…',
|
||||||
match_length = this.state.query.length,
|
match_length = reader.search_state.query.length,
|
||||||
span = '<span class="search_match">',
|
span = '<span class="search_match">',
|
||||||
span_close = '</span>',
|
span_close = '</span>',
|
||||||
limit = 160 + span.length + span_close.length,
|
limit = 160 + span.length + span_close.length,
|
||||||
|
@ -201,10 +176,10 @@ PDFJS.reader.SearchController = function () {
|
||||||
trailer,
|
trailer,
|
||||||
context;
|
context;
|
||||||
|
|
||||||
leader = this.pageContents[pageIndex].substring(position - limit/2, position);
|
leader = reader.pageContents[pageIndex].substring(position - limit/2, position);
|
||||||
leader = leader.slice(leader.indexOf(" "));
|
leader = leader.slice(leader.indexOf(" "));
|
||||||
trailer = this.pageContents[pageIndex].substring(position + match_length, position + limit/2 + match_length);
|
trailer = reader.pageContents[pageIndex].substring(position + match_length, position + limit/2 + match_length);
|
||||||
query = this.pageContents[pageIndex].substring(position, position + match_length);
|
query = reader.pageContents[pageIndex].substring(position, position + match_length);
|
||||||
|
|
||||||
context = ellipse + leader + span + query + span_close + trailer;
|
context = ellipse + leader + span + query + span_close + trailer;
|
||||||
|
|
||||||
|
@ -215,7 +190,6 @@ PDFJS.reader.SearchController = function () {
|
||||||
|
|
||||||
var listitem = document.createElement("li"),
|
var listitem = document.createElement("li"),
|
||||||
link = document.createElement("a"),
|
link = document.createElement("a"),
|
||||||
id = parseInt(pageIndex + 1) + ":" + position,
|
|
||||||
item = {
|
item = {
|
||||||
url: null,
|
url: null,
|
||||||
dest: null,
|
dest: null,
|
||||||
|
@ -227,10 +201,9 @@ PDFJS.reader.SearchController = function () {
|
||||||
item.dest = [pageIndex,position];
|
item.dest = [pageIndex,position];
|
||||||
|
|
||||||
//link.textContent = getSnippet(pageIndex, position);
|
//link.textContent = getSnippet(pageIndex, position);
|
||||||
link.innerHTML = getSnippet(pageIndex, position);
|
listitem.dataset.index = ++matchCount;
|
||||||
|
link.innerHTML = '<span class="match_label">' + matchCount + '</span>' + getSnippet(pageIndex, position);
|
||||||
listitem.classList.add("list_item");
|
listitem.classList.add("list_item");
|
||||||
listitem.id = "search-"+id;
|
|
||||||
listitem.dataset.position = position;
|
|
||||||
reader.bindLink(link, item);
|
reader.bindLink(link, item);
|
||||||
link.classList.add("search_link");
|
link.classList.add("search_link");
|
||||||
listitem.appendChild(link);
|
listitem.appendChild(link);
|
||||||
|
@ -238,16 +211,40 @@ PDFJS.reader.SearchController = function () {
|
||||||
return listitem;
|
return listitem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var createItemList = function (pageIdx) {
|
||||||
|
|
||||||
|
var currentIdx = reader.settings.currentPage - 1,
|
||||||
|
item,
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
// currentIdx can be up to 2 different from pageIdx due to oddPageFirst and spread rendering
|
||||||
|
if (Math.abs(pageIdx - currentIdx) <= 2)
|
||||||
|
updatePage(pageIdx);
|
||||||
|
var fragment = document.createDocumentFragment();
|
||||||
|
var listitem = document.createElement("li");
|
||||||
|
listitem.textContent="page " + parseInt(pageIdx + 1);
|
||||||
|
listitem.classList.add("search_page_header");
|
||||||
|
fragment.appendChild(listitem);
|
||||||
|
reader.pageMatches[pageIdx].forEach(function (match) {
|
||||||
|
item = createItem(pageIdx, match);
|
||||||
|
item.id = "match:" + pageIdx + ":" + i;
|
||||||
|
item.classList.add("match:" + pageIdx + ":" + i++);
|
||||||
|
fragment.appendChild(item);
|
||||||
|
updateMatchCounter();
|
||||||
|
});
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
var calcFindMatch = function (pageIndex) {
|
var calcFindMatch = function (pageIndex) {
|
||||||
var pageContent = normalize(this.pageContents[pageIndex]);
|
var pageContent = normalize(reader.pageContents[pageIndex]);
|
||||||
var query = normalize(this.state.query);
|
var query = normalize(reader.search_state.query);
|
||||||
var caseSensitive = this.state.caseSensitive;
|
var caseSensitive = reader.search_state.caseSensitive;
|
||||||
var phraseSearch = this.state.phraseSearch;
|
var phraseSearch = reader.search_state.phraseSearch;
|
||||||
var queryLen = query.length;
|
var queryLen = query.length;
|
||||||
|
|
||||||
if (queryLen === 0) {
|
if (queryLen === 0) {
|
||||||
// Do nothing: the matches should be wiped out already.
|
reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,24 +262,25 @@ PDFJS.reader.SearchController = function () {
|
||||||
|
|
||||||
var extractText = function () {
|
var extractText = function () {
|
||||||
|
|
||||||
if (this.startedTextExtraction) {
|
if (startedTextExtraction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.startedTextExtraction = true;
|
startedTextExtraction = true;
|
||||||
|
|
||||||
this.pageContents = [];
|
reader.pageContents = [];
|
||||||
var extractTextPromisesResolves = [];
|
var extractTextPromisesResolves = [];
|
||||||
var numPages = reader.settings.numPages;
|
var numPages = reader.settings.numPages;
|
||||||
|
|
||||||
for (var i = 0; i < numPages; i++) {
|
for (var i = 0; i < numPages; i++) {
|
||||||
this.extractTextPromises.push(new Promise(function (resolve) {
|
extractTextPromises.push(new Promise(function (resolve) {
|
||||||
extractTextPromisesResolves.push(resolve);
|
extractTextPromisesResolves.push(resolve);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
|
||||||
function extractPageText(pageIndex) {
|
function extractPageText(pageIndex) {
|
||||||
reader.getPageTextContent(pageIndex).then(
|
reader.getPageTextContent(pageIndex).then(
|
||||||
function textContentResolved(textContent) {
|
function textContentResolved(textContent) {
|
||||||
|
reader.ControlsController.setStatus("extracting text page " + parseInt(pageIndex + 1),true);
|
||||||
var textItems = textContent.items;
|
var textItems = textContent.items;
|
||||||
var str = [];
|
var str = [];
|
||||||
|
|
||||||
|
@ -291,7 +289,7 @@ PDFJS.reader.SearchController = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the pageContent as a string.
|
// Store the pageContent as a string.
|
||||||
self.pageContents.push(str.join(' ').replace(/\s\s+/g, ' '));
|
reader.pageContents.push(str.join(''));
|
||||||
|
|
||||||
extractTextPromisesResolves[pageIndex](pageIndex);
|
extractTextPromisesResolves[pageIndex](pageIndex);
|
||||||
if ((pageIndex + 1) < reader.settings.numPages) {
|
if ((pageIndex + 1) < reader.settings.numPages) {
|
||||||
|
@ -303,264 +301,211 @@ PDFJS.reader.SearchController = function () {
|
||||||
extractPageText(0);
|
extractPageText(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
var executeCommand = function (cmd, state) {
|
var updatePage = function (pageIdx) {
|
||||||
if (this.state === null || cmd !== 'findagain') {
|
|
||||||
this.dirtyMatch = true;
|
var pageNum = (pageIdx) ? pageIdx + 1 : null;
|
||||||
|
|
||||||
|
if (reader.resourcelst) {
|
||||||
|
|
||||||
|
reader.resourcelst.forEach(function(list) {
|
||||||
|
|
||||||
|
if (list.textLayer && (pageNum === list.pageNum || pageNum === null)) {
|
||||||
|
list.textLayer.updateMatches();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.state = state;
|
|
||||||
updateUIState(FindStates.FIND_PENDING);
|
|
||||||
|
|
||||||
console.log("execute command ", cmd, " with state ", state);
|
|
||||||
|
|
||||||
reader.firstPagePromise.then(function() {
|
|
||||||
extractText();
|
|
||||||
|
|
||||||
clearTimeout(this.findTimeout);
|
|
||||||
if (cmd === 'find') {
|
|
||||||
// Only trigger the find action after 250ms of silence.
|
|
||||||
//this.findTimeout = setTimeout(nextMatch.bind(this), 250);
|
|
||||||
generateMatchList();
|
|
||||||
} else {
|
|
||||||
nextMatch();
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var updatePage = function (index) {
|
var executeCommand = function (cmd, state) {
|
||||||
|
|
||||||
if (this.selected.pageIdx === index) {
|
reader.search_state = state;
|
||||||
// If the page is selected, scroll the page into view, which triggers
|
|
||||||
// rendering the page, which adds the textLayer. Once the textLayer is
|
|
||||||
// build, it will scroll onto the selected match.
|
|
||||||
reader.settings.currentPage = index + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//var page = this.pdfViewer.getPageView(index);
|
reader.firstPagePromise.then(function() {
|
||||||
//if (page.textLayer) {
|
if (reader.pageContents.length < reader.settings.numPages)
|
||||||
// page.textLayer.updateMatches();
|
extractText();
|
||||||
//}
|
|
||||||
|
if (cmd === 'find') {
|
||||||
|
reader.search_active = true;
|
||||||
|
$match_count.show();
|
||||||
|
generateMatchList();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
var generateMatchList = function () {
|
var generateMatchList = function () {
|
||||||
|
|
||||||
var container = document.getElementById("searchResults"),
|
var container = document.getElementById("searchResults"),
|
||||||
numPages = reader.settings.numPages,
|
numPages = reader.settings.numPages,
|
||||||
self = this;
|
currentIdx = reader.settings.currentPage - 1,
|
||||||
|
i;
|
||||||
|
|
||||||
for (var i = 0; i < numPages; i++) {
|
if (reader.pageContents.length !== numPages) {
|
||||||
//var placeholder = document.createElement("li");
|
extractText();
|
||||||
//placeholder.style.display = "none";
|
for (i = 0; i < numPages; i++) {
|
||||||
//container.appendChild(placeholder);
|
if (!(i in pendingFindMatches)) {
|
||||||
if (!(i in this.pendingFindMatches)) {
|
pendingFindMatches[i] = true;
|
||||||
this.pendingFindMatches[i] = true;
|
extractTextPromises[i].then(function(pageIdx) {
|
||||||
this.extractTextPromises[i].then(function(pageIdx) {
|
delete pendingFindMatches[pageIdx];
|
||||||
delete self.pendingFindMatches[pageIdx];
|
|
||||||
calcFindMatch(pageIdx);
|
|
||||||
if (self.pageMatches[pageIdx].length > 0) {
|
|
||||||
reader.pageMatches[pageIdx] = self.pageMatches[pageIdx];
|
|
||||||
var fragment = document.createDocumentFragment();
|
|
||||||
var listitem = document.createElement("li");
|
|
||||||
listitem.textContent="page " + parseInt(pageIdx + 1);
|
|
||||||
listitem.classList.add("search_page_header");
|
|
||||||
fragment.appendChild(listitem);
|
|
||||||
self.pageMatches[pageIdx].forEach(function (match) {
|
|
||||||
fragment.appendChild(createItem(pageIdx, match));
|
|
||||||
});
|
|
||||||
|
|
||||||
container.appendChild(fragment);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var nextMatch = function () {
|
|
||||||
|
|
||||||
var previous = this.state.findPrevious;
|
|
||||||
var currentPageIndex = reader.settings.currentPage - 1;
|
|
||||||
var numPages = reader.settings.numPages;
|
|
||||||
|
|
||||||
this.active = true;
|
|
||||||
|
|
||||||
if (this.dirtyMatch) {
|
|
||||||
// Need to recalculate the matches, reset everything.
|
|
||||||
this.dirtyMatch = false;
|
|
||||||
this.selected.pageIdx = this.selected.matchIdx = -1;
|
|
||||||
this.offset.pageIdx = currentPageIndex;
|
|
||||||
this.offset.matchIdx = null;
|
|
||||||
this.hadMatch = false;
|
|
||||||
this.resumePageIdx = null;
|
|
||||||
this.pageMatches = [];
|
|
||||||
this.matchCount = 0;
|
|
||||||
this.pageMatchesLength = null;
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
for (var i = 0; i < numPages; i++) {
|
|
||||||
// Wipe out any previous highlighted matches.
|
|
||||||
updatePage(i);
|
|
||||||
|
|
||||||
// As soon as the text is extracted start finding the matches.
|
|
||||||
if (!(i in this.pendingFindMatches)) {
|
|
||||||
this.pendingFindMatches[i] = true;
|
|
||||||
this.extractTextPromises[i].then(function(pageIdx) {
|
|
||||||
delete self.pendingFindMatches[pageIdx];
|
|
||||||
calcFindMatch(pageIdx);
|
calcFindMatch(pageIdx);
|
||||||
|
if (reader.pageMatches[pageIdx].length > 0) {
|
||||||
|
container.appendChild(createItemList(pageIdx));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
for (i = 0; i < numPages; i++) {
|
||||||
// If there's no query there's no point in searching.
|
calcFindMatch(i);
|
||||||
if (this.state.query === '') {
|
if (reader.pageMatches[i].length > 0) {
|
||||||
updateUIState(FindStates.FIND_FOUND);
|
container.appendChild(createItemList(i));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're waiting on a page, we return since we can't do anything else.
|
|
||||||
if (this.resumePageIdx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset = this.offset;
|
|
||||||
// Keep track of how many pages we should maximally iterate through.
|
|
||||||
this.pagesToSearch = numPages;
|
|
||||||
// If there's already a matchIdx that means we are iterating through a
|
|
||||||
// page's matches.
|
|
||||||
if (offset.matchIdx !== null) {
|
|
||||||
var numPageMatches = this.pageMatches[offset.pageIdx].length;
|
|
||||||
if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
|
|
||||||
(previous && offset.matchIdx > 0)) {
|
|
||||||
// The simple case; we just have advance the matchIdx to select
|
|
||||||
// the next match on the page.
|
|
||||||
this.hadMatch = true;
|
|
||||||
offset.matchIdx = (previous ? offset.matchIdx - 1 :
|
|
||||||
offset.matchIdx + 1);
|
|
||||||
updateMatch(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// We went beyond the current page's matches, so we advance to
|
|
||||||
// the next page.
|
|
||||||
advanceOffsetPage(previous);
|
|
||||||
}
|
|
||||||
// Start searching through the page.
|
|
||||||
nextPageMatch();
|
|
||||||
};
|
|
||||||
|
|
||||||
var matchesReady = function (matches) {
|
|
||||||
var offset = this.offset;
|
|
||||||
var numMatches = matches.length;
|
|
||||||
var previous = this.state.findPrevious;
|
|
||||||
|
|
||||||
if (numMatches) {
|
|
||||||
// There were matches for the page, so initialize the matchIdx.
|
|
||||||
this.hadMatch = true;
|
|
||||||
offset.matchIdx = (previous ? numMatches - 1 : 0);
|
|
||||||
updateMatch(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// No matches, so attempt to search the next page.
|
|
||||||
advanceOffsetPage(previous);
|
|
||||||
if (offset.wrapped) {
|
|
||||||
offset.matchIdx = null;
|
|
||||||
if (this.pagesToSearch < 0) {
|
|
||||||
// No point in wrapping again, there were no matches.
|
|
||||||
updateMatch(false);
|
|
||||||
// while matches were not found, searching for a page
|
|
||||||
// with matches should nevertheless halt.
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Matches were not found (and searching is not done).
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
var nextMatch = function (previous) {
|
||||||
* The method is called back from the text layer when match presentation
|
|
||||||
* is updated.
|
|
||||||
* @param {number} pageIndex - page index.
|
|
||||||
* @param {number} index - match index.
|
|
||||||
* @param {Array} elements - text layer div elements array.
|
|
||||||
* @param {number} beginIdx - start index of the div array for the match.
|
|
||||||
*/
|
|
||||||
var updateMatchPosition = function (
|
|
||||||
pageIndex, index, elements, beginIdx) {
|
|
||||||
if (this.selected.matchIdx === index &&
|
|
||||||
this.selected.pageIdx === pageIndex) {
|
|
||||||
//var spot = {
|
|
||||||
// top: FIND_SCROLL_OFFSET_TOP,
|
|
||||||
// left: FIND_SCROLL_OFFSET_LEFT
|
|
||||||
//};
|
|
||||||
//scrollIntoView(elements[beginIdx], spot,
|
|
||||||
// /* skipOverflowHiddenElements = */ true);
|
|
||||||
}
|
|
||||||
console.log("would scroll into view here except for the fact that Reader is a non-scrolling reader...");
|
|
||||||
};
|
|
||||||
|
|
||||||
var nextPageMatch = function () {
|
/* don't try to follow non-existing matches */
|
||||||
if (this.resumePageIdx !== null) {
|
if (!reader.search_active ||
|
||||||
console.error('There can only be one pending page.');
|
reader.pageMatches.length === 0)
|
||||||
}
|
return;
|
||||||
do {
|
|
||||||
var pageIdx = this.offset.pageIdx;
|
|
||||||
var matches = this.pageMatches[pageIdx];
|
|
||||||
if (!matches) {
|
|
||||||
// The matches don't exist yet for processing by "matchesReady",
|
|
||||||
// so set a resume point for when they do exist.
|
|
||||||
this.resumePageIdx = pageIdx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (!matchesReady(matches));
|
|
||||||
};
|
|
||||||
|
|
||||||
var advanceOffsetPage = function (previous) {
|
var numPages = reader.settings.numPages,
|
||||||
var offset = this.offset;
|
selected = reader.selected,
|
||||||
var numPages = this.extractTextPromises.length;
|
leftIdx = idxOrNull(reader.resourcelst[0].pageNum),
|
||||||
offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1);
|
rightIdx = idxOrNull(reader.resourcelst[1].pageNum),
|
||||||
offset.matchIdx = null;
|
try_match = false;
|
||||||
|
|
||||||
this.pagesToSearch--;
|
/* prevent match cycling on first or last page */
|
||||||
|
if (!((previous && selected.at_start) || (!previous && selected.at_end))) {
|
||||||
|
|
||||||
if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
|
selected.at_start = selected.at_end = false;
|
||||||
offset.pageIdx = (previous ? numPages - 1 : 0);
|
|
||||||
offset.wrapped = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateMatch = function (found) {
|
/* when in spread view, start at left (forward search) or right (backward search) page
|
||||||
var state = FindStates.FIND_NOTFOUND;
|
* if not iterating over matches on currently visible pages
|
||||||
var wrapped = this.offset.wrapped;
|
*/
|
||||||
this.offset.wrapped = false;
|
if (!(selected.matchIdx !== -1 && isVisible(selected.pageIdx))) {
|
||||||
|
if (previous) {
|
||||||
|
selected.pageIdx = (typeof rightIdx === "number") ? rightIdx : leftIdx;
|
||||||
|
} else {
|
||||||
|
selected.pageIdx = (typeof leftIdx === "number") ? leftIdx : rightIdx;
|
||||||
|
}
|
||||||
|
try_match = true;
|
||||||
|
|
||||||
if (found) {
|
} else {
|
||||||
var previousPage = this.selected.pageIdx;
|
|
||||||
this.selected.pageIdx = this.offset.pageIdx;
|
var numPageMatches = reader.pageMatches[selected.pageIdx].length;
|
||||||
this.selected.matchIdx = this.offset.matchIdx;
|
|
||||||
state = (wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND);
|
if ((!previous && selected.matchIdx + 1 < numPageMatches) || (previous && selected.matchIdx > 0)) {
|
||||||
// Update the currently selected page to wipe out any selected matches.
|
selected.matchIdx = (previous ? selected.matchIdx - 1 : selected.matchIdx + 1);
|
||||||
if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
|
updateOrQueue();
|
||||||
updatePage(previousPage);
|
return;
|
||||||
|
} else {
|
||||||
|
selected.pageIdx += (previous) ? -1 : 1;
|
||||||
|
try_match = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUIState(state, this.state.findPrevious);
|
if (try_match && nextPageMatch(previous)) {
|
||||||
if (this.selected.pageIdx !== -1) {
|
updateOrQueue();
|
||||||
updatePage(this.selected.pageIdx);
|
return;
|
||||||
|
} else {
|
||||||
|
if (previous) {
|
||||||
|
reader.ControlsController.setStatus("at first match", true);
|
||||||
|
selected.at_start = true;
|
||||||
|
} else {
|
||||||
|
reader.ControlsController.setStatus("at last match", true);
|
||||||
|
selected.at_end = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function idxOrNull(num) {
|
||||||
|
|
||||||
|
if (typeof num === "number") {
|
||||||
|
return num - 1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVisible (idx) {
|
||||||
|
return (idx === leftIdx || idx === rightIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPageMatch (previous) {
|
||||||
|
|
||||||
|
var i,
|
||||||
|
found;
|
||||||
|
|
||||||
|
if (previous) {
|
||||||
|
for (i = selected.pageIdx; i >= -1 && reader.pageMatches[i] === undefined; i--) {}
|
||||||
|
} else {
|
||||||
|
for (i = selected.pageIdx; i <= numPages && reader.pageMatches[i] === undefined; i++) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0 || i >= numPages) {
|
||||||
|
i = -1;
|
||||||
|
//selected.pageIdx = selected.matchIdx = -1;
|
||||||
|
selected.matchIdx = -1;
|
||||||
|
found = false;
|
||||||
|
} else {
|
||||||
|
selected.pageIdx = i;
|
||||||
|
selected.matchIdx = (previous) ? reader.pageMatches[i].length - 1 : 0;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOrQueue() {
|
||||||
|
|
||||||
|
var root = document.getElementById("searchResults"),
|
||||||
|
item,
|
||||||
|
match,
|
||||||
|
i;
|
||||||
|
|
||||||
|
item = root.getElementsByClassName("selected");
|
||||||
|
while (item.length)
|
||||||
|
item[0].classList.remove("selected");
|
||||||
|
|
||||||
|
match = document.getElementById("match:" + selected.pageIdx + ":" + selected.matchIdx);
|
||||||
|
match.classList.add("selected");
|
||||||
|
match = document.getElementsByClassName("match:" + selected.pageIdx + ":" + selected.matchIdx);
|
||||||
|
for (i = 0; i < match.length; i++)
|
||||||
|
match[i].classList.add("selected_again");
|
||||||
|
|
||||||
|
|
||||||
|
updateMatchCounter(match.dataset.index);
|
||||||
|
if (!reader.isVisible(match))
|
||||||
|
match.scrollIntoView();
|
||||||
|
|
||||||
|
if (isVisible(selected.pageIdx)) {
|
||||||
|
[ leftIdx, rightIdx ].forEach(function (idx) {
|
||||||
|
if (typeof idx === "number") updatePage(idx);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reader.queuePage(selected.pageIdx + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateUIResultsCount = function () {
|
var updateMatchCounter = function (index) {
|
||||||
if (this.onUpdateResultsCount) {
|
|
||||||
onUpdateResultsCount(this.matchCount);
|
var prefix = "";
|
||||||
}
|
|
||||||
|
if (index)
|
||||||
|
prefix = index + "/";
|
||||||
|
|
||||||
|
$match_count[0].textContent = prefix + matchCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateUIState = function (state, previous) {
|
var resetMatchCounter = function () {
|
||||||
if (this.onUpdateState) {
|
$match_count[0].textContent = "0";
|
||||||
onUpdateState(state, previous, this.matchCount);
|
$match_count.hide();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var search = function(q) {
|
var search = function(q) {
|
||||||
if (q === undefined) {
|
if (q === undefined) {
|
||||||
q = $searchBox.val();
|
q = $searchBox.val();
|
||||||
|
@ -576,10 +521,7 @@ PDFJS.reader.SearchController = function () {
|
||||||
reset();
|
reset();
|
||||||
$searchResults.empty();
|
$searchResults.empty();
|
||||||
|
|
||||||
this.query = q;
|
|
||||||
|
|
||||||
executeCommand('find', {query: q});
|
executeCommand('find', {query: q});
|
||||||
highlightQuery();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$searchBox.on("keydown", function(e) {
|
$searchBox.on("keydown", function(e) {
|
||||||
|
@ -601,14 +543,13 @@ PDFJS.reader.SearchController = function () {
|
||||||
|
|
||||||
$clear_search.on("click", function () {
|
$clear_search.on("click", function () {
|
||||||
reset();
|
reset();
|
||||||
unhighlight();
|
|
||||||
$searchResults.empty();
|
$searchResults.empty();
|
||||||
|
$searchBox.val("");
|
||||||
});
|
});
|
||||||
|
|
||||||
var clear = function () {
|
var clear = function () {
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
unhighlight();
|
|
||||||
$searchResults.empty();
|
$searchResults.empty();
|
||||||
|
|
||||||
if (reader.SidebarController.getActivePanel() == "Search") {
|
if (reader.SidebarController.getActivePanel() == "Search") {
|
||||||
|
@ -616,14 +557,24 @@ PDFJS.reader.SearchController = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var highlightQuery = function(e) {
|
// initialize search
|
||||||
$("#text_left").contents().highlight(this.state.query, { element: 'span' });
|
reset();
|
||||||
$("#text_right").contents().highlight(this.state.query, { element: 'span' });
|
|
||||||
|
if (reader.settings.preloadTextcontent) {
|
||||||
|
reader.firstPagePromise.then(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
extractText();
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var onShow = function() {
|
||||||
|
$searchView.addClass("open");
|
||||||
|
$searchBox.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
var unhighlight = function(e) {
|
var onHide = function() {
|
||||||
$("#text_left").unhighlight();
|
$searchView.removeClass("open");
|
||||||
$("#text_right").unhighlight();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -632,7 +583,7 @@ PDFJS.reader.SearchController = function () {
|
||||||
"hide": onHide,
|
"hide": onHide,
|
||||||
"search": search,
|
"search": search,
|
||||||
"executeCommand": executeCommand,
|
"executeCommand": executeCommand,
|
||||||
"highlightQuery": highlightQuery,
|
"nextMatch": nextMatch
|
||||||
"unhighlight": unhighlight
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,40 +1,8 @@
|
||||||
/* Copyright 2012 Mozilla Foundation
|
PDFJS.Reader.TextLayerController = function (options, reader) {
|
||||||
*
|
|
||||||
* 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
|
var EXPAND_DIVS_TIMEOUT = 300; // ms
|
||||||
|
|
||||||
|
this.reader = reader;
|
||||||
this.textLayerDiv = options.textLayerDiv;
|
this.textLayerDiv = options.textLayerDiv;
|
||||||
this.eventBus = options.eventBus || null;
|
this.eventBus = options.eventBus || null;
|
||||||
this.textContent = null;
|
this.textContent = null;
|
||||||
|
@ -44,7 +12,6 @@ PDFJS.Reader.TextLayerController = function (options) {
|
||||||
this.matches = [];
|
this.matches = [];
|
||||||
this.viewport = options.viewport;
|
this.viewport = options.viewport;
|
||||||
this.textDivs = [];
|
this.textDivs = [];
|
||||||
this.findController = options.findController || null;
|
|
||||||
this.textLayerRenderTask = null;
|
this.textLayerRenderTask = null;
|
||||||
this.enhanceTextSelection = options.enhanceTextSelection;
|
this.enhanceTextSelection = options.enhanceTextSelection;
|
||||||
this._bindMouse();
|
this._bindMouse();
|
||||||
|
@ -117,13 +84,14 @@ PDFJS.Reader.TextLayerController.prototype.setTextContent = function (textConten
|
||||||
|
|
||||||
PDFJS.Reader.TextLayerController.prototype.convertMatches = function(matches, matchesLength) {
|
PDFJS.Reader.TextLayerController.prototype.convertMatches = function(matches, matchesLength) {
|
||||||
|
|
||||||
var reader = this;
|
var reader = this.reader;
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var iIndex = 0;
|
var iIndex = 0;
|
||||||
var bidiTexts = this.textContent.items;
|
var bidiTexts = this.textContent.items;
|
||||||
var end = bidiTexts.length - 1;
|
var end = bidiTexts.length - 1;
|
||||||
var queryLen = reader.search.query.length;
|
var queryLen = reader.search_state ?
|
||||||
|
reader.search_state.query.length : null;
|
||||||
var ret = [];
|
var ret = [];
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -179,18 +147,15 @@ PDFJS.Reader.TextLayerController.prototype.renderMatches = function (matches) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader = this;
|
var reader = this.reader;
|
||||||
|
|
||||||
var bidiTexts = this.textContent.items;
|
var bidiTexts = this.textContent.items;
|
||||||
var textDivs = this.textDivs;
|
var textDivs = this.textDivs;
|
||||||
var prevEnd = null;
|
var prevEnd = null;
|
||||||
var pageIdx = this.pageIdx;
|
var pageIdx = this.pageIdx;
|
||||||
var isSelectedPage = (this.findController === null ?
|
var isSelectedPage = (pageIdx === reader.selected.pageIdx);
|
||||||
false : (pageIdx === this.findController.selected.pageIdx));
|
var selectedMatchIdx = reader.selected.matchIdx;
|
||||||
var selectedMatchIdx = (this.findController === null ?
|
var highlightAll = true;
|
||||||
-1 : this.findController.selected.matchIdx);
|
|
||||||
var highlightAll = (this.findController === null ?
|
|
||||||
false : this.findController.state.highlightAll);
|
|
||||||
var infinity = {
|
var infinity = {
|
||||||
divIdx: -1,
|
divIdx: -1,
|
||||||
offset: undefined
|
offset: undefined
|
||||||
|
@ -230,12 +195,8 @@ PDFJS.Reader.TextLayerController.prototype.renderMatches = function (matches) {
|
||||||
var begin = match.begin;
|
var begin = match.begin;
|
||||||
var end = match.end;
|
var end = match.end;
|
||||||
var isSelected = (isSelectedPage && i === selectedMatchIdx);
|
var isSelected = (isSelectedPage && i === selectedMatchIdx);
|
||||||
var highlightSuffix = (isSelected ? ' selected' : '');
|
var id = "match:" + pageIdx + ":" + i;
|
||||||
|
var highlightSuffix = (isSelected ? ' selected ' + id : ' ' + id);
|
||||||
if (this.findController) {
|
|
||||||
this.findController.updateMatchPosition(pageIdx, i, textDivs,
|
|
||||||
begin.divIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match inside new div.
|
// Match inside new div.
|
||||||
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
||||||
|
@ -274,6 +235,13 @@ PDFJS.Reader.TextLayerController.prototype.updateMatches = function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reader = this.reader;
|
||||||
|
|
||||||
|
// Only show matches when search is active
|
||||||
|
if (reader.search_active !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear all matches.
|
// Clear all matches.
|
||||||
var matches = this.matches;
|
var matches = this.matches;
|
||||||
var textDivs = this.textDivs;
|
var textDivs = this.textDivs;
|
||||||
|
@ -292,18 +260,12 @@ PDFJS.Reader.TextLayerController.prototype.updateMatches = function () {
|
||||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
clearedUntilDivIdx = match.end.divIdx + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.findController === null || !this.findController.active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the matches on the page controller into the match format
|
// Convert the matches on the page controller into the match format
|
||||||
// used for the textLayer.
|
// used for the textLayer.
|
||||||
var pageMatches, pageMatchesLength;
|
var pageMatches = reader.pageMatches[this.pageIdx] || null,
|
||||||
if (this.findController !== null) {
|
pageMatchesLength = reader.pageMatchesLength ?
|
||||||
pageMatches = this.findController.pageMatches[this.pageIdx] || null;
|
reader.pageMatchesLength[this.pageIdx] || null : null;
|
||||||
pageMatchesLength = (this.findController.pageMatchesLength) ?
|
|
||||||
this.findController.pageMatchesLength[this.pageIdx] || null : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.matches = this.convertMatches(pageMatches, pageMatchesLength);
|
this.matches = this.convertMatches(pageMatches, pageMatchesLength);
|
||||||
this.renderMatches(this.matches);
|
this.renderMatches(this.matches);
|
||||||
|
|
|
@ -120,7 +120,7 @@ PDFJS.reader.TocController = function() {
|
||||||
scale = parseFloat(settings.thumbnailWidth / width);
|
scale = parseFloat(settings.thumbnailWidth / width);
|
||||||
preloadcount = parseInt(window.innerHeight / placeholder_height) + 2;
|
preloadcount = parseInt(window.innerHeight / placeholder_height) + 2;
|
||||||
if (preloadcount > settings.numPages)
|
if (preloadcount > settings.numPages)
|
||||||
preloadcount = numPages;
|
preloadcount = settings.numPages;
|
||||||
|
|
||||||
var _timeout = setTimeout(function () {
|
var _timeout = setTimeout(function () {
|
||||||
for (var i = 1; i <= preloadcount; i++) {
|
for (var i = 1; i <= preloadcount; i++) {
|
||||||
|
|
9
files_reader/vendor/pdfjs/css/main.css
vendored
9
files_reader/vendor/pdfjs/css/main.css
vendored
|
@ -125,6 +125,15 @@ body {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#status_message_left {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status_message_right,
|
||||||
|
#match_count {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
#metainfo {
|
#metainfo {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
/* width: 80%;
|
/* width: 80%;
|
||||||
|
|
18
files_reader/vendor/pdfjs/css/sidebar.css
vendored
18
files_reader/vendor/pdfjs/css/sidebar.css
vendored
|
@ -122,6 +122,10 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list_item.selected a {
|
||||||
|
color: #D2D2D2;
|
||||||
|
}
|
||||||
|
|
||||||
legend {
|
legend {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
@ -329,6 +333,20 @@ legend {
|
||||||
font-decoration: underline;
|
font-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search_page_header {
|
||||||
|
text-align: right;
|
||||||
|
color: #DDD;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.match_label {
|
||||||
|
background-color: #DDD;
|
||||||
|
color: black;
|
||||||
|
box-shadow: 1px 1px 10px rgba(0,0,0,0.5);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
.searchbox {
|
.searchbox {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
62
files_reader/vendor/pdfjs/pdf.reader.js
vendored
62
files_reader/vendor/pdfjs/pdf.reader.js
vendored
|
@ -37,6 +37,7 @@ PDFJS.Reader = function(bookPath, _options) {
|
||||||
bookPath: bookPath,
|
bookPath: bookPath,
|
||||||
textRenderDelay: TEXT_RENDER_DELAY,
|
textRenderDelay: TEXT_RENDER_DELAY,
|
||||||
pageRenderDelay: PAGE_RENDER_DELAY,
|
pageRenderDelay: PAGE_RENDER_DELAY,
|
||||||
|
preloadTextcontent: true, // true || false, preload text content to speed up first full-text search operation
|
||||||
canvasLimit: 0,
|
canvasLimit: 0,
|
||||||
cssZoomOnly: false, // true || false, only zoom using CSS, render document at 100% size
|
cssZoomOnly: false, // true || false, only zoom using CSS, render document at 100% size
|
||||||
textSelect: true, // true || false, add selectable text layer
|
textSelect: true, // true || false, add selectable text layer
|
||||||
|
@ -70,9 +71,13 @@ PDFJS.Reader = function(bookPath, _options) {
|
||||||
83: 'toggleSidebar',// s
|
83: 'toggleSidebar',// s
|
||||||
84: 'toggleTitlebar', // t
|
84: 'toggleTitlebar', // t
|
||||||
68: 'toggleDay', // d
|
68: 'toggleDay', // d
|
||||||
78: 'toggleNight', // n
|
//78: 'toggleNight', // n
|
||||||
|
55: 'search', // '/'
|
||||||
|
80: 'previousMatch', // p
|
||||||
|
78: 'nextMatch', // n
|
||||||
70: 'toggleFullscreen', // f
|
70: 'toggleFullscreen', // f
|
||||||
27: 'closeSidebar' // esc
|
27: 'closeSidebar', // esc
|
||||||
|
114: 'nextMatch' // F3
|
||||||
},
|
},
|
||||||
nightMode: false,
|
nightMode: false,
|
||||||
dayMode: false,
|
dayMode: false,
|
||||||
|
@ -129,8 +134,17 @@ PDFJS.Reader = function(bookPath, _options) {
|
||||||
|
|
||||||
this.renderQueue = false;
|
this.renderQueue = false;
|
||||||
|
|
||||||
// used for search
|
// used for search, textlayer, hightlight etc
|
||||||
|
this.pageContents = [];
|
||||||
this.pageMatches = [];
|
this.pageMatches = [];
|
||||||
|
this.pageMatchesLength = null;
|
||||||
|
this.search_state = null;
|
||||||
|
this.selected = {
|
||||||
|
pageIdx: -1,
|
||||||
|
matchIdx: -1,
|
||||||
|
at_start: false,
|
||||||
|
at_end: false
|
||||||
|
};
|
||||||
|
|
||||||
// define which zoom states to cycle through in cycleZoom
|
// define which zoom states to cycle through in cycleZoom
|
||||||
this.zoomCycle = {
|
this.zoomCycle = {
|
||||||
|
@ -184,7 +198,7 @@ PDFJS.Reader = function(bookPath, _options) {
|
||||||
|
|
||||||
function(_book) {
|
function(_book) {
|
||||||
reader.book = book = _book;
|
reader.book = book = _book;
|
||||||
console.log(book);
|
//console.log(book);
|
||||||
reader.settings.numPages = reader.book.numPages;
|
reader.settings.numPages = reader.book.numPages;
|
||||||
document.getElementById('total_pages').textContent = reader.settings.numPages;
|
document.getElementById('total_pages').textContent = reader.settings.numPages;
|
||||||
if(!$.isEmptyObject(reader.settings.session.cursor)) {
|
if(!$.isEmptyObject(reader.settings.session.cursor)) {
|
||||||
|
@ -439,6 +453,8 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||||
reader.resourcelst[1].canvas.style.display = "none";
|
reader.resourcelst[1].canvas.style.display = "none";
|
||||||
// clear text layer
|
// clear text layer
|
||||||
reader.resourcelst[1].textdiv.innerHTML = "";
|
reader.resourcelst[1].textdiv.innerHTML = "";
|
||||||
|
// clear page number
|
||||||
|
reader.resourcelst[1].pageNum = null;
|
||||||
|
|
||||||
// don't try to render non-existing page 0 (which is used
|
// don't try to render non-existing page 0 (which is used
|
||||||
// to indicate the empty left page when oddPageRight === true)
|
// to indicate the empty left page when oddPageRight === true)
|
||||||
|
@ -480,7 +496,6 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||||
//console.log(page);
|
//console.log(page);
|
||||||
page_rotation = page.rotate;
|
page_rotation = page.rotate;
|
||||||
rotation = (page_rotation + reader.settings.rotation) % 360;
|
rotation = (page_rotation + reader.settings.rotation) % 360;
|
||||||
//initial_viewport = page.getViewport({scale: 1, rotation: rotation});
|
|
||||||
initial_viewport = page.getViewport(1, rotation);
|
initial_viewport = page.getViewport(1, rotation);
|
||||||
page_width = initial_viewport.width;
|
page_width = initial_viewport.width;
|
||||||
page_height = initial_viewport.height;
|
page_height = initial_viewport.height;
|
||||||
|
@ -491,21 +506,6 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||||
scale_height = parseFloat(max_view_height / page_height);
|
scale_height = parseFloat(max_view_height / page_height);
|
||||||
scale_width = parseFloat(max_view_width / page_width);
|
scale_width = parseFloat(max_view_width / page_width);
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
|
||||||
+ " p_r: " + page_rotation
|
|
||||||
+ " r: " + rotation
|
|
||||||
+ " o: " + outputscale);
|
|
||||||
console.log("fraction: ", fraction);
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (reader.settings.zoomLevel) {
|
switch (reader.settings.zoomLevel) {
|
||||||
|
|
||||||
case "spread":
|
case "spread":
|
||||||
|
@ -604,10 +604,10 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||||
page.getTextContent({ normalizeWhitespace: true }).then(function (textContent) {
|
page.getTextContent({ normalizeWhitespace: true }).then(function (textContent) {
|
||||||
resourcelst.textLayer = textLayer = new PDFJS.Reader.TextLayerController({
|
resourcelst.textLayer = textLayer = new PDFJS.Reader.TextLayerController({
|
||||||
textLayerDiv: textdiv,
|
textLayerDiv: textdiv,
|
||||||
pageIdx: pageNum - 1,
|
pageIndex: pageNum - 1,
|
||||||
viewport: viewport,
|
viewport: viewport,
|
||||||
enhanceTextSelection: true
|
enhanceTextSelection: true
|
||||||
});
|
}, reader);
|
||||||
textLayer.setTextContent(textContent);
|
textLayer.setTextContent(textContent);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -661,6 +661,8 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||||
canvas.style.width = reader.roundToDivide(max_view_width, fraction[1]) + 'px';
|
canvas.style.width = reader.roundToDivide(max_view_width, fraction[1]) + 'px';
|
||||||
canvas.style.height = reader.roundToDivide(max_view_height, fraction[1]) + 'px';
|
canvas.style.height = reader.roundToDivide(max_view_height, fraction[1]) + 'px';
|
||||||
}
|
}
|
||||||
|
// reset pageNum
|
||||||
|
resourcelst.pageNum = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1065,3 +1067,19 @@ PDFJS.Reader.prototype.ellipsize = function(str, max, opts) {
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PDFJS.Reader.prototype.isVisible = function (element) {
|
||||||
|
|
||||||
|
var reader = this,
|
||||||
|
viewport = element.getBoundingClientRect(),
|
||||||
|
visible;
|
||||||
|
|
||||||
|
visible = (
|
||||||
|
viewport.top >= 0
|
||||||
|
&& viewport.left >= 0
|
||||||
|
&& viewport.right < window.innerWidth
|
||||||
|
&& viewport.bottom < window.innerHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
return visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue