Added multiple term search functionality (with default phrase search)
This commit is contained in:
parent
7ac48ef4d5
commit
0a347ec04d
13
web/app.js
13
web/app.js
@ -1251,6 +1251,7 @@ var PDFViewerApplication = {
|
||||
eventBus.on('rotateccw', webViewerRotateCcw);
|
||||
eventBus.on('documentproperties', webViewerDocumentProperties);
|
||||
eventBus.on('find', webViewerFind);
|
||||
eventBus.on('findfromurlhash', webViewerFindFromUrlHash);
|
||||
//#if GENERIC
|
||||
eventBus.on('fileinputchange', webViewerFileInputChange);
|
||||
//#endif
|
||||
@ -1906,12 +1907,23 @@ function webViewerDocumentProperties() {
|
||||
function webViewerFind(e) {
|
||||
PDFViewerApplication.findController.executeCommand('find' + e.type, {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: e.caseSensitive,
|
||||
highlightAll: e.highlightAll,
|
||||
findPrevious: e.findPrevious
|
||||
});
|
||||
}
|
||||
|
||||
function webViewerFindFromUrlHash(e) {
|
||||
PDFViewerApplication.findController.executeCommand('find', {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: false,
|
||||
highlightAll: true,
|
||||
findPrevious: false
|
||||
});
|
||||
}
|
||||
|
||||
function webViewerScaleChanging(e) {
|
||||
var appConfig = PDFViewerApplication.appConfig;
|
||||
appConfig.toolbar.zoomOut.disabled = (e.scale === MIN_SCALE);
|
||||
@ -2055,6 +2067,7 @@ window.addEventListener('keydown', function keydown(evt) {
|
||||
if (findState) {
|
||||
PDFViewerApplication.findController.executeCommand('findagain', {
|
||||
query: findState.query,
|
||||
phraseSearch: findState.phraseSearch,
|
||||
caseSensitive: findState.caseSensitive,
|
||||
highlightAll: findState.highlightAll,
|
||||
findPrevious: cmd === 5 || cmd === 12
|
||||
|
@ -88,6 +88,7 @@
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('find' + e.type, true, true, {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: e.caseSensitive,
|
||||
highlightAll: e.highlightAll,
|
||||
findPrevious: e.findPrevious
|
||||
|
@ -163,6 +163,7 @@ Preferences._readFromStorage = function (prefObj) {
|
||||
source: window,
|
||||
type: evt.type.substring('find'.length),
|
||||
query: evt.detail.query,
|
||||
phraseSearch: true,
|
||||
caseSensitive: !!evt.detail.caseSensitive,
|
||||
highlightAll: !!evt.detail.highlightAll,
|
||||
findPrevious: !!evt.detail.findPrevious
|
||||
|
@ -109,6 +109,7 @@ var PDFFindBar = (function PDFFindBarClosure() {
|
||||
type: type,
|
||||
query: this.findField.value,
|
||||
caseSensitive: this.caseSensitive.checked,
|
||||
phraseSearch: true,
|
||||
highlightAll: this.highlightAll.checked,
|
||||
findPrevious: findPrev
|
||||
});
|
||||
|
@ -78,6 +78,7 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
this.active = false; // If active, find results will be highlighted.
|
||||
this.pageContents = []; // Stores the text for each page.
|
||||
this.pageMatches = [];
|
||||
this.pageMatchesLength = null;
|
||||
this.matchCount = 0;
|
||||
this.selected = { // Currently selected match.
|
||||
pageIdx: -1,
|
||||
@ -104,10 +105,114 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
});
|
||||
},
|
||||
|
||||
// Helper for multiple search - fills matchesWithLength array
|
||||
// and takes into account cases when one search term
|
||||
// include another search term (for example, "tamed tame" or "this is").
|
||||
// Looking for intersecting terms in the 'matches' and
|
||||
// leave elements with a longer match-length.
|
||||
|
||||
_prepareMatches: function PDFFindController_prepareMatches(
|
||||
matchesWithLength, matches, matchesLength) {
|
||||
|
||||
function isSubTerm(matchesWithLength, currentIndex) {
|
||||
var currentElem, prevElem, nextElem;
|
||||
currentElem = matchesWithLength[currentIndex];
|
||||
nextElem = matchesWithLength[currentIndex + 1];
|
||||
// checking for cases like "TAMEd TAME"
|
||||
if (currentIndex < matchesWithLength.length - 1 &&
|
||||
currentElem.match === nextElem.match) {
|
||||
currentElem.skipped = true;
|
||||
return true;
|
||||
}
|
||||
// checking for cases like "thIS IS"
|
||||
for (var i = currentIndex - 1; i >= 0; i--) {
|
||||
prevElem = matchesWithLength[i];
|
||||
if (prevElem.skipped) {
|
||||
continue;
|
||||
}
|
||||
if (prevElem.match + prevElem.matchLength < currentElem.match) {
|
||||
break;
|
||||
}
|
||||
if (prevElem.match + prevElem.matchLength >=
|
||||
currentElem.match + currentElem.matchLength) {
|
||||
currentElem.skipped = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var i, len;
|
||||
// Sorting array of objects { match: <match>, matchLength: <matchLength> }
|
||||
// in increasing index first and then the lengths.
|
||||
matchesWithLength.sort(function(a, b) {
|
||||
return a.match === b.match ?
|
||||
a.matchLength - b.matchLength : a.match - b.match;
|
||||
});
|
||||
for (i = 0, len = matchesWithLength.length; i < len; i++) {
|
||||
if (isSubTerm(matchesWithLength, i)) {
|
||||
continue;
|
||||
}
|
||||
matches.push(matchesWithLength[i].match);
|
||||
matchesLength.push(matchesWithLength[i].matchLength);
|
||||
}
|
||||
},
|
||||
|
||||
calcFindPhraseMatch: function PDFFindController_calcFindPhraseMatch(
|
||||
query, pageIndex, pageContent) {
|
||||
var matches = [];
|
||||
var queryLen = query.length;
|
||||
var matchIdx = -queryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
matches.push(matchIdx);
|
||||
}
|
||||
this.pageMatches[pageIndex] = matches;
|
||||
},
|
||||
|
||||
calcFindWordMatch: function PDFFindController_calcFindWordMatch(
|
||||
query, pageIndex, pageContent) {
|
||||
var matchesWithLength = [];
|
||||
// Divide the query into pieces and search for text on each piece.
|
||||
var queryArray = query.match(/\S+/g);
|
||||
var subquery, subqueryLen, matchIdx;
|
||||
for (var i = 0, len = queryArray.length; i < len; i++) {
|
||||
subquery = queryArray[i];
|
||||
subqueryLen = subquery.length;
|
||||
matchIdx = -subqueryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
// Other searches do not, so we store the length.
|
||||
matchesWithLength.push({
|
||||
match: matchIdx,
|
||||
matchLength: subqueryLen,
|
||||
skipped: false
|
||||
});
|
||||
}
|
||||
}
|
||||
// Prepare arrays for store the matches.
|
||||
if (!this.pageMatchesLength) {
|
||||
this.pageMatchesLength = [];
|
||||
}
|
||||
this.pageMatchesLength[pageIndex] = [];
|
||||
this.pageMatches[pageIndex] = [];
|
||||
// Sort matchesWithLength, clean up intersecting terms
|
||||
// and put the result into the two arrays.
|
||||
this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex],
|
||||
this.pageMatchesLength[pageIndex]);
|
||||
},
|
||||
|
||||
calcFindMatch: function PDFFindController_calcFindMatch(pageIndex) {
|
||||
var pageContent = this.normalize(this.pageContents[pageIndex]);
|
||||
var query = this.normalize(this.state.query);
|
||||
var caseSensitive = this.state.caseSensitive;
|
||||
var phraseSearch = this.state.phraseSearch;
|
||||
var queryLen = query.length;
|
||||
|
||||
if (queryLen === 0) {
|
||||
@ -120,16 +225,12 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
query = query.toLowerCase();
|
||||
}
|
||||
|
||||
var matches = [];
|
||||
var matchIdx = -queryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
matches.push(matchIdx);
|
||||
if (phraseSearch) {
|
||||
this.calcFindPhraseMatch(query, pageIndex, pageContent);
|
||||
} else {
|
||||
this.calcFindWordMatch(query, pageIndex, pageContent);
|
||||
}
|
||||
this.pageMatches[pageIndex] = matches;
|
||||
|
||||
this.updatePage(pageIndex);
|
||||
if (this.resumePageIdx === pageIndex) {
|
||||
this.resumePageIdx = null;
|
||||
@ -137,8 +238,8 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
}
|
||||
|
||||
// Update the matches count
|
||||
if (matches.length > 0) {
|
||||
this.matchCount += matches.length;
|
||||
if (this.pageMatches[pageIndex].length > 0) {
|
||||
this.matchCount += this.pageMatches[pageIndex].length;
|
||||
this.updateUIResultsCount();
|
||||
}
|
||||
},
|
||||
@ -233,6 +334,7 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
this.resumePageIdx = null;
|
||||
this.pageMatches = [];
|
||||
this.matchCount = 0;
|
||||
this.pageMatchesLength = null;
|
||||
var self = this;
|
||||
|
||||
for (var i = 0; i < numPages; i++) {
|
||||
|
@ -194,6 +194,13 @@ var PDFLinkService = (function () {
|
||||
setHash: function PDFLinkService_setHash(hash) {
|
||||
if (hash.indexOf('=') >= 0) {
|
||||
var params = parseQueryString(hash);
|
||||
if ('search' in params) {
|
||||
this.eventBus.dispatch('findfromurlhash', {
|
||||
source: this,
|
||||
query: params['search'].replace(/"/g, ''),
|
||||
phraseSearch: (params['phrase'] === 'true')
|
||||
});
|
||||
}
|
||||
// borrowing syntax from "Parameters for Opening PDF Files"
|
||||
if ('nameddest' in params) {
|
||||
if (this.pdfHistory) {
|
||||
|
@ -116,7 +116,8 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
this.divContentDone = true;
|
||||
},
|
||||
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches) {
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches,
|
||||
matchesLength) {
|
||||
var i = 0;
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent.items;
|
||||
@ -124,7 +125,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
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];
|
||||
@ -147,7 +150,11 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
};
|
||||
|
||||
// Calculate the end position.
|
||||
matchIdx += queryLen;
|
||||
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.
|
||||
@ -289,8 +296,14 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
|
||||
// Convert the matches on the page controller into the match format
|
||||
// used for the textLayer.
|
||||
this.matches = this.convertMatches(this.findController === null ?
|
||||
[] : (this.findController.pageMatches[this.pageIdx] || []));
|
||||
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);
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user