|
|
@ -13,37 +13,37 @@ |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* limitations under the License. |
|
|
|
* limitations under the License. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
/* globals PDFFindBar, PDFJS, FindStates, FirefoxCom, Promise */ |
|
|
|
/* globals PDFJS, FindStates, FirefoxCom, Promise */ |
|
|
|
|
|
|
|
|
|
|
|
'use strict'; |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Provides a "search" or "find" functionality for the PDF. |
|
|
|
* Provides "search" or "find" functionality for the PDF. |
|
|
|
* This object actually performs the search for a given string. |
|
|
|
* This object actually performs the search for a given string. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
var PDFFindController = (function PDFFindControllerClosure() { |
|
|
|
var PDFFindController = { |
|
|
|
function PDFFindController(options) { |
|
|
|
startedTextExtraction: false, |
|
|
|
this.startedTextExtraction = false; |
|
|
|
extractTextPromises: [], |
|
|
|
this.extractTextPromises = []; |
|
|
|
pendingFindMatches: {}, |
|
|
|
this.pendingFindMatches = {}; |
|
|
|
active: false, // If active, find results will be highlighted.
|
|
|
|
this.active = false; // If active, find results will be highlighted.
|
|
|
|
pageContents: [], // Stores the text for each page.
|
|
|
|
this.pageContents = []; // Stores the text for each page.
|
|
|
|
pageMatches: [], |
|
|
|
this.pageMatches = []; |
|
|
|
selected: { // Currently selected match.
|
|
|
|
this.selected = { // Currently selected match.
|
|
|
|
pageIdx: -1, |
|
|
|
pageIdx: -1, |
|
|
|
matchIdx: -1 |
|
|
|
matchIdx: -1 |
|
|
|
}, |
|
|
|
}; |
|
|
|
offset: { // Where the find algorithm currently is in the document.
|
|
|
|
this.offset = { // Where the find algorithm currently is in the document.
|
|
|
|
pageIdx: null, |
|
|
|
pageIdx: null, |
|
|
|
matchIdx: null |
|
|
|
matchIdx: null |
|
|
|
}, |
|
|
|
}; |
|
|
|
resumePageIdx: null, |
|
|
|
this.resumePageIdx = null; |
|
|
|
state: null, |
|
|
|
this.state = null; |
|
|
|
dirtyMatch: false, |
|
|
|
this.dirtyMatch = false; |
|
|
|
findTimeout: null, |
|
|
|
this.findTimeout = null; |
|
|
|
pdfPageSource: null, |
|
|
|
this.pdfPageSource = options.pdfPageSource || null; |
|
|
|
integratedFind: false, |
|
|
|
this.integratedFind = options.integratedFind || false; |
|
|
|
charactersToNormalize: { |
|
|
|
this.charactersToNormalize = { |
|
|
|
'\u2018': '\'', // Left single quotation mark
|
|
|
|
'\u2018': '\'', // Left single quotation mark
|
|
|
|
'\u2019': '\'', // Right single quotation mark
|
|
|
|
'\u2019': '\'', // Right single quotation mark
|
|
|
|
'\u201A': '\'', // Single low-9 quotation mark
|
|
|
|
'\u201A': '\'', // Single low-9 quotation mark
|
|
|
@ -55,16 +55,8 @@ var PDFFindController = { |
|
|
|
'\u00BC': '1/4', // Vulgar fraction one quarter
|
|
|
|
'\u00BC': '1/4', // Vulgar fraction one quarter
|
|
|
|
'\u00BD': '1/2', // Vulgar fraction one half
|
|
|
|
'\u00BD': '1/2', // Vulgar fraction one half
|
|
|
|
'\u00BE': '3/4' // Vulgar fraction three quarters
|
|
|
|
'\u00BE': '3/4' // Vulgar fraction three quarters
|
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
this.findBar = options.findBar || null; |
|
|
|
initialize: function(options) { |
|
|
|
|
|
|
|
if (typeof PDFFindBar === 'undefined' || PDFFindBar === null) { |
|
|
|
|
|
|
|
throw 'PDFFindController cannot be initialized ' + |
|
|
|
|
|
|
|
'without a PDFFindBar instance'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.pdfPageSource = options.pdfPageSource; |
|
|
|
|
|
|
|
this.integratedFind = options.integratedFind; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Compile the regular expression for text normalization once
|
|
|
|
// Compile the regular expression for text normalization once
|
|
|
|
var replace = Object.keys(this.charactersToNormalize).join(''); |
|
|
|
var replace = Object.keys(this.charactersToNormalize).join(''); |
|
|
@ -85,29 +77,34 @@ var PDFFindController = { |
|
|
|
for (var i = 0, len = events.length; i < len; i++) { |
|
|
|
for (var i = 0, len = events.length; i < len; i++) { |
|
|
|
window.addEventListener(events[i], this.handleEvent); |
|
|
|
window.addEventListener(events[i], this.handleEvent); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFFindController.prototype = { |
|
|
|
|
|
|
|
setFindBar: function PDFFindController_setFindBar(findBar) { |
|
|
|
|
|
|
|
this.findBar = findBar; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
reset: function pdfFindControllerReset() { |
|
|
|
reset: function PDFFindController_reset() { |
|
|
|
this.startedTextExtraction = false; |
|
|
|
this.startedTextExtraction = false; |
|
|
|
this.extractTextPromises = []; |
|
|
|
this.extractTextPromises = []; |
|
|
|
this.active = false; |
|
|
|
this.active = false; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
normalize: function pdfFindControllerNormalize(text) { |
|
|
|
normalize: function PDFFindController_normalize(text) { |
|
|
|
|
|
|
|
var self = this; |
|
|
|
return text.replace(this.normalizationRegex, function (ch) { |
|
|
|
return text.replace(this.normalizationRegex, function (ch) { |
|
|
|
return PDFFindController.charactersToNormalize[ch]; |
|
|
|
return self.charactersToNormalize[ch]; |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
calcFindMatch: function(pageIndex) { |
|
|
|
calcFindMatch: function PDFFindController_calcFindMatch(pageIndex) { |
|
|
|
var pageContent = this.normalize(this.pageContents[pageIndex]); |
|
|
|
var pageContent = this.normalize(this.pageContents[pageIndex]); |
|
|
|
var query = this.normalize(this.state.query); |
|
|
|
var query = this.normalize(this.state.query); |
|
|
|
var caseSensitive = this.state.caseSensitive; |
|
|
|
var caseSensitive = this.state.caseSensitive; |
|
|
|
var queryLen = query.length; |
|
|
|
var queryLen = query.length; |
|
|
|
|
|
|
|
|
|
|
|
if (queryLen === 0) { |
|
|
|
if (queryLen === 0) { |
|
|
|
// Do nothing: the matches should be wiped out already.
|
|
|
|
return; // Do nothing: the matches should be wiped out already.
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!caseSensitive) { |
|
|
|
if (!caseSensitive) { |
|
|
@ -132,7 +129,7 @@ var PDFFindController = { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
extractText: function() { |
|
|
|
extractText: function PDFFindController_extractText() { |
|
|
|
if (this.startedTextExtraction) { |
|
|
|
if (this.startedTextExtraction) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
@ -171,7 +168,7 @@ var PDFFindController = { |
|
|
|
extractPageText(0); |
|
|
|
extractPageText(0); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
handleEvent: function(e) { |
|
|
|
handleEvent: function PDFFindController_handleEvent(e) { |
|
|
|
if (this.state === null || e.type !== 'findagain') { |
|
|
|
if (this.state === null || e.type !== 'findagain') { |
|
|
|
this.dirtyMatch = true; |
|
|
|
this.dirtyMatch = true; |
|
|
|
} |
|
|
|
} |
|
|
@ -191,10 +188,10 @@ var PDFFindController = { |
|
|
|
}.bind(this)); |
|
|
|
}.bind(this)); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
updatePage: function(idx) { |
|
|
|
updatePage: function PDFFindController_updatePage(index) { |
|
|
|
var page = this.pdfPageSource.pages[idx]; |
|
|
|
var page = this.pdfPageSource.pages[index]; |
|
|
|
|
|
|
|
|
|
|
|
if (this.selected.pageIdx === idx) { |
|
|
|
if (this.selected.pageIdx === index) { |
|
|
|
// If the page is selected, scroll the page into view, which triggers
|
|
|
|
// If the page is selected, scroll the page into view, which triggers
|
|
|
|
// rendering the page, which adds the textLayer. Once the textLayer is
|
|
|
|
// rendering the page, which adds the textLayer. Once the textLayer is
|
|
|
|
// build, it will scroll onto the selected match.
|
|
|
|
// build, it will scroll onto the selected match.
|
|
|
@ -206,7 +203,7 @@ var PDFFindController = { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
nextMatch: function() { |
|
|
|
nextMatch: function PDFFindController_nextMatch() { |
|
|
|
var previous = this.state.findPrevious; |
|
|
|
var previous = this.state.findPrevious; |
|
|
|
var currentPageIndex = this.pdfPageSource.page - 1; |
|
|
|
var currentPageIndex = this.pdfPageSource.page - 1; |
|
|
|
var numPages = this.pdfPageSource.pages.length; |
|
|
|
var numPages = this.pdfPageSource.pages.length; |
|
|
@ -273,10 +270,11 @@ var PDFFindController = { |
|
|
|
this.nextPageMatch(); |
|
|
|
this.nextPageMatch(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
matchesReady: function(matches) { |
|
|
|
matchesReady: function PDFFindController_matchesReady(matches) { |
|
|
|
var offset = this.offset; |
|
|
|
var offset = this.offset; |
|
|
|
var numMatches = matches.length; |
|
|
|
var numMatches = matches.length; |
|
|
|
var previous = this.state.findPrevious; |
|
|
|
var previous = this.state.findPrevious; |
|
|
|
|
|
|
|
|
|
|
|
if (numMatches) { |
|
|
|
if (numMatches) { |
|
|
|
// There were matches for the page, so initialize the matchIdx.
|
|
|
|
// There were matches for the page, so initialize the matchIdx.
|
|
|
|
this.hadMatch = true; |
|
|
|
this.hadMatch = true; |
|
|
@ -301,7 +299,7 @@ var PDFFindController = { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
nextPageMatch: function() { |
|
|
|
nextPageMatch: function PDFFindController_nextPageMatch() { |
|
|
|
if (this.resumePageIdx !== null) { |
|
|
|
if (this.resumePageIdx !== null) { |
|
|
|
console.error('There can only be one pending page.'); |
|
|
|
console.error('There can only be one pending page.'); |
|
|
|
} |
|
|
|
} |
|
|
@ -317,11 +315,12 @@ var PDFFindController = { |
|
|
|
} while (!this.matchesReady(matches)); |
|
|
|
} while (!this.matchesReady(matches)); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
advanceOffsetPage: function(previous) { |
|
|
|
advanceOffsetPage: function PDFFindController_advanceOffsetPage(previous) { |
|
|
|
var offset = this.offset; |
|
|
|
var offset = this.offset; |
|
|
|
var numPages = this.extractTextPromises.length; |
|
|
|
var numPages = this.extractTextPromises.length; |
|
|
|
offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1); |
|
|
|
offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1); |
|
|
|
offset.matchIdx = null; |
|
|
|
offset.matchIdx = null; |
|
|
|
|
|
|
|
|
|
|
|
if (offset.pageIdx >= numPages || offset.pageIdx < 0) { |
|
|
|
if (offset.pageIdx >= numPages || offset.pageIdx < 0) { |
|
|
|
offset.pageIdx = (previous ? numPages - 1 : 0); |
|
|
|
offset.pageIdx = (previous ? numPages - 1 : 0); |
|
|
|
offset.wrapped = true; |
|
|
|
offset.wrapped = true; |
|
|
@ -329,10 +328,11 @@ var PDFFindController = { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
updateMatch: function(found) { |
|
|
|
updateMatch: function PDFFindController_updateMatch(found) { |
|
|
|
var state = FindStates.FIND_NOTFOUND; |
|
|
|
var state = FindStates.FIND_NOTFOUND; |
|
|
|
var wrapped = this.offset.wrapped; |
|
|
|
var wrapped = this.offset.wrapped; |
|
|
|
this.offset.wrapped = false; |
|
|
|
this.offset.wrapped = false; |
|
|
|
|
|
|
|
|
|
|
|
if (found) { |
|
|
|
if (found) { |
|
|
|
var previousPage = this.selected.pageIdx; |
|
|
|
var previousPage = this.selected.pageIdx; |
|
|
|
this.selected.pageIdx = this.offset.pageIdx; |
|
|
|
this.selected.pageIdx = this.offset.pageIdx; |
|
|
@ -343,19 +343,26 @@ var PDFFindController = { |
|
|
|
this.updatePage(previousPage); |
|
|
|
this.updatePage(previousPage); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.updateUIState(state, this.state.findPrevious); |
|
|
|
this.updateUIState(state, this.state.findPrevious); |
|
|
|
if (this.selected.pageIdx !== -1) { |
|
|
|
if (this.selected.pageIdx !== -1) { |
|
|
|
this.updatePage(this.selected.pageIdx, true); |
|
|
|
this.updatePage(this.selected.pageIdx, true); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
updateUIState: function(state, previous) { |
|
|
|
updateUIState: function PDFFindController_updateUIState(state, previous) { |
|
|
|
if (this.integratedFind) { |
|
|
|
if (this.integratedFind) { |
|
|
|
FirefoxCom.request('updateFindControlState', |
|
|
|
FirefoxCom.request('updateFindControlState', |
|
|
|
{ result: state, findPrevious: previous }); |
|
|
|
{ result: state, findPrevious: previous }); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
PDFFindBar.updateUIState(state, previous); |
|
|
|
if (this.findBar === null) { |
|
|
|
|
|
|
|
throw new Error('PDFFindController is not initialized with a ' + |
|
|
|
|
|
|
|
'PDFFindBar instance.'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.findBar.updateUIState(state, previous); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
return PDFFindController; |
|
|
|
|
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|