diff --git a/CHANGELOG.md b/CHANGELOG.md index 360b52e..6362b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +- Allow to change the target of observer ([#28](https://github.com/marp-team/marpit-svg-polyfill/pull/28)) + ## v1.5.0 - 2020-07-16 ### Changed diff --git a/src/entry.ts b/src/entry.ts index 52134eb..6ea3d8b 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -1,2 +1,8 @@ /* istanbul ignore file */ -export { observe as default, observe, polyfills, webkit } from './polyfill' +export { + PolyfillOption, + observe as default, + observe, + polyfills, + webkit, +} from './polyfill' diff --git a/src/polyfill.ts b/src/polyfill.ts index 7b570d8..5d6fecf 100644 --- a/src/polyfill.ts +++ b/src/polyfill.ts @@ -1,28 +1,39 @@ const msgPrefix = 'marpitSVGPolyfill:setZoomFactor,' +export type PolyfillOption = { target?: ParentNode } + export const observerSymbol = Symbol() export const zoomFactorRecieverSymbol = Symbol() -export function observe() { - if (window[observerSymbol]) return +export function observe(target: ParentNode = document): () => void { + if (target[observerSymbol]) return target[observerSymbol] + + let enableObserver = true + + const cleanup = () => { + enableObserver = false + delete target[observerSymbol] + } - Object.defineProperty(window, observerSymbol, { + Object.defineProperty(target, observerSymbol, { configurable: true, - value: true, + value: cleanup, }) const observedPolyfills = polyfills() if (observedPolyfills.length > 0) { const observer = () => { - for (const polyfill of observedPolyfills) polyfill() - window.requestAnimationFrame(observer) + for (const polyfill of observedPolyfills) polyfill({ target }) + if (enableObserver) window.requestAnimationFrame(observer) } observer() } + + return cleanup } -export const polyfills = () => +export const polyfills = (): Array<(opts: PolyfillOption) => void> => navigator.vendor === 'Apple Computer, Inc.' ? [webkit] : [] let previousZoomFactor: number @@ -35,7 +46,10 @@ export const _resetCachedZoomFactor = () => { _resetCachedZoomFactor() -export function webkit(zoom?: number) { +export function webkit(opts?: number | (PolyfillOption & { zoom?: number })) { + const target = (typeof opts === 'object' && opts.target) || document + const zoom = typeof opts === 'object' ? opts.zoom : opts + if (!window[zoomFactorRecieverSymbol]) { Object.defineProperty(window, zoomFactorRecieverSymbol, { configurable: true, @@ -61,7 +75,7 @@ export function webkit(zoom?: number) { let changedZoomFactor: false | number = false Array.from( - document.querySelectorAll('svg[data-marpit-svg]'), + target.querySelectorAll('svg[data-marpit-svg]'), (svg) => { const { children, clientHeight, clientWidth, viewBox } = svg if (!svg.style.transform) svg.style.transform = 'translateZ(0)' @@ -93,7 +107,7 @@ export function webkit(zoom?: number) { if (changedZoomFactor !== false) { Array.from( - document.querySelectorAll('iframe'), + target.querySelectorAll('iframe'), ({ contentWindow }) => { contentWindow?.postMessage( `${msgPrefix}${changedZoomFactor}`, diff --git a/test/polyfill.ts b/test/polyfill.ts index 6299dfc..68f9696 100644 --- a/test/polyfill.ts +++ b/test/polyfill.ts @@ -9,7 +9,7 @@ import { let vendor: jest.SpyInstance beforeEach(() => { - window[observerSymbol] = false + delete document[observerSymbol] vendor = jest.spyOn(navigator, 'vendor', 'get').mockImplementation(() => '') _resetCachedZoomFactor() }) @@ -37,6 +37,37 @@ describe('Marpit SVG polyfill', () => { observe() expect(spy).toHaveBeenCalledTimes(1) }) + + describe('Clean-up function', () => { + it('returns function for clean-up', () => { + vendor.mockImplementation(() => 'Apple Computer, Inc.') + + const cleanup = observe() + expect(cleanup).toStrictEqual(expect.any(Function)) + + // Observer can enable again after cleaning up + cleanup() + observe() + expect(spy).toHaveBeenCalledTimes(2) + }) + }) + + describe('Different target', () => { + it('availables observation for different target', () => { + vendor.mockImplementation(() => 'Apple Computer, Inc.') + + const element = document.createElement('div') + const querySpy = jest.spyOn(element, 'querySelectorAll') + const cleanup = observe(element) + + expect(element[observerSymbol]).toStrictEqual(cleanup) + expect(querySpy).toHaveBeenCalled() + + // Returns always same clean-up function even if observing some times + expect(observe(element)).toStrictEqual(cleanup) + expect(spy).toHaveBeenCalledTimes(1) + }) + }) }) describe('#webkit', () => {