Browse Source

Convert `PDFLinkService` to an ES6 class

Jonas Jenwald 8 years ago
parent
commit
8ba8072937
  1. 5
      web/interfaces.js
  2. 782
      web/pdf_link_service.js

5
web/interfaces.js

@ -57,6 +57,11 @@ class IPDFLinkService {
*/ */
executeNamedAction(action) {} executeNamedAction(action) {}
/**
* @param {Object} params
*/
onFileAttachmentAnnotation({ id, filename, content, }) {}
/** /**
* @param {number} pageNum - page number. * @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page. * @param {Object} pageRef - reference to the page.

782
web/pdf_link_service.js

@ -24,17 +24,14 @@ import { parseQueryString } from './ui_utils';
/** /**
* Performs navigation functions inside PDF, such as opening specified page, * Performs navigation functions inside PDF, such as opening specified page,
* or destination. * or destination.
* @class
* @implements {IPDFLinkService} * @implements {IPDFLinkService}
*/ */
var PDFLinkService = (function PDFLinkServiceClosure() { class PDFLinkService {
/** /**
* @constructs PDFLinkService
* @param {PDFLinkServiceOptions} options * @param {PDFLinkServiceOptions} options
*/ */
function PDFLinkService(options) { constructor({ eventBus, } = {}) {
options = options || {}; this.eventBus = eventBus || getGlobalEventBus();
this.eventBus = options.eventBus || getGlobalEventBus();
this.baseUrl = null; this.baseUrl = null;
this.pdfDocument = null; this.pdfDocument = null;
this.pdfViewer = null; this.pdfViewer = null;
@ -43,432 +40,423 @@ var PDFLinkService = (function PDFLinkServiceClosure() {
this._pagesRefCache = null; this._pagesRefCache = null;
} }
PDFLinkService.prototype = { setDocument(pdfDocument, baseUrl) {
setDocument: function PDFLinkService_setDocument(pdfDocument, baseUrl) { this.baseUrl = baseUrl;
this.baseUrl = baseUrl; this.pdfDocument = pdfDocument;
this.pdfDocument = pdfDocument; this._pagesRefCache = Object.create(null);
this._pagesRefCache = Object.create(null); }
},
setViewer: function PDFLinkService_setViewer(pdfViewer) {
this.pdfViewer = pdfViewer;
},
setHistory: function PDFLinkService_setHistory(pdfHistory) {
this.pdfHistory = pdfHistory;
},
/**
* @returns {number}
*/
get pagesCount() {
return this.pdfDocument ? this.pdfDocument.numPages : 0;
},
/**
* @returns {number}
*/
get page() {
return this.pdfViewer.currentPageNumber;
},
/**
* @param {number} value
*/
set page(value) {
this.pdfViewer.currentPageNumber = value;
},
/**
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/
navigateTo(dest) {
let goToDestination = ({ namedDest, explicitDest, }) => {
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
let destRef = explicitDest[0], pageNumber;
if (destRef instanceof Object) {
pageNumber = this._cachedPageNumber(destRef);
if (pageNumber === null) {
// Fetch the page reference if it's not yet available. This could
// only occur during loading, before all pages have been resolved.
this.pdfDocument.getPageIndex(destRef).then((pageIndex) => {
this.cachePageRef(pageIndex + 1, destRef);
goToDestination({ namedDest, explicitDest, });
}).catch(() => {
console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid page reference, for dest="${dest}".`);
});
return;
}
} else if ((destRef | 0) === destRef) { // Integer
pageNumber = destRef + 1;
} else {
console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid destination reference, for dest="${dest}".`);
return;
}
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
console.error(`PDFLinkService.navigateTo: "${pageNumber}" is not ` +
`a valid page number, for dest="${dest}".`);
return;
}
this.pdfViewer.scrollPageIntoView({ setViewer(pdfViewer) {
pageNumber, this.pdfViewer = pdfViewer;
destArray: explicitDest, }
});
if (this.pdfHistory) { // Update the browsing history, if enabled. setHistory(pdfHistory) {
this.pdfHistory.push({ this.pdfHistory = pdfHistory;
dest: explicitDest, }
hash: namedDest,
page: pageNumber, /**
}); * @returns {number}
} */
}; get pagesCount() {
return this.pdfDocument ? this.pdfDocument.numPages : 0;
new Promise((resolve, reject) => { }
if (typeof dest === 'string') {
this.pdfDocument.getDestination(dest).then((destArray) => { /**
resolve({ * @returns {number}
namedDest: dest, */
explicitDest: destArray, get page() {
}); return this.pdfViewer.currentPageNumber;
}
/**
* @param {number} value
*/
set page(value) {
this.pdfViewer.currentPageNumber = value;
}
/**
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/
navigateTo(dest) {
let goToDestination = ({ namedDest, explicitDest, }) => {
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
let destRef = explicitDest[0], pageNumber;
if (destRef instanceof Object) {
pageNumber = this._cachedPageNumber(destRef);
if (pageNumber === null) {
// Fetch the page reference if it's not yet available. This could
// only occur during loading, before all pages have been resolved.
this.pdfDocument.getPageIndex(destRef).then((pageIndex) => {
this.cachePageRef(pageIndex + 1, destRef);
goToDestination({ namedDest, explicitDest, });
}).catch(() => {
console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid page reference, for dest="${dest}".`);
}); });
return; return;
} }
resolve({ } else if ((destRef | 0) === destRef) { // Integer
namedDest: '', pageNumber = destRef + 1;
explicitDest: dest, } else {
}); console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
}).then((data) => { `a valid destination reference, for dest="${dest}".`);
if (!(data.explicitDest instanceof Array)) { return;
console.error(`PDFLinkService.navigateTo: "${data.explicitDest}" is` + }
` not a valid destination array, for dest="${dest}".`); if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
return; console.error(`PDFLinkService.navigateTo: "${pageNumber}" is not ` +
} `a valid page number, for dest="${dest}".`);
goToDestination(data); return;
}
this.pdfViewer.scrollPageIntoView({
pageNumber,
destArray: explicitDest,
}); });
},
/** if (this.pdfHistory) { // Update the browsing history, if enabled.
* @param {string|Array} dest - The PDF destination object. this.pdfHistory.push({
* @returns {string} The hyperlink to the PDF object. dest: explicitDest,
*/ hash: namedDest,
getDestinationHash(dest) { page: pageNumber,
});
}
};
new Promise((resolve, reject) => {
if (typeof dest === 'string') { if (typeof dest === 'string') {
return this.getAnchorUrl('#' + escape(dest)); this.pdfDocument.getDestination(dest).then((destArray) => {
resolve({
namedDest: dest,
explicitDest: destArray,
});
});
return;
} }
if (dest instanceof Array) { resolve({
let str = JSON.stringify(dest); namedDest: '',
return this.getAnchorUrl('#' + escape(str)); explicitDest: dest,
});
}).then((data) => {
if (!(data.explicitDest instanceof Array)) {
console.error(`PDFLinkService.navigateTo: "${data.explicitDest}" is` +
` not a valid destination array, for dest="${dest}".`);
return;
} }
return this.getAnchorUrl(''); goToDestination(data);
}, });
}
/**
* Prefix the full url on anchor links to make sure that links are resolved /**
* relative to the current URL instead of the one defined in <base href>. * @param {string|Array} dest - The PDF destination object.
* @param {String} anchor The anchor hash, including the #. * @returns {string} The hyperlink to the PDF object.
* @returns {string} The hyperlink to the PDF object. */
*/ getDestinationHash(dest) {
getAnchorUrl: function PDFLinkService_getAnchorUrl(anchor) { if (typeof dest === 'string') {
return (this.baseUrl || '') + anchor; return this.getAnchorUrl('#' + escape(dest));
}, }
if (dest instanceof Array) {
/** let str = JSON.stringify(dest);
* @param {string} hash return this.getAnchorUrl('#' + escape(str));
*/ }
setHash: function PDFLinkService_setHash(hash) { return this.getAnchorUrl('');
var pageNumber, dest; }
if (hash.indexOf('=') >= 0) {
var params = parseQueryString(hash); /**
if ('search' in params) { * Prefix the full url on anchor links to make sure that links are resolved
this.eventBus.dispatch('findfromurlhash', { * relative to the current URL instead of the one defined in <base href>.
source: this, * @param {String} anchor The anchor hash, including the #.
query: params['search'].replace(/"/g, ''), * @returns {string} The hyperlink to the PDF object.
phraseSearch: (params['phrase'] === 'true'), */
}); getAnchorUrl(anchor) {
} return (this.baseUrl || '') + anchor;
// borrowing syntax from "Parameters for Opening PDF Files" }
if ('nameddest' in params) {
if (this.pdfHistory) { /**
this.pdfHistory.updateNextHashParam(params.nameddest); * @param {string} hash
} */
this.navigateTo(params.nameddest); setHash(hash) {
return; let pageNumber, dest;
} if (hash.indexOf('=') >= 0) {
if ('page' in params) { let params = parseQueryString(hash);
pageNumber = (params.page | 0) || 1; 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) {
this.pdfHistory.updateNextHashParam(params.nameddest);
} }
if ('zoom' in params) { this.navigateTo(params.nameddest);
// Build the destination array. return;
var zoomArgs = params.zoom.split(','); // scale,left,top }
var zoomArg = zoomArgs[0]; if ('page' in params) {
var zoomArgNumber = parseFloat(zoomArg); pageNumber = (params.page | 0) || 1;
}
if (zoomArg.indexOf('Fit') === -1) { if ('zoom' in params) {
// If the zoomArg is a number, it has to get divided by 100. If it's // Build the destination array.
// a string, it should stay as it is. let zoomArgs = params.zoom.split(','); // scale,left,top
dest = [null, { name: 'XYZ', }, let zoomArg = zoomArgs[0];
zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null, let zoomArgNumber = parseFloat(zoomArg);
zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
(zoomArgNumber ? zoomArgNumber / 100 : zoomArg)]; if (zoomArg.indexOf('Fit') === -1) {
} else { // If the zoomArg is a number, it has to get divided by 100. If it's
if (zoomArg === 'Fit' || zoomArg === 'FitB') { // a string, it should stay as it is.
dest = [null, { name: zoomArg, }]; dest = [null, { name: 'XYZ', },
} else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') || zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
(zoomArg === 'FitV' || zoomArg === 'FitBV')) { zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
dest = [null, { name: zoomArg, }, (zoomArgNumber ? zoomArgNumber / 100 : zoomArg)];
zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null]; } else {
} else if (zoomArg === 'FitR') { if (zoomArg === 'Fit' || zoomArg === 'FitB') {
if (zoomArgs.length !== 5) { dest = [null, { name: zoomArg, }];
console.error('PDFLinkService_setHash: ' + } else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') ||
'Not enough parameters for \'FitR\'.'); (zoomArg === 'FitV' || zoomArg === 'FitBV')) {
} else { dest = [null, { name: zoomArg, },
dest = [null, { name: zoomArg, }, zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null];
(zoomArgs[1] | 0), (zoomArgs[2] | 0), } else if (zoomArg === 'FitR') {
(zoomArgs[3] | 0), (zoomArgs[4] | 0)]; if (zoomArgs.length !== 5) {
} console.error(
'PDFLinkService.setHash: Not enough parameters for "FitR".');
} else { } else {
console.error('PDFLinkService_setHash: \'' + zoomArg + dest = [null, { name: zoomArg, },
'\' is not a valid zoom value.'); (zoomArgs[1] | 0), (zoomArgs[2] | 0),
(zoomArgs[3] | 0), (zoomArgs[4] | 0)];
} }
} else {
console.error(`PDFLinkService.setHash: "${zoomArg}" is not ` +
'a valid zoom value.');
} }
} }
if (dest) { }
this.pdfViewer.scrollPageIntoView({ if (dest) {
pageNumber: pageNumber || this.page, this.pdfViewer.scrollPageIntoView({
destArray: dest, pageNumber: pageNumber || this.page,
allowNegativeOffset: true, destArray: dest,
}); allowNegativeOffset: true,
} else if (pageNumber) { });
this.page = pageNumber; // simple page } else if (pageNumber) {
} this.page = pageNumber; // simple page
if ('pagemode' in params) { }
this.eventBus.dispatch('pagemode', { if ('pagemode' in params) {
source: this, this.eventBus.dispatch('pagemode', {
mode: params.pagemode, source: this,
}); mode: params.pagemode,
} });
} else { // Named (or explicit) destination. }
if ((typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) && } else { // Named (or explicit) destination.
/^\d+$/.test(hash) && hash <= this.pagesCount) { if ((typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) &&
console.warn('PDFLinkService_setHash: specifying a page number ' + /^\d+$/.test(hash) && hash <= this.pagesCount) {
'directly after the hash symbol (#) is deprecated, ' + console.warn('PDFLinkService_setHash: specifying a page number ' +
'please use the "#page=' + hash + '" form instead.'); 'directly after the hash symbol (#) is deprecated, ' +
this.page = hash | 0; `please use the "#page=${hash}" form instead.`);
} this.page = hash | 0;
}
dest = unescape(hash); dest = unescape(hash);
try { try {
dest = JSON.parse(dest); dest = JSON.parse(dest);
if (!(dest instanceof Array)) { if (!(dest instanceof Array)) {
// Avoid incorrectly rejecting a valid named destination, such as // Avoid incorrectly rejecting a valid named destination, such as
// e.g. "4.3" or "true", because `JSON.parse` converted its type. // e.g. "4.3" or "true", because `JSON.parse` converted its type.
dest = dest.toString(); dest = dest.toString();
} }
} catch (ex) {} } catch (ex) {}
if (typeof dest === 'string' || isValidExplicitDestination(dest)) { if (typeof dest === 'string' || isValidExplicitDestination(dest)) {
if (this.pdfHistory) { if (this.pdfHistory) {
this.pdfHistory.updateNextHashParam(dest); this.pdfHistory.updateNextHashParam(dest);
}
this.navigateTo(dest);
return;
} }
console.error('PDFLinkService_setHash: \'' + unescape(hash) + this.navigateTo(dest);
'\' is not a valid destination.'); return;
} }
}, console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not ` +
'a valid destination.');
/** }
* @param {string} action }
*/
executeNamedAction: function PDFLinkService_executeNamedAction(action) {
// See PDF reference, table 8.45 - Named action
switch (action) {
case 'GoBack':
if (this.pdfHistory) {
this.pdfHistory.back();
}
break;
case 'GoForward':
if (this.pdfHistory) {
this.pdfHistory.forward();
}
break;
case 'NextPage':
if (this.page < this.pagesCount) {
this.page++;
}
break;
case 'PrevPage':
if (this.page > 1) {
this.page--;
}
break;
case 'LastPage':
this.page = this.pagesCount;
break;
case 'FirstPage':
this.page = 1;
break;
default: /**
break; // No action according to spec * @param {string} action
} */
executeNamedAction(action) {
// See PDF reference, table 8.45 - Named action
switch (action) {
case 'GoBack':
if (this.pdfHistory) {
this.pdfHistory.back();
}
break;
this.eventBus.dispatch('namedaction', { case 'GoForward':
source: this, if (this.pdfHistory) {
action, this.pdfHistory.forward();
});
},
/**
* @param {Object} params
*/
onFileAttachmentAnnotation(params = {}) {
this.eventBus.dispatch('fileattachmentannotation', {
source: this,
id: params.id,
filename: params.filename,
content: params.content,
});
},
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) {
var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
this._pagesRefCache[refStr] = pageNum;
},
_cachedPageNumber: function PDFLinkService_cachedPageNumber(pageRef) {
var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
return (this._pagesRefCache && this._pagesRefCache[refStr]) || null;
},
};
function isValidExplicitDestination(dest) {
if (!(dest instanceof Array)) {
return false;
}
var destLength = dest.length, allowNull = true;
if (destLength < 2) {
return false;
}
var page = dest[0];
if (!(typeof page === 'object' &&
typeof page.num === 'number' && (page.num | 0) === page.num &&
typeof page.gen === 'number' && (page.gen | 0) === page.gen) &&
!(typeof page === 'number' && (page | 0) === page && page >= 0)) {
return false;
}
var zoom = dest[1];
if (!(typeof zoom === 'object' && typeof zoom.name === 'string')) {
return false;
}
switch (zoom.name) {
case 'XYZ':
if (destLength !== 5) {
return false;
} }
break; break;
case 'Fit':
case 'FitB': case 'NextPage':
return destLength === 2; if (this.page < this.pagesCount) {
case 'FitH': this.page++;
case 'FitBH':
case 'FitV':
case 'FitBV':
if (destLength !== 3) {
return false;
} }
break; break;
case 'FitR':
if (destLength !== 6) { case 'PrevPage':
return false; if (this.page > 1) {
this.page--;
} }
allowNull = false;
break; break;
case 'LastPage':
this.page = this.pagesCount;
break;
case 'FirstPage':
this.page = 1;
break;
default: default:
return false; break; // No action according to spec
} }
for (var i = 2; i < destLength; i++) {
var param = dest[i]; this.eventBus.dispatch('namedaction', {
if (!(typeof param === 'number' || (allowNull && param === null))) { source: this,
action,
});
}
/**
* @param {Object} params
*/
onFileAttachmentAnnotation({ id, filename, content, }) {
this.eventBus.dispatch('fileattachmentannotation', {
source: this,
id,
filename,
content,
});
}
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef(pageNum, pageRef) {
let refStr = pageRef.num + ' ' + pageRef.gen + ' R';
this._pagesRefCache[refStr] = pageNum;
}
_cachedPageNumber(pageRef) {
let refStr = pageRef.num + ' ' + pageRef.gen + ' R';
return (this._pagesRefCache && this._pagesRefCache[refStr]) || null;
}
}
function isValidExplicitDestination(dest) {
if (!(dest instanceof Array)) {
return false;
}
let destLength = dest.length, allowNull = true;
if (destLength < 2) {
return false;
}
let page = dest[0];
if (!(typeof page === 'object' &&
typeof page.num === 'number' && (page.num | 0) === page.num &&
typeof page.gen === 'number' && (page.gen | 0) === page.gen) &&
!(typeof page === 'number' && (page | 0) === page && page >= 0)) {
return false;
}
let zoom = dest[1];
if (!(typeof zoom === 'object' && typeof zoom.name === 'string')) {
return false;
}
switch (zoom.name) {
case 'XYZ':
if (destLength !== 5) {
return false;
}
break;
case 'Fit':
case 'FitB':
return destLength === 2;
case 'FitH':
case 'FitBH':
case 'FitV':
case 'FitBV':
if (destLength !== 3) {
return false;
}
break;
case 'FitR':
if (destLength !== 6) {
return false; return false;
} }
allowNull = false;
break;
default:
return false;
}
for (let i = 2; i < destLength; i++) {
let param = dest[i];
if (!(typeof param === 'number' || (allowNull && param === null))) {
return false;
} }
return true;
} }
return true;
}
return PDFLinkService; class SimpleLinkService {
})(); /**
* @returns {number}
var SimpleLinkService = (function SimpleLinkServiceClosure() { */
function SimpleLinkService() {} get page() {
return 0;
SimpleLinkService.prototype = { }
/** /**
* @returns {number} * @param {number} value
*/ */
get page() { set page(value) {}
return 0; /**
}, * @param dest - The PDF destination object.
/** */
* @param {number} value navigateTo(dest) {}
*/ /**
set page(value) {}, * @param dest - The PDF destination object.
/** * @returns {string} The hyperlink to the PDF object.
* @param dest - The PDF destination object. */
*/ getDestinationHash(dest) {
navigateTo(dest) {}, return '#';
/** }
* @param dest - The PDF destination object. /**
* @returns {string} The hyperlink to the PDF object. * @param hash - The PDF parameters/hash.
*/ * @returns {string} The hyperlink to the PDF object.
getDestinationHash(dest) { */
return '#'; getAnchorUrl(hash) {
}, return '#';
/** }
* @param hash - The PDF parameters/hash. /**
* @returns {string} The hyperlink to the PDF object. * @param {string} hash
*/ */
getAnchorUrl(hash) { setHash(hash) {}
return '#'; /**
}, * @param {string} action
/** */
* @param {string} hash executeNamedAction(action) {}
*/ /**
setHash(hash) {}, * @param {Object} params
/** */
* @param {string} action onFileAttachmentAnnotation({ id, filename, content, }) {}
*/ /**
executeNamedAction(action) {}, * @param {number} pageNum - page number.
/** * @param {Object} pageRef - reference to the page.
* @param {Object} params */
*/ cachePageRef(pageNum, pageRef) {}
onFileAttachmentAnnotation(params) {}, }
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef(pageNum, pageRef) {},
};
return SimpleLinkService;
})();
export { export {
PDFLinkService, PDFLinkService,

Loading…
Cancel
Save