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",
"default": false
},
"eventBusDispatchToDOM": {
"type": "boolean",
"default": false
},
"pdfBugEnabled": {
"title": "Enable debugging tools",
"description": "Whether to enable debugging tools.",

View File

@ -262,6 +262,45 @@ describe('ui_utils', function() {
eventBus.dispatch('test');
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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -683,8 +683,9 @@ let animationStarted = new Promise(function (resolve) {
* used.
*/
class EventBus {
constructor() {
constructor({ dispatchToDOM = false, } = {}) {
this._listeners = Object.create(null);
this._dispatchToDOM = dispatchToDOM === true;
}
on(eventName, listener) {
@ -708,6 +709,9 @@ class EventBus {
dispatch(eventName) {
let eventListeners = this._listeners[eventName];
if (!eventListeners || eventListeners.length === 0) {
if (this._dispatchToDOM) {
this._dispatchDOMEvent(eventName);
}
return;
}
// Passing all arguments after the eventName to the listeners.
@ -717,6 +721,35 @@ class EventBus {
eventListeners.slice(0).forEach(function (listener) {
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);
}
}