diff --git a/src/js/BookReader.js b/src/js/BookReader.js index b23107f2e..7ec614546 100644 --- a/src/js/BookReader.js +++ b/src/js/BookReader.js @@ -503,6 +503,8 @@ BookReader.prototype.init = function() { BookReader.prototype.trigger = function(name, props = this) { const eventName = 'BookReader:' + name; $(document).trigger(eventName, props); + + utils.polyfillCustomEvent(window); window.dispatchEvent(new CustomEvent(eventName, { bubbles: true, composed: true, diff --git a/src/js/BookReader/utils.js b/src/js/BookReader/utils.js index a5fef1724..7bd14fbfe 100644 --- a/src/js/BookReader/utils.js +++ b/src/js/BookReader/utils.js @@ -137,3 +137,25 @@ export function throttle(fn, threshold, delay) { } }; } + +/** + * FIXME we need a better way to do this :/ This is not automatically poly-filled by + * core-js https://github.com/zloirock/core-js/issues/354 + * @param {Window} window + */ +export function polyfillCustomEvent(window) { + if (typeof window.CustomEvent === "function") return false; + window.CustomEvent = PolyfilledCustomEvent; +} + +/** + * https://caniuse.com/customevent has issues on older browsers where it can't be + * called as a constructor, so we have to use older methods. + * @param {String} eventName + * @return {CustomEvent} + */ +export function PolyfilledCustomEvent(eventName, {bubbles = false, cancelable = false, detail = null} = {}) { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventName, bubbles, cancelable, detail); + return event; +} diff --git a/tests/BookReader/utils.test.js b/tests/BookReader/utils.test.js index a1b8d8cd9..c1feae141 100644 --- a/tests/BookReader/utils.test.js +++ b/tests/BookReader/utils.test.js @@ -6,6 +6,8 @@ import { decodeURIComponentPlus, encodeURIComponentPlus, escapeHTML, + polyfillCustomEvent, + PolyfilledCustomEvent, } from '../../src/js/BookReader/utils.js'; test('clamp function returns Math.min(Math.max(value, min), max)', () => { @@ -61,3 +63,32 @@ test('testing debounce', () => { clock.tick(1000); expect(func).toHaveBeenCalledTimes(1); // func called }); + + +describe('polyfillCustomEvent', () => { + test('Overrides when missing', () => { + const win = {}; + polyfillCustomEvent(win); + expect(win).toHaveProperty('CustomEvent'); + }); + + test('Overrides when not a function', () => { + const win = { CustomEvent: {} }; + polyfillCustomEvent(win); + expect(typeof win.CustomEvent).toBe('function'); + }); +}); + +describe('PolyfilledCustomEvent', () => { + test('Can be called as a constructor', () => { + new PolyfilledCustomEvent('foo'); + }); + + test('Calls deprecated browser methods', () => { + const createEventSpy = sinon.spy(document, 'createEvent'); + const initCustomEventSpy = sinon.spy(CustomEvent.prototype, 'initCustomEvent'); + new PolyfilledCustomEvent('foo'); + expect(createEventSpy.callCount).toBe(1); + expect(initCustomEventSpy.callCount).toBe(1); + }); +});