From c7764afd5921f7c895f4c9a6e7d30646cb8ca808 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Tue, 21 Oct 2025 18:10:16 +0100 Subject: [PATCH 01/42] Add `both` to Angular directive type --- packages/angular/directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/directive.ts b/packages/angular/directive.ts index 1c8a53a1f..fb1704798 100644 --- a/packages/angular/directive.ts +++ b/packages/angular/directive.ts @@ -34,7 +34,7 @@ export type iframeResizerOptions = { bodyMargin?: string | number | null bodyPadding?: string | number | null checkOrigin?: boolean | string[] - direction?: 'vertical' | 'horizontal' | 'none' + direction?: 'vertical' | 'horizontal' | 'both' | 'none' inPageLinks?: boolean license: string offsetSize?: number From 895182132859d44648c2594415331587d35d2634 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:12:24 +0100 Subject: [PATCH 02/42] Refactor --- packages/child/check-blocking-css.js | 4 +- packages/child/observers/overflow.js | 4 +- packages/child/observers/perf.js | 6 - packages/core/send/timeout.js | 81 +++++++++++ packages/core/send/timeout.test.js | 201 +++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 packages/core/send/timeout.js create mode 100644 packages/core/send/timeout.test.js diff --git a/packages/child/check-blocking-css.js b/packages/child/check-blocking-css.js index c1f521683..4ab4877d3 100644 --- a/packages/child/check-blocking-css.js +++ b/packages/child/check-blocking-css.js @@ -1,4 +1,4 @@ -import { AUTO } from '../common/consts' +import { AUTO, NONE } from '../common/consts' import { advise, log } from './console' const nodes = () => [document.documentElement, document.body] @@ -7,7 +7,7 @@ const properties = ['min-height', 'min-width', 'max-height', 'max-width'] const blockedStyleSheets = new Set() const hasCssValue = (value) => - value && value !== '0px' && value !== AUTO && value !== 'none' + value && value !== '0px' && value !== AUTO && value !== NONE const getElementName = (node) => node.tagName ? node.tagName.toLowerCase() : 'unknown' diff --git a/packages/child/observers/overflow.js b/packages/child/observers/overflow.js index 2a06e79ba..7f594d0d2 100644 --- a/packages/child/observers/overflow.js +++ b/packages/child/observers/overflow.js @@ -1,6 +1,6 @@ import { HIGHLIGHT } from 'auto-console-group' -import { HEIGHT_EDGE, OVERFLOW_ATTR } from '../../common/consts' +import { HEIGHT_EDGE, NONE, OVERFLOW_ATTR } from '../../common/consts' import { id } from '../../common/utils' import { info } from '../console' import { @@ -17,7 +17,7 @@ const logNewlyObserved = createLogNewlyObserved(OVERFLOW) const warnAlreadyObserved = createWarnAlreadyObserved(OVERFLOW) const isHidden = (node) => - node.hidden || node.offsetParent === null || node.style.display === 'none' + node.hidden || node.offsetParent === null || node.style.display === NONE const createOverflowObserver = (callback, options) => { const side = options.side || HEIGHT_EDGE diff --git a/packages/child/observers/perf.js b/packages/child/observers/perf.js index 6704e6eb9..8c92b16df 100644 --- a/packages/child/observers/perf.js +++ b/packages/child/observers/perf.js @@ -14,9 +14,6 @@ export const PREF_END = '--ifr-end' const PREF_MEASURE = '--ifr-measure' const timings = [] -// const usedTags = new WeakSet() - -// const addUsedTag = (el) => typeof el === OBJECT && usedTags.add(el) let detail = {} let oldAverage = 0 @@ -98,9 +95,6 @@ export default function createPerformanceObserver() { const observer = new PerformanceObserver(perfObserver) observer.observe({ entryTypes: ['mark'] }) - // addUsedTag(document.documentElement) - // addUsedTag(document.body) - startTimingCheck() return { diff --git a/packages/core/send/timeout.js b/packages/core/send/timeout.js new file mode 100644 index 000000000..a18ee5ac4 --- /dev/null +++ b/packages/core/send/timeout.js @@ -0,0 +1,81 @@ +import { OBJECT } from '../../common/consts' +import { advise, event } from '../console' + +const getOrigin = (url) => { + try { + return new URL(url).origin + } catch (error) { + return null + } +} + +const allowsScriptsAndOrigin = (sandbox) => + typeof sandbox === OBJECT && + sandbox.length > 0 && + !(sandbox.contains('allow-scripts') && sandbox.contains('allow-same-origin')) + +function showWarning(id, settings) { + const { + checkOrigin, + iframe: { src, sandbox }, + initialisedFirstPage, + waitForLoad, + } = settings[id] + const targetOrigin = getOrigin(src) + + event(id, 'noResponse') + advise( + id, + `No response from iframe + +The iframe (${id}) has not responded within ${settings[id].warningTimeout / 1000} seconds. Check @iframe-resizer/child package has been loaded in the iframe. +${ + checkOrigin && targetOrigin + ? ` +The checkOrigin option is currently enabled. If the iframe redirects away from ${targetOrigin}, then the connection to the iframe may be blocked by the browser. To disable this option, set checkOrigin to false or an array of allowed origins. See https://iframe-resizer.com/checkorigin for more information. +` + : '' +}${ + waitForLoad && !initialisedFirstPage + ? ` +The waitForLoad option is currently set to true. If the iframe loads before iframe-resizer runs, this option will prevent iframe-resizer initialising. To disable this option, set waitForLoad to false. +` + : '' + }${ + allowsScriptsAndOrigin(sandbox) + ? ` +The iframe has the sandbox attribute, please ensure it contains both the allow-same-origin and allow-scripts values. +` + : '' + } +This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning. +`, + ) +} + +export default function warnOnNoResponse(id, settings) { + function warning() { + if (settings[id] === undefined) return // iframe has been closed while we were waiting + + const { initialised, loadErrorShown } = settings[id] + + settings[id].msgTimeout = undefined + + if (initialised) { + settings[id].initialisedFirstPage = true + return + } + + if (loadErrorShown) return + + settings[id].loadErrorShown = true + showWarning(id, settings) + } + + const { msgTimeout, warningTimeout } = settings[id] + + if (!warningTimeout) return + if (msgTimeout) clearTimeout(msgTimeout) + + settings[id].msgTimeout = setTimeout(warning, warningTimeout) +} diff --git a/packages/core/send/timeout.test.js b/packages/core/send/timeout.test.js new file mode 100644 index 000000000..0ce6efbbc --- /dev/null +++ b/packages/core/send/timeout.test.js @@ -0,0 +1,201 @@ +import { advise, event } from '../console' +import warnOnNoResponse from './timeout' + +// Mock console integration used by showWarning +jest.mock('../console', () => ({ + advise: jest.fn(), + event: jest.fn(), +})) + +describe('warnOnNoResponse', () => { + beforeEach(() => { + jest.useFakeTimers() + jest.spyOn(global, 'setTimeout') + jest.spyOn(global, 'clearTimeout') + jest.clearAllMocks() + }) + + afterEach(() => { + jest.runOnlyPendingTimers() + jest.useRealTimers() + jest.restoreAllMocks() + }) + + const makeSettings = ({ + id = 'frame1', + src = 'https://example.com/path', + sandbox, + checkOrigin = true, + waitForLoad = false, + initialised = false, + initialisedFirstPage = false, + loadErrorShown = false, + warningTimeout = 50, + msgTimeout, + } = {}) => ({ + [id]: { + iframe: { src, sandbox }, + checkOrigin, + waitForLoad, + initialised, + initialisedFirstPage, + loadErrorShown, + warningTimeout, + msgTimeout, + }, + }) + + it('schedules a warning and includes origin when checkOrigin is true', () => { + const id = 'f1' + const settings = makeSettings({ + id, + src: 'https://foo.example:8443/a/b', + checkOrigin: true, + }) + + warnOnNoResponse(id, settings) + + expect(setTimeout).toHaveBeenCalledWith( + expect.any(Function), + settings[id].warningTimeout, + ) + + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(event).toHaveBeenCalledWith(id, 'noResponse') + expect(advise).toHaveBeenCalledTimes(1) + expect(advise).toHaveBeenCalledWith( + id, + expect.stringMatching(/No response from iframe/), + ) + + expect(advise).toHaveBeenCalledWith( + id, + expect.stringContaining('https://foo.example:8443'), + ) + + expect(settings[id].loadErrorShown).toBe(true) + }) + + it('omits checkOrigin advice when checkOrigin is false', () => { + const id = 'f2' + const settings = makeSettings({ id, checkOrigin: false }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(advise).toHaveBeenCalledWith( + id, + expect.not.stringMatching(/checkOrigin/), + ) + }) + + it('adds sandbox advice when sandbox is present but missing required tokens', () => { + const id = 'f3' + const sandbox = { + length: 1, + contains(token) { + // only allow-scripts present; missing allow-same-origin + return token === 'allow-scripts' + }, + } + const settings = makeSettings({ id, sandbox }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(advise).toHaveBeenCalledWith(id, expect.stringMatching(/sandbox/)) + expect(advise).toHaveBeenCalledWith( + id, + expect.stringMatching(/allow-same-origin/), + ) + + expect(advise).toHaveBeenCalledWith( + id, + expect.stringMatching(/allow-scripts/), + ) + }) + + it('includes waitForLoad advice when enabled and first page not initialised', () => { + const id = 'f4' + const settings = makeSettings({ + id, + waitForLoad: true, + initialisedFirstPage: false, + }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(advise).toHaveBeenCalledWith( + id, + expect.stringMatching(/waitForLoad/), + ) + }) + + it('when already initialised: sets initialisedFirstPage and does not warn', () => { + const id = 'f5' + const settings = makeSettings({ + id, + initialised: true, + initialisedFirstPage: false, + }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(settings[id].initialisedFirstPage).toBe(true) + expect(advise).not.toHaveBeenCalled() + }) + + it('does nothing if loadErrorShown is already true', () => { + const id = 'f6' + const settings = makeSettings({ id, loadErrorShown: true }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(advise).not.toHaveBeenCalled() + }) + + it('does not schedule when warningTimeout is 0', () => { + const id = 'f7' + const settings = makeSettings({ id, warningTimeout: 0 }) + + warnOnNoResponse(id, settings) + + expect(setTimeout).not.toHaveBeenCalled() + expect(settings[id].msgTimeout).toBeUndefined() + expect(advise).not.toHaveBeenCalled() + }) + + it('replaces an existing timeout if called again', () => { + const id = 'f8' + const settings = makeSettings({ id, warningTimeout: 100 }) + + warnOnNoResponse(id, settings) + const first = settings[id].msgTimeout + + expect(first).toBeDefined() + + warnOnNoResponse(id, settings) + const second = settings[id].msgTimeout + + expect(clearTimeout).toHaveBeenCalledWith(first) + expect(second).toBeDefined() + expect(second).not.toBe(first) + }) + + it('does not include origin when URL parsing fails', () => { + const id = 'f9' + const settings = makeSettings({ id, src: '::::not-a-url' }) + + warnOnNoResponse(id, settings) + jest.advanceTimersByTime(settings[id].warningTimeout + 1) + + expect(advise).toHaveBeenCalledWith( + id, + expect.not.stringMatching(/checkOrigin/), + ) + }) +}) From a279ffa43a1cdc0f25bbdf758f1690622dd3225e Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:12:45 +0100 Subject: [PATCH 03/42] Move file --- packages/core/timeout.js | 81 -------------- packages/core/timeout.test.js | 201 ---------------------------------- 2 files changed, 282 deletions(-) delete mode 100644 packages/core/timeout.js delete mode 100644 packages/core/timeout.test.js diff --git a/packages/core/timeout.js b/packages/core/timeout.js deleted file mode 100644 index acd9343b7..000000000 --- a/packages/core/timeout.js +++ /dev/null @@ -1,81 +0,0 @@ -import { OBJECT } from '../common/consts' -import { advise, event } from './console' - -const getOrigin = (url) => { - try { - return new URL(url).origin - } catch (error) { - return null - } -} - -const allowsScriptsAndOrigin = (sandbox) => - typeof sandbox === OBJECT && - sandbox.length > 0 && - !(sandbox.contains('allow-scripts') && sandbox.contains('allow-same-origin')) - -function showWarning(id, settings) { - const { - checkOrigin, - iframe: { src, sandbox }, - initialisedFirstPage, - waitForLoad, - } = settings[id] - const targetOrigin = getOrigin(src) - - event(id, 'noResponse') - advise( - id, - `No response from iframe - -The iframe (${id}) has not responded within ${settings[id].warningTimeout / 1000} seconds. Check @iframe-resizer/child package has been loaded in the iframe. -${ - checkOrigin && targetOrigin - ? ` -The checkOrigin option is currently enabled. If the iframe redirects away from ${targetOrigin}, then the connection to the iframe may be blocked by the browser. To disable this option, set checkOrigin to false or an array of allowed origins. See https://iframe-resizer.com/checkorigin for more information. -` - : '' -}${ - waitForLoad && !initialisedFirstPage - ? ` -The waitForLoad option is currently set to true. If the iframe loads before iframe-resizer runs, this option will prevent iframe-resizer initialising. To disable this option, set waitForLoad to false. -` - : '' - }${ - allowsScriptsAndOrigin(sandbox) - ? ` -The iframe has the sandbox attribute, please ensure it contains both the allow-same-origin and allow-scripts values. -` - : '' - } -This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning. -`, - ) -} - -export default function warnOnNoResponse(id, settings) { - function warning() { - if (settings[id] === undefined) return // iframe has been closed while we were waiting - - const { initialised, loadErrorShown } = settings[id] - - settings[id].msgTimeout = undefined - - if (initialised) { - settings[id].initialisedFirstPage = true - return - } - - if (loadErrorShown) return - - settings[id].loadErrorShown = true - showWarning(id, settings) - } - - const { msgTimeout, warningTimeout } = settings[id] - - if (!warningTimeout) return - if (msgTimeout) clearTimeout(msgTimeout) - - settings[id].msgTimeout = setTimeout(warning, warningTimeout) -} diff --git a/packages/core/timeout.test.js b/packages/core/timeout.test.js deleted file mode 100644 index 82fc3cb7a..000000000 --- a/packages/core/timeout.test.js +++ /dev/null @@ -1,201 +0,0 @@ -import { advise, event } from './console' -import warnOnNoResponse from './timeout' - -// Mock console integration used by showWarning -jest.mock('./console', () => ({ - advise: jest.fn(), - event: jest.fn(), -})) - -describe('warnOnNoResponse', () => { - beforeEach(() => { - jest.useFakeTimers() - jest.spyOn(global, 'setTimeout') - jest.spyOn(global, 'clearTimeout') - jest.clearAllMocks() - }) - - afterEach(() => { - jest.runOnlyPendingTimers() - jest.useRealTimers() - jest.restoreAllMocks() - }) - - const makeSettings = ({ - id = 'frame1', - src = 'https://example.com/path', - sandbox, - checkOrigin = true, - waitForLoad = false, - initialised = false, - initialisedFirstPage = false, - loadErrorShown = false, - warningTimeout = 50, - msgTimeout, - } = {}) => ({ - [id]: { - iframe: { src, sandbox }, - checkOrigin, - waitForLoad, - initialised, - initialisedFirstPage, - loadErrorShown, - warningTimeout, - msgTimeout, - }, - }) - - it('schedules a warning and includes origin when checkOrigin is true', () => { - const id = 'f1' - const settings = makeSettings({ - id, - src: 'https://foo.example:8443/a/b', - checkOrigin: true, - }) - - warnOnNoResponse(id, settings) - - expect(setTimeout).toHaveBeenCalledWith( - expect.any(Function), - settings[id].warningTimeout, - ) - - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(event).toHaveBeenCalledWith(id, 'noResponse') - expect(advise).toHaveBeenCalledTimes(1) - expect(advise).toHaveBeenCalledWith( - id, - expect.stringMatching(/No response from iframe/), - ) - - expect(advise).toHaveBeenCalledWith( - id, - expect.stringContaining('https://foo.example:8443'), - ) - - expect(settings[id].loadErrorShown).toBe(true) - }) - - it('omits checkOrigin advice when checkOrigin is false', () => { - const id = 'f2' - const settings = makeSettings({ id, checkOrigin: false }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(advise).toHaveBeenCalledWith( - id, - expect.not.stringMatching(/checkOrigin/), - ) - }) - - it('adds sandbox advice when sandbox is present but missing required tokens', () => { - const id = 'f3' - const sandbox = { - length: 1, - contains(token) { - // only allow-scripts present; missing allow-same-origin - return token === 'allow-scripts' - }, - } - const settings = makeSettings({ id, sandbox }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(advise).toHaveBeenCalledWith(id, expect.stringMatching(/sandbox/)) - expect(advise).toHaveBeenCalledWith( - id, - expect.stringMatching(/allow-same-origin/), - ) - - expect(advise).toHaveBeenCalledWith( - id, - expect.stringMatching(/allow-scripts/), - ) - }) - - it('includes waitForLoad advice when enabled and first page not initialised', () => { - const id = 'f4' - const settings = makeSettings({ - id, - waitForLoad: true, - initialisedFirstPage: false, - }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(advise).toHaveBeenCalledWith( - id, - expect.stringMatching(/waitForLoad/), - ) - }) - - it('when already initialised: sets initialisedFirstPage and does not warn', () => { - const id = 'f5' - const settings = makeSettings({ - id, - initialised: true, - initialisedFirstPage: false, - }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(settings[id].initialisedFirstPage).toBe(true) - expect(advise).not.toHaveBeenCalled() - }) - - it('does nothing if loadErrorShown is already true', () => { - const id = 'f6' - const settings = makeSettings({ id, loadErrorShown: true }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(advise).not.toHaveBeenCalled() - }) - - it('does not schedule when warningTimeout is 0', () => { - const id = 'f7' - const settings = makeSettings({ id, warningTimeout: 0 }) - - warnOnNoResponse(id, settings) - - expect(setTimeout).not.toHaveBeenCalled() - expect(settings[id].msgTimeout).toBeUndefined() - expect(advise).not.toHaveBeenCalled() - }) - - it('replaces an existing timeout if called again', () => { - const id = 'f8' - const settings = makeSettings({ id, warningTimeout: 100 }) - - warnOnNoResponse(id, settings) - const first = settings[id].msgTimeout - - expect(first).toBeDefined() - - warnOnNoResponse(id, settings) - const second = settings[id].msgTimeout - - expect(clearTimeout).toHaveBeenCalledWith(first) - expect(second).toBeDefined() - expect(second).not.toBe(first) - }) - - it('does not include origin when URL parsing fails', () => { - const id = 'f9' - const settings = makeSettings({ id, src: '::::not-a-url' }) - - warnOnNoResponse(id, settings) - jest.advanceTimersByTime(settings[id].warningTimeout + 1) - - expect(advise).toHaveBeenCalledWith( - id, - expect.not.stringMatching(/checkOrigin/), - ) - }) -}) From 371a19e5045a8aa5851671ab5d8e391d9534fd5b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:13:02 +0100 Subject: [PATCH 04/42] Extract title functions from core --- packages/core/index.js | 42 ++++++++++++++++++++++++++++++------------ packages/core/title.js | 15 +++++++++++++++ 2 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 packages/core/title.js diff --git a/packages/core/index.js b/packages/core/index.js index e56f2f7d7..dac2af361 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -74,7 +74,36 @@ import { vInfo, warn, } from './console' -import warnOnNoResponse from './timeout' +// import checkEvent from './event' +// import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor-page-info' +// import { +// startParentInfoMonitor, +// stopParentInfoMonitor, +// } from './monitor-parent-props' +// import onMouse from './mouse' +// import { getPagePosition } from './page-position' +// import decodeMessage from './receive/decode' +// import { onMessage } from './receive/message' +// import { +// checkIframeExists, +// isMessageForUs, +// isMessageFromIframe, +// isMessageFromMetaParent, +// } from './receive/preflight' +// import { +// getElementPosition, +// scrollBy, +// scrollTo, +// scrollToLink, +// scrollToOffset, +// } from './scroll' +// import { setOffsetSize } from './send/offset' +// import createOutgoingMessage from './send/outgoing' +// import iframeReady from './send/ready' +import warnOnNoResponse from './send/timeout' +// import trigger from './send/trigger' +// import { resizeIframe, setSize } from './size' +import { checkTitle, setTitle } from './title' import checkUniqueId from './unique' import defaults from './values/defaults' import page from './values/page' @@ -564,12 +593,6 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. ) } - function setTitle(title, iframeId) { - if (!settings[iframeId]?.syncTitle) return - settings[iframeId].iframe.title = title - info(iframeId, `Set iframe title attribute: %c${title}`, HIGHLIGHT) - } - function eventMsg() { const { height, iframe, msg, type, width } = messageData if (settings[iframeId]?.firstRun) firstRun() @@ -1229,11 +1252,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep if (mode !== -1) checkMode(iframeId, mode) } - function checkTitle(iframeId) { - const title = settings[iframeId]?.iframe?.title - return title === '' || title === undefined - } - function updateOptionName(oldName, newName) { if (hasOwn(settings[iframeId], oldName)) { advise( diff --git a/packages/core/title.js b/packages/core/title.js new file mode 100644 index 000000000..e09048916 --- /dev/null +++ b/packages/core/title.js @@ -0,0 +1,15 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { info } from './console' +import settings from './values/settings' + +export function checkTitle(id) { + const title = settings[id]?.iframe?.title + return title === '' || title === undefined +} + +export function setTitle(id, title) { + if (!settings[id]?.syncTitle) return + settings[id].iframe.title = title + info(id, `Set iframe title attribute: %c${title}`, HIGHLIGHT) +} From 3375f5c0dff544f579ee618d9256764dce2097c3 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:27:56 +0100 Subject: [PATCH 05/42] Extract createOutgoingMessage() --- packages/core/index.js | 55 ++----------------------------- packages/core/send/outgoing.js | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 packages/core/send/outgoing.js diff --git a/packages/core/index.js b/packages/core/index.js index dac2af361..6cd9b409c 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -6,7 +6,6 @@ import { AUTO_RESIZE, BEFORE_UNLOAD, BOTH, - CHILD, CHILD_READY_MESSAGE, CLOSE, COLLAPSE, @@ -98,7 +97,7 @@ import { // scrollToOffset, // } from './scroll' // import { setOffsetSize } from './send/offset' -// import createOutgoingMessage from './send/outgoing' +import createOutgoingMessage from './send/outgoing' // import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' // import trigger from './send/trigger' @@ -932,56 +931,6 @@ function trigger(calleeMsg, msg, id) { if (settings[id]) checkAndSend() } -function createOutgoingMsg(id) { - const { - autoResize, - bodyBackground, - bodyMargin, - bodyPadding, - heightCalculationMethod, - inPageLinks, - license, - log, - logExpand, - mouseEvents, - offsetHeight, - offsetWidth, - mode, - sizeHeight, - // sizeSelector, - sizeWidth, - tolerance, - widthCalculationMethod, - } = settings[id] - - return [ - id, - '8', // Backwards compatibility (PaddingV1) - sizeWidth, - log, - '32', // Backwards compatibility (Interval) - true, // Backwards compatibility (EnablePublicMethods) - autoResize, - bodyMargin, - heightCalculationMethod, - bodyBackground, - bodyPadding, - tolerance, - inPageLinks, - CHILD, // Backwards compatibility (resizeFrom) - widthCalculationMethod, - mouseEvents, - offsetHeight, - offsetWidth, - sizeHeight, - license, - page.version, - mode, - '', // sizeSelector, - logExpand, - ].join(SEPARATOR) -} - let count = 0 let vAdvised = false let vInfoDisable = false @@ -1316,7 +1265,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep setupEventListenersOnce() setScrolling() setupBodyMarginValues() - init(iframeId, createOutgoingMsg(iframeId)) + init(iframeId, createOutgoingMessage(iframeId)) setupIframeObject() log(iframeId, 'Setup complete') endAutoGroup(iframeId) diff --git a/packages/core/send/outgoing.js b/packages/core/send/outgoing.js new file mode 100644 index 000000000..aedc147ad --- /dev/null +++ b/packages/core/send/outgoing.js @@ -0,0 +1,59 @@ +import { CHILD, SEPARATOR } from '../../common/consts' +import page from '../values/page' +import settings from '../values/settings' + +// Backwards compatibility consts +const V1_PADDING = '8' +const INTERVAL = '32' +const RESIZE_FROM = CHILD +const PUBLIC_METHODS = true + +export default function createOutgoingMessage(id) { + const { + autoResize, + bodyBackground, + bodyMargin, + bodyPadding, + heightCalculationMethod, + inPageLinks, + license, + log, + logExpand, + mouseEvents, + offsetHeight, + offsetWidth, + mode, + sizeHeight, + // sizeSelector, + sizeWidth, + tolerance, + widthCalculationMethod, + } = settings[id] + + return [ + id, + V1_PADDING, + sizeWidth, + log, + INTERVAL, + PUBLIC_METHODS, + autoResize, + bodyMargin, + heightCalculationMethod, + bodyBackground, + bodyPadding, + tolerance, + inPageLinks, + RESIZE_FROM, + widthCalculationMethod, + mouseEvents, + offsetHeight, + offsetWidth, + sizeHeight, + license, + page.version, + mode, + '', // sizeSelector, + logExpand, + ].join(SEPARATOR) +} From 7416b44c1264871b33480da1bd72d89cff279c9a Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:43:44 +0100 Subject: [PATCH 06/42] Extract iframeReady() --- packages/core/index.js | 13 ++----------- packages/core/send/ready.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 packages/core/send/ready.js diff --git a/packages/core/index.js b/packages/core/index.js index 6cd9b409c..c4c4fdff3 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -98,7 +98,7 @@ import { // } from './scroll' // import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' -// import iframeReady from './send/ready' +import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' // import trigger from './send/trigger' // import { resizeIframe, setSize } from './size' @@ -704,15 +704,6 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. } } - const iframeReady = - (source) => - ({ initChild, postMessageTarget }) => { - if (source === postMessageTarget) initChild() - } - - const iFrameReadyMsgReceived = (source) => - Object.values(settings).forEach(iframeReady(source)) - function firstRun() { if (!settings[iframeId]) return log(iframeId, `First run for ${iframeId}`) @@ -735,7 +726,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. let msg = event.data if (msg === CHILD_READY_MESSAGE) { - iFrameReadyMsgReceived(event.source) + iframeReady(event.source) return } diff --git a/packages/core/send/ready.js b/packages/core/send/ready.js new file mode 100644 index 000000000..95a89b684 --- /dev/null +++ b/packages/core/send/ready.js @@ -0,0 +1,10 @@ +import settings from '../values/settings' + +export const sendIframeReady = + (source) => + ({ initChild, postMessageTarget }) => { + if (source === postMessageTarget) initChild() + } + +export default (source) => + Object.values(settings).forEach(sendIframeReady(source)) From 1ea7486b668a4723706246d32979d45664b29abc Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:50:24 +0100 Subject: [PATCH 07/42] Extract trigger --- packages/core/index.js | 55 +---------------------------------- packages/core/send/trigger.js | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 54 deletions(-) create mode 100644 packages/core/send/trigger.js diff --git a/packages/core/index.js b/packages/core/index.js index c4c4fdff3..24be455bc 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -16,7 +16,6 @@ import { HORIZONTAL, IN_PAGE_LINK, INIT, - INIT_EVENTS, INIT_FROM_IFRAME, LABEL, LAZY, @@ -100,7 +99,7 @@ import { import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' -// import trigger from './send/trigger' +import trigger from './send/trigger' // import { resizeIframe, setSize } from './size' import { checkTitle, setTitle } from './title' import checkUniqueId from './unique' @@ -870,58 +869,6 @@ function setSize(messageData) { if (sizeWidth) setDimension(WIDTH) } -const filterMsg = (msg) => - msg - .split(SEPARATOR) - .filter((_, index) => index !== 19) - .join(SEPARATOR) - -function trigger(calleeMsg, msg, id) { - function logSent(route) { - const displayMsg = calleeMsg in INIT_EVENTS ? filterMsg(msg) : msg - info(id, route, HIGHLIGHT, FOREGROUND, HIGHLIGHT) - info(id, `Message data: %c${displayMsg}`, HIGHLIGHT) - } - - function postMessageToIframe() { - const { iframe, postMessageTarget, sameOrigin, targetOrigin } = settings[id] - - if (sameOrigin) { - try { - iframe.contentWindow.iframeChildListener(MESSAGE_ID + msg) - logSent(`Sending message to iframe %c${id}%c via same origin%c`) - return - } catch (error) { - if (calleeMsg in INIT_EVENTS) { - settings[id].sameOrigin = false - log(id, 'New iframe does not support same origin') - } else { - warn(id, 'Same origin messaging failed, falling back to postMessage') - } - } - } - - logSent( - `Sending message to iframe: %c${id}%c targetOrigin: %c${targetOrigin}`, - ) - - postMessageTarget.postMessage(MESSAGE_ID + msg, targetOrigin) - } - - function checkAndSend() { - if (!settings[id]?.postMessageTarget) { - warn(id, `Iframe(${id}) not found`) - return - } - - postMessageToIframe() - } - - consoleEvent(id, calleeMsg) - - if (settings[id]) checkAndSend() -} - let count = 0 let vAdvised = false let vInfoDisable = false diff --git a/packages/core/send/trigger.js b/packages/core/send/trigger.js new file mode 100644 index 000000000..604fc4b2c --- /dev/null +++ b/packages/core/send/trigger.js @@ -0,0 +1,54 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +import { INIT_EVENTS, MESSAGE_ID, SEPARATOR } from '../../common/consts' +import { event as consoleEvent, info, log, warn } from '../console' +import settings from '../values/settings' + +const filterMsg = (msg) => + msg + .split(SEPARATOR) + .filter((_, index) => index !== 19) + .join(SEPARATOR) + +function postMessageToIframe(calleeMsg, msg, id) { + function logSent(route) { + const displayMsg = calleeMsg in INIT_EVENTS ? filterMsg(msg) : msg + info(id, route, HIGHLIGHT, FOREGROUND, HIGHLIGHT) + info(id, `Message data: %c${displayMsg}`, HIGHLIGHT) + } + const { iframe, postMessageTarget, sameOrigin, targetOrigin } = settings[id] + + if (sameOrigin) { + try { + iframe.contentWindow.iframeChildListener(MESSAGE_ID + msg) + logSent(`Sending message to iframe %c${id}%c via same origin%c`) + return + } catch (error) { + if (calleeMsg in INIT_EVENTS) { + settings[id].sameOrigin = false + log(id, 'New iframe does not support same origin') + } else { + warn(id, 'Same origin messaging failed, falling back to postMessage') + } + } + } + + logSent( + `Sending message to iframe: %c${id}%c targetOrigin: %c${targetOrigin}`, + ) + + postMessageTarget.postMessage(MESSAGE_ID + msg, targetOrigin) +} + +function trigger(calleeMsg, msg, id) { + consoleEvent(id, calleeMsg) + + if (!settings[id]?.postMessageTarget) { + warn(id, `Iframe not found`) + return + } + + postMessageToIframe(calleeMsg, msg, id) +} + +export default trigger From 4985b98b2748ae5c9813a6dde7d57eef84807d4c Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 11:57:33 +0100 Subject: [PATCH 08/42] Extract setOffsetSize() --- packages/core/index.js | 16 ++-------------- packages/core/send/offset.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 packages/core/send/offset.js diff --git a/packages/core/index.js b/packages/core/index.js index 24be455bc..ff766e67e 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -95,7 +95,7 @@ import { // scrollToLink, // scrollToOffset, // } from './scroll' -// import { setOffsetSize } from './send/offset' +import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' @@ -1110,18 +1110,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep log(iframeId, `direction: %c${direction}`, HIGHLIGHT) } - function setOffsetSize(offset) { - if (!offset) return // No offset set or offset is zero - - if (settings[iframeId].direction === VERTICAL) { - settings[iframeId].offsetHeight = offset - log(iframeId, `Offset height: %c${offset}`, HIGHLIGHT) - } else { - settings[iframeId].offsetWidth = offset - log(iframeId, `Offset width: %c${offset}`, HIGHLIGHT) - } - } - const getTargetOrigin = (remoteHost) => remoteHost === '' || remoteHost.match(/^(about:blank|javascript:|file:\/\/)/) !== null @@ -1184,7 +1172,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep consoleEvent(iframeId, 'setup') setDirection() - setOffsetSize(options?.offsetSize || options?.offset) // ignore zero offset + setOffsetSize(iframeId, options) checkWarningTimeout() getPostMessageTarget() setTargetOrigin() diff --git a/packages/core/send/offset.js b/packages/core/send/offset.js new file mode 100644 index 000000000..741048ade --- /dev/null +++ b/packages/core/send/offset.js @@ -0,0 +1,20 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { VERTICAL } from '../../common/consts' +import { log } from '../console' +import settings from '../values/settings' + +// eslint-disable-next-line import/prefer-default-export +export function setOffsetSize(id, { offset, offsetSize }) { + const newOffset = offsetSize || offset + + if (!newOffset) return // No offset set or offset is zero + + if (settings[id].direction === VERTICAL) { + settings[id].offsetHeight = newOffset + log(id, `Offset height: %c${newOffset}`, HIGHLIGHT) + } else { + settings[id].offsetWidth = newOffset + log(id, `Offset width: %c${newOffset}`, HIGHLIGHT) + } +} From 55e7a37e9f994cea0f142a50b4775682534f1c6a Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 12:18:33 +0100 Subject: [PATCH 09/42] Extract parent-props --- packages/core/index.js | 160 +---------------------------- packages/core/monitor/common.js | 93 +++++++++++++++++ packages/core/monitor/page-info.js | 33 ++++++ packages/core/monitor/props.js | 34 ++++++ 4 files changed, 163 insertions(+), 157 deletions(-) create mode 100644 packages/core/monitor/common.js create mode 100644 packages/core/monitor/page-info.js create mode 100644 packages/core/monitor/props.js diff --git a/packages/core/index.js b/packages/core/index.js index ff766e67e..56c889a6c 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -44,7 +44,6 @@ import { RESET, RESET_REQUIRED_METHODS, RESIZE, - SCROLL, SCROLL_BY, SCROLL_TO, SCROLL_TO_OFFSET, @@ -55,7 +54,7 @@ import { VERTICAL, WIDTH, } from '../common/consts' -import { addEventListener, removeEventListener } from '../common/listeners' +import { addEventListener } from '../common/listeners' import setMode, { getModeData, getModeLabel } from '../common/mode' import { hasOwn, isolateUserCode, once, typeAssert } from '../common/utils' import { @@ -73,11 +72,8 @@ import { warn, } from './console' // import checkEvent from './event' -// import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor-page-info' -// import { -// startParentInfoMonitor, -// stopParentInfoMonitor, -// } from './monitor-parent-props' +import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' +import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' // import onMouse from './mouse' // import { getPagePosition } from './page-position' // import decodeMessage from './receive/decode' @@ -242,156 +238,6 @@ function iframeListener(event) { }) } - function getPageInfo() { - const bodyPosition = document.body.getBoundingClientRect() - const iFramePosition = messageData.iframe.getBoundingClientRect() - const { scrollY, scrollX, innerHeight, innerWidth } = window - const { clientHeight, clientWidth } = document.documentElement - - return JSON.stringify({ - iframeHeight: iFramePosition.height, - iframeWidth: iFramePosition.width, - clientHeight: Math.max(clientHeight, innerHeight || 0), - clientWidth: Math.max(clientWidth, innerWidth || 0), - offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10), - offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10), - scrollTop: scrollY, - scrollLeft: scrollX, - documentHeight: clientHeight, - documentWidth: clientWidth, - windowHeight: innerHeight, - windowWidth: innerWidth, - }) - } - - function getParentProps() { - const { iframe } = messageData - const { scrollWidth, scrollHeight } = document.documentElement - const { width, height, offsetLeft, offsetTop, pageLeft, pageTop, scale } = - window.visualViewport - - return JSON.stringify({ - iframe: iframe.getBoundingClientRect(), - document: { - scrollWidth, - scrollHeight, - }, - viewport: { - width, - height, - offsetLeft, - offsetTop, - pageLeft, - pageTop, - scale, - }, - }) - } - - const sendInfoToIframe = (type, infoFunction) => (requestType, iframeId) => { - const gate = {} - - function throttle(func, frameId) { - if (!gate[frameId]) { - func() - gate[frameId] = requestAnimationFrame(() => { - gate[frameId] = null - }) - } - } - - function gatedTrigger() { - trigger(`${requestType} (${type})`, `${type}:${infoFunction()}`, iframeId) - } - - throttle(gatedTrigger, iframeId) - } - - const startInfoMonitor = (sendInfoToIframe, type) => () => { - let pending = false - - const sendInfo = (requestType) => () => { - if (settings[id]) { - if (!pending || pending === requestType) { - sendInfoToIframe(requestType, id) - - pending = requestType - requestAnimationFrame(() => { - pending = false - }) - } - } else { - stop() - } - } - - const sendScroll = sendInfo(SCROLL) - const sendResize = sendInfo('resize window') - - function setListener(requestType, listener) { - log(id, `${requestType}listeners for send${type}`) - listener(window, SCROLL, sendScroll) - listener(window, RESIZE, sendResize) - } - - function stop() { - consoleEvent(id, `stop${type}`) - setListener('Remove ', removeEventListener) - pageObserver.disconnect() - iframeObserver.disconnect() - removeEventListener(settings[id].iframe, LOAD, stop) - } - - function start() { - setListener('Add ', addEventListener) - - pageObserver.observe(document.body, { - attributes: true, - childList: true, - subtree: true, - }) - - iframeObserver.observe(settings[id].iframe, { - attributes: true, - childList: false, - subtree: false, - }) - } - - const id = iframeId // Create locally scoped copy of iFrame ID - - const pageObserver = new ResizeObserver(sendInfo('pageObserver')) - const iframeObserver = new ResizeObserver(sendInfo('iframeObserver')) - - if (settings[id]) { - settings[id][`stop${type}`] = stop - addEventListener(settings[id].iframe, LOAD, stop) - start() - } - } - - const stopInfoMonitor = (stopFunction) => () => { - if (stopFunction in settings[iframeId]) { - settings[iframeId][stopFunction]() - delete settings[iframeId][stopFunction] - } - } - - const sendPageInfoToIframe = sendInfoToIframe(PAGE_INFO, getPageInfo) - const sendParentInfoToIframe = sendInfoToIframe(PARENT_INFO, getParentProps) - - const startPageInfoMonitor = startInfoMonitor( - sendPageInfoToIframe, - 'PageInfo', - ) - const startParentInfoMonitor = startInfoMonitor( - sendParentInfoToIframe, - 'ParentInfo', - ) - - const stopPageInfoMonitor = stopInfoMonitor('stopPageInfo') - const stopParentInfoMonitor = stopInfoMonitor('stopParentInfo') - function checkIframeExists() { if (messageData.iframe === null) { warn(iframeId, `The iframe (${messageData.id}) was not found.`) diff --git a/packages/core/monitor/common.js b/packages/core/monitor/common.js new file mode 100644 index 000000000..f281e37ba --- /dev/null +++ b/packages/core/monitor/common.js @@ -0,0 +1,93 @@ +import { LOAD, RESIZE, SCROLL } from '../../common/consts' +import { addEventListener, removeEventListener } from '../../common/listeners' +import { event, log } from '../console' +import trigger from '../send/trigger' +import settings from '../values/settings' + +export const sendInfoToIframe = (type, infoFunction) => (requestType, id) => { + const gate = {} + const { iframe } = settings[id] + + function throttle(func, frameId) { + if (!gate[frameId]) { + func() + gate[frameId] = requestAnimationFrame(() => { + gate[frameId] = null + }) + } + } + + function gatedTrigger() { + trigger(`${requestType} (${type})`, `${type}:${infoFunction(iframe)}`, id) + } + + throttle(gatedTrigger, id) +} + +export const stopInfoMonitor = (stopFunction) => (id) => { + if (stopFunction in settings[id]) { + settings[id][stopFunction]() + delete settings[id][stopFunction] + } +} + +export const startInfoMonitor = (sendInfoToIframe, type) => (id) => { + let pending = false + + const sendInfo = (requestType) => () => { + if (settings[id]) { + if (!pending || pending === requestType) { + sendInfoToIframe(requestType, id) + + pending = requestType + requestAnimationFrame(() => { + pending = false + }) + } + } else { + stop() + } + } + + const sendScroll = sendInfo(SCROLL) + const sendResize = sendInfo('resize window') + + function setListener(requestType, listener) { + log(id, `${requestType}listeners for send${type}`) + listener(window, SCROLL, sendScroll) + listener(window, RESIZE, sendResize) + } + + function stop() { + event(id, `stop${type}`) + setListener('Remove ', removeEventListener) + pageObserver.disconnect() + iframeObserver.disconnect() + removeEventListener(settings[id].iframe, LOAD, stop) + } + + function start() { + setListener('Add ', addEventListener) + + pageObserver.observe(document.body, { + attributes: true, + childList: true, + subtree: true, + }) + + iframeObserver.observe(settings[id].iframe, { + attributes: true, + childList: false, + subtree: false, + }) + } + + const pageObserver = new ResizeObserver(sendInfo('pageObserver')) + const iframeObserver = new ResizeObserver(sendInfo('iframeObserver')) + + if (!settings[id]) return + + settings[id][`stop${type}`] = stop + addEventListener(settings[id].iframe, LOAD, stop) + start() +} diff --git a/packages/core/monitor/page-info.js b/packages/core/monitor/page-info.js new file mode 100644 index 000000000..99aa9f5d1 --- /dev/null +++ b/packages/core/monitor/page-info.js @@ -0,0 +1,33 @@ +import { PAGE_INFO } from '../../common/consts' +import { sendInfoToIframe, startInfoMonitor, stopInfoMonitor } from './common' + +export function getPageInfo(iframe) { + const bodyPosition = document.body.getBoundingClientRect() + const iFramePosition = iframe.getBoundingClientRect() + const { scrollY, scrollX, innerHeight, innerWidth } = window + const { clientHeight, clientWidth } = document.documentElement + + return JSON.stringify({ + iframeHeight: iFramePosition.height, + iframeWidth: iFramePosition.width, + clientHeight: Math.max(clientHeight, innerHeight || 0), + clientWidth: Math.max(clientWidth, innerWidth || 0), + offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10), + offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10), + scrollTop: scrollY, + scrollLeft: scrollX, + documentHeight: clientHeight, + documentWidth: clientWidth, + windowHeight: innerHeight, + windowWidth: innerWidth, + }) +} + +const sendPageInfoToIframe = sendInfoToIframe(PAGE_INFO, getPageInfo) + +export const startPageInfoMonitor = startInfoMonitor( + sendPageInfoToIframe, + 'PageInfo', +) + +export const stopPageInfoMonitor = stopInfoMonitor('stopPageInfo') diff --git a/packages/core/monitor/props.js b/packages/core/monitor/props.js new file mode 100644 index 000000000..73806f953 --- /dev/null +++ b/packages/core/monitor/props.js @@ -0,0 +1,34 @@ +import { PARENT_INFO } from '../../common/consts' +import { sendInfoToIframe, startInfoMonitor, stopInfoMonitor } from './common' + +export function getParentProps(iframe) { + const { scrollWidth, scrollHeight } = document.documentElement + const { width, height, offsetLeft, offsetTop, pageLeft, pageTop, scale } = + window.visualViewport + + return JSON.stringify({ + iframe: iframe.getBoundingClientRect(), + document: { + scrollWidth, + scrollHeight, + }, + viewport: { + width, + height, + offsetLeft, + offsetTop, + pageLeft, + pageTop, + scale, + }, + }) +} + +const sendParentInfoToIframe = sendInfoToIframe(PARENT_INFO, getParentProps) + +export const startParentInfoMonitor = startInfoMonitor( + sendParentInfoToIframe, + 'ParentInfo', +) + +export const stopParentInfoMonitor = stopInfoMonitor('stopParentInfo') From 9646eb2cd2ab1be982b7a954546958e0a7d3667b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 12:30:03 +0100 Subject: [PATCH 10/42] Extract checkEvent --- packages/core/event.js | 27 +++++++++++++++++++++++++++ packages/core/index.js | 31 ++----------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) create mode 100644 packages/core/event.js diff --git a/packages/core/event.js b/packages/core/event.js new file mode 100644 index 000000000..c524a47d1 --- /dev/null +++ b/packages/core/event.js @@ -0,0 +1,27 @@ +import { FUNCTION } from '../common/consts' +import { isolateUserCode } from '../common/utils' +import { warn } from './console' +import settings from './values/settings' + +function on(iframeId, funcName, val) { + if (!settings[iframeId]) return null + + const func = settings[iframeId][funcName] + + if (typeof func !== FUNCTION) + throw new TypeError(`${funcName} on iframe[${iframeId}] is not a function`) + + if (funcName !== 'onBeforeClose' && funcName !== 'onScroll') + return isolateUserCode(func, val) + + try { + return func(val) + } catch (error) { + // eslint-disable-next-line no-console + console.error(error) + warn(iframeId, `Error in ${funcName} callback`) + return null + } +} + +export default on diff --git a/packages/core/index.js b/packages/core/index.js index 56c889a6c..90648f3c0 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -10,7 +10,6 @@ import { CLOSE, COLLAPSE, EXPAND, - FUNCTION, HEIGHT, HIDDEN, HORIZONTAL, @@ -56,7 +55,7 @@ import { } from '../common/consts' import { addEventListener } from '../common/listeners' import setMode, { getModeData, getModeLabel } from '../common/mode' -import { hasOwn, isolateUserCode, once, typeAssert } from '../common/utils' +import { hasOwn, once, typeAssert } from '../common/utils' import { advise, debug, @@ -71,7 +70,7 @@ import { vInfo, warn, } from './console' -// import checkEvent from './event' +import checkEvent from './event' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' // import onMouse from './mouse' @@ -599,32 +598,6 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. errorBoundary(iframeId, screenMessage)(msg) } -function checkEvent(iframeId, funcName, val) { - let func = null - let retVal = null - - if (settings[iframeId]) { - func = settings[iframeId][funcName] - - if (typeof func === FUNCTION) - if (funcName === 'onBeforeClose' || funcName === 'onScroll') { - try { - retVal = func(val) - } catch (error) { - // eslint-disable-next-line no-console - console.error(error) - warn(iframeId, `Error in ${funcName} callback`) - } - } else isolateUserCode(func, val) - else - throw new TypeError( - `${funcName} on iFrame[${iframeId}] is not a function`, - ) - } - - return retVal -} - function removeIframeListeners(iframe) { const { id } = iframe log(id, 'Disconnected from iframe') From c6ffe8c28f31ef1875781745eb8a365c299ed0b7 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 12:38:19 +0100 Subject: [PATCH 11/42] Extract page-position --- packages/core/index.js | 74 ++++++++++++++++++---------------- packages/core/page-position.js | 49 ++++++++++++++++++++++ 2 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 packages/core/page-position.js diff --git a/packages/core/index.js b/packages/core/index.js index 90648f3c0..9aaa9d057 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -74,7 +74,11 @@ import checkEvent from './event' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' // import onMouse from './mouse' -// import { getPagePosition } from './page-position' +import { + getPagePosition, + setPagePosition, + unsetPagePosition, +} from './page-position' // import decodeMessage from './receive/decode' // import { onMessage } from './receive/message' // import { @@ -628,40 +632,40 @@ function closeIframe(iframe) { removeIframeListeners(iframe) } -function getPagePosition(iframeId) { - if (page.position !== null) return - - page.position = { - x: window.scrollX, - y: window.scrollY, - } - - log( - iframeId, - `Get page position: %c${page.position.x}%c, %c${page.position.y}`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - ) -} - -function unsetPagePosition() { - page.position = null -} - -function setPagePosition(iframeId) { - if (page.position === null) return - - window.scrollTo(page.position.x, page.position.y) - info( - iframeId, - `Set page position: %c${page.position.x}%c, %c${page.position.y}`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - ) - unsetPagePosition() -} +// function getPagePosition(iframeId) { +// if (page.position !== null) return + +// page.position = { +// x: window.scrollX, +// y: window.scrollY, +// } + +// log( +// iframeId, +// `Get page position: %c${page.position.x}%c, %c${page.position.y}`, +// HIGHLIGHT, +// FOREGROUND, +// HIGHLIGHT, +// ) +// } + +// function unsetPagePosition() { +// page.position = null +// } + +// function setPagePosition(iframeId) { +// if (page.position === null) return + +// window.scrollTo(page.position.x, page.position.y) +// info( +// iframeId, +// `Set page position: %c${page.position.x}%c, %c${page.position.y}`, +// HIGHLIGHT, +// FOREGROUND, +// HIGHLIGHT, +// ) +// unsetPagePosition() +// } function resetIframe(messageData) { log( diff --git a/packages/core/page-position.js b/packages/core/page-position.js new file mode 100644 index 000000000..5866a10c9 --- /dev/null +++ b/packages/core/page-position.js @@ -0,0 +1,49 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +import { info, log } from './console' +import page from './values/page' + +export function unsetPagePosition() { + page.position = null +} + +export const getStoredPagePosition = () => page.position + +export function setStoredPagePosition(position) { + page.position = position +} + +export function getPagePosition(id) { + if (page.position !== null) return page.position + + page.position = { + x: window.scrollX, + y: window.scrollY, + } + + log( + id, + `Get page position: %c${page.position.x}%c, %c${page.position.y}`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + ) + + return page.position +} + +export function setPagePosition(id) { + if (page.position === null) return + + window.scrollTo(page.position.x, page.position.y) + + info( + id, + `Set page position: %c${page.position.x}%c, %c${page.position.y}`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + ) + + unsetPagePosition() +} From f2ad2e22af31b2f425f56cac070fb044a37cec93 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 12:41:39 +0100 Subject: [PATCH 12/42] Create `core/page` --- packages/core/index.js | 2 +- packages/core/{ => page}/title.js | 0 packages/core/{ => page}/unique.js | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename packages/core/{ => page}/title.js (100%) rename packages/core/{ => page}/unique.js (90%) diff --git a/packages/core/index.js b/packages/core/index.js index 9aaa9d057..d809f4bbb 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -101,7 +101,7 @@ import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' // import { resizeIframe, setSize } from './size' import { checkTitle, setTitle } from './title' -import checkUniqueId from './unique' +import checkUniqueId from './page/unique' import defaults from './values/defaults' import page from './values/page' import settings from './values/settings' diff --git a/packages/core/title.js b/packages/core/page/title.js similarity index 100% rename from packages/core/title.js rename to packages/core/page/title.js diff --git a/packages/core/unique.js b/packages/core/page/unique.js similarity index 90% rename from packages/core/unique.js rename to packages/core/page/unique.js index b08be45f9..efcce5630 100644 --- a/packages/core/unique.js +++ b/packages/core/page/unique.js @@ -1,5 +1,5 @@ -import { NEW_LINE } from '../common/consts' -import { advise } from './console' +import { NEW_LINE } from '../../common/consts' +import { advise } from '../console' const shownDuplicateIdWarning = {} From 61f506e2ef434c2445a39673828f186c4efdfb73 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 12:44:58 +0100 Subject: [PATCH 13/42] Fix move of title --- packages/core/index.js | 6 +++--- packages/core/page/title.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/index.js b/packages/core/index.js index d809f4bbb..f04d2374a 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -73,6 +73,9 @@ import { import checkEvent from './event' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' +// import { resizeIframe, setSize } from './size' +import { checkTitle, setTitle } from './page/title' +import checkUniqueId from './page/unique' // import onMouse from './mouse' import { getPagePosition, @@ -99,9 +102,6 @@ import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' -// import { resizeIframe, setSize } from './size' -import { checkTitle, setTitle } from './title' -import checkUniqueId from './page/unique' import defaults from './values/defaults' import page from './values/page' import settings from './values/settings' diff --git a/packages/core/page/title.js b/packages/core/page/title.js index e09048916..6d1fe064b 100644 --- a/packages/core/page/title.js +++ b/packages/core/page/title.js @@ -1,7 +1,7 @@ import { HIGHLIGHT } from 'auto-console-group' -import { info } from './console' -import settings from './values/settings' +import { info } from '../console' +import settings from '../values/settings' export function checkTitle(id) { const title = settings[id]?.iframe?.title From 807917c54523ba6afb36a275a62bc9c102e25341 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 13:01:15 +0100 Subject: [PATCH 14/42] Extract scroll functions --- packages/core/index.js | 121 ++++++---------------------------------- packages/core/scroll.js | 87 +++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 packages/core/scroll.js diff --git a/packages/core/index.js b/packages/core/index.js index f04d2374a..27f719de3 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -77,11 +77,7 @@ import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' // import onMouse from './mouse' -import { - getPagePosition, - setPagePosition, - unsetPagePosition, -} from './page-position' +import { getPagePosition, setPagePosition } from './page-position' // import decodeMessage from './receive/decode' // import { onMessage } from './receive/message' // import { @@ -90,13 +86,13 @@ import { // isMessageFromIframe, // isMessageFromMetaParent, // } from './receive/preflight' -// import { -// getElementPosition, -// scrollBy, -// scrollTo, -// scrollToLink, -// scrollToOffset, -// } from './scroll' +import { + getElementPosition, + scrollBy, + scrollTo, + scrollToLink, + scrollToOffset, +} from './scroll' import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' @@ -250,89 +246,6 @@ function iframeListener(event) { return true } - function getElementPosition(target) { - const iFramePosition = target.getBoundingClientRect() - - getPagePosition(iframeId) - - return { - x: Number(iFramePosition.left) + Number(page.position.x), - y: Number(iFramePosition.top) + Number(page.position.y), - } - } - - function scrollBy() { - const x = messageData.width - const y = messageData.height - - // Check for V4 as well - const target = window.parentIframe || window.parentIFrame || window - - info( - iframeId, - `scrollBy: x: %c${x}%c y: %c${y}`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - ) - - target.scrollBy(x, y) - } - - function scrollRequestFromChild(addOffset) { - /* istanbul ignore next */ // Not testable in Karma - function reposition(newPosition) { - page.position = newPosition - scrollTo(iframeId) - } - - function scrollParent(target, newPosition) { - setTimeout(() => - target[`scrollTo${addOffset ? 'Offset' : ''}`]( - newPosition.x, - newPosition.y, - ), - ) - } - - const calcOffset = (messageData, offset) => ({ - x: messageData.width + offset.x, - y: messageData.height + offset.y, - }) - - const offset = addOffset - ? getElementPosition(messageData.iframe) - : { x: 0, y: 0 } - - info( - iframeId, - `Reposition requested (offset x:%c${offset.x}%c y:%c${offset.y})`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - ) - - const newPosition = calcOffset(messageData, offset) - - // Check for V4 as well - const target = window.parentIframe || window.parentIFrame - - if (target) scrollParent(target, newPosition) - else reposition(newPosition) - } - - function scrollTo(iframeId) { - const { x, y } = page.position - const iframe = settings[iframeId]?.iframe - - if (on('onScroll', { iframe, top: y, left: x, x, y }) === false) { - unsetPagePosition() - return - } - - setPagePosition(iframeId) - } - function findTarget(location) { function jumpToTarget() { const jumpPosition = getElementPosition(target) @@ -344,7 +257,7 @@ function iframeListener(event) { y: jumpPosition.y, } - scrollTo(iframeId) + scrollToLink(iframeId) window.location.hash = hash } @@ -471,31 +384,31 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case SCROLL_BY: - scrollBy() + scrollBy(messageData) break case SCROLL_TO: - scrollRequestFromChild(false) + scrollTo(messageData) break case SCROLL_TO_OFFSET: - scrollRequestFromChild(true) + scrollToOffset(messageData) break case PAGE_INFO: - startPageInfoMonitor() + startPageInfoMonitor(id) break case PARENT_INFO: - startParentInfoMonitor() + startParentInfoMonitor(id) break case PAGE_INFO_STOP: - stopPageInfoMonitor() + stopPageInfoMonitor(id) break case PARENT_INFO_STOP: - stopParentInfoMonitor() + stopParentInfoMonitor(id) break case IN_PAGE_LINK: @@ -656,7 +569,7 @@ function closeIframe(iframe) { // function setPagePosition(iframeId) { // if (page.position === null) return -// window.scrollTo(page.position.x, page.position.y) +// window.Link(page.position.x, page.position.y) // info( // iframeId, // `Set page position: %c${page.position.x}%c, %c${page.position.y}`, diff --git a/packages/core/scroll.js b/packages/core/scroll.js new file mode 100644 index 000000000..abe25cbb2 --- /dev/null +++ b/packages/core/scroll.js @@ -0,0 +1,87 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +import { info } from './console' +import on from './event' +import { + getPagePosition, + getStoredPagePosition, + setPagePosition, + setStoredPagePosition, + unsetPagePosition, +} from './page-position' +import settings from './values/settings' + +export function getElementPosition(target) { + const iFramePosition = target.getBoundingClientRect() + const pagePosition = getPagePosition(target.id) + + return { + x: Number(iFramePosition.left) + Number(pagePosition.x), + y: Number(iFramePosition.top) + Number(pagePosition.y), + } +} + +export function scrollBy(messageData) { + const { id, height, width } = messageData + + // Check for V4 as well + const target = window.parentIframe || window.parentIFrame || window + + info( + id, + `scrollBy: x: %c${width}%c y: %c${height}`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + ) + + target.scrollBy(width, height) +} + +const scrollRequestFromChild = (addOffset) => (messageData) => { + /* istanbul ignore next */ // Not testable in Karma + function reposition(newPosition) { + setStoredPagePosition(newPosition) + scrollTo(id) + } + + function scrollParent(target, newPosition) { + target[`scrollTo${addOffset ? 'Offset' : ''}`](newPosition.x, newPosition.y) + } + + const calcOffset = (offset) => ({ + x: width + offset.x, + y: height + offset.y, + }) + + const { id, iframe, height, width } = messageData + const offset = addOffset ? getElementPosition(iframe) : { x: 0, y: 0 } + const newPosition = calcOffset(offset) + const target = window.parentIframe || window.parentIFrame // Check for V4 as well + + info( + id, + `Reposition requested (offset x:%c${offset.x}%c y:%c${offset.y})`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + ) + + if (target) scrollParent(target, newPosition) + else reposition(newPosition) +} + +export const scrollTo = scrollRequestFromChild(false) +export const scrollToOffset = scrollRequestFromChild(true) + +export function scrollToLink(id) { + const { x, y } = getStoredPagePosition() + const iframe = settings[id]?.iframe + + if (on(id, 'onScroll', { iframe, top: y, left: x, x, y }) === false) { + unsetPagePosition() + return + } + + setPagePosition(id) +} From 0b6a37125e86894e9a18153114273b0e1fabcd7c Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 13:35:31 +0100 Subject: [PATCH 15/42] Extract sizing functions from core --- packages/core/events/size.js | 28 ++++++++++++++++++++++++++++ packages/core/index.js | 31 ++++--------------------------- 2 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 packages/core/events/size.js diff --git a/packages/core/events/size.js b/packages/core/events/size.js new file mode 100644 index 000000000..0e031a0e9 --- /dev/null +++ b/packages/core/events/size.js @@ -0,0 +1,28 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { HEIGHT, WIDTH } from '../../common/consts' +import { info } from '../console' +import checkEvent from '../event' +import { setPagePosition } from '../page-position' +import settings from '../values/settings' + +export function setSize(messageData) { + function setDimension(dimension) { + const size = `${messageData[dimension]}px` + messageData.iframe.style[dimension] = size + info(id, `Set ${dimension}: %c${size}`, HIGHLIGHT) + } + + const { id } = messageData + const { sizeHeight, sizeWidth } = settings[id] + + if (sizeHeight) setDimension(HEIGHT) + if (sizeWidth) setDimension(WIDTH) +} + +export function resizeIframe(messageData) { + const { id } = messageData + setSize(messageData) + setPagePosition(id) + checkEvent(id, 'onResized', messageData) +} diff --git a/packages/core/index.js b/packages/core/index.js index 27f719de3..f89c4b73c 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -10,7 +10,6 @@ import { CLOSE, COLLAPSE, EXPAND, - HEIGHT, HIDDEN, HORIZONTAL, IN_PAGE_LINK, @@ -51,7 +50,6 @@ import { TITLE, VERSION, VERTICAL, - WIDTH, } from '../common/consts' import { addEventListener } from '../common/listeners' import setMode, { getModeData, getModeLabel } from '../common/mode' @@ -71,13 +69,13 @@ import { warn, } from './console' import checkEvent from './event' +import { resizeIframe, setSize } from './events/size' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' -// import { resizeIframe, setSize } from './size' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' // import onMouse from './mouse' -import { getPagePosition, setPagePosition } from './page-position' +import { getPagePosition } from './page-position' // import decodeMessage from './receive/decode' // import { onMessage } from './receive/message' // import { @@ -103,13 +101,6 @@ import page from './values/page' import settings from './values/settings' function iframeListener(event) { - function resizeIframe() { - setSize(messageData) - setPagePosition(iframeId) - - on('onResized', messageData) - } - function getPaddingEnds(compStyle) { if (compStyle.boxSizing !== 'border-box') return 0 @@ -424,7 +415,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case INIT: - resizeIframe() + resizeIframe(messageData) checkSameDomain(iframeId) checkVersion(msg) settings[iframeId].initialised = true @@ -453,7 +444,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. return } - resizeIframe() + resizeIframe(messageData) } } @@ -591,20 +582,6 @@ function resetIframe(messageData) { trigger(RESET, RESET, messageData.id) } -function setSize(messageData) { - function setDimension(dimension) { - const size = `${messageData[dimension]}px` - messageData.iframe.style[dimension] = size - info(id, `Set ${dimension}: %c${size}`, HIGHLIGHT) - } - - const { id } = messageData - const { sizeHeight, sizeWidth } = settings[id] - - if (sizeHeight) setDimension(HEIGHT) - if (sizeWidth) setDimension(WIDTH) -} - let count = 0 let vAdvised = false let vInfoDisable = false From f909df3fd49cc4992fa01251fc2267ccc435a477 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 16:30:44 +0100 Subject: [PATCH 16/42] Extract receiver functions --- packages/core/index.js | 245 ++++------------------------- packages/core/receive/decode.js | 48 ++++++ packages/core/receive/message.js | 27 ++++ packages/core/receive/preflight.js | 80 ++++++++++ 4 files changed, 184 insertions(+), 216 deletions(-) create mode 100644 packages/core/receive/decode.js create mode 100644 packages/core/receive/message.js create mode 100644 packages/core/receive/preflight.js diff --git a/packages/core/index.js b/packages/core/index.js index f89c4b73c..7c3cb2508 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -21,13 +21,10 @@ import { LOG_OPTIONS, MESSAGE, MESSAGE_HEADER_LENGTH, - MESSAGE_ID, - MESSAGE_ID_LENGTH, MIN_SIZE, MOUSE_ENTER, MOUSE_LEAVE, NONE, - NULL, NUMBER, OBJECT, OFFSET, @@ -76,14 +73,14 @@ import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' // import onMouse from './mouse' import { getPagePosition } from './page-position' -// import decodeMessage from './receive/decode' -// import { onMessage } from './receive/message' -// import { -// checkIframeExists, -// isMessageForUs, -// isMessageFromIframe, -// isMessageFromMetaParent, -// } from './receive/preflight' +import decodeMessage from './receive/decode' +import { onMessage } from './receive/message' +import { + checkIframeExists, + isMessageForUs, + isMessageFromIframe, + isMessageFromMetaParent, +} from './receive/preflight' import { getElementPosition, scrollBy, @@ -101,142 +98,9 @@ import page from './values/page' import settings from './values/settings' function iframeListener(event) { - function getPaddingEnds(compStyle) { - if (compStyle.boxSizing !== 'border-box') return 0 - - const top = compStyle.paddingTop ? parseInt(compStyle.paddingTop, 10) : 0 - const bot = compStyle.paddingBottom - ? parseInt(compStyle.paddingBottom, 10) - : 0 - - return top + bot - } - - function getBorderEnds(compStyle) { - if (compStyle.boxSizing !== 'border-box') return 0 - - const top = compStyle.borderTopWidth - ? parseInt(compStyle.borderTopWidth, 10) - : 0 - - const bot = compStyle.borderBottomWidth - ? parseInt(compStyle.borderBottomWidth, 10) - : 0 - - return top + bot - } - - function processMessage(msg) { - const data = msg.slice(MESSAGE_ID_LENGTH).split(SEPARATOR) - const height = data[1] ? Number(data[1]) : 0 - const iframe = settings[data[0]]?.iframe - const compStyle = getComputedStyle(iframe) - - const messageData = { - iframe, - id: data[0], - height: height + getPaddingEnds(compStyle) + getBorderEnds(compStyle), - width: Number(data[2]), - type: data[3], - msg: data[4], - } - - // eslint-disable-next-line prefer-destructuring - if (data[5]) messageData.mode = data[5] - - return messageData - } - - function isMessageFromIframe() { - function checkAllowedOrigin() { - function checkList() { - let i = 0 - let retCode = false - - log( - iframeId, - `Checking connection is from allowed list of origins: %c${checkOrigin}`, - HIGHLIGHT, - ) - - for (; i < checkOrigin.length; i++) { - if (checkOrigin[i] === origin) { - retCode = true - break - } - } - - return retCode - } - - function checkSingle() { - const remoteHost = settings[iframeId]?.remoteHost - log(iframeId, `Checking connection is from: %c${remoteHost}`, HIGHLIGHT) - return origin === remoteHost - } - - return checkOrigin.constructor === Array ? checkList() : checkSingle() - } - - const { origin, sameOrigin } = event - - if (sameOrigin) return true - - let checkOrigin = settings[iframeId]?.checkOrigin - - if (checkOrigin && `${origin}` !== NULL && !checkAllowedOrigin()) { - throw new Error( - `Unexpected message received from: ${origin} for ${messageData.iframe.id}. Message was: ${event.data}. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.`, - ) - } - - return true - } - - const isMessageForUs = (msg) => - MESSAGE_ID === `${msg}`.slice(0, MESSAGE_ID_LENGTH) && - msg.slice(MESSAGE_ID_LENGTH).split(SEPARATOR)[0] in settings - - function isMessageFromMetaParent() { - // Test if this message is from a parent above us. This is an ugly test, however, updating - // the message format would break backwards compatibility. - const retCode = messageData.type in { true: 1, false: 1, undefined: 1 } - - if (retCode) { - log(iframeId, 'Ignoring init message from meta parent page') - } - - return retCode - } - - const getMsgBody = (offset) => + const getMessageBody = (offset) => msg.slice(msg.indexOf(SEPARATOR) + MESSAGE_HEADER_LENGTH + offset) - function forwardMsgFromIframe(msgBody) { - log( - iframeId, - `onMessage passed: {iframe: %c${messageData.iframe.id}%c, message: %c${msgBody}%c}`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - FOREGROUND, - ) - - on('onMessage', { - iframe: messageData.iframe, - message: JSON.parse(msgBody), - }) - } - - function checkIframeExists() { - if (messageData.iframe === null) { - warn(iframeId, `The iframe (${messageData.id}) was not found.`) - return false - } - - return true - } - function findTarget(location) { function jumpToTarget() { const jumpPosition = getElementPosition(target) @@ -288,7 +152,7 @@ function iframeListener(event) { let mousePos = {} if (messageData.width === 0 && messageData.height === 0) { - const coords = getMsgBody(9).split(SEPARATOR) + const coords = getMessageBody(9).split(SEPARATOR) mousePos = { x: coords[1], y: coords[0], @@ -344,7 +208,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. ) } - function eventMsg() { + function receivedMessage() { const { height, iframe, msg, type, width } = messageData if (settings[iframeId]?.firstRun) firstRun() @@ -354,7 +218,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case MESSAGE: - forwardMsgFromIframe(getMsgBody(6)) + onMessage(messageData, getMessageBody(6)) break case MOUSE_ENTER: @@ -371,7 +235,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case AUTO_RESIZE: - settings[iframeId].autoResize = JSON.parse(getMsgBody(9)) + settings[iframeId].autoResize = JSON.parse(getMessageBody(9)) break case SCROLL_BY: @@ -403,7 +267,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case IN_PAGE_LINK: - findTarget(getMsgBody(9)) + findTarget(getMessageBody(9)) break case TITLE: @@ -448,14 +312,6 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. } } - function checkSettings(iframeId) { - if (!settings[iframeId]) { - throw new Error( - `${messageData.type} No settings for ${iframeId}. Message was: ${msg}`, - ) - } - } - function firstRun() { if (!settings[iframeId]) return log(iframeId, `First run for ${iframeId}`) @@ -463,18 +319,6 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. settings[iframeId].firstRun = false } - function screenMessage(msg) { - checkSettings(iframeId) - - if (!isMessageFromMetaParent()) { - log(iframeId, `Received: %c${msg}`, HIGHLIGHT) - - if (checkIframeExists() && isMessageFromIframe()) { - eventMsg() - } - } - } - let msg = event.data if (msg === CHILD_READY_MESSAGE) { @@ -489,21 +333,25 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. return } - const messageData = processMessage(msg) + const messageData = decodeMessage(msg) const { id, type } = messageData const iframeId = id - if (!iframeId) { - warn( - '', - 'iframeResizer received messageData without id, message was: ', - msg, - ) - return - } + consoleEvent(id, type) + + switch (true) { + case !settings[id]: + throw new Error(`${type} No settings for ${id}. Message was: ${msg}`) - consoleEvent(iframeId, type) - errorBoundary(iframeId, screenMessage)(msg) + case !checkIframeExists(messageData): + case isMessageFromMetaParent(messageData): + case !isMessageFromIframe(messageData, event): + return + + default: + settings[id].lastMessage = event.data + errorBoundary(id, receivedMessage)(messageData) + } } function removeIframeListeners(iframe) { @@ -536,41 +384,6 @@ function closeIframe(iframe) { removeIframeListeners(iframe) } -// function getPagePosition(iframeId) { -// if (page.position !== null) return - -// page.position = { -// x: window.scrollX, -// y: window.scrollY, -// } - -// log( -// iframeId, -// `Get page position: %c${page.position.x}%c, %c${page.position.y}`, -// HIGHLIGHT, -// FOREGROUND, -// HIGHLIGHT, -// ) -// } - -// function unsetPagePosition() { -// page.position = null -// } - -// function setPagePosition(iframeId) { -// if (page.position === null) return - -// window.Link(page.position.x, page.position.y) -// info( -// iframeId, -// `Set page position: %c${page.position.x}%c, %c${page.position.y}`, -// HIGHLIGHT, -// FOREGROUND, -// HIGHLIGHT, -// ) -// unsetPagePosition() -// } - function resetIframe(messageData) { log( messageData.id, diff --git a/packages/core/receive/decode.js b/packages/core/receive/decode.js new file mode 100644 index 000000000..beb6a8f3c --- /dev/null +++ b/packages/core/receive/decode.js @@ -0,0 +1,48 @@ +import { MESSAGE_ID_LENGTH } from '../../common/consts' +import settings from '../values/settings' + +export function getPaddingEnds(compStyle) { + if (compStyle.boxSizing !== 'border-box') return 0 + + const top = compStyle.paddingTop ? parseInt(compStyle.paddingTop, 10) : 0 + const bot = compStyle.paddingBottom + ? parseInt(compStyle.paddingBottom, 10) + : 0 + + return top + bot +} + +export function getBorderEnds(compStyle) { + if (compStyle.boxSizing !== 'border-box') return 0 + + const top = compStyle.borderTopWidth + ? parseInt(compStyle.borderTopWidth, 10) + : 0 + + const bot = compStyle.borderBottomWidth + ? parseInt(compStyle.borderBottomWidth, 10) + : 0 + + return top + bot +} + +export default function decodeMessage(msg) { + const data = msg.slice(MESSAGE_ID_LENGTH).split(':') + const height = data[1] ? Number(data[1]) : 0 + const iframe = settings[data[0]]?.iframe + const compStyle = getComputedStyle(iframe) + + const messageData = { + iframe, + id: data[0], + height: height + getPaddingEnds(compStyle) + getBorderEnds(compStyle), + width: Number(data[2]), + type: data[3], + msg: data[4], + } + + // eslint-disable-next-line prefer-destructuring + if (data[5]) messageData.mode = data[5] + + return messageData +} diff --git a/packages/core/receive/message.js b/packages/core/receive/message.js new file mode 100644 index 000000000..1f073b391 --- /dev/null +++ b/packages/core/receive/message.js @@ -0,0 +1,27 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +// import { MESSAGE_HEADER_LENGTH } from '../../common/consts' +import { log } from '../console' +import on from '../event' + +// export const getMessageBody = (message, offset) => +// message.slice(message.indexOf(':') + MESSAGE_HEADER_LENGTH + offset) + +// eslint-disable-next-line import/prefer-default-export +export function onMessage(messageData, messageBody) { + const { id, iframe } = messageData + + log( + id, + `onMessage passed: {iframe: %c${id}%c, message: %c${messageBody}%c}`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + FOREGROUND, + ) + + on(id, 'onMessage', { + iframe, + message: JSON.parse(messageBody), + }) +} diff --git a/packages/core/receive/preflight.js b/packages/core/receive/preflight.js new file mode 100644 index 000000000..7bfcd31d3 --- /dev/null +++ b/packages/core/receive/preflight.js @@ -0,0 +1,80 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { MESSAGE_ID, MESSAGE_ID_LENGTH } from '../../common/consts' +import { log, warn } from '../console' +import settings from '../values/settings' + +const ABOVE_TYPES = { true: 1, false: 1, undefined: 1 } + +export function checkIframeExists(messageData) { + const { id, msg, iframe } = messageData + const detectedIframe = iframe && iframe !== null + + if (!detectedIframe) { + log(id, `Received: %c${msg}`, HIGHLIGHT) + warn(id, `The target iframe was not found.`) + } + + return detectedIframe +} + +export function isMessageFromIframe(messageData, event) { + function checkAllowedOrigin() { + function checkList() { + log( + id, + `Checking connection is from allowed list of origins: %c${checkOrigin}`, + HIGHLIGHT, + ) + + for (const element of checkOrigin) { + if (element === origin) { + return true + } + } + + return false + } + + function checkSingle() { + const remoteHost = settings[id]?.remoteHost + log(id, `Checking connection is from: %c${remoteHost}`, HIGHLIGHT) + return origin === remoteHost + } + + return checkOrigin.constructor === Array ? checkList() : checkSingle() + } + + const { id } = messageData + const { data, origin, sameOrigin } = event + + if (sameOrigin) return true + + let checkOrigin = settings[id]?.checkOrigin + + if (checkOrigin && `${origin}` !== 'null' && !checkAllowedOrigin()) { + throw new Error( + `Unexpected message received from: ${origin} for ${id}. Message was: ${data}. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.`, + ) + } + + return true +} + +export const isMessageForUs = (message) => + MESSAGE_ID === `${message}`.slice(0, MESSAGE_ID_LENGTH) && + message.slice(MESSAGE_ID_LENGTH).split(':')[0] in settings + +export function isMessageFromMetaParent(messageData) { + const { id, type } = messageData + + // Test if this message is from a parent above us. This is an ugly test, + // however, updating the message format would break backwards compatibility. + const isMetaParent = type in ABOVE_TYPES + + if (isMetaParent) { + log(id, 'Ignoring init message from meta parent page') + } + + return isMetaParent +} From 93c490175a5a7bddeed25d87c117cbd1891e27d4 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 16:45:44 +0100 Subject: [PATCH 17/42] Extract mouse events from core --- packages/child/index.js | 17 +++++++++-------- packages/core/events/mouse.js | 30 ++++++++++++++++++++++++++++++ packages/core/index.js | 30 +++--------------------------- packages/core/receive/message.js | 6 +++--- 4 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 packages/core/events/mouse.js diff --git a/packages/child/index.js b/packages/child/index.js index 7c398c3f4..9c44c9e96 100644 --- a/packages/child/index.js +++ b/packages/child/index.js @@ -1774,22 +1774,23 @@ This version of iframe-resizer can auto detect the most suitable ${label} return win } + // Provide stable hooks for Karma/RequireJS environments try { - // eslint-disable-next-line no-restricted-globals - if (top?.document?.getElementById('banner')) { - win = {} + // Use a stubbed window for tests + win = {} + window.mockMsgListener = mockMsgListener - // Create test hooks - window.mockMsgListener = mockMsgListener - - removeEventListener(window, MESSAGE, received) + // Detach real message listener to avoid side-effects during tests + removeEventListener(window, MESSAGE, received) + // Register AMD module if RequireJS is present + if (typeof define === 'function' && define.amd) { + // Keep it anonymous so it maps to the requested path define([], () => mockMsgListener) } } catch (error) { // do nothing } - /* TEST CODE END */ } diff --git a/packages/core/events/mouse.js b/packages/core/events/mouse.js new file mode 100644 index 000000000..5d1ca8021 --- /dev/null +++ b/packages/core/events/mouse.js @@ -0,0 +1,30 @@ +import { SEPARATOR } from '../../common/consts' +import on from '../event' +import { getMessageBody } from '../receive/message' +import settings from '../values/settings' + +export default function onMouse(event, messageData) { + const { id, iframe, height, type, width } = messageData + const { lastMessage } = settings[id] + let mousePos = {} + + if (width === 0 && height === 0) { + const coords = getMessageBody(lastMessage, 9).split(SEPARATOR) + mousePos = { + x: coords[1], + y: coords[0], + } + } else { + mousePos = { + x: width, + y: height, + } + } + + on(id, event, { + iframe, + screenX: Number(mousePos.x), + screenY: Number(mousePos.y), + type, + }) +} diff --git a/packages/core/index.js b/packages/core/index.js index 7c3cb2508..c10b0c079 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -66,12 +66,12 @@ import { warn, } from './console' import checkEvent from './event' +import onMouse from './events/mouse' import { resizeIframe, setSize } from './events/size' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' -// import onMouse from './mouse' import { getPagePosition } from './page-position' import decodeMessage from './receive/decode' import { onMessage } from './receive/message' @@ -148,30 +148,6 @@ function iframeListener(event) { jumpToParent() } - function onMouse(event) { - let mousePos = {} - - if (messageData.width === 0 && messageData.height === 0) { - const coords = getMessageBody(9).split(SEPARATOR) - mousePos = { - x: coords[1], - y: coords[0], - } - } else { - mousePos = { - x: messageData.width, - y: messageData.height, - } - } - - on(event, { - iframe: messageData.iframe, - screenX: Number(mousePos.x), - screenY: Number(mousePos.y), - type: messageData.type, - }) - } - const on = (funcName, val) => checkEvent(iframeId, funcName, val) function checkSameDomain(id) { @@ -222,11 +198,11 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case MOUSE_ENTER: - onMouse('onMouseEnter') + onMouse('onMouseEnter', messageData) break case MOUSE_LEAVE: - onMouse('onMouseLeave') + onMouse('onMouseLeave', messageData) break case BEFORE_UNLOAD: diff --git a/packages/core/receive/message.js b/packages/core/receive/message.js index 1f073b391..99a020686 100644 --- a/packages/core/receive/message.js +++ b/packages/core/receive/message.js @@ -1,11 +1,11 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' -// import { MESSAGE_HEADER_LENGTH } from '../../common/consts' +import { MESSAGE_HEADER_LENGTH } from '../../common/consts' import { log } from '../console' import on from '../event' -// export const getMessageBody = (message, offset) => -// message.slice(message.indexOf(':') + MESSAGE_HEADER_LENGTH + offset) +export const getMessageBody = (message, offset) => + message.slice(message.indexOf(':') + MESSAGE_HEADER_LENGTH + offset) // eslint-disable-next-line import/prefer-default-export export function onMessage(messageData, messageBody) { From d78694584cd17f4a6671c78aba0f1e60a06e63b5 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 17:06:49 +0100 Subject: [PATCH 18/42] Reorder router --- packages/core/index.js | 75 +++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/packages/core/index.js b/packages/core/index.js index c10b0c079..d9386a30c 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -184,46 +184,47 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. ) } - function receivedMessage() { - const { height, iframe, msg, type, width } = messageData - if (settings[iframeId]?.firstRun) firstRun() + function routeMessage({ height, id, iframe, msg, type, width }) { + const { lastMessage } = settings[id] + if (settings[id]?.firstRun) firstRun() + log(id, `Received: %c${lastMessage}`, HIGHLIGHT) switch (type) { - case CLOSE: - closeIframe(iframe) - break - - case MESSAGE: - onMessage(messageData, getMessageBody(6)) + case AUTO_RESIZE: + settings[iframeId].autoResize = JSON.parse(getMessageBody(9)) break - case MOUSE_ENTER: - onMouse('onMouseEnter', messageData) + case BEFORE_UNLOAD: + info(iframeId, 'Ready state reset') + settings[iframeId].initialised = false break - case MOUSE_LEAVE: - onMouse('onMouseLeave', messageData) + case CLOSE: + closeIframe(iframe) break - case BEFORE_UNLOAD: - info(iframeId, 'Ready state reset') - settings[iframeId].initialised = false + case IN_PAGE_LINK: + findTarget(getMessageBody(9)) break - case AUTO_RESIZE: - settings[iframeId].autoResize = JSON.parse(getMessageBody(9)) + case INIT: + resizeIframe(messageData) + checkSameDomain(iframeId) + checkVersion(msg) + settings[iframeId].initialised = true + on('onReady', iframe) break - case SCROLL_BY: - scrollBy(messageData) + case MESSAGE: + onMessage(messageData, getMessageBody(6)) break - case SCROLL_TO: - scrollTo(messageData) + case MOUSE_ENTER: + onMouse('onMouseEnter', messageData) break - case SCROLL_TO_OFFSET: - scrollToOffset(messageData) + case MOUSE_LEAVE: + onMouse('onMouseLeave', messageData) break case PAGE_INFO: @@ -242,24 +243,24 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. stopParentInfoMonitor(id) break - case IN_PAGE_LINK: - findTarget(getMessageBody(9)) + case RESET: + resetIframe(messageData) break - case TITLE: - setTitle(msg, iframeId) + case SCROLL_BY: + scrollBy(messageData) break - case RESET: - resetIframe(messageData) + case SCROLL_TO: + scrollTo(messageData) break - case INIT: - resizeIframe(messageData) - checkSameDomain(iframeId) - checkVersion(msg) - settings[iframeId].initialised = true - on('onReady', iframe) + case SCROLL_TO_OFFSET: + scrollToOffset(messageData) + break + + case TITLE: + setTitle(msg, iframeId) break default: @@ -326,7 +327,7 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. default: settings[id].lastMessage = event.data - errorBoundary(id, receivedMessage)(messageData) + errorBoundary(id, routeMessage)(messageData) } } From 64a62d01a72559501295b57364a13ec70b604355 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 17:42:15 +0100 Subject: [PATCH 19/42] rename function --- packages/core/send/trigger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/send/trigger.js b/packages/core/send/trigger.js index 604fc4b2c..24662f429 100644 --- a/packages/core/send/trigger.js +++ b/packages/core/send/trigger.js @@ -10,7 +10,7 @@ const filterMsg = (msg) => .filter((_, index) => index !== 19) .join(SEPARATOR) -function postMessageToIframe(calleeMsg, msg, id) { +function dispatch(calleeMsg, msg, id) { function logSent(route) { const displayMsg = calleeMsg in INIT_EVENTS ? filterMsg(msg) : msg info(id, route, HIGHLIGHT, FOREGROUND, HIGHLIGHT) @@ -48,7 +48,7 @@ function trigger(calleeMsg, msg, id) { return } - postMessageToIframe(calleeMsg, msg, id) + dispatch(calleeMsg, msg, id) } export default trigger From 586bef2ff7cc3818f487513157a3155a9f0cb29b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 17:58:03 +0100 Subject: [PATCH 20/42] Revert git stash pop --- packages/child/index.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/child/index.js b/packages/child/index.js index 9c44c9e96..7c398c3f4 100644 --- a/packages/child/index.js +++ b/packages/child/index.js @@ -1774,23 +1774,22 @@ This version of iframe-resizer can auto detect the most suitable ${label} return win } - // Provide stable hooks for Karma/RequireJS environments try { - // Use a stubbed window for tests - win = {} - window.mockMsgListener = mockMsgListener + // eslint-disable-next-line no-restricted-globals + if (top?.document?.getElementById('banner')) { + win = {} - // Detach real message listener to avoid side-effects during tests - removeEventListener(window, MESSAGE, received) + // Create test hooks + window.mockMsgListener = mockMsgListener + + removeEventListener(window, MESSAGE, received) - // Register AMD module if RequireJS is present - if (typeof define === 'function' && define.amd) { - // Keep it anonymous so it maps to the requested path define([], () => mockMsgListener) } } catch (error) { // do nothing } + /* TEST CODE END */ } From d223da5407b0aef8e503ecb7effc304fcb21929e Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 18:36:30 +0100 Subject: [PATCH 21/42] Extract checks --- packages/core/checks/mode.js | 38 ++++++ packages/core/checks/origin.js | 15 +++ packages/core/checks/version.js | 27 ++++ packages/core/events/size.js | 2 +- packages/core/index.js | 127 +++++------------- .../{page-position.js => page/position.js} | 4 +- packages/core/{ => page}/scroll.js | 8 +- 7 files changed, 117 insertions(+), 104 deletions(-) create mode 100644 packages/core/checks/mode.js create mode 100644 packages/core/checks/origin.js create mode 100644 packages/core/checks/version.js rename packages/core/{page-position.js => page/position.js} (92%) rename packages/core/{ => page}/scroll.js (94%) diff --git a/packages/core/checks/mode.js b/packages/core/checks/mode.js new file mode 100644 index 000000000..c698b2ee6 --- /dev/null +++ b/packages/core/checks/mode.js @@ -0,0 +1,38 @@ +import { VERSION } from '../../common/consts' +import { getModeData, getModeLabel } from '../../common/mode' +import { advise, purge as consoleClear, vInfo } from '../console' +import settings from '../values/settings' + +let vAdvised = false +let vInfoDisable = false + +export default function checkMode(id, childMode = -3) { + if (vAdvised) return + const mode = Math.max(settings[id].mode, childMode) + if (mode > settings[id].mode) settings[id].mode = mode + if (mode < 0) { + consoleClear(id) + if (!settings[id].vAdvised) + advise(id || 'Parent', `${getModeData(mode + 2)}${getModeData(2)}`) + settings[id].vAdvised = true + throw getModeData(mode + 2).replace(/<\/?[a-z][^>]*>|<\/>/gi, '') + } + if (!(mode > 0 && vInfoDisable)) { + vInfo(`v${VERSION} (${getModeLabel(mode)})`, mode) + } + if (mode < 1) advise('Parent', getModeData(3)) + vAdvised = true +} + +export function preModeCheck(id) { + if (vAdvised) return + const { mode } = settings[id] + if (mode !== -1) checkMode(id, mode) +} + +export function enableVInfo(options) { + if (options?.log === -1) { + options.log = false + vInfoDisable = true + } +} diff --git a/packages/core/checks/origin.js b/packages/core/checks/origin.js new file mode 100644 index 000000000..265c32eb1 --- /dev/null +++ b/packages/core/checks/origin.js @@ -0,0 +1,15 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { log } from '../console' +import settings from '../values/settings' + +export default function checkSameDomain(id) { + try { + settings[id].sameOrigin = + !!settings[id]?.iframe?.contentWindow?.iframeChildListener + } catch (error) { + settings[id].sameOrigin = false + } + + log(id, `sameOrigin: %c${settings[id].sameOrigin}`, HIGHLIGHT) +} diff --git a/packages/core/checks/version.js b/packages/core/checks/version.js new file mode 100644 index 000000000..3286f253d --- /dev/null +++ b/packages/core/checks/version.js @@ -0,0 +1,27 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +import { VERSION } from '../../common/consts' +import { advise, log } from '../console' + +export default function checkVersion(id, version) { + if (version === VERSION) return + if (version === undefined) { + advise( + id, + `Legacy version detected in iframe + +Detected legacy version of child page script. It is recommended to update the page in the iframe to use @iframe-resizer/child. + +See https://iframe-resizer.com/setup/#child-page-setup for more details. +`, + ) + return + } + log( + id, + `Version mismatch (Child: %c${version}%c !== Parent: %c${VERSION})`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + ) +} diff --git a/packages/core/events/size.js b/packages/core/events/size.js index 0e031a0e9..3823ee909 100644 --- a/packages/core/events/size.js +++ b/packages/core/events/size.js @@ -3,7 +3,7 @@ import { HIGHLIGHT } from 'auto-console-group' import { HEIGHT, WIDTH } from '../../common/consts' import { info } from '../console' import checkEvent from '../event' -import { setPagePosition } from '../page-position' +import { setPagePosition } from '../page/position' import settings from '../values/settings' export function setSize(messageData) { diff --git a/packages/core/index.js b/packages/core/index.js index d9386a30c..46119bd8a 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,4 +1,4 @@ -import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' +import { HIGHLIGHT } from 'auto-console-group' import { AFTER_EVENT_STACK, @@ -45,12 +45,14 @@ import { SEPARATOR, STRING, TITLE, - VERSION, VERTICAL, } from '../common/consts' import { addEventListener } from '../common/listeners' -import setMode, { getModeData, getModeLabel } from '../common/mode' +import setMode from '../common/mode' import { hasOwn, once, typeAssert } from '../common/utils' +import checkMode, { enableVInfo, preModeCheck } from './checks/mode' +import checkSameDomain from './checks/origin' +import checkVersion from './checks/version' import { advise, debug, @@ -60,9 +62,7 @@ import { event as consoleEvent, info, log, - purge as consoleClear, setupConsole, - vInfo, warn, } from './console' import checkEvent from './event' @@ -70,9 +70,16 @@ import onMouse from './events/mouse' import { resizeIframe, setSize } from './events/size' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' +import { getPagePosition } from './page/position' +import { + getElementPosition, + scrollBy, + scrollTo, + scrollToLink, + scrollToOffset, +} from './page/scroll' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' -import { getPagePosition } from './page-position' import decodeMessage from './receive/decode' import { onMessage } from './receive/message' import { @@ -81,13 +88,6 @@ import { isMessageFromIframe, isMessageFromMetaParent, } from './receive/preflight' -import { - getElementPosition, - scrollBy, - scrollTo, - scrollToLink, - scrollToOffset, -} from './scroll' import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' @@ -150,53 +150,19 @@ function iframeListener(event) { const on = (funcName, val) => checkEvent(iframeId, funcName, val) - function checkSameDomain(id) { - try { - settings[id].sameOrigin = - !!settings[id]?.iframe?.contentWindow?.iframeChildListener - } catch (error) { - settings[id].sameOrigin = false - } - - log(id, `sameOrigin: %c${settings[id].sameOrigin}`, HIGHLIGHT) - } - - function checkVersion(version) { - if (version === VERSION) return - if (version === undefined) { - advise( - iframeId, - `Legacy version detected in iframe - -Detected legacy version of child page script. It is recommended to update the page in the iframe to use @iframe-resizer/child. - -See https://iframe-resizer.com/setup/#child-page-setup for more details. -`, - ) - return - } - log( - iframeId, - `Version mismatch (Child: %c${version}%c !== Parent: %c${VERSION})`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - ) - } - function routeMessage({ height, id, iframe, msg, type, width }) { const { lastMessage } = settings[id] - if (settings[id]?.firstRun) firstRun() + if (settings[id]?.firstRun) firstRun(id) log(id, `Received: %c${lastMessage}`, HIGHLIGHT) switch (type) { case AUTO_RESIZE: - settings[iframeId].autoResize = JSON.parse(getMessageBody(9)) + settings[id].autoResize = JSON.parse(getMessageBody(9)) break case BEFORE_UNLOAD: - info(iframeId, 'Ready state reset') - settings[iframeId].initialised = false + info(id, 'Ready state reset') + settings[id].initialised = false break case CLOSE: @@ -209,9 +175,9 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. case INIT: resizeIframe(messageData) - checkSameDomain(iframeId) - checkVersion(msg) - settings[iframeId].initialised = true + checkSameDomain(id) + checkVersion(id, msg) + settings[id].initialised = true on('onReady', iframe) break @@ -260,13 +226,13 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. break case TITLE: - setTitle(msg, iframeId) + setTitle(msg, id) break default: if (width === 0 && height === 0) { warn( - iframeId, + id, `Unsupported message received (${type}), this is likely due to the iframe containing a later ` + `version of iframe-resizer than the parent page`, ) @@ -274,14 +240,14 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. } if (width === 0 || height === 0) { - log(iframeId, 'Ignoring message with 0 height or width') + log(id, 'Ignoring message with 0 height or width') return } // Recheck document.hidden here, as only Firefox // correctly supports this in the iframe if (document.hidden) { - log(iframeId, 'Page hidden - ignored resize request') + log(id, 'Page hidden - ignored resize request') return } @@ -289,11 +255,11 @@ See https://iframe-resizer.com/setup/#child-page-setup for more details. } } - function firstRun() { - if (!settings[iframeId]) return - log(iframeId, `First run for ${iframeId}`) - checkMode(iframeId, messageData.mode) - settings[iframeId].firstRun = false + function firstRun(id) { + if (!settings[id]) return + log(id, `First run for ${id}`) + checkMode(id, messageData.mode) + settings[id].firstRun = false } let msg = event.data @@ -373,26 +339,6 @@ function resetIframe(messageData) { } let count = 0 -let vAdvised = false -let vInfoDisable = false - -function checkMode(iframeId, childMode = -3) { - if (vAdvised) return - const mode = Math.max(settings[iframeId].mode, childMode) - if (mode > settings[iframeId].mode) settings[iframeId].mode = mode - if (mode < 0) { - consoleClear(iframeId) - if (!settings[iframeId].vAdvised) - advise(iframeId || 'Parent', `${getModeData(mode + 2)}${getModeData(2)}`) - settings[iframeId].vAdvised = true - throw getModeData(mode + 2).replace(/<\/?[a-z][^>]*>|<\/>/gi, '') - } - if (!(mode > 0 && vInfoDisable)) { - vInfo(`v${VERSION} (${getModeLabel(mode)})`, mode) - } - if (mode < 1) advise('Parent', getModeData(3)) - vAdvised = true -} export default (options) => (iframe) => { function newId() { @@ -624,12 +570,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep settings[iframeId].postMessageTarget = iframe.contentWindow } - function preModeCheck() { - if (vAdvised) return - const { mode } = settings[iframeId] - if (mode !== -1) checkMode(iframeId, mode) - } - function updateOptionName(oldName, newName) { if (hasOwn(settings[iframeId], oldName)) { advise( @@ -690,7 +630,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep processOptions(options) checkUniqueId(iframeId) log(iframeId, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) - preModeCheck() + preModeCheck(iframeId) setupEventListenersOnce() setScrolling() setupBodyMarginValues() @@ -700,13 +640,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep endAutoGroup(iframeId) } - function enableVInfo(options) { - if (options?.log === -1) { - options.log = false - vInfoDisable = true - } - } - function checkLocationSearch(options) { const { search } = window.location diff --git a/packages/core/page-position.js b/packages/core/page/position.js similarity index 92% rename from packages/core/page-position.js rename to packages/core/page/position.js index 5866a10c9..79990d090 100644 --- a/packages/core/page-position.js +++ b/packages/core/page/position.js @@ -1,7 +1,7 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' -import { info, log } from './console' -import page from './values/page' +import { info, log } from '../console' +import page from '../values/page' export function unsetPagePosition() { page.position = null diff --git a/packages/core/scroll.js b/packages/core/page/scroll.js similarity index 94% rename from packages/core/scroll.js rename to packages/core/page/scroll.js index abe25cbb2..b7ac17f00 100644 --- a/packages/core/scroll.js +++ b/packages/core/page/scroll.js @@ -1,15 +1,15 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' -import { info } from './console' -import on from './event' +import { info } from '../console' +import on from '../event' +import settings from '../values/settings' import { getPagePosition, getStoredPagePosition, setPagePosition, setStoredPagePosition, unsetPagePosition, -} from './page-position' -import settings from './values/settings' +} from './position' export function getElementPosition(target) { const iFramePosition = target.getBoundingClientRect() From 019c0e6814fb6b9b57682b285de5a477ca20a95c Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 19:10:50 +0100 Subject: [PATCH 22/42] Extract more checks --- packages/core/checks/id.js | 32 +++++++++++++++++++ packages/core/checks/manual-logging.js | 10 ++++++ packages/core/index.js | 43 +++----------------------- 3 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 packages/core/checks/id.js create mode 100644 packages/core/checks/manual-logging.js diff --git a/packages/core/checks/id.js b/packages/core/checks/id.js new file mode 100644 index 000000000..f43d933c0 --- /dev/null +++ b/packages/core/checks/id.js @@ -0,0 +1,32 @@ +import { isString } from '../../common/utils' +import { event as consoleEvent, log } from '../console' +import defaults from '../values/defaults' + +let count = 0 + +function newId(options) { + let id = options?.id || defaults.id + count++ + + if (document.getElementById(id) !== null) { + id += count++ + } + + return id +} + +export default function ensureHasId(iframe, options) { + let { id } = iframe + + if (id && !isString(id)) { + throw new TypeError('Invalid id for iFrame. Expected String') + } + + if (!id || id === '') { + id = newId(options) + iframe.id = id + consoleEvent(id, 'assignId') + log(id, `Added missing iframe ID: ${id} (${iframe.src})`) + } + + return id +} diff --git a/packages/core/checks/manual-logging.js b/packages/core/checks/manual-logging.js new file mode 100644 index 000000000..c8b8f2f81 --- /dev/null +++ b/packages/core/checks/manual-logging.js @@ -0,0 +1,10 @@ +import { COLLAPSE } from '../../common/consts' + +export default function (options) { + const { search } = window.location + + if (search.includes('ifrlog')) { + options.log = COLLAPSE + options.logExpand = search.includes('ifrlog=expanded') + } +} diff --git a/packages/core/index.js b/packages/core/index.js index 46119bd8a..fdd58a63f 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -8,7 +8,6 @@ import { BOTH, CHILD_READY_MESSAGE, CLOSE, - COLLAPSE, EXPAND, HIDDEN, HORIZONTAL, @@ -50,6 +49,8 @@ import { import { addEventListener } from '../common/listeners' import setMode from '../common/mode' import { hasOwn, once, typeAssert } from '../common/utils' +import ensureHasId from './checks/id' +import checkManualLogging from './checks/manual-logging' import checkMode, { enableVInfo, preModeCheck } from './checks/mode' import checkSameDomain from './checks/origin' import checkVersion from './checks/version' @@ -338,34 +339,7 @@ function resetIframe(messageData) { trigger(RESET, RESET, messageData.id) } -let count = 0 - export default (options) => (iframe) => { - function newId() { - let id = options?.id || defaults.id + count++ - - if (document.getElementById(id) !== null) { - id += count++ - } - - return id - } - - function ensureHasId(iframeId) { - if (iframeId && typeof iframeId !== STRING) { - throw new TypeError('Invalid id for iFrame. Expected String') - } - - if (iframeId === '' || !iframeId) { - iframeId = newId() - iframe.id = iframeId - consoleEvent(iframeId, 'assignId') - log(iframeId, `Added missing iframe ID: ${iframeId} (${iframe.src})`) - } - - return iframeId - } - function setScrolling() { log( iframeId, @@ -640,15 +614,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep endAutoGroup(iframeId) } - function checkLocationSearch(options) { - const { search } = window.location - - if (search.includes('ifrlog')) { - options.log = COLLAPSE - options.logExpand = search.includes('ifrlog=expanded') - } - } - function startLogging(iframeId, options) { const isLogEnabled = hasOwn(options, 'log') const isLogString = typeof options.log === STRING @@ -683,13 +648,13 @@ The sizeWidth, sizeHeight and autoResize options have been rep const beenHere = () => LABEL in iframe - const iframeId = ensureHasId(iframe.id) + const iframeId = ensureHasId(iframe, options) if (typeof options !== OBJECT) { throw new TypeError('Options is not an object') } - checkLocationSearch(options) + checkManualLogging(options) startLogging(iframeId, options) errorBoundary(iframeId, setupIframe)(options) From fe2b8e912c42a40043cc8f1e40173534abafd8f6 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 19:22:31 +0100 Subject: [PATCH 23/42] Yet more extracted checks --- packages/core/checks/options.js | 28 ++++++++++++++++++++++ packages/core/checks/warning-timeout.js | 10 ++++++++ packages/core/index.js | 32 ++++--------------------- 3 files changed, 42 insertions(+), 28 deletions(-) create mode 100644 packages/core/checks/options.js create mode 100644 packages/core/checks/warning-timeout.js diff --git a/packages/core/checks/options.js b/packages/core/checks/options.js new file mode 100644 index 000000000..d6d81cc86 --- /dev/null +++ b/packages/core/checks/options.js @@ -0,0 +1,28 @@ +import { + AUTO_RESIZE, + BOTH, + HORIZONTAL, + NONE, + VERTICAL, +} from '../../common/consts' +import { advise } from '../console' + +export default function checkOptions(id, options) { + if (!options) return {} + + if ( + 'sizeWidth' in options || + 'sizeHeight' in options || + AUTO_RESIZE in options + ) { + advise( + id, + `Deprecated Option + +The sizeWidth, sizeHeight and autoResize options have been replaced with new direction option which expects values of ${VERTICAL}, ${HORIZONTAL}, ${BOTH} or ${NONE}. +`, + ) + } + + return options +} diff --git a/packages/core/checks/warning-timeout.js b/packages/core/checks/warning-timeout.js new file mode 100644 index 000000000..d79939de6 --- /dev/null +++ b/packages/core/checks/warning-timeout.js @@ -0,0 +1,10 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { info } from '../console' +import settings from '../values/settings' + +export default function checkWarningTimeout(id) { + if (!settings[id].warningTimeout) { + info(id, 'warningTimeout:%c disabled', HIGHLIGHT) + } +} diff --git a/packages/core/index.js b/packages/core/index.js index fdd58a63f..fd108e41f 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -52,8 +52,10 @@ import { hasOwn, once, typeAssert } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' import checkMode, { enableVInfo, preModeCheck } from './checks/mode' +import checkOptions from './checks/options' import checkSameDomain from './checks/origin' import checkVersion from './checks/version' +import checkWarningTimeout from './checks/warning-timeout' import { advise, debug, @@ -483,26 +485,6 @@ Use of the resize() method from the parent page is deprecated and will be sendInit(id, createInitChild(INIT)) } - function checkOptions(options) { - if (!options) return {} - - if ( - 'sizeWidth' in options || - 'sizeHeight' in options || - AUTO_RESIZE in options - ) { - advise( - iframeId, - `Deprecated Option - -The sizeWidth, sizeHeight and autoResize options have been replaced with new direction option which expects values of ${VERTICAL}, ${HORIZONTAL}, ${BOTH} or ${NONE}. -`, - ) - } - - return options - } - function setDirection() { const { direction } = settings[iframeId] @@ -555,12 +537,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep } } - function checkWarningTimeout() { - if (!settings[iframeId].warningTimeout) { - info(iframeId, 'warningTimeout:%c disabled', HIGHLIGHT) - } - } - const hasMouseEvents = (options) => hasOwn(options, 'onMouseEnter') || hasOwn(options, 'onMouseLeave') @@ -577,7 +553,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep iframe, remoteHost: iframe?.src.split('/').slice(0, 3).join('/'), ...defaults, - ...checkOptions(options), + ...checkOptions(iframeId, options), mouseEvents: hasMouseEvents(options), mode: setMode(options), syncTitle: checkTitle(iframeId), @@ -590,7 +566,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep consoleEvent(iframeId, 'setup') setDirection() setOffsetSize(iframeId, options) - checkWarningTimeout() + checkWarningTimeout(iframeId) getPostMessageTarget() setTargetOrigin() } From 03b74733354b235c332a1887388f0c4b79db094d Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Wed, 22 Oct 2025 19:29:28 +0100 Subject: [PATCH 24/42] Extract firstRun() --- packages/core/index.js | 14 ++++---------- packages/core/setup/first-run.js | 11 +++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 packages/core/setup/first-run.js diff --git a/packages/core/index.js b/packages/core/index.js index fd108e41f..b6b8723bb 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -51,7 +51,7 @@ import setMode from '../common/mode' import { hasOwn, once, typeAssert } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' -import checkMode, { enableVInfo, preModeCheck } from './checks/mode' +import { enableVInfo, preModeCheck } from './checks/mode' import checkOptions from './checks/options' import checkSameDomain from './checks/origin' import checkVersion from './checks/version' @@ -96,6 +96,7 @@ import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' +import firstRun from './setup/first-run' import defaults from './values/defaults' import page from './values/page' import settings from './values/settings' @@ -153,9 +154,9 @@ function iframeListener(event) { const on = (funcName, val) => checkEvent(iframeId, funcName, val) - function routeMessage({ height, id, iframe, msg, type, width }) { + function routeMessage({ height, id, iframe, mode, msg, type, width }) { const { lastMessage } = settings[id] - if (settings[id]?.firstRun) firstRun(id) + if (settings[id]?.firstRun) firstRun(id, mode) log(id, `Received: %c${lastMessage}`, HIGHLIGHT) switch (type) { @@ -258,13 +259,6 @@ function iframeListener(event) { } } - function firstRun(id) { - if (!settings[id]) return - log(id, `First run for ${id}`) - checkMode(id, messageData.mode) - settings[id].firstRun = false - } - let msg = event.data if (msg === CHILD_READY_MESSAGE) { diff --git a/packages/core/setup/first-run.js b/packages/core/setup/first-run.js new file mode 100644 index 000000000..9224ff9d9 --- /dev/null +++ b/packages/core/setup/first-run.js @@ -0,0 +1,11 @@ +import checkMode from '../checks/mode' +import { log } from '../console' +import settings from '../values/settings' + +export default function firstRun(id, mode) { + if (!settings[id]) return + + log(id, `First run for ${id}`) + checkMode(id, mode) + settings[id].firstRun = false +} From dbd48af3561e0067d4b3d06729f470c7acc58099 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 10:55:14 +0100 Subject: [PATCH 25/42] Extract inPageLink and fix scrollTo --- packages/core/index.js | 70 +++--------------------------- packages/core/page/in-page-link.js | 51 ++++++++++++++++++++++ packages/core/page/scroll.js | 26 +++++------ packages/core/receive/decode.js | 1 + 4 files changed, 72 insertions(+), 76 deletions(-) create mode 100644 packages/core/page/in-page-link.js diff --git a/packages/core/index.js b/packages/core/index.js index b6b8723bb..816dbe605 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -68,19 +68,14 @@ import { setupConsole, warn, } from './console' -import checkEvent from './event' +import on from './event' import onMouse from './events/mouse' import { resizeIframe, setSize } from './events/size' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' +import inPageLink from './page/in-page-link' import { getPagePosition } from './page/position' -import { - getElementPosition, - scrollBy, - scrollTo, - scrollToLink, - scrollToOffset, -} from './page/scroll' +import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' import decodeMessage from './receive/decode' @@ -98,62 +93,12 @@ import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' import firstRun from './setup/first-run' import defaults from './values/defaults' -import page from './values/page' import settings from './values/settings' function iframeListener(event) { const getMessageBody = (offset) => msg.slice(msg.indexOf(SEPARATOR) + MESSAGE_HEADER_LENGTH + offset) - function findTarget(location) { - function jumpToTarget() { - const jumpPosition = getElementPosition(target) - - info(iframeId, `Moving to in page link: %c#${hash}`, HIGHLIGHT) - - page.position = { - x: jumpPosition.x, - y: jumpPosition.y, - } - - scrollToLink(iframeId) - window.location.hash = hash - } - - function jumpToParent() { - // Check for V4 as well - const target = window.parentIframe || window.parentIFrame - - if (target) { - target.moveToAnchor(hash) - return - } - - log(iframeId, `In page link #${hash} not found`) - } - - const hash = location.split('#')[1] || '' - const hashData = decodeURIComponent(hash) - - let target = - document.getElementById(hashData) || - document.getElementsByName(hashData)[0] - - if (target) { - jumpToTarget() - return - } - - if (window.top === window.self) { - log(iframeId, `In page link #${hash} not found`) - return - } - - jumpToParent() - } - - const on = (funcName, val) => checkEvent(iframeId, funcName, val) - function routeMessage({ height, id, iframe, mode, msg, type, width }) { const { lastMessage } = settings[id] if (settings[id]?.firstRun) firstRun(id, mode) @@ -174,7 +119,7 @@ function iframeListener(event) { break case IN_PAGE_LINK: - findTarget(getMessageBody(9)) + inPageLink(id, getMessageBody(9)) break case INIT: @@ -182,7 +127,7 @@ function iframeListener(event) { checkSameDomain(id) checkVersion(id, msg) settings[id].initialised = true - on('onReady', iframe) + on(id, 'onReady', iframe) break case MESSAGE: @@ -275,7 +220,6 @@ function iframeListener(event) { const messageData = decodeMessage(msg) const { id, type } = messageData - const iframeId = id consoleEvent(id, type) @@ -304,7 +248,7 @@ function removeIframeListeners(iframe) { function closeIframe(iframe) { const { id } = iframe - if (checkEvent(id, 'onBeforeClose', id) === false) { + if (on(id, 'onBeforeClose', id) === false) { log(id, 'Close iframe cancelled by onBeforeClose') return } @@ -320,7 +264,7 @@ function closeIframe(iframe) { warn(id, error) } - checkEvent(id, 'onAfterClose', id) + on(id, 'onAfterClose', id) removeIframeListeners(iframe) } diff --git a/packages/core/page/in-page-link.js b/packages/core/page/in-page-link.js new file mode 100644 index 000000000..5625708d8 --- /dev/null +++ b/packages/core/page/in-page-link.js @@ -0,0 +1,51 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { info, log } from '../console' +import page from '../values/page' +import { getElementPosition, scrollToLink } from './scroll' + +export default function inPageLink(id, location) { + function jumpToTarget() { + const jumpPosition = getElementPosition(target) + + info(id, `Moving to in page link: %c#${hash}`, HIGHLIGHT) + + page.position = { + x: jumpPosition.x, + y: jumpPosition.y, + } + + scrollToLink(id) + window.location.hash = hash + } + + function jumpToParent() { + // Check for V4 as well + const target = window.parentIframe || window.parentIFrame + + if (target) { + target.moveToAnchor(hash) + return + } + + log(id, `In page link #${hash} not found`) + } + + const hash = location.split('#')[1] || '' + const hashData = decodeURIComponent(hash) + + let target = + document.getElementById(hashData) || document.getElementsByName(hashData)[0] + + if (target) { + jumpToTarget() + return + } + + if (window.top === window.self) { + log(id, `In page link #${hash} not found`) + return + } + + jumpToParent() +} diff --git a/packages/core/page/scroll.js b/packages/core/page/scroll.js index b7ac17f00..c4a4fa36f 100644 --- a/packages/core/page/scroll.js +++ b/packages/core/page/scroll.js @@ -21,6 +21,18 @@ export function getElementPosition(target) { } } +export function scrollToLink(id) { + const { x, y } = getStoredPagePosition() + const iframe = settings[id]?.iframe + + if (on(id, 'onScroll', { iframe, top: y, left: x, x, y }) === false) { + unsetPagePosition() + return + } + + setPagePosition(id) +} + export function scrollBy(messageData) { const { id, height, width } = messageData @@ -42,7 +54,7 @@ const scrollRequestFromChild = (addOffset) => (messageData) => { /* istanbul ignore next */ // Not testable in Karma function reposition(newPosition) { setStoredPagePosition(newPosition) - scrollTo(id) + scrollToLink(id) } function scrollParent(target, newPosition) { @@ -73,15 +85,3 @@ const scrollRequestFromChild = (addOffset) => (messageData) => { export const scrollTo = scrollRequestFromChild(false) export const scrollToOffset = scrollRequestFromChild(true) - -export function scrollToLink(id) { - const { x, y } = getStoredPagePosition() - const iframe = settings[id]?.iframe - - if (on(id, 'onScroll', { iframe, top: y, left: x, x, y }) === false) { - unsetPagePosition() - return - } - - setPagePosition(id) -} diff --git a/packages/core/receive/decode.js b/packages/core/receive/decode.js index beb6a8f3c..71f36034f 100644 --- a/packages/core/receive/decode.js +++ b/packages/core/receive/decode.js @@ -39,6 +39,7 @@ export default function decodeMessage(msg) { width: Number(data[2]), type: data[3], msg: data[4], + message: data[4], } // eslint-disable-next-line prefer-destructuring From ed7a602fc0e63f42809ea9b42c63555552ea5dad Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 11:21:11 +0100 Subject: [PATCH 26/42] Extract methods from core --- packages/core/index.js | 98 +++-------------------------- packages/core/methods/attach.js | 57 +++++++++++++++++ packages/core/methods/close.js | 28 +++++++++ packages/core/methods/disconnect.js | 9 +++ packages/core/methods/reset.js | 16 +++++ 5 files changed, 117 insertions(+), 91 deletions(-) create mode 100644 packages/core/methods/attach.js create mode 100644 packages/core/methods/close.js create mode 100644 packages/core/methods/disconnect.js create mode 100644 packages/core/methods/reset.js diff --git a/packages/core/index.js b/packages/core/index.js index 816dbe605..ef7181936 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -48,7 +48,7 @@ import { } from '../common/consts' import { addEventListener } from '../common/listeners' import setMode from '../common/mode' -import { hasOwn, once, typeAssert } from '../common/utils' +import { hasOwn, once } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' import { enableVInfo, preModeCheck } from './checks/mode' @@ -70,11 +70,13 @@ import { } from './console' import on from './event' import onMouse from './events/mouse' -import { resizeIframe, setSize } from './events/size' +import { resizeIframe } from './events/size' +import attachMethods from './methods/attach' +import closeIframe from './methods/close' +import resetIframe from './methods/reset' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import inPageLink from './page/in-page-link' -import { getPagePosition } from './page/position' import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' import { checkTitle, setTitle } from './page/title' import checkUniqueId from './page/unique' @@ -204,7 +206,7 @@ function iframeListener(event) { } } - let msg = event.data + const msg = event.data if (msg === CHILD_READY_MESSAGE) { iframeReady(event.source) @@ -238,47 +240,6 @@ function iframeListener(event) { } } -function removeIframeListeners(iframe) { - const { id } = iframe - log(id, 'Disconnected from iframe') - delete settings[id] - delete iframe.iframeResizer -} - -function closeIframe(iframe) { - const { id } = iframe - - if (on(id, 'onBeforeClose', id) === false) { - log(id, 'Close iframe cancelled by onBeforeClose') - return - } - - log(id, `Removing iFrame: %c${id}`, HIGHLIGHT) - - try { - // Catch race condition error with React - if (iframe.parentNode) { - iframe.remove() - } - } catch (error) { - warn(id, error) - } - - on(id, 'onAfterClose', id) - removeIframeListeners(iframe) -} - -function resetIframe(messageData) { - log( - messageData.id, - `Size reset requested by ${messageData.type === INIT ? 'parent page' : 'child page'}`, - ) - - getPagePosition(messageData.id) - setSize(messageData) - trigger(RESET, RESET, messageData.id) -} - export default (options) => (iframe) => { function setScrolling() { log( @@ -327,51 +288,6 @@ export default (options) => (iframe) => { resetIframe({ iframe, height: MIN_SIZE, width: MIN_SIZE, type: INIT }) } - function setupIframeObject() { - if (settings[iframeId]) { - const { iframe } = settings[iframeId] - const resizer = { - close: closeIframe.bind(null, iframe), - - disconnect: removeIframeListeners.bind(null, iframe), - - removeListeners() { - advise( - iframeId, - `Deprecated Method Name - -The removeListeners() method has been renamed to disconnect(). ${REMOVED_NEXT_VERSION} -`, - ) - this.disconnect() - }, - - resize() { - advise( - iframeId, - `Deprecated Method - -Use of the resize() method from the parent page is deprecated and will be removed in a future version of iframe-resizer. As their are no longer any edge cases that require triggering a resize from the parent page, it is recommended to remove this method from your code.`, - ) - trigger.bind(null, 'Window resize', RESIZE, iframeId) - }, - - moveToAnchor(anchor) { - typeAssert(anchor, STRING, 'moveToAnchor(anchor) anchor') - trigger('Move to anchor', `moveToAnchor:${anchor}`, iframeId) - }, - - sendMessage(message) { - message = JSON.stringify(message) - trigger(MESSAGE, `${MESSAGE}:${message}`, iframeId) - }, - } - - iframe.iframeResizer = resizer - iframe.iFrameResizer = resizer - } - } - function addLoadListener(iframe, initChild) { // allow other concurrent events to go first const onload = () => setTimeout(initChild, AFTER_EVENT_STACK) @@ -523,7 +439,7 @@ Use of the resize() method from the parent page is deprecated and will be setScrolling() setupBodyMarginValues() init(iframeId, createOutgoingMessage(iframeId)) - setupIframeObject() + attachMethods(iframeId) log(iframeId, 'Setup complete') endAutoGroup(iframeId) } diff --git a/packages/core/methods/attach.js b/packages/core/methods/attach.js new file mode 100644 index 000000000..dfced05e0 --- /dev/null +++ b/packages/core/methods/attach.js @@ -0,0 +1,57 @@ +import { + MESSAGE, + REMOVED_NEXT_VERSION, + RESIZE, + STRING, +} from '../../common/consts' +import { typeAssert } from '../../common/utils' +import { advise } from '../console' +import trigger from '../send/trigger' +import settings from '../values/settings' +import closeIframe from './close' +import disconnect from './disconnect' + +export default function attachMethods(id) { + if (settings[id]) { + const { iframe } = settings[id] + const resizer = { + close: closeIframe.bind(null, iframe), + + disconnect: disconnect.bind(null, iframe), + + removeListeners() { + advise( + id, + `Deprecated Method Name + +The removeListeners() method has been renamed to disconnect(). ${REMOVED_NEXT_VERSION} +`, + ) + this.disconnect() + }, + + resize() { + advise( + id, + `Deprecated Method + +Use of the resize() method from the parent page is deprecated and will be removed in a future version of iframe-resizer. As their are no longer any edge cases that require triggering a resize from the parent page, it is recommended to remove this method from your code.`, + ) + trigger.bind(null, 'Window resize', RESIZE, id) + }, + + moveToAnchor(anchor) { + typeAssert(anchor, STRING, 'moveToAnchor(anchor) anchor') + trigger('Move to anchor', `moveToAnchor:${anchor}`, id) + }, + + sendMessage(message) { + message = JSON.stringify(message) + trigger(MESSAGE, `${MESSAGE}:${message}`, id) + }, + } + + iframe.iframeResizer = resizer + iframe.iFrameResizer = resizer + } +} diff --git a/packages/core/methods/close.js b/packages/core/methods/close.js new file mode 100644 index 000000000..658a7dbe7 --- /dev/null +++ b/packages/core/methods/close.js @@ -0,0 +1,28 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { log, warn } from '../console' +import on from '../event' +import disconnect from './disconnect' + +export default function closeIframe(iframe) { + const { id } = iframe + + if (on(id, 'onBeforeClose', id) === false) { + log(id, 'Close iframe cancelled by onBeforeClose') + return + } + + log(id, `Removing iFrame: %c${id}`, HIGHLIGHT) + + try { + // Catch race condition error with React + if (iframe.parentNode) { + iframe.remove() + } + } catch (error) { + warn(id, error) + } + + on(id, 'onAfterClose', id) + disconnect(iframe) +} diff --git a/packages/core/methods/disconnect.js b/packages/core/methods/disconnect.js new file mode 100644 index 000000000..8537ed263 --- /dev/null +++ b/packages/core/methods/disconnect.js @@ -0,0 +1,9 @@ +import { log } from '../console' +import settings from '../values/settings' + +export default function disconnect(iframe) { + const { id } = iframe + log(id, 'Disconnected from iframe') + delete settings[id] + delete iframe.iframeResizer +} diff --git a/packages/core/methods/reset.js b/packages/core/methods/reset.js new file mode 100644 index 000000000..9b8275be6 --- /dev/null +++ b/packages/core/methods/reset.js @@ -0,0 +1,16 @@ +import { INIT, RESET } from '../../common/consts' +import { log } from '../console' +import { setSize } from '../events/size' +import { getPagePosition } from '../page/position' +import trigger from '../send/trigger' + +export default function resetIframe(messageData) { + log( + messageData.id, + `Size reset requested by ${messageData.type === INIT ? 'parent page' : 'child page'}`, + ) + + getPagePosition(messageData.id) + setSize(messageData) + trigger(RESET, RESET, messageData.id) +} From 9c8d07abb9ca853a0b25009c1a859603c1ea9ecb Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 12:24:10 +0100 Subject: [PATCH 27/42] Extract setup props --- packages/core/index.js | 61 +++++------------------------- packages/core/setup/body-margin.js | 12 ++++++ packages/core/setup/scrolling.js | 36 ++++++++++++++++++ 3 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 packages/core/setup/body-margin.js create mode 100644 packages/core/setup/scrolling.js diff --git a/packages/core/index.js b/packages/core/index.js index ef7181936..38204414e 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -2,14 +2,12 @@ import { HIGHLIGHT } from 'auto-console-group' import { AFTER_EVENT_STACK, - AUTO, AUTO_RESIZE, BEFORE_UNLOAD, BOTH, CHILD_READY_MESSAGE, CLOSE, EXPAND, - HIDDEN, HORIZONTAL, IN_PAGE_LINK, INIT, @@ -24,7 +22,6 @@ import { MOUSE_ENTER, MOUSE_LEAVE, NONE, - NUMBER, OBJECT, OFFSET, OFFSET_SIZE, @@ -93,7 +90,9 @@ import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' +import setupBodyMargin from './setup/body-margin' import firstRun from './setup/first-run' +import setScrolling from './setup/scrolling' import defaults from './values/defaults' import settings from './values/settings' @@ -241,48 +240,8 @@ function iframeListener(event) { } export default (options) => (iframe) => { - function setScrolling() { - log( - iframeId, - `Iframe scrolling ${ - settings[iframeId]?.scrolling ? 'enabled' : 'disabled' - } for ${iframeId}`, - ) - - iframe.style.overflow = - settings[iframeId]?.scrolling === false ? HIDDEN : AUTO - - switch (settings[iframeId]?.scrolling) { - case 'omit': - break - - case true: - iframe.scrolling = 'yes' - break - - case false: - iframe.scrolling = 'no' - break - - default: - iframe.scrolling = settings[iframeId] - ? settings[iframeId].scrolling - : 'no' - } - } - - function setupBodyMarginValues() { - const { bodyMargin } = settings[iframeId] - - if (typeof bodyMargin === NUMBER || bodyMargin === '0') { - settings[iframeId].bodyMargin = `${bodyMargin}px` - } - } - - function checkReset() { - if ( - !(settings[iframeId]?.heightCalculationMethod in RESET_REQUIRED_METHODS) - ) + function checkReset(id) { + if (!(settings[id]?.heightCalculationMethod in RESET_REQUIRED_METHODS)) return resetIframe({ iframe, height: MIN_SIZE, width: MIN_SIZE, type: INIT }) @@ -329,7 +288,7 @@ export default (options) => (iframe) => { trigger(eventType, message, id) if (!(isInit(eventType) && isLazy(iframe))) warnOnNoResponse(id, settings) - if (!firstRun) checkReset() + if (!firstRun) checkReset(id) } const { iframe } = settings[id] @@ -426,7 +385,7 @@ export default (options) => (iframe) => { } function setupIframe(options) { - if (beenHere()) { + if (LABEL in iframe) { warn(iframeId, `Ignored iframe (${iframeId}), already setup.`) return } @@ -435,9 +394,8 @@ export default (options) => (iframe) => { checkUniqueId(iframeId) log(iframeId, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) preModeCheck(iframeId) - setupEventListenersOnce() - setScrolling() - setupBodyMarginValues() + setScrolling(iframe) + setupBodyMargin(iframeId) init(iframeId, createOutgoingMessage(iframeId)) attachMethods(iframeId) log(iframeId, 'Setup complete') @@ -476,8 +434,6 @@ export default (options) => (iframe) => { options.log = enabled } - const beenHere = () => LABEL in iframe - const iframeId = ensureHasId(iframe, options) if (typeof options !== OBJECT) { @@ -486,6 +442,7 @@ export default (options) => (iframe) => { checkManualLogging(options) startLogging(iframeId, options) + setupEventListenersOnce() errorBoundary(iframeId, setupIframe)(options) return iframe?.iframeResizer diff --git a/packages/core/setup/body-margin.js b/packages/core/setup/body-margin.js new file mode 100644 index 000000000..704207bd8 --- /dev/null +++ b/packages/core/setup/body-margin.js @@ -0,0 +1,12 @@ +import { NUMBER } from '../../common/consts' +import settings from '../values/settings' + +const ZERO = '0' + +export default function setupBodyMargin(id) { + const { bodyMargin } = settings[id] + + if (typeof bodyMargin === NUMBER || bodyMargin === ZERO) { + settings[id].bodyMargin = `${bodyMargin}px` + } +} diff --git a/packages/core/setup/scrolling.js b/packages/core/setup/scrolling.js new file mode 100644 index 000000000..2afbe244a --- /dev/null +++ b/packages/core/setup/scrolling.js @@ -0,0 +1,36 @@ +import { AUTO, HIDDEN } from '../../common/consts' +import { log } from '../console' +import settings from '../values/settings' + +const YES = 'yes' +const NO = 'no' +const OMIT = 'omit' + +export default function setScrolling(iframe) { + const { id } = iframe + + log( + id, + `Iframe scrolling ${ + settings[id]?.scrolling ? 'enabled' : 'disabled' + } for ${id}`, + ) + + iframe.style.overflow = settings[id]?.scrolling === false ? HIDDEN : AUTO + + switch (settings[id]?.scrolling) { + case OMIT: + break + + case true: + iframe.scrolling = YES + break + + case false: + iframe.scrolling = NO + break + + default: + iframe.scrolling = settings[id] ? settings[id].scrolling : NO + } +} From c76b508858ad53325aa2fcb10cc84287ab29a0b6 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 12:24:21 +0100 Subject: [PATCH 28/42] Extract setup logging --- packages/core/index.js | 85 ++++++++++------------------------ packages/core/setup/logging.js | 35 ++++++++++++++ 2 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 packages/core/setup/logging.js diff --git a/packages/core/index.js b/packages/core/index.js index 38204414e..c608d5d26 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -7,7 +7,6 @@ import { BOTH, CHILD_READY_MESSAGE, CLOSE, - EXPAND, HORIZONTAL, IN_PAGE_LINK, INIT, @@ -15,7 +14,6 @@ import { LABEL, LAZY, LOAD, - LOG_OPTIONS, MESSAGE, MESSAGE_HEADER_LENGTH, MIN_SIZE, @@ -48,7 +46,7 @@ import setMode from '../common/mode' import { hasOwn, once } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' -import { enableVInfo, preModeCheck } from './checks/mode' +import { preModeCheck } from './checks/mode' import checkOptions from './checks/options' import checkSameDomain from './checks/origin' import checkVersion from './checks/version' @@ -57,12 +55,10 @@ import { advise, debug, endAutoGroup, - error, errorBoundary, event as consoleEvent, info, log, - setupConsole, warn, } from './console' import on from './event' @@ -92,6 +88,7 @@ import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' import setupBodyMargin from './setup/body-margin' import firstRun from './setup/first-run' +import startLogging from './setup/logging' import setScrolling from './setup/scrolling' import defaults from './values/defaults' import settings from './values/settings' @@ -360,78 +357,43 @@ export default (options) => (iframe) => { : '*' } - function processOptions(options) { - settings[iframeId] = { - ...settings[iframeId], + function processOptions(id, options) { + settings[id] = { + ...settings[id], iframe, remoteHost: iframe?.src.split('/').slice(0, 3).join('/'), ...defaults, - ...checkOptions(iframeId, options), + ...checkOptions(id, options), mouseEvents: hasMouseEvents(options), mode: setMode(options), - syncTitle: checkTitle(iframeId), + syncTitle: checkTitle(id), } updateOptionName(OFFSET, OFFSET_SIZE) updateOptionName('onClose', 'onBeforeClose') updateOptionName('onClosed', 'onAfterClose') - consoleEvent(iframeId, 'setup') + consoleEvent(id, 'setup') setDirection() - setOffsetSize(iframeId, options) - checkWarningTimeout(iframeId) + setOffsetSize(id, options) + checkWarningTimeout(id) getPostMessageTarget() setTargetOrigin() } - function setupIframe(options) { - if (LABEL in iframe) { - warn(iframeId, `Ignored iframe (${iframeId}), already setup.`) - return - } + function setupIframe(iframe, options) { + const { id } = iframe - processOptions(options) - checkUniqueId(iframeId) - log(iframeId, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) - preModeCheck(iframeId) + processOptions(id, options) + checkUniqueId(id) + log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) + preModeCheck(id) setScrolling(iframe) - setupBodyMargin(iframeId) - init(iframeId, createOutgoingMessage(iframeId)) - attachMethods(iframeId) - log(iframeId, 'Setup complete') - endAutoGroup(iframeId) - } - - function startLogging(iframeId, options) { - const isLogEnabled = hasOwn(options, 'log') - const isLogString = typeof options.log === STRING - const enabled = isLogEnabled - ? isLogString - ? true - : options.log - : defaults.log - - if (!hasOwn(options, 'logExpand')) { - options.logExpand = - isLogEnabled && isLogString - ? options.log === EXPAND - : defaults.logExpand - } - - enableVInfo(options) - setupConsole({ - enabled, - expand: options.logExpand, - iframeId, - }) - - if (isLogString && !(options.log in LOG_OPTIONS)) - error( - iframeId, - 'Invalid value for options.log: Accepted values are "expanded" and "collapsed"', - ) - - options.log = enabled + setupBodyMargin(id) + init(id, createOutgoingMessage(id)) + attachMethods(id) + log(id, 'Setup complete') + endAutoGroup(id) } const iframeId = ensureHasId(iframe, options) @@ -440,10 +402,13 @@ export default (options) => (iframe) => { throw new TypeError('Options is not an object') } + if (LABEL in iframe) + return warn(iframeId, `Ignored iframe (${iframeId}), already setup.`) + checkManualLogging(options) startLogging(iframeId, options) setupEventListenersOnce() - errorBoundary(iframeId, setupIframe)(options) + errorBoundary(iframeId, setupIframe)(iframe, options) return iframe?.iframeResizer } diff --git a/packages/core/setup/logging.js b/packages/core/setup/logging.js new file mode 100644 index 000000000..ea4dd3771 --- /dev/null +++ b/packages/core/setup/logging.js @@ -0,0 +1,35 @@ +import { COLLAPSE, EXPAND, LOG_OPTIONS } from '../../common/consts' +import { hasOwn, isString } from '../../common/utils' +import { enableVInfo } from '../checks/mode' +import { error, setupConsole } from '../console' +import defaults from '../values/defaults' + +export default function startLogging(id, options) { + const isLogEnabled = hasOwn(options, 'log') + const isLogString = isString(options.log) + const enabled = isLogEnabled + ? isLogString + ? true + : options.log + : defaults.log + + if (!hasOwn(options, 'logExpand')) { + options.logExpand = + isLogEnabled && isLogString ? options.log === EXPAND : defaults.logExpand + } + + enableVInfo(options) + setupConsole({ + enabled, + expand: options.logExpand, + iframeId: id, + }) + + if (isLogString && !(options.log in LOG_OPTIONS)) + error( + id, + `Invalid value for options.log: Accepted values are "${EXPAND}" and "${COLLAPSE}"`, + ) + + options.log = enabled +} From 33a72277cb66616085cd99eb32071109d3e26d1f Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 12:34:11 +0100 Subject: [PATCH 29/42] Extract init --- packages/core/index.js | 67 +------------------------------ packages/core/setup/init.js | 80 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 66 deletions(-) create mode 100644 packages/core/setup/init.js diff --git a/packages/core/index.js b/packages/core/index.js index c608d5d26..ca87501fd 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,7 +1,6 @@ import { HIGHLIGHT } from 'auto-console-group' import { - AFTER_EVENT_STACK, AUTO_RESIZE, BEFORE_UNLOAD, BOTH, @@ -10,20 +9,15 @@ import { HORIZONTAL, IN_PAGE_LINK, INIT, - INIT_FROM_IFRAME, LABEL, - LAZY, - LOAD, MESSAGE, MESSAGE_HEADER_LENGTH, - MIN_SIZE, MOUSE_ENTER, MOUSE_LEAVE, NONE, OBJECT, OFFSET, OFFSET_SIZE, - ONLOAD, PAGE_INFO, PAGE_INFO_STOP, PARENT, @@ -31,7 +25,6 @@ import { PARENT_INFO_STOP, REMOVED_NEXT_VERSION, RESET, - RESET_REQUIRED_METHODS, RESIZE, SCROLL_BY, SCROLL_TO, @@ -84,10 +77,10 @@ import { import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' -import warnOnNoResponse from './send/timeout' import trigger from './send/trigger' import setupBodyMargin from './setup/body-margin' import firstRun from './setup/first-run' +import init from './setup/init' import startLogging from './setup/logging' import setScrolling from './setup/scrolling' import defaults from './values/defaults' @@ -237,64 +230,6 @@ function iframeListener(event) { } export default (options) => (iframe) => { - function checkReset(id) { - if (!(settings[id]?.heightCalculationMethod in RESET_REQUIRED_METHODS)) - return - - resetIframe({ iframe, height: MIN_SIZE, width: MIN_SIZE, type: INIT }) - } - - function addLoadListener(iframe, initChild) { - // allow other concurrent events to go first - const onload = () => setTimeout(initChild, AFTER_EVENT_STACK) - addEventListener(iframe, LOAD, onload) - } - - const noContent = (iframe) => { - const { src, srcdoc } = iframe - return !srcdoc && (src == null || src === '' || src === 'about:blank') - } - - const isLazy = (iframe) => iframe.loading === LAZY - const isInit = (eventType) => eventType === INIT - - function sendInit(id, initChild) { - const { iframe, waitForLoad } = settings[id] - - if (waitForLoad === true) return - if (noContent(iframe)) { - setTimeout(() => { - consoleEvent(id, 'noContent') - info(id, 'No content detected in the iframe, delaying initialisation') - }) - return - } - - setTimeout(initChild) - } - - // We have to call trigger twice, as we can not be sure if all - // iframes have completed loading when this code runs. The - // event listener also catches the page changing in the iFrame. - function init(id, message) { - const createInitChild = (eventType) => () => { - if (!settings[id]) return // iframe removed before load event - - const { firstRun, iframe } = settings[id] - - trigger(eventType, message, id) - if (!(isInit(eventType) && isLazy(iframe))) warnOnNoResponse(id, settings) - - if (!firstRun) checkReset(id) - } - - const { iframe } = settings[id] - - settings[id].initChild = createInitChild(INIT_FROM_IFRAME) - addLoadListener(iframe, createInitChild(ONLOAD)) - sendInit(id, createInitChild(INIT)) - } - function setDirection() { const { direction } = settings[iframeId] diff --git a/packages/core/setup/init.js b/packages/core/setup/init.js new file mode 100644 index 000000000..7ef5cd85c --- /dev/null +++ b/packages/core/setup/init.js @@ -0,0 +1,80 @@ +import { + AFTER_EVENT_STACK, + INIT, + INIT_FROM_IFRAME, + LAZY, + LOAD, + MIN_SIZE, + ONLOAD, + RESET_REQUIRED_METHODS, +} from '../../common/consts' +import { addEventListener } from '../../common/listeners' +import { event as consoleEvent, info } from '../console' +import resetIframe from '../methods/reset' +import warnOnNoResponse from '../send/timeout' +import trigger from '../send/trigger' +import settings from '../values/settings' + +function checkReset(id) { + if (!(settings[id]?.heightCalculationMethod in RESET_REQUIRED_METHODS)) return + + const iframe = settings[id] + + resetIframe({ + iframe, + height: MIN_SIZE, + width: MIN_SIZE, + type: INIT, + }) +} + +function addLoadListener(iframe, initChild) { + // allow other concurrent events to go first + const onload = () => setTimeout(initChild, AFTER_EVENT_STACK) + addEventListener(iframe, LOAD, onload) +} + +const noContent = (iframe) => { + const { src, srcdoc } = iframe + return !srcdoc && (src == null || src === '' || src === 'about:blank') +} + +const isLazy = (iframe) => iframe.loading === LAZY +const isInit = (eventType) => eventType === INIT + +function sendInit(id, initChild) { + const { iframe, waitForLoad } = settings[id] + + if (waitForLoad === true) return + if (noContent(iframe)) { + setTimeout(() => { + consoleEvent(id, 'noContent') + info(id, 'No content detected in the iframe, delaying initialisation') + }) + return + } + + setTimeout(initChild) +} + +// We have to call trigger twice, as we can not be sure if all +// iframes have completed loading when this code runs. The +// event listener also catches the page changing in the iFrame. +export default function init(id, message) { + const createInitChild = (eventType) => () => { + if (!settings[id]) return // iframe removed before load event + + const { firstRun, iframe } = settings[id] + + trigger(eventType, message, id) + if (!(isInit(eventType) && isLazy(iframe))) warnOnNoResponse(id, settings) + + if (!firstRun) checkReset(id) + } + + const { iframe } = settings[id] + + settings[id].initChild = createInitChild(INIT_FROM_IFRAME) + addLoadListener(iframe, createInitChild(ONLOAD)) + sendInit(id, createInitChild(INIT)) +} From b8214f2f1de6d593714ac7775f02a61dc352fb1b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:03:49 +0100 Subject: [PATCH 30/42] Extract processOption() --- packages/core/index.js | 106 +-------------------- packages/core/setup/direction.js | 32 +++++++ packages/core/setup/process-options.js | 37 +++++++ packages/core/setup/target-origin.js | 20 ++++ packages/core/setup/update-option-names.js | 21 ++++ 5 files changed, 114 insertions(+), 102 deletions(-) create mode 100644 packages/core/setup/direction.js create mode 100644 packages/core/setup/process-options.js create mode 100644 packages/core/setup/target-origin.js create mode 100644 packages/core/setup/update-option-names.js diff --git a/packages/core/index.js b/packages/core/index.js index ca87501fd..dd8a4e48d 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -3,10 +3,8 @@ import { HIGHLIGHT } from 'auto-console-group' import { AUTO_RESIZE, BEFORE_UNLOAD, - BOTH, CHILD_READY_MESSAGE, CLOSE, - HORIZONTAL, IN_PAGE_LINK, INIT, LABEL, @@ -14,16 +12,12 @@ import { MESSAGE_HEADER_LENGTH, MOUSE_ENTER, MOUSE_LEAVE, - NONE, OBJECT, - OFFSET, - OFFSET_SIZE, PAGE_INFO, PAGE_INFO_STOP, PARENT, PARENT_INFO, PARENT_INFO_STOP, - REMOVED_NEXT_VERSION, RESET, RESIZE, SCROLL_BY, @@ -32,20 +26,15 @@ import { SEPARATOR, STRING, TITLE, - VERTICAL, } from '../common/consts' import { addEventListener } from '../common/listeners' -import setMode from '../common/mode' -import { hasOwn, once } from '../common/utils' +import { once } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' import { preModeCheck } from './checks/mode' -import checkOptions from './checks/options' import checkSameDomain from './checks/origin' import checkVersion from './checks/version' -import checkWarningTimeout from './checks/warning-timeout' import { - advise, debug, endAutoGroup, errorBoundary, @@ -64,7 +53,7 @@ import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import inPageLink from './page/in-page-link' import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' -import { checkTitle, setTitle } from './page/title' +import { setTitle } from './page/title' import checkUniqueId from './page/unique' import decodeMessage from './receive/decode' import { onMessage } from './receive/message' @@ -74,7 +63,6 @@ import { isMessageFromIframe, isMessageFromMetaParent, } from './receive/preflight' -import { setOffsetSize } from './send/offset' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import trigger from './send/trigger' @@ -82,8 +70,8 @@ import setupBodyMargin from './setup/body-margin' import firstRun from './setup/first-run' import init from './setup/init' import startLogging from './setup/logging' +import processOptions from './setup/process-options' import setScrolling from './setup/scrolling' -import defaults from './values/defaults' import settings from './values/settings' function iframeListener(event) { @@ -230,96 +218,10 @@ function iframeListener(event) { } export default (options) => (iframe) => { - function setDirection() { - const { direction } = settings[iframeId] - - switch (direction) { - case VERTICAL: - break - - case HORIZONTAL: - settings[iframeId].sizeHeight = false - // eslint-disable-next-line no-fallthrough - case BOTH: - settings[iframeId].sizeWidth = true - break - - case NONE: - settings[iframeId].sizeWidth = false - settings[iframeId].sizeHeight = false - settings[iframeId].autoResize = false - break - - default: - throw new TypeError( - iframeId, - `Direction value of "${direction}" is not valid`, - ) - } - - log(iframeId, `direction: %c${direction}`, HIGHLIGHT) - } - - const getTargetOrigin = (remoteHost) => - remoteHost === '' || - remoteHost.match(/^(about:blank|javascript:|file:\/\/)/) !== null - ? '*' - : remoteHost - - function getPostMessageTarget() { - if (settings[iframeId].postMessageTarget === null) - settings[iframeId].postMessageTarget = iframe.contentWindow - } - - function updateOptionName(oldName, newName) { - if (hasOwn(settings[iframeId], oldName)) { - advise( - iframeId, - `Deprecated option\n\nThe ${oldName} option has been renamed to ${newName}. ${REMOVED_NEXT_VERSION}`, - ) - settings[iframeId][newName] = settings[iframeId][oldName] - delete settings[iframeId][oldName] - } - } - - const hasMouseEvents = (options) => - hasOwn(options, 'onMouseEnter') || hasOwn(options, 'onMouseLeave') - - function setTargetOrigin() { - settings[iframeId].targetOrigin = - settings[iframeId].checkOrigin === true - ? getTargetOrigin(settings[iframeId].remoteHost) - : '*' - } - - function processOptions(id, options) { - settings[id] = { - ...settings[id], - iframe, - remoteHost: iframe?.src.split('/').slice(0, 3).join('/'), - ...defaults, - ...checkOptions(id, options), - mouseEvents: hasMouseEvents(options), - mode: setMode(options), - syncTitle: checkTitle(id), - } - - updateOptionName(OFFSET, OFFSET_SIZE) - updateOptionName('onClose', 'onBeforeClose') - updateOptionName('onClosed', 'onAfterClose') - - consoleEvent(id, 'setup') - setDirection() - setOffsetSize(id, options) - checkWarningTimeout(id) - getPostMessageTarget() - setTargetOrigin() - } - function setupIframe(iframe, options) { const { id } = iframe - processOptions(id, options) + processOptions(iframe, options) checkUniqueId(id) log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) preModeCheck(id) diff --git a/packages/core/setup/direction.js b/packages/core/setup/direction.js new file mode 100644 index 000000000..ef56ad9c4 --- /dev/null +++ b/packages/core/setup/direction.js @@ -0,0 +1,32 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { BOTH, HORIZONTAL, NONE, VERTICAL } from '../../common/consts' +import { log } from '../console' +import settings from '../values/settings' + +export default function setDirection(id) { + const { direction } = settings[id] + + switch (direction) { + case VERTICAL: + break + + case HORIZONTAL: + settings[id].sizeHeight = false + // eslint-disable-next-line no-fallthrough + case BOTH: + settings[id].sizeWidth = true + break + + case NONE: + settings[id].sizeWidth = false + settings[id].sizeHeight = false + settings[id].autoResize = false + break + + default: + throw new TypeError(id, `Direction value of "${direction}" is not valid`) + } + + log(id, `direction: %c${direction}`, HIGHLIGHT) +} diff --git a/packages/core/setup/process-options.js b/packages/core/setup/process-options.js new file mode 100644 index 000000000..c8f586f12 --- /dev/null +++ b/packages/core/setup/process-options.js @@ -0,0 +1,37 @@ +import setMode from '../../common/mode' +import { hasOwn } from '../../common/utils' +import checkOptions from '../checks/options' +import checkWarningTimeout from '../checks/warning-timeout' +import { event as consoleEvent } from '../console' +import { checkTitle } from '../page/title' +import { setOffsetSize } from '../send/offset' +import defaults from '../values/defaults' +import settings from '../values/settings' +import setDirection from './direction' +import { getPostMessageTarget, setTargetOrigin } from './target-origin' +import updateOptionNames from './update-option-names' + +const hasMouseEvents = (options) => + hasOwn(options, 'onMouseEnter') || hasOwn(options, 'onMouseLeave') + +export default function processOptions(iframe, options) { + const { id } = iframe + settings[id] = { + ...settings[id], + iframe, + remoteHost: iframe?.src.split('/').slice(0, 3).join('/'), + ...defaults, + ...checkOptions(id, options), + mouseEvents: hasMouseEvents(options), + mode: setMode(options), + syncTitle: checkTitle(id), + } + + consoleEvent(id, 'setup') + updateOptionNames(id) + setDirection(id) + setOffsetSize(id, options) + checkWarningTimeout(id) + getPostMessageTarget(iframe) + setTargetOrigin(id) +} diff --git a/packages/core/setup/target-origin.js b/packages/core/setup/target-origin.js new file mode 100644 index 000000000..148996e66 --- /dev/null +++ b/packages/core/setup/target-origin.js @@ -0,0 +1,20 @@ +import settings from '../values/settings' + +export const getTargetOrigin = (remoteHost) => + remoteHost === '' || + remoteHost.match(/^(about:blank|javascript:|file:\/\/)/) !== null + ? '*' + : remoteHost + +export function setTargetOrigin(id) { + settings[id].targetOrigin = + settings[id].checkOrigin === true + ? getTargetOrigin(settings[id].remoteHost) + : '*' +} + +export function getPostMessageTarget(iframe) { + const { id } = iframe + if (settings[id].postMessageTarget === null) + settings[id].postMessageTarget = iframe.contentWindow +} diff --git a/packages/core/setup/update-option-names.js b/packages/core/setup/update-option-names.js new file mode 100644 index 000000000..5d1ecd2c6 --- /dev/null +++ b/packages/core/setup/update-option-names.js @@ -0,0 +1,21 @@ +import { OFFSET, OFFSET_SIZE, REMOVED_NEXT_VERSION } from '../../common/consts' +import { hasOwn } from '../../common/utils' +import { advise } from '../console' +import settings from '../values/settings' + +function updateOptionName(id, oldName, newName) { + if (hasOwn(settings[id], oldName)) { + advise( + id, + `Deprecated option\n\nThe ${oldName} option has been renamed to ${newName}. ${REMOVED_NEXT_VERSION}`, + ) + settings[id][newName] = settings[id][oldName] + delete settings[id][oldName] + } +} + +export default function updateOptionNames(id) { + updateOptionName(id, OFFSET, OFFSET_SIZE) + updateOptionName(id, 'onClose', 'onBeforeClose') + updateOptionName(id, 'onClosed', 'onAfterClose') +} From 17a873d2ad6b0dde3fd43d8499ea0ebbaee6a452 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:26:05 +0100 Subject: [PATCH 31/42] Extract getMessageBody() --- packages/core/events/mouse.js | 4 +- packages/core/index.js | 76 +++++++++++++++----------------- packages/core/receive/message.js | 11 +++-- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/packages/core/events/mouse.js b/packages/core/events/mouse.js index 5d1ca8021..3cd556d1a 100644 --- a/packages/core/events/mouse.js +++ b/packages/core/events/mouse.js @@ -1,15 +1,13 @@ import { SEPARATOR } from '../../common/consts' import on from '../event' import { getMessageBody } from '../receive/message' -import settings from '../values/settings' export default function onMouse(event, messageData) { const { id, iframe, height, type, width } = messageData - const { lastMessage } = settings[id] let mousePos = {} if (width === 0 && height === 0) { - const coords = getMessageBody(lastMessage, 9).split(SEPARATOR) + const coords = getMessageBody(id, 9).split(SEPARATOR) mousePos = { x: coords[1], y: coords[0], diff --git a/packages/core/index.js b/packages/core/index.js index dd8a4e48d..b090ad341 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -56,7 +56,7 @@ import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' import { setTitle } from './page/title' import checkUniqueId from './page/unique' import decodeMessage from './receive/decode' -import { onMessage } from './receive/message' +import { getMessageBody, onMessage } from './receive/message' import { checkIframeExists, isMessageForUs, @@ -75,9 +75,6 @@ import setScrolling from './setup/scrolling' import settings from './values/settings' function iframeListener(event) { - const getMessageBody = (offset) => - msg.slice(msg.indexOf(SEPARATOR) + MESSAGE_HEADER_LENGTH + offset) - function routeMessage({ height, id, iframe, mode, msg, type, width }) { const { lastMessage } = settings[id] if (settings[id]?.firstRun) firstRun(id, mode) @@ -85,7 +82,7 @@ function iframeListener(event) { switch (type) { case AUTO_RESIZE: - settings[id].autoResize = JSON.parse(getMessageBody(9)) + settings[id].autoResize = JSON.parse(getMessageBody(id, 9)) break case BEFORE_UNLOAD: @@ -98,7 +95,7 @@ function iframeListener(event) { break case IN_PAGE_LINK: - inPageLink(id, getMessageBody(9)) + inPageLink(id, getMessageBody(id, 9)) break case INIT: @@ -110,7 +107,7 @@ function iframeListener(event) { break case MESSAGE: - onMessage(messageData, getMessageBody(6)) + onMessage(messageData, getMessageBody(id, 6)) break case MOUSE_ENTER: @@ -217,39 +214,6 @@ function iframeListener(event) { } } -export default (options) => (iframe) => { - function setupIframe(iframe, options) { - const { id } = iframe - - processOptions(iframe, options) - checkUniqueId(id) - log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) - preModeCheck(id) - setScrolling(iframe) - setupBodyMargin(id) - init(id, createOutgoingMessage(id)) - attachMethods(id) - log(id, 'Setup complete') - endAutoGroup(id) - } - - const iframeId = ensureHasId(iframe, options) - - if (typeof options !== OBJECT) { - throw new TypeError('Options is not an object') - } - - if (LABEL in iframe) - return warn(iframeId, `Ignored iframe (${iframeId}), already setup.`) - - checkManualLogging(options) - startLogging(iframeId, options) - setupEventListenersOnce() - errorBoundary(iframeId, setupIframe)(iframe, options) - - return iframe?.iframeResizer -} - const sendTriggerMsg = (eventName, event) => Object.values(settings) .filter(({ autoResize, firstRun }) => autoResize && !firstRun) @@ -266,3 +230,35 @@ const setupEventListenersOnce = once(() => { window.iframeParentListener = (data) => setTimeout(() => iframeListener({ data, sameOrigin: true })) }) + +function setupIframe(iframe, options) { + const { id } = iframe + + processOptions(iframe, options) + checkUniqueId(id) + log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) + preModeCheck(id) + setScrolling(iframe) + setupBodyMargin(id) + init(id, createOutgoingMessage(id)) + attachMethods(id) + log(id, 'Setup complete') + endAutoGroup(id) +} + +export default (options) => (iframe) => { + const id = ensureHasId(iframe, options) + + if (typeof options !== OBJECT) { + throw new TypeError('Options is not an object') + } + + if (LABEL in iframe) return warn(id, `Ignored iframe (${id}), already setup.`) + + checkManualLogging(options) + startLogging(id, options) + setupEventListenersOnce() + errorBoundary(id, setupIframe)(iframe, options) + + return iframe?.iframeResizer +} diff --git a/packages/core/receive/message.js b/packages/core/receive/message.js index 99a020686..4303683f2 100644 --- a/packages/core/receive/message.js +++ b/packages/core/receive/message.js @@ -1,11 +1,16 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' -import { MESSAGE_HEADER_LENGTH } from '../../common/consts' +import { MESSAGE_HEADER_LENGTH, SEPARATOR } from '../../common/consts' import { log } from '../console' import on from '../event' +import settings from '../values/settings' -export const getMessageBody = (message, offset) => - message.slice(message.indexOf(':') + MESSAGE_HEADER_LENGTH + offset) +export function getMessageBody(id, offset) { + const { lastMessage } = settings[id] + return lastMessage.slice( + lastMessage.indexOf(SEPARATOR) + MESSAGE_HEADER_LENGTH + offset, + ) +} // eslint-disable-next-line import/prefer-default-export export function onMessage(messageData, messageBody) { From d163e726df7b68c8c0c09a4162f51cca3503ba22 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:32:13 +0100 Subject: [PATCH 32/42] Extract message router from core --- packages/core/index.js | 139 +------------------------------------- packages/core/router.js | 144 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 138 deletions(-) create mode 100644 packages/core/router.js diff --git a/packages/core/index.js b/packages/core/index.js index b090ad341..c5ec01d61 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,73 +1,41 @@ import { HIGHLIGHT } from 'auto-console-group' import { - AUTO_RESIZE, - BEFORE_UNLOAD, CHILD_READY_MESSAGE, - CLOSE, - IN_PAGE_LINK, - INIT, LABEL, MESSAGE, - MESSAGE_HEADER_LENGTH, - MOUSE_ENTER, - MOUSE_LEAVE, OBJECT, - PAGE_INFO, - PAGE_INFO_STOP, PARENT, - PARENT_INFO, - PARENT_INFO_STOP, - RESET, RESIZE, - SCROLL_BY, - SCROLL_TO, - SCROLL_TO_OFFSET, - SEPARATOR, STRING, - TITLE, } from '../common/consts' import { addEventListener } from '../common/listeners' import { once } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' import { preModeCheck } from './checks/mode' -import checkSameDomain from './checks/origin' -import checkVersion from './checks/version' import { debug, endAutoGroup, errorBoundary, event as consoleEvent, - info, log, warn, } from './console' -import on from './event' -import onMouse from './events/mouse' -import { resizeIframe } from './events/size' import attachMethods from './methods/attach' -import closeIframe from './methods/close' -import resetIframe from './methods/reset' -import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' -import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' -import inPageLink from './page/in-page-link' -import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' -import { setTitle } from './page/title' import checkUniqueId from './page/unique' import decodeMessage from './receive/decode' -import { getMessageBody, onMessage } from './receive/message' import { checkIframeExists, isMessageForUs, isMessageFromIframe, isMessageFromMetaParent, } from './receive/preflight' +import routeMessage from './router' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' import trigger from './send/trigger' import setupBodyMargin from './setup/body-margin' -import firstRun from './setup/first-run' import init from './setup/init' import startLogging from './setup/logging' import processOptions from './setup/process-options' @@ -75,111 +43,6 @@ import setScrolling from './setup/scrolling' import settings from './values/settings' function iframeListener(event) { - function routeMessage({ height, id, iframe, mode, msg, type, width }) { - const { lastMessage } = settings[id] - if (settings[id]?.firstRun) firstRun(id, mode) - log(id, `Received: %c${lastMessage}`, HIGHLIGHT) - - switch (type) { - case AUTO_RESIZE: - settings[id].autoResize = JSON.parse(getMessageBody(id, 9)) - break - - case BEFORE_UNLOAD: - info(id, 'Ready state reset') - settings[id].initialised = false - break - - case CLOSE: - closeIframe(iframe) - break - - case IN_PAGE_LINK: - inPageLink(id, getMessageBody(id, 9)) - break - - case INIT: - resizeIframe(messageData) - checkSameDomain(id) - checkVersion(id, msg) - settings[id].initialised = true - on(id, 'onReady', iframe) - break - - case MESSAGE: - onMessage(messageData, getMessageBody(id, 6)) - break - - case MOUSE_ENTER: - onMouse('onMouseEnter', messageData) - break - - case MOUSE_LEAVE: - onMouse('onMouseLeave', messageData) - break - - case PAGE_INFO: - startPageInfoMonitor(id) - break - - case PARENT_INFO: - startParentInfoMonitor(id) - break - - case PAGE_INFO_STOP: - stopPageInfoMonitor(id) - break - - case PARENT_INFO_STOP: - stopParentInfoMonitor(id) - break - - case RESET: - resetIframe(messageData) - break - - case SCROLL_BY: - scrollBy(messageData) - break - - case SCROLL_TO: - scrollTo(messageData) - break - - case SCROLL_TO_OFFSET: - scrollToOffset(messageData) - break - - case TITLE: - setTitle(msg, id) - break - - default: - if (width === 0 && height === 0) { - warn( - id, - `Unsupported message received (${type}), this is likely due to the iframe containing a later ` + - `version of iframe-resizer than the parent page`, - ) - return - } - - if (width === 0 || height === 0) { - log(id, 'Ignoring message with 0 height or width') - return - } - - // Recheck document.hidden here, as only Firefox - // correctly supports this in the iframe - if (document.hidden) { - log(id, 'Page hidden - ignored resize request') - return - } - - resizeIframe(messageData) - } - } - const msg = event.data if (msg === CHILD_READY_MESSAGE) { diff --git a/packages/core/router.js b/packages/core/router.js new file mode 100644 index 000000000..c40db276a --- /dev/null +++ b/packages/core/router.js @@ -0,0 +1,144 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { + AUTO_RESIZE, + BEFORE_UNLOAD, + CLOSE, + IN_PAGE_LINK, + INIT, + MESSAGE, + MOUSE_ENTER, + MOUSE_LEAVE, + PAGE_INFO, + PAGE_INFO_STOP, + PARENT_INFO, + PARENT_INFO_STOP, + RESET, + SCROLL_BY, + SCROLL_TO, + SCROLL_TO_OFFSET, + TITLE, +} from '../common/consts' +import checkSameDomain from './checks/origin' +import checkVersion from './checks/version' +import { info, log, warn } from './console' +import on from './event' +import onMouse from './events/mouse' +import { resizeIframe } from './events/size' +import closeIframe from './methods/close' +import resetIframe from './methods/reset' +import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' +import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' +import inPageLink from './page/in-page-link' +import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' +import { setTitle } from './page/title' +import { getMessageBody, onMessage } from './receive/message' +import firstRun from './setup/first-run' +import settings from './values/settings' + +export default function routeMessage(messageData) { + const { height, id, iframe, mode, msg, type, width } = messageData + const { lastMessage } = settings[id] + + if (settings[id]?.firstRun) firstRun(id, mode) + log(id, `Received: %c${lastMessage}`, HIGHLIGHT) + + switch (type) { + case AUTO_RESIZE: + settings[id].autoResize = JSON.parse(getMessageBody(id, 9)) + break + + case BEFORE_UNLOAD: + info(id, 'Ready state reset') + settings[id].initialised = false + break + + case CLOSE: + closeIframe(iframe) + break + + case IN_PAGE_LINK: + inPageLink(id, getMessageBody(id, 9)) + break + + case INIT: + resizeIframe(messageData) + checkSameDomain(id) + checkVersion(id, msg) + settings[id].initialised = true + on(id, 'onReady', iframe) + break + + case MESSAGE: + onMessage(messageData, getMessageBody(id, 6)) + break + + case MOUSE_ENTER: + onMouse('onMouseEnter', messageData) + break + + case MOUSE_LEAVE: + onMouse('onMouseLeave', messageData) + break + + case PAGE_INFO: + startPageInfoMonitor(id) + break + + case PARENT_INFO: + startParentInfoMonitor(id) + break + + case PAGE_INFO_STOP: + stopPageInfoMonitor(id) + break + + case PARENT_INFO_STOP: + stopParentInfoMonitor(id) + break + + case RESET: + resetIframe(messageData) + break + + case SCROLL_BY: + scrollBy(messageData) + break + + case SCROLL_TO: + scrollTo(messageData) + break + + case SCROLL_TO_OFFSET: + scrollToOffset(messageData) + break + + case TITLE: + setTitle(msg, id) + break + + default: + if (width === 0 && height === 0) { + warn( + id, + `Unsupported message received (${type}), this is likely due to the iframe containing a later ` + + `version of iframe-resizer than the parent page`, + ) + return + } + + if (width === 0 || height === 0) { + log(id, 'Ignoring message with 0 height or width') + return + } + + // Recheck document.hidden here, as only Firefox + // correctly supports this in the iframe + if (document.hidden) { + log(id, 'Page hidden - ignored resize request') + return + } + + resizeIframe(messageData) + } +} From 8d03c4c80e5abfb989a6b9fce8cd7f03fd8ee153 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:35:01 +0100 Subject: [PATCH 33/42] receive -> received --- packages/core/events/mouse.js | 2 +- packages/core/index.js | 4 ++-- packages/core/{receive => received}/decode.js | 0 packages/core/{receive => received}/message.js | 0 packages/core/{receive => received}/preflight.js | 0 packages/core/router.js | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename packages/core/{receive => received}/decode.js (100%) rename packages/core/{receive => received}/message.js (100%) rename packages/core/{receive => received}/preflight.js (100%) diff --git a/packages/core/events/mouse.js b/packages/core/events/mouse.js index 3cd556d1a..4dc391323 100644 --- a/packages/core/events/mouse.js +++ b/packages/core/events/mouse.js @@ -1,6 +1,6 @@ import { SEPARATOR } from '../../common/consts' import on from '../event' -import { getMessageBody } from '../receive/message' +import { getMessageBody } from '../received/message' export default function onMouse(event, messageData) { const { id, iframe, height, type, width } = messageData diff --git a/packages/core/index.js b/packages/core/index.js index c5ec01d61..d629c2dec 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -24,13 +24,13 @@ import { } from './console' import attachMethods from './methods/attach' import checkUniqueId from './page/unique' -import decodeMessage from './receive/decode' +import decodeMessage from './received/decode' import { checkIframeExists, isMessageForUs, isMessageFromIframe, isMessageFromMetaParent, -} from './receive/preflight' +} from './received/preflight' import routeMessage from './router' import createOutgoingMessage from './send/outgoing' import iframeReady from './send/ready' diff --git a/packages/core/receive/decode.js b/packages/core/received/decode.js similarity index 100% rename from packages/core/receive/decode.js rename to packages/core/received/decode.js diff --git a/packages/core/receive/message.js b/packages/core/received/message.js similarity index 100% rename from packages/core/receive/message.js rename to packages/core/received/message.js diff --git a/packages/core/receive/preflight.js b/packages/core/received/preflight.js similarity index 100% rename from packages/core/receive/preflight.js rename to packages/core/received/preflight.js diff --git a/packages/core/router.js b/packages/core/router.js index c40db276a..a8125e5e1 100644 --- a/packages/core/router.js +++ b/packages/core/router.js @@ -32,7 +32,7 @@ import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import inPageLink from './page/in-page-link' import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' import { setTitle } from './page/title' -import { getMessageBody, onMessage } from './receive/message' +import { getMessageBody, onMessage } from './received/message' import firstRun from './setup/first-run' import settings from './values/settings' From 2917bae0f49e1fb0c0d53b62f327ca8d9a266698 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:38:06 +0100 Subject: [PATCH 34/42] move on --- packages/core/events/mouse.js | 2 +- packages/core/events/size.js | 2 +- packages/core/{event.js => events/wrapper.js} | 8 ++++---- packages/core/methods/close.js | 2 +- packages/core/page/scroll.js | 2 +- packages/core/received/message.js | 3 +-- packages/core/router.js | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) rename packages/core/{event.js => events/wrapper.js} (75%) diff --git a/packages/core/events/mouse.js b/packages/core/events/mouse.js index 4dc391323..365d2e88e 100644 --- a/packages/core/events/mouse.js +++ b/packages/core/events/mouse.js @@ -1,6 +1,6 @@ import { SEPARATOR } from '../../common/consts' -import on from '../event' import { getMessageBody } from '../received/message' +import on from './wrapper' export default function onMouse(event, messageData) { const { id, iframe, height, type, width } = messageData diff --git a/packages/core/events/size.js b/packages/core/events/size.js index 3823ee909..086bf7ff5 100644 --- a/packages/core/events/size.js +++ b/packages/core/events/size.js @@ -2,9 +2,9 @@ import { HIGHLIGHT } from 'auto-console-group' import { HEIGHT, WIDTH } from '../../common/consts' import { info } from '../console' -import checkEvent from '../event' import { setPagePosition } from '../page/position' import settings from '../values/settings' +import checkEvent from './wrapper' export function setSize(messageData) { function setDimension(dimension) { diff --git a/packages/core/event.js b/packages/core/events/wrapper.js similarity index 75% rename from packages/core/event.js rename to packages/core/events/wrapper.js index c524a47d1..2d477d4db 100644 --- a/packages/core/event.js +++ b/packages/core/events/wrapper.js @@ -1,7 +1,7 @@ -import { FUNCTION } from '../common/consts' -import { isolateUserCode } from '../common/utils' -import { warn } from './console' -import settings from './values/settings' +import { FUNCTION } from '../../common/consts' +import { isolateUserCode } from '../../common/utils' +import { warn } from '../console' +import settings from '../values/settings' function on(iframeId, funcName, val) { if (!settings[iframeId]) return null diff --git a/packages/core/methods/close.js b/packages/core/methods/close.js index 658a7dbe7..788c05b5d 100644 --- a/packages/core/methods/close.js +++ b/packages/core/methods/close.js @@ -1,7 +1,7 @@ import { HIGHLIGHT } from 'auto-console-group' import { log, warn } from '../console' -import on from '../event' +import on from '../events/wrapper' import disconnect from './disconnect' export default function closeIframe(iframe) { diff --git a/packages/core/page/scroll.js b/packages/core/page/scroll.js index c4a4fa36f..d293e02ab 100644 --- a/packages/core/page/scroll.js +++ b/packages/core/page/scroll.js @@ -1,7 +1,7 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' import { info } from '../console' -import on from '../event' +import on from '../events/wrapper' import settings from '../values/settings' import { getPagePosition, diff --git a/packages/core/received/message.js b/packages/core/received/message.js index 4303683f2..aabd2d018 100644 --- a/packages/core/received/message.js +++ b/packages/core/received/message.js @@ -2,7 +2,7 @@ import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' import { MESSAGE_HEADER_LENGTH, SEPARATOR } from '../../common/consts' import { log } from '../console' -import on from '../event' +import on from '../events/wrapper' import settings from '../values/settings' export function getMessageBody(id, offset) { @@ -12,7 +12,6 @@ export function getMessageBody(id, offset) { ) } -// eslint-disable-next-line import/prefer-default-export export function onMessage(messageData, messageBody) { const { id, iframe } = messageData diff --git a/packages/core/router.js b/packages/core/router.js index a8125e5e1..b022cf847 100644 --- a/packages/core/router.js +++ b/packages/core/router.js @@ -22,9 +22,9 @@ import { import checkSameDomain from './checks/origin' import checkVersion from './checks/version' import { info, log, warn } from './console' -import on from './event' import onMouse from './events/mouse' import { resizeIframe } from './events/size' +import on from './events/wrapper' import closeIframe from './methods/close' import resetIframe from './methods/reset' import { startPageInfoMonitor, stopPageInfoMonitor } from './monitor/page-info' From 3874353d815866a3470b8174589350b05b770127 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:43:26 +0100 Subject: [PATCH 35/42] Split message.js --- packages/core/events/message.js | 22 ++++++++++++++++++++++ packages/core/events/mouse.js | 2 +- packages/core/received/message.js | 24 +----------------------- packages/core/router.js | 3 ++- 4 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 packages/core/events/message.js diff --git a/packages/core/events/message.js b/packages/core/events/message.js new file mode 100644 index 000000000..c7e418e30 --- /dev/null +++ b/packages/core/events/message.js @@ -0,0 +1,22 @@ +import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' + +import { log } from '../console' +import on from './wrapper' + +export default function onMessage(messageData, messageBody) { + const { id, iframe } = messageData + + log( + id, + `onMessage passed: {iframe: %c${id}%c, message: %c${messageBody}%c}`, + HIGHLIGHT, + FOREGROUND, + HIGHLIGHT, + FOREGROUND, + ) + + on(id, 'onMessage', { + iframe, + message: JSON.parse(messageBody), + }) +} diff --git a/packages/core/events/mouse.js b/packages/core/events/mouse.js index 365d2e88e..4773af99b 100644 --- a/packages/core/events/mouse.js +++ b/packages/core/events/mouse.js @@ -1,5 +1,5 @@ import { SEPARATOR } from '../../common/consts' -import { getMessageBody } from '../received/message' +import getMessageBody from '../received/message' import on from './wrapper' export default function onMouse(event, messageData) { diff --git a/packages/core/received/message.js b/packages/core/received/message.js index aabd2d018..01c01eb4e 100644 --- a/packages/core/received/message.js +++ b/packages/core/received/message.js @@ -1,31 +1,9 @@ -import { FOREGROUND, HIGHLIGHT } from 'auto-console-group' - import { MESSAGE_HEADER_LENGTH, SEPARATOR } from '../../common/consts' -import { log } from '../console' -import on from '../events/wrapper' import settings from '../values/settings' -export function getMessageBody(id, offset) { +export default function getMessageBody(id, offset) { const { lastMessage } = settings[id] return lastMessage.slice( lastMessage.indexOf(SEPARATOR) + MESSAGE_HEADER_LENGTH + offset, ) } - -export function onMessage(messageData, messageBody) { - const { id, iframe } = messageData - - log( - id, - `onMessage passed: {iframe: %c${id}%c, message: %c${messageBody}%c}`, - HIGHLIGHT, - FOREGROUND, - HIGHLIGHT, - FOREGROUND, - ) - - on(id, 'onMessage', { - iframe, - message: JSON.parse(messageBody), - }) -} diff --git a/packages/core/router.js b/packages/core/router.js index b022cf847..6b934fad2 100644 --- a/packages/core/router.js +++ b/packages/core/router.js @@ -22,6 +22,7 @@ import { import checkSameDomain from './checks/origin' import checkVersion from './checks/version' import { info, log, warn } from './console' +import onMessage from './events/message' import onMouse from './events/mouse' import { resizeIframe } from './events/size' import on from './events/wrapper' @@ -32,7 +33,7 @@ import { startParentInfoMonitor, stopParentInfoMonitor } from './monitor/props' import inPageLink from './page/in-page-link' import { scrollBy, scrollTo, scrollToOffset } from './page/scroll' import { setTitle } from './page/title' -import { getMessageBody, onMessage } from './received/message' +import getMessageBody from './received/message' import firstRun from './setup/first-run' import settings from './values/settings' From aa6131228b87936193082987941b0a602e28b982 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 13:58:34 +0100 Subject: [PATCH 36/42] Extract iframe setup from core --- packages/core/index.js | 111 ++--------------------------------- packages/core/listeners.js | 73 +++++++++++++++++++++++ packages/core/setup/index.js | 26 ++++++++ 3 files changed, 103 insertions(+), 107 deletions(-) create mode 100644 packages/core/listeners.js create mode 100644 packages/core/setup/index.js diff --git a/packages/core/index.js b/packages/core/index.js index d629c2dec..387876878 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,113 +1,10 @@ -import { HIGHLIGHT } from 'auto-console-group' - -import { - CHILD_READY_MESSAGE, - LABEL, - MESSAGE, - OBJECT, - PARENT, - RESIZE, - STRING, -} from '../common/consts' -import { addEventListener } from '../common/listeners' -import { once } from '../common/utils' +import { LABEL, OBJECT } from '../common/consts' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' -import { preModeCheck } from './checks/mode' -import { - debug, - endAutoGroup, - errorBoundary, - event as consoleEvent, - log, - warn, -} from './console' -import attachMethods from './methods/attach' -import checkUniqueId from './page/unique' -import decodeMessage from './received/decode' -import { - checkIframeExists, - isMessageForUs, - isMessageFromIframe, - isMessageFromMetaParent, -} from './received/preflight' -import routeMessage from './router' -import createOutgoingMessage from './send/outgoing' -import iframeReady from './send/ready' -import trigger from './send/trigger' -import setupBodyMargin from './setup/body-margin' -import init from './setup/init' +import { errorBoundary, warn } from './console' +import setupEventListenersOnce from './listeners' +import setupIframe from './setup' import startLogging from './setup/logging' -import processOptions from './setup/process-options' -import setScrolling from './setup/scrolling' -import settings from './values/settings' - -function iframeListener(event) { - const msg = event.data - - if (msg === CHILD_READY_MESSAGE) { - iframeReady(event.source) - return - } - - if (!isMessageForUs(msg)) { - if (typeof msg !== STRING) return - consoleEvent(PARENT, 'ignoredMessage') - debug(PARENT, msg) - return - } - - const messageData = decodeMessage(msg) - const { id, type } = messageData - - consoleEvent(id, type) - - switch (true) { - case !settings[id]: - throw new Error(`${type} No settings for ${id}. Message was: ${msg}`) - - case !checkIframeExists(messageData): - case isMessageFromMetaParent(messageData): - case !isMessageFromIframe(messageData, event): - return - - default: - settings[id].lastMessage = event.data - errorBoundary(id, routeMessage)(messageData) - } -} - -const sendTriggerMsg = (eventName, event) => - Object.values(settings) - .filter(({ autoResize, firstRun }) => autoResize && !firstRun) - .forEach(({ iframe }) => trigger(eventName, event, iframe.id)) - -function tabVisible() { - if (document.hidden === true) return - sendTriggerMsg('tabVisible', RESIZE) -} - -const setupEventListenersOnce = once(() => { - addEventListener(window, MESSAGE, iframeListener) - addEventListener(document, 'visibilitychange', tabVisible) - window.iframeParentListener = (data) => - setTimeout(() => iframeListener({ data, sameOrigin: true })) -}) - -function setupIframe(iframe, options) { - const { id } = iframe - - processOptions(iframe, options) - checkUniqueId(id) - log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) - preModeCheck(id) - setScrolling(iframe) - setupBodyMargin(id) - init(id, createOutgoingMessage(id)) - attachMethods(id) - log(id, 'Setup complete') - endAutoGroup(id) -} export default (options) => (iframe) => { const id = ensureHasId(iframe, options) diff --git a/packages/core/listeners.js b/packages/core/listeners.js new file mode 100644 index 000000000..29d0451d6 --- /dev/null +++ b/packages/core/listeners.js @@ -0,0 +1,73 @@ +import { + CHILD_READY_MESSAGE, + MESSAGE, + PARENT, + RESIZE, + STRING, +} from '../common/consts' +import { addEventListener } from '../common/listeners' +import { once } from '../common/utils' +import { debug, errorBoundary, event as consoleEvent } from './console' +import decodeMessage from './received/decode' +import { + checkIframeExists, + isMessageForUs, + isMessageFromIframe, + isMessageFromMetaParent, +} from './received/preflight' +import routeMessage from './router' +import iframeReady from './send/ready' +import trigger from './send/trigger' +import settings from './values/settings' + +function iframeListener(event) { + const msg = event.data + + if (msg === CHILD_READY_MESSAGE) { + iframeReady(event.source) + return + } + + if (!isMessageForUs(msg)) { + if (typeof msg !== STRING) return + consoleEvent(PARENT, 'ignoredMessage') + debug(PARENT, msg) + return + } + + const messageData = decodeMessage(msg) + const { id, type } = messageData + + consoleEvent(id, type) + + switch (true) { + case !settings[id]: + throw new Error(`${type} No settings for ${id}. Message was: ${msg}`) + + case !checkIframeExists(messageData): + case isMessageFromMetaParent(messageData): + case !isMessageFromIframe(messageData, event): + return + + default: + settings[id].lastMessage = event.data + errorBoundary(id, routeMessage)(messageData) + } +} + +const sendTriggerMsg = (eventName, event) => + Object.values(settings) + .filter(({ autoResize, firstRun }) => autoResize && !firstRun) + .forEach(({ iframe }) => trigger(eventName, event, iframe.id)) + +function tabVisible() { + if (document.hidden === true) return + sendTriggerMsg('tabVisible', RESIZE) +} + +export default once(() => { + addEventListener(window, MESSAGE, iframeListener) + addEventListener(document, 'visibilitychange', tabVisible) + window.iframeParentListener = (data) => + setTimeout(() => iframeListener({ data, sameOrigin: true })) +}) diff --git a/packages/core/setup/index.js b/packages/core/setup/index.js new file mode 100644 index 000000000..badcca8ff --- /dev/null +++ b/packages/core/setup/index.js @@ -0,0 +1,26 @@ +import { HIGHLIGHT } from 'auto-console-group' + +import { preModeCheck } from '../checks/mode' +import { endAutoGroup, log } from '../console' +import attachMethods from '../methods/attach' +import checkUniqueId from '../page/unique' +import createOutgoingMessage from '../send/outgoing' +import setupBodyMargin from './body-margin' +import init from './init' +import processOptions from './process-options' +import setScrolling from './scrolling' + +export default function setupIframe(iframe, options) { + const { id } = iframe + + processOptions(iframe, options) + checkUniqueId(id) + log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) + preModeCheck(id) + setScrolling(iframe) + setupBodyMargin(id) + init(id, createOutgoingMessage(id)) + attachMethods(id) + log(id, 'Setup complete') + endAutoGroup(id) +} From e3ac7618a27b299d5cdff3d4c37f7dcfcdd7395b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 14:13:53 +0100 Subject: [PATCH 37/42] Extract tabVisible --- packages/core/events/visible.js | 13 +++++++++++++ packages/core/listeners.js | 20 ++------------------ 2 files changed, 15 insertions(+), 18 deletions(-) create mode 100644 packages/core/events/visible.js diff --git a/packages/core/events/visible.js b/packages/core/events/visible.js new file mode 100644 index 000000000..1ae5c66ad --- /dev/null +++ b/packages/core/events/visible.js @@ -0,0 +1,13 @@ +import { RESIZE } from '../../common/consts' +import trigger from '../send/trigger' +import settings from '../values/settings' + +const sendTriggerMsg = (eventName, event) => + Object.values(settings) + .filter(({ autoResize, firstRun }) => autoResize && !firstRun) + .forEach(({ iframe }) => trigger(eventName, event, iframe.id)) + +export default function tabVisible() { + if (document.hidden === true) return + sendTriggerMsg('tabVisible', RESIZE) +} diff --git a/packages/core/listeners.js b/packages/core/listeners.js index 29d0451d6..89ea77191 100644 --- a/packages/core/listeners.js +++ b/packages/core/listeners.js @@ -1,13 +1,8 @@ -import { - CHILD_READY_MESSAGE, - MESSAGE, - PARENT, - RESIZE, - STRING, -} from '../common/consts' +import { CHILD_READY_MESSAGE, MESSAGE, PARENT, STRING } from '../common/consts' import { addEventListener } from '../common/listeners' import { once } from '../common/utils' import { debug, errorBoundary, event as consoleEvent } from './console' +import tabVisible from './events/visible' import decodeMessage from './received/decode' import { checkIframeExists, @@ -17,7 +12,6 @@ import { } from './received/preflight' import routeMessage from './router' import iframeReady from './send/ready' -import trigger from './send/trigger' import settings from './values/settings' function iframeListener(event) { @@ -55,16 +49,6 @@ function iframeListener(event) { } } -const sendTriggerMsg = (eventName, event) => - Object.values(settings) - .filter(({ autoResize, firstRun }) => autoResize && !firstRun) - .forEach(({ iframe }) => trigger(eventName, event, iframe.id)) - -function tabVisible() { - if (document.hidden === true) return - sendTriggerMsg('tabVisible', RESIZE) -} - export default once(() => { addEventListener(window, MESSAGE, iframeListener) addEventListener(document, 'visibilitychange', tabVisible) From 1eab6bbccfb2195012416a4bbb02d29856de76bd Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 15:04:05 +0100 Subject: [PATCH 38/42] Update banner --- build/banner.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build/banner.js b/build/banner.js index 779db6249..a25292acc 100644 --- a/build/banner.js +++ b/build/banner.js @@ -6,19 +6,19 @@ const today = date.toISOString().split('T')[0] export default (file, type) => `/*! * @preserve - * + * * @module iframe-resizer/${file} ${pkg.version} (${type}) ${type === 'iife' ? '' : `- ${today}`} * - * @license ${pkg.license} for non-commercial use only. - * For commercial use, you must purchase a license from + * @license ${pkg.license} For use with GPL compliant sites (fully published front & backend source code) + * Alternatively for commercial use, you can purchase a license from * ${pkg.homepage}/pricing - * - * @description Keep same and cross domain iFrames sized to their content + * + * @description Keep same and cross domain iFrames sized to their content * * @author ${pkg.author.name} <${pkg.author.email}> - * + * * @see {@link ${pkg.homepage}} - * + * * @copyright (c) 2013 - ${year}, ${pkg.author.name}. All rights reserved. */ From feecf77fb9da82281066ced0bfde400d5a259cda Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 15:04:21 +0100 Subject: [PATCH 39/42] isObject() --- packages/common/utils.js | 5 ++--- packages/core/index.js | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/common/utils.js b/packages/common/utils.js index a6a167adb..66d4cb14e 100644 --- a/packages/common/utils.js +++ b/packages/common/utils.js @@ -1,9 +1,8 @@ -import { STRING } from './consts' +import { OBJECT, STRING } from './consts' export const isElement = (node) => node.nodeType === Node.ELEMENT_NODE - export const isNumber = (value) => !Number.isNaN(value) - +export const isObject = (value) => typeof value === OBJECT export const isString = (value) => typeof value === STRING export const isSafari = /^((?!chrome|android).)*safari/i.test( diff --git a/packages/core/index.js b/packages/core/index.js index 387876878..28fdd849b 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,4 +1,5 @@ -import { LABEL, OBJECT } from '../common/consts' +import { LABEL } from '../common/consts' +import { isObject } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' import { errorBoundary, warn } from './console' @@ -6,19 +7,22 @@ import setupEventListenersOnce from './listeners' import setupIframe from './setup' import startLogging from './setup/logging' -export default (options) => (iframe) => { - const id = ensureHasId(iframe, options) +export default function connectResizer(options) { + if (!isObject(options)) throw new TypeError('Options is not an object') - if (typeof options !== OBJECT) { - throw new TypeError('Options is not an object') - } + setupEventListenersOnce() + checkManualLogging(options) - if (LABEL in iframe) return warn(id, `Ignored iframe (${id}), already setup.`) + return (iframe) => { + const id = ensureHasId(iframe, options) - checkManualLogging(options) - startLogging(id, options) - setupEventListenersOnce() - errorBoundary(id, setupIframe)(iframe, options) + if (LABEL in iframe) { + warn(id, `Ignored iframe (${id}), already setup.`) + } else { + startLogging(id, options) + errorBoundary(id, setupIframe)(iframe, options) + } - return iframe?.iframeResizer + return iframe?.iframeResizer + } } From 83a20c4f2156a957288d110b2389ea19657e2258 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 15:10:08 +0100 Subject: [PATCH 40/42] tidy --- packages/core/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/index.js b/packages/core/index.js index 28fdd849b..9e5f6165b 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -5,7 +5,7 @@ import checkManualLogging from './checks/manual-logging' import { errorBoundary, warn } from './console' import setupEventListenersOnce from './listeners' import setupIframe from './setup' -import startLogging from './setup/logging' +import setupLogging from './setup/logging' export default function connectResizer(options) { if (!isObject(options)) throw new TypeError('Options is not an object') @@ -19,7 +19,7 @@ export default function connectResizer(options) { if (LABEL in iframe) { warn(id, `Ignored iframe (${id}), already setup.`) } else { - startLogging(id, options) + setupLogging(id, options) errorBoundary(id, setupIframe)(iframe, options) } From 713dffeadd9f9551e7c1026d66881c04a138b946 Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 15:28:57 +0100 Subject: [PATCH 41/42] Tidy --- packages/core/{page => checks}/unique.js | 0 packages/core/index.js | 3 ++- packages/core/setup/index.js | 15 +++++++++------ packages/core/setup/process-options.js | 2 -- 4 files changed, 11 insertions(+), 9 deletions(-) rename packages/core/{page => checks}/unique.js (100%) diff --git a/packages/core/page/unique.js b/packages/core/checks/unique.js similarity index 100% rename from packages/core/page/unique.js rename to packages/core/checks/unique.js diff --git a/packages/core/index.js b/packages/core/index.js index 9e5f6165b..65d8ba7f1 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -2,7 +2,7 @@ import { LABEL } from '../common/consts' import { isObject } from '../common/utils' import ensureHasId from './checks/id' import checkManualLogging from './checks/manual-logging' -import { errorBoundary, warn } from './console' +import { errorBoundary, event as consoleEvent, warn } from './console' import setupEventListenersOnce from './listeners' import setupIframe from './setup' import setupLogging from './setup/logging' @@ -17,6 +17,7 @@ export default function connectResizer(options) { const id = ensureHasId(iframe, options) if (LABEL in iframe) { + consoleEvent('alreadySetup') warn(id, `Ignored iframe (${id}), already setup.`) } else { setupLogging(id, options) diff --git a/packages/core/setup/index.js b/packages/core/setup/index.js index badcca8ff..7804446bd 100644 --- a/packages/core/setup/index.js +++ b/packages/core/setup/index.js @@ -1,20 +1,17 @@ import { HIGHLIGHT } from 'auto-console-group' import { preModeCheck } from '../checks/mode' -import { endAutoGroup, log } from '../console' +import checkUniqueId from '../checks/unique' +import { endAutoGroup, event as consoleEvent, log } from '../console' import attachMethods from '../methods/attach' -import checkUniqueId from '../page/unique' import createOutgoingMessage from '../send/outgoing' import setupBodyMargin from './body-margin' import init from './init' import processOptions from './process-options' import setScrolling from './scrolling' -export default function setupIframe(iframe, options) { - const { id } = iframe - +function setup(id, iframe, options) { processOptions(iframe, options) - checkUniqueId(id) log(id, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) preModeCheck(id) setScrolling(iframe) @@ -22,5 +19,11 @@ export default function setupIframe(iframe, options) { init(id, createOutgoingMessage(id)) attachMethods(id) log(id, 'Setup complete') +} + +export default function (iframe, options) { + const { id } = iframe + consoleEvent(id, 'setup') + if (checkUniqueId(id)) setup(id, iframe, options) endAutoGroup(id) } diff --git a/packages/core/setup/process-options.js b/packages/core/setup/process-options.js index c8f586f12..ebcf8c0e9 100644 --- a/packages/core/setup/process-options.js +++ b/packages/core/setup/process-options.js @@ -2,7 +2,6 @@ import setMode from '../../common/mode' import { hasOwn } from '../../common/utils' import checkOptions from '../checks/options' import checkWarningTimeout from '../checks/warning-timeout' -import { event as consoleEvent } from '../console' import { checkTitle } from '../page/title' import { setOffsetSize } from '../send/offset' import defaults from '../values/defaults' @@ -27,7 +26,6 @@ export default function processOptions(iframe, options) { syncTitle: checkTitle(id), } - consoleEvent(id, 'setup') updateOptionNames(id) setDirection(id) setOffsetSize(id, options) From 3d679cdde1e89e324a4299a648ff492d5b3b344b Mon Sep 17 00:00:00 2001 From: "David J. Bradshaw" Date: Thu, 23 Oct 2025 18:25:59 +0100 Subject: [PATCH 42/42] Code Review --- packages/common/consts.js | 2 -- packages/core/checks/id.js | 9 ++---- packages/core/events/resize.js | 10 +++++++ packages/core/events/size.js | 26 ++++++---------- packages/core/methods/attach.js | 33 ++++++++++----------- packages/core/methods/reset.js | 12 ++++---- packages/core/page/in-page-link.js | 41 ++++++++++++-------------- packages/core/page/position.js | 11 ++++--- packages/core/page/scroll.js | 6 ++-- packages/core/router.js | 8 ++--- packages/core/send/offset.js | 3 +- packages/core/send/trigger.js | 1 + packages/core/setup/init.js | 8 ++--- packages/core/setup/process-options.js | 2 +- packages/core/setup/scrolling.js | 2 +- packages/core/values/defaults.js | 1 + 16 files changed, 83 insertions(+), 92 deletions(-) create mode 100644 packages/core/events/resize.js diff --git a/packages/common/consts.js b/packages/common/consts.js index dded1e5af..c3c73a498 100644 --- a/packages/common/consts.js +++ b/packages/common/consts.js @@ -3,8 +3,6 @@ export const LABEL = 'iframeResizer' export const SEPARATOR = ':' export const CHILD_READY_MESSAGE = '[iFrameResizerChild]Ready' -export const AFTER_EVENT_STACK = 1 - export const AUTO_RESIZE = 'autoResize' export const BEFORE_UNLOAD = 'beforeUnload' export const CLOSE = 'close' diff --git a/packages/core/checks/id.js b/packages/core/checks/id.js index f43d933c0..407ada14c 100644 --- a/packages/core/checks/id.js +++ b/packages/core/checks/id.js @@ -5,13 +5,8 @@ import defaults from '../values/defaults' let count = 0 function newId(options) { - let id = options?.id || defaults.id + count++ - - if (document.getElementById(id) !== null) { - id += count++ - } - - return id + const id = options?.id || defaults.id + count++ + return document.getElementById(id) === null ? id : `${id}${count++}` } export default function ensureHasId(iframe, options) { diff --git a/packages/core/events/resize.js b/packages/core/events/resize.js new file mode 100644 index 000000000..f8e2cc878 --- /dev/null +++ b/packages/core/events/resize.js @@ -0,0 +1,10 @@ +import { setPagePosition } from '../page/position' +import setSize from './size' +import on from './wrapper' + +export default function resizeIframe(messageData) { + const { id } = messageData + setSize(messageData) + setPagePosition(id) + on(id, 'onResized', messageData) +} diff --git a/packages/core/events/size.js b/packages/core/events/size.js index 086bf7ff5..b7f7a7664 100644 --- a/packages/core/events/size.js +++ b/packages/core/events/size.js @@ -2,27 +2,19 @@ import { HIGHLIGHT } from 'auto-console-group' import { HEIGHT, WIDTH } from '../../common/consts' import { info } from '../console' -import { setPagePosition } from '../page/position' import settings from '../values/settings' -import checkEvent from './wrapper' - -export function setSize(messageData) { - function setDimension(dimension) { - const size = `${messageData[dimension]}px` - messageData.iframe.style[dimension] = size - info(id, `Set ${dimension}: %c${size}`, HIGHLIGHT) - } +function setDimension(dimension, messageData) { const { id } = messageData - const { sizeHeight, sizeWidth } = settings[id] - - if (sizeHeight) setDimension(HEIGHT) - if (sizeWidth) setDimension(WIDTH) + const size = `${messageData[dimension]}px` + messageData.iframe.style[dimension] = size + info(id, `Set ${dimension}: %c${size}`, HIGHLIGHT) } -export function resizeIframe(messageData) { +export default function setSize(messageData) { const { id } = messageData - setSize(messageData) - setPagePosition(id) - checkEvent(id, 'onResized', messageData) + const { sizeHeight, sizeWidth } = settings[id] + + if (sizeHeight) setDimension(HEIGHT, messageData) + if (sizeWidth) setDimension(WIDTH, messageData) } diff --git a/packages/core/methods/attach.js b/packages/core/methods/attach.js index dfced05e0..65d66a1ee 100644 --- a/packages/core/methods/attach.js +++ b/packages/core/methods/attach.js @@ -11,6 +11,14 @@ import settings from '../values/settings' import closeIframe from './close' import disconnect from './disconnect' +const DEPRECATED_REMOVE_LISTENERS = `Deprecated Method Name + +The removeListeners() method has been renamed to disconnect(). ${REMOVED_NEXT_VERSION}` + +const DEPRECATED_RESIZE = `Deprecated Method + +Use of the resize() method from the parent page is deprecated and will be removed in a future version of iframe-resizer. As their are no longer any edge cases that require triggering a resize from the parent page, it is recommended to remove this method from your code.` + export default function attachMethods(id) { if (settings[id]) { const { iframe } = settings[id] @@ -19,32 +27,21 @@ export default function attachMethods(id) { disconnect: disconnect.bind(null, iframe), - removeListeners() { - advise( - id, - `Deprecated Method Name + moveToAnchor(anchor) { + typeAssert(anchor, STRING, 'moveToAnchor(anchor) anchor') + trigger('Move to anchor', `moveToAnchor:${anchor}`, id) + }, -The removeListeners() method has been renamed to disconnect(). ${REMOVED_NEXT_VERSION} -`, - ) + removeListeners() { + advise(id, DEPRECATED_REMOVE_LISTENERS) this.disconnect() }, resize() { - advise( - id, - `Deprecated Method - -Use of the resize() method from the parent page is deprecated and will be removed in a future version of iframe-resizer. As their are no longer any edge cases that require triggering a resize from the parent page, it is recommended to remove this method from your code.`, - ) + advise(id, DEPRECATED_RESIZE) trigger.bind(null, 'Window resize', RESIZE, id) }, - moveToAnchor(anchor) { - typeAssert(anchor, STRING, 'moveToAnchor(anchor) anchor') - trigger('Move to anchor', `moveToAnchor:${anchor}`, id) - }, - sendMessage(message) { message = JSON.stringify(message) trigger(MESSAGE, `${MESSAGE}:${message}`, id) diff --git a/packages/core/methods/reset.js b/packages/core/methods/reset.js index 9b8275be6..112c70d2d 100644 --- a/packages/core/methods/reset.js +++ b/packages/core/methods/reset.js @@ -1,16 +1,18 @@ import { INIT, RESET } from '../../common/consts' import { log } from '../console' -import { setSize } from '../events/size' +import setSize from '../events/size' import { getPagePosition } from '../page/position' import trigger from '../send/trigger' export default function resetIframe(messageData) { + const { id, type } = messageData + log( - messageData.id, - `Size reset requested by ${messageData.type === INIT ? 'parent page' : 'child page'}`, + id, + `Size reset requested by ${type === INIT ? 'parent page' : 'child page'}`, ) - getPagePosition(messageData.id) + getPagePosition(id) setSize(messageData) - trigger(RESET, RESET, messageData.id) + trigger(RESET, RESET, id) } diff --git a/packages/core/page/in-page-link.js b/packages/core/page/in-page-link.js index 5625708d8..c87734fa9 100644 --- a/packages/core/page/in-page-link.js +++ b/packages/core/page/in-page-link.js @@ -4,41 +4,38 @@ import { info, log } from '../console' import page from '../values/page' import { getElementPosition, scrollToLink } from './scroll' -export default function inPageLink(id, location) { - function jumpToTarget() { - const jumpPosition = getElementPosition(target) - - info(id, `Moving to in page link: %c#${hash}`, HIGHLIGHT) +function jumpToTarget(id, hash, target) { + const { x, y } = getElementPosition(target) - page.position = { - x: jumpPosition.x, - y: jumpPosition.y, - } + info(id, `Moving to in page link: %c#${hash}`, HIGHLIGHT) - scrollToLink(id) - window.location.hash = hash - } + page.position = { x, y } - function jumpToParent() { - // Check for V4 as well - const target = window.parentIframe || window.parentIFrame + scrollToLink(id) + window.location.hash = hash +} - if (target) { - target.moveToAnchor(hash) - return - } +function jumpToParent(id, hash) { + // Check for V4 as well + const target = window.parentIframe || window.parentIFrame + if (!target) { log(id, `In page link #${hash} not found`) + return } + target.moveToAnchor(hash) +} + +export default function inPageLink(id, location) { const hash = location.split('#')[1] || '' const hashData = decodeURIComponent(hash) - let target = + const target = document.getElementById(hashData) || document.getElementsByName(hashData)[0] if (target) { - jumpToTarget() + jumpToTarget(id, hash, target) return } @@ -47,5 +44,5 @@ export default function inPageLink(id, location) { return } - jumpToParent() + jumpToParent(id, hash) } diff --git a/packages/core/page/position.js b/packages/core/page/position.js index 79990d090..30a909881 100644 --- a/packages/core/page/position.js +++ b/packages/core/page/position.js @@ -14,12 +14,11 @@ export function setStoredPagePosition(position) { } export function getPagePosition(id) { - if (page.position !== null) return page.position - - page.position = { - x: window.scrollX, - y: window.scrollY, - } + if (page.position === null) + page.position = { + x: window.scrollX, + y: window.scrollY, + } log( id, diff --git a/packages/core/page/scroll.js b/packages/core/page/scroll.js index d293e02ab..da6e71663 100644 --- a/packages/core/page/scroll.js +++ b/packages/core/page/scroll.js @@ -12,12 +12,12 @@ import { } from './position' export function getElementPosition(target) { - const iFramePosition = target.getBoundingClientRect() + const iframePosition = target.getBoundingClientRect() const pagePosition = getPagePosition(target.id) return { - x: Number(iFramePosition.left) + Number(pagePosition.x), - y: Number(iFramePosition.top) + Number(pagePosition.y), + x: Number(iframePosition.left) + Number(pagePosition.x), + y: Number(iframePosition.top) + Number(pagePosition.y), } } diff --git a/packages/core/router.js b/packages/core/router.js index 6b934fad2..cb07795b6 100644 --- a/packages/core/router.js +++ b/packages/core/router.js @@ -24,7 +24,7 @@ import checkVersion from './checks/version' import { info, log, warn } from './console' import onMessage from './events/message' import onMouse from './events/mouse' -import { resizeIframe } from './events/size' +import resizeIframe from './events/resize' import on from './events/wrapper' import closeIframe from './methods/close' import resetIframe from './methods/reset' @@ -38,7 +38,7 @@ import firstRun from './setup/first-run' import settings from './values/settings' export default function routeMessage(messageData) { - const { height, id, iframe, mode, msg, type, width } = messageData + const { height, id, iframe, mode, message, type, width } = messageData const { lastMessage } = settings[id] if (settings[id]?.firstRun) firstRun(id, mode) @@ -65,7 +65,7 @@ export default function routeMessage(messageData) { case INIT: resizeIframe(messageData) checkSameDomain(id) - checkVersion(id, msg) + checkVersion(id, message) settings[id].initialised = true on(id, 'onReady', iframe) break @@ -115,7 +115,7 @@ export default function routeMessage(messageData) { break case TITLE: - setTitle(msg, id) + setTitle(message, id) break default: diff --git a/packages/core/send/offset.js b/packages/core/send/offset.js index 741048ade..fb950ba11 100644 --- a/packages/core/send/offset.js +++ b/packages/core/send/offset.js @@ -4,8 +4,7 @@ import { VERTICAL } from '../../common/consts' import { log } from '../console' import settings from '../values/settings' -// eslint-disable-next-line import/prefer-default-export -export function setOffsetSize(id, { offset, offsetSize }) { +export default function setOffsetSize(id, { offset, offsetSize }) { const newOffset = offsetSize || offset if (!newOffset) return // No offset set or offset is zero diff --git a/packages/core/send/trigger.js b/packages/core/send/trigger.js index 24662f429..0a5ae8a82 100644 --- a/packages/core/send/trigger.js +++ b/packages/core/send/trigger.js @@ -16,6 +16,7 @@ function dispatch(calleeMsg, msg, id) { info(id, route, HIGHLIGHT, FOREGROUND, HIGHLIGHT) info(id, `Message data: %c${displayMsg}`, HIGHLIGHT) } + const { iframe, postMessageTarget, sameOrigin, targetOrigin } = settings[id] if (sameOrigin) { diff --git a/packages/core/setup/init.js b/packages/core/setup/init.js index 7ef5cd85c..c691e3043 100644 --- a/packages/core/setup/init.js +++ b/packages/core/setup/init.js @@ -1,5 +1,4 @@ import { - AFTER_EVENT_STACK, INIT, INIT_FROM_IFRAME, LAZY, @@ -15,6 +14,10 @@ import warnOnNoResponse from '../send/timeout' import trigger from '../send/trigger' import settings from '../values/settings' +const AFTER_EVENT_STACK = 1 +const isLazy = (iframe) => iframe.loading === LAZY +const isInit = (eventType) => eventType === INIT + function checkReset(id) { if (!(settings[id]?.heightCalculationMethod in RESET_REQUIRED_METHODS)) return @@ -39,9 +42,6 @@ const noContent = (iframe) => { return !srcdoc && (src == null || src === '' || src === 'about:blank') } -const isLazy = (iframe) => iframe.loading === LAZY -const isInit = (eventType) => eventType === INIT - function sendInit(id, initChild) { const { iframe, waitForLoad } = settings[id] diff --git a/packages/core/setup/process-options.js b/packages/core/setup/process-options.js index ebcf8c0e9..d204e3761 100644 --- a/packages/core/setup/process-options.js +++ b/packages/core/setup/process-options.js @@ -3,7 +3,7 @@ import { hasOwn } from '../../common/utils' import checkOptions from '../checks/options' import checkWarningTimeout from '../checks/warning-timeout' import { checkTitle } from '../page/title' -import { setOffsetSize } from '../send/offset' +import setOffsetSize from '../send/offset' import defaults from '../values/defaults' import settings from '../values/settings' import setDirection from './direction' diff --git a/packages/core/setup/scrolling.js b/packages/core/setup/scrolling.js index 2afbe244a..c6aa033ba 100644 --- a/packages/core/setup/scrolling.js +++ b/packages/core/setup/scrolling.js @@ -31,6 +31,6 @@ export default function setScrolling(iframe) { break default: - iframe.scrolling = settings[id] ? settings[id].scrolling : NO + iframe.scrolling = settings[id]?.scrolling || NO } } diff --git a/packages/core/values/defaults.js b/packages/core/values/defaults.js index 5460fe679..fbfbb0d3d 100644 --- a/packages/core/values/defaults.js +++ b/packages/core/values/defaults.js @@ -36,6 +36,7 @@ export default Object.freeze({ waitForLoad: false, warningTimeout: 5000, widthCalculationMethod: AUTO, + onBeforeClose: () => true, onAfterClose() {}, onInit: false,