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:
commit
f2f2e05bb8
@ -47,6 +47,10 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"eventBusDispatchToDOM": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"pdfBugEnabled": {
|
||||
"title": "Enable debugging tools",
|
||||
"description": "Whether to enable debugging tools.",
|
||||
|
@ -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() {
|
||||
|
16
web/app.js
16
web/app.js
@ -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,
|
||||
});
|
||||
});
|
||||
|
@ -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',
|
||||
|
@ -4,6 +4,7 @@
|
||||
"sidebarViewOnLoad": 0,
|
||||
"cursorToolOnLoad": 0,
|
||||
"enableWebGL": false,
|
||||
"eventBusDispatchToDOM": false,
|
||||
"pdfBugEnabled": false,
|
||||
"disableRange": false,
|
||||
"disableStream": false,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user