Merge pull request #10019 from Snuffleupagus/eventBusDispatchToDOM

Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This commit is contained in:
Tim van der Meij 2018-09-01 19:11:23 +02:00 committed by GitHub
commit f2f2e05bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 113 additions and 22 deletions

View File

@ -47,6 +47,10 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"eventBusDispatchToDOM": {
"type": "boolean",
"default": false
},
"pdfBugEnabled": { "pdfBugEnabled": {
"title": "Enable debugging tools", "title": "Enable debugging tools",
"description": "Whether to enable debugging tools.", "description": "Whether to enable debugging tools.",

View File

@ -262,6 +262,45 @@ describe('ui_utils', function() {
eventBus.dispatch('test'); eventBus.dispatch('test');
expect(count).toEqual(2); expect(count).toEqual(2);
}); });
it('should not, by default, re-dispatch to DOM', function(done) {
if (isNodeJS()) {
pending('Document in not supported in Node.js.');
}
const eventBus = new EventBus();
let count = 0;
eventBus.on('test', function() {
count++;
});
document.addEventListener('test', function() {
count++;
});
eventBus.dispatch('test');
Promise.resolve().then(() => {
expect(count).toEqual(1);
done();
});
});
it('should re-dispatch to DOM', function(done) {
if (isNodeJS()) {
pending('Document in not supported in Node.js.');
}
const eventBus = new EventBus({ dispatchToDOM: true, });
let count = 0;
eventBus.on('test', function() {
count++;
});
document.addEventListener('test', function() {
count++;
});
eventBus.dispatch('test');
Promise.resolve().then(() => {
expect(count).toEqual(2);
done();
});
});
}); });
describe('isValidRotation', function() { describe('isValidRotation', function() {

View File

@ -160,7 +160,7 @@ let PDFViewerApplication = {
this.l10n.translate(appContainer).then(() => { this.l10n.translate(appContainer).then(() => {
// Dispatch the 'localized' event on the `eventBus` once the viewer // Dispatch the 'localized' event on the `eventBus` once the viewer
// has been fully initialized and translated. // has been fully initialized and translated.
this.eventBus.dispatch('localized'); this.eventBus.dispatch('localized', { source: this, });
}); });
this.initialized = true; this.initialized = true;
@ -288,7 +288,8 @@ let PDFViewerApplication = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.overlayManager = new OverlayManager(); this.overlayManager = new OverlayManager();
let eventBus = appConfig.eventBus || getGlobalEventBus(); const dispatchToDOM = AppOptions.get('eventBusDispatchToDOM');
let eventBus = appConfig.eventBus || getGlobalEventBus(dispatchToDOM);
this.eventBus = eventBus; this.eventBus = eventBus;
let pdfRenderingQueue = new PDFRenderingQueue(); let pdfRenderingQueue = new PDFRenderingQueue();
@ -897,6 +898,9 @@ let PDFViewerApplication = {
this.loadingBar.hide(); this.loadingBar.hide();
firstPagePromise.then(() => { firstPagePromise.then(() => {
this.eventBus.dispatch('documentloaded', { source: this, });
// TODO: Remove the following event, i.e. 'documentload',
// once the mozilla-central tests have been updated.
this.eventBus.dispatch('documentload', { source: this, }); this.eventBus.dispatch('documentload', { source: this, });
}); });
}); });
@ -1000,6 +1004,7 @@ let PDFViewerApplication = {
this.setInitialView(hash, { this.setInitialView(hash, {
rotation, sidebarView, scrollMode, spreadMode, rotation, sidebarView, scrollMode, spreadMode,
}); });
this.eventBus.dispatch('documentinit', { source: this, });
// Make all navigation keys work on document load, // Make all navigation keys work on document load,
// unless the viewer is embedded in a web page. // unless the viewer is embedded in a web page.
@ -1371,14 +1376,15 @@ let PDFViewerApplication = {
}; };
_boundEvents.windowHashChange = () => { _boundEvents.windowHashChange = () => {
eventBus.dispatch('hashchange', { eventBus.dispatch('hashchange', {
source: window,
hash: document.location.hash.substring(1), hash: document.location.hash.substring(1),
}); });
}; };
_boundEvents.windowBeforePrint = () => { _boundEvents.windowBeforePrint = () => {
eventBus.dispatch('beforeprint'); eventBus.dispatch('beforeprint', { source: window, });
}; };
_boundEvents.windowAfterPrint = () => { _boundEvents.windowAfterPrint = () => {
eventBus.dispatch('afterprint'); eventBus.dispatch('afterprint', { source: window, });
}; };
window.addEventListener('wheel', webViewerWheel); window.addEventListener('wheel', webViewerWheel);
@ -1560,6 +1566,7 @@ function webViewerInitialized() {
return; return;
} }
PDFViewerApplication.eventBus.dispatch('fileinputchange', { PDFViewerApplication.eventBus.dispatch('fileinputchange', {
source: this,
fileInput: evt.target, fileInput: evt.target,
}); });
}); });
@ -1578,6 +1585,7 @@ function webViewerInitialized() {
return; return;
} }
PDFViewerApplication.eventBus.dispatch('fileinputchange', { PDFViewerApplication.eventBus.dispatch('fileinputchange', {
source: this,
fileInput: evt.dataTransfer, fileInput: evt.dataTransfer,
}); });
}); });

View File

@ -68,6 +68,11 @@ const defaultOptions = {
value: false, value: false,
kind: OptionKind.VIEWER, kind: OptionKind.VIEWER,
}, },
eventBusDispatchToDOM: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER,
},
externalLinkRel: { externalLinkRel: {
/** @type {string} */ /** @type {string} */
value: 'noopener noreferrer nofollow', value: 'noopener noreferrer nofollow',

View File

@ -4,6 +4,7 @@
"sidebarViewOnLoad": 0, "sidebarViewOnLoad": 0,
"cursorToolOnLoad": 0, "cursorToolOnLoad": 0,
"enableWebGL": false, "enableWebGL": false,
"eventBusDispatchToDOM": false,
"pdfBugEnabled": false, "pdfBugEnabled": false,
"disableRange": false, "disableRange": false,
"disableStream": false, "disableStream": false,

View File

@ -129,12 +129,13 @@ function attachDOMEventsToEventBus(eventBus) {
} }
let globalEventBus = null; let globalEventBus = null;
function getGlobalEventBus() { function getGlobalEventBus(dispatchToDOM = false) {
if (globalEventBus) { if (!globalEventBus) {
return globalEventBus; globalEventBus = new EventBus({ dispatchToDOM, });
if (!dispatchToDOM) {
attachDOMEventsToEventBus(globalEventBus);
}
} }
globalEventBus = new EventBus();
attachDOMEventsToEventBus(globalEventBus);
return globalEventBus; return globalEventBus;
} }

View File

@ -60,19 +60,19 @@ class PDFPresentationMode {
if (contextMenuItems) { if (contextMenuItems) {
contextMenuItems.contextFirstPage.addEventListener('click', () => { contextMenuItems.contextFirstPage.addEventListener('click', () => {
this.contextMenuOpen = false; this.contextMenuOpen = false;
this.eventBus.dispatch('firstpage'); this.eventBus.dispatch('firstpage', { source: this, });
}); });
contextMenuItems.contextLastPage.addEventListener('click', () => { contextMenuItems.contextLastPage.addEventListener('click', () => {
this.contextMenuOpen = false; this.contextMenuOpen = false;
this.eventBus.dispatch('lastpage'); this.eventBus.dispatch('lastpage', { source: this, });
}); });
contextMenuItems.contextPageRotateCw.addEventListener('click', () => { contextMenuItems.contextPageRotateCw.addEventListener('click', () => {
this.contextMenuOpen = false; this.contextMenuOpen = false;
this.eventBus.dispatch('rotatecw'); this.eventBus.dispatch('rotatecw', { source: this, });
}); });
contextMenuItems.contextPageRotateCcw.addEventListener('click', () => { contextMenuItems.contextPageRotateCcw.addEventListener('click', () => {
this.contextMenuOpen = false; this.contextMenuOpen = false;
this.eventBus.dispatch('rotateccw'); this.eventBus.dispatch('rotateccw', { source: this, });
}); });
} }
} }

View File

@ -100,19 +100,19 @@ class Toolbar {
let self = this; let self = this;
items.previous.addEventListener('click', function() { items.previous.addEventListener('click', function() {
eventBus.dispatch('previouspage'); eventBus.dispatch('previouspage', { source: self, });
}); });
items.next.addEventListener('click', function() { items.next.addEventListener('click', function() {
eventBus.dispatch('nextpage'); eventBus.dispatch('nextpage', { source: self, });
}); });
items.zoomIn.addEventListener('click', function() { items.zoomIn.addEventListener('click', function() {
eventBus.dispatch('zoomin'); eventBus.dispatch('zoomin', { source: self, });
}); });
items.zoomOut.addEventListener('click', function() { items.zoomOut.addEventListener('click', function() {
eventBus.dispatch('zoomout'); eventBus.dispatch('zoomout', { source: self, });
}); });
items.pageNumber.addEventListener('click', function() { items.pageNumber.addEventListener('click', function() {
@ -137,19 +137,19 @@ class Toolbar {
}); });
items.presentationModeButton.addEventListener('click', function() { items.presentationModeButton.addEventListener('click', function() {
eventBus.dispatch('presentationmode'); eventBus.dispatch('presentationmode', { source: self, });
}); });
items.openFile.addEventListener('click', function() { items.openFile.addEventListener('click', function() {
eventBus.dispatch('openfile'); eventBus.dispatch('openfile', { source: self, });
}); });
items.print.addEventListener('click', function() { items.print.addEventListener('click', function() {
eventBus.dispatch('print'); eventBus.dispatch('print', { source: self, });
}); });
items.download.addEventListener('click', function() { items.download.addEventListener('click', function() {
eventBus.dispatch('download'); eventBus.dispatch('download', { source: self, });
}); });
// Suppress context menus for some controls. // Suppress context menus for some controls.

View File

@ -683,8 +683,9 @@ let animationStarted = new Promise(function (resolve) {
* used. * used.
*/ */
class EventBus { class EventBus {
constructor() { constructor({ dispatchToDOM = false, } = {}) {
this._listeners = Object.create(null); this._listeners = Object.create(null);
this._dispatchToDOM = dispatchToDOM === true;
} }
on(eventName, listener) { on(eventName, listener) {
@ -708,6 +709,9 @@ class EventBus {
dispatch(eventName) { dispatch(eventName) {
let eventListeners = this._listeners[eventName]; let eventListeners = this._listeners[eventName];
if (!eventListeners || eventListeners.length === 0) { if (!eventListeners || eventListeners.length === 0) {
if (this._dispatchToDOM) {
this._dispatchDOMEvent(eventName);
}
return; return;
} }
// Passing all arguments after the eventName to the listeners. // Passing all arguments after the eventName to the listeners.
@ -717,6 +721,35 @@ class EventBus {
eventListeners.slice(0).forEach(function (listener) { eventListeners.slice(0).forEach(function (listener) {
listener.apply(null, args); listener.apply(null, args);
}); });
if (this._dispatchToDOM) {
this._dispatchDOMEvent(eventName, args);
}
}
/**
* @private
*/
_dispatchDOMEvent(eventName, args = null) {
if (!this._dispatchToDOM) {
return;
}
const details = Object.create(null);
if (args && args.length > 0) {
const obj = args[0];
for (let key in obj) {
const value = obj[key];
if (key === 'source') {
if (value === window || value === document) {
return; // No need to re-dispatch (already) global events.
}
continue; // Ignore the `source` property.
}
details[key] = value;
}
}
const event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, details);
document.dispatchEvent(event);
} }
} }