diff --git a/packages/child/console.js b/packages/child/console.js index b8417aef8..0772cff0c 100644 --- a/packages/child/console.js +++ b/packages/child/console.js @@ -2,7 +2,7 @@ import acg, { NORMAL } from 'auto-console-group' import { BOLD, LABEL } from '../common/consts' import deprecate from '../common/deprecate' -import formatAdvise from '../common/format-advise' +import createFormatAdvise from '../common/format-advise' import { esModuleInterop, id as identity } from '../common/utils' let enabled = true @@ -51,7 +51,8 @@ export const { warn, } = childConsole -export const advise = (msg) => childConsole.warn(formatAdvise(identity)(msg)) +const formatAdvise = createFormatAdvise(identity) +export const advise = (...args) => childConsole.warn(...args.map(formatAdvise)) const deprecateAdvise = deprecate((id, msg) => advise(msg)) export const deprecateMethod = deprecateAdvise('Method') diff --git a/packages/child/index.js b/packages/child/index.js index d7bc15c17..de17c664f 100644 --- a/packages/child/index.js +++ b/packages/child/index.js @@ -132,7 +132,6 @@ function iframeResizerChild() { const eventCancelTimer = 128 const eventHandlersByName = {} const heightCalcModeDefault = AUTO - // const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) const tearDown = [] const widthCalcModeDefault = SCROLL @@ -572,7 +571,7 @@ See https://iframe-resizer.com/api/child for more details.`, if (found) { advise( `Deprecated Attributes - + The data-iframe-height and data-iframe-width attributes have been deprecated and replaced with the single data-iframe-size attribute. Use of the old attributes will be removed in a future version of iframe-resizer.`, ) } @@ -591,7 +590,7 @@ The data-iframe-height and data-iframe-width attributes have been de const actionMsg = version ? 'remove this option.' : `set this option to 'auto' when using an older version of iframe-resizer on the parent page. This can be done on the child page by adding the following code: - + window.iframeResizer = { license: 'xxxx', ${label}CalculationMethod: AUTO, diff --git a/packages/child/observers/utils.js b/packages/child/observers/utils.js index 78cc5d1aa..575f70048 100644 --- a/packages/child/observers/utils.js +++ b/packages/child/observers/utils.js @@ -1,5 +1,6 @@ import { HIGHLIGHT, NORMAL } from 'auto-console-group' +import { NEW_LINE } from '../../common/consts' import { debug, error, info } from '../console' export const metaCreateDebugObserved = @@ -9,7 +10,7 @@ export const metaCreateDebugObserved = if (observed.size > 0) { debug( `${type}Observer ${text}:`, - ...Array.from(observed).flatMap((node) => ['\n', node]), + ...Array.from(observed).flatMap((node) => [NEW_LINE, node]), ) } } @@ -21,7 +22,7 @@ export const metaCreateErrorObserved = if (observed.size > 0) { error( `${type}Observer ${text}:`, - ...Array.from(observed).flatMap((node) => ['\n', node]), + ...Array.from(observed).flatMap((node) => [NEW_LINE, node]), ) } } diff --git a/packages/common/consts.js b/packages/common/consts.js index d238c1dfe..dded1e5af 100644 --- a/packages/common/consts.js +++ b/packages/common/consts.js @@ -43,6 +43,7 @@ export const OFFSET = 'offset' export const OFFSET_HEIGHT = 'offsetHeight' export const OFFSET_SIZE = 'offsetSize' export const SCROLL = 'scroll' +export const NEW_LINE = '\n' export const HIDDEN = 'hidden' export const VISIBLE = 'visible' @@ -142,3 +143,6 @@ export const IGNORE_TAGS = new Set([ 'wbr', 'nobr', ]) + +export const REMOVED_NEXT_VERSION = + 'Use of the old name will be removed in a future version of iframe-resizer.' diff --git a/packages/common/format-advise.js b/packages/common/format-advise.js index c72b00138..b813467ae 100644 --- a/packages/common/format-advise.js +++ b/packages/common/format-advise.js @@ -1,15 +1,35 @@ -const encode = (s) => - s - .replaceAll('
', '\n') - .replaceAll('', '\u001B[31;1m') - .replaceAll('', '\u001B[m') - .replaceAll('', '\u001B[1m') - .replaceAll('', '\u001B[3m') - .replaceAll('', '\u001B[4m') - -const remove = (s) => s.replaceAll('
', '\n').replaceAll(/<[/a-z]+>/gi, '') - -export default (formatLogMsg) => (msg) => - window.chrome // Only show formatting in Chrome as not supported in other browsers - ? formatLogMsg(encode(msg)) - : formatLogMsg(remove(msg)) +/* eslint-disable no-useless-escape */ +/* eslint-disable security/detect-non-literal-regexp */ + +import { NEW_LINE } from './consts' +import { isString } from './utils' + +const TAGS = { + br: '\n', + rb: '\u001B[31;1m', // red bold + bb: '\u001B[34;1m', // blue bold + b: '\u001B[1m', // bold + i: '\u001B[3m', // italic + u: '\u001B[4m', // underline + '/': '\u001B[m', // reset +} + +const keys = Object.keys(TAGS) +const tags = new RegExp(`<(${keys.join('|')})>`, 'gi') +const lookup = (_, tag) => TAGS[tag] ?? '' +const encode = (s) => s.replace(tags, lookup) + +const filter = (s) => + s.replaceAll('
', NEW_LINE).replaceAll(/<\/?[^>]+>/gi, '') + +export default (formatLogMessage) => (message) => + formatLogMessage( + isString(message) + ? window.chrome + ? encode(message) + : filter(message) + : message, + ) + +/* eslint-enable security/detect-non-literal-regexp */ +/* eslint-enable no-useless-escape */ diff --git a/packages/common/format-advise.test.js b/packages/common/format-advise.test.js index 71f52a7f0..98b6d3593 100644 --- a/packages/common/format-advise.test.js +++ b/packages/common/format-advise.test.js @@ -1,6 +1,6 @@ -import formatAdvise from './format-advise' +import createFormatAdvise from './format-advise' -describe('formatAdvise', () => { +describe('createFormatAdvise', () => { let mockFormatLogMsg beforeEach(() => { @@ -15,8 +15,8 @@ describe('formatAdvise', () => { }) const msg = 'Error: Something went wrong
Details' - const formattedAdvise = formatAdvise(mockFormatLogMsg) - formattedAdvise(msg) + const formatAdvise = createFormatAdvise(mockFormatLogMsg) + formatAdvise(msg) const expectedOutput = '\u001B[31;1mError:\u001B[m \u001B[1mSomething went wrong\u001B[m\n\u001B[3mDetails\u001B[m' @@ -32,8 +32,8 @@ describe('formatAdvise', () => { }) const msg = 'Error: Something went wrong
Details' - const formattedAdvise = formatAdvise(mockFormatLogMsg) - formattedAdvise(msg) + const formatAdvise = createFormatAdvise(mockFormatLogMsg) + formatAdvise(msg) const expectedOutput = 'Error: Something went wrong\nDetails' @@ -42,8 +42,8 @@ describe('formatAdvise', () => { test('should handle empty messages gracefully', () => { const msg = '' - const formattedAdvise = formatAdvise(mockFormatLogMsg) - formattedAdvise(msg) + const formatAdvise = createFormatAdvise(mockFormatLogMsg) + formatAdvise(msg) expect(mockFormatLogMsg).toHaveBeenCalledWith('') }) diff --git a/packages/common/utils.js b/packages/common/utils.js index 2ffe7d4ce..a6a167adb 100644 --- a/packages/common/utils.js +++ b/packages/common/utils.js @@ -1,7 +1,15 @@ +import { STRING } from './consts' + export const isElement = (node) => node.nodeType === Node.ELEMENT_NODE export const isNumber = (value) => !Number.isNaN(value) +export const isString = (value) => typeof value === STRING + +export const isSafari = /^((?!chrome|android).)*safari/i.test( + navigator.userAgent, +) + export const isolateUserCode = (func, ...val) => setTimeout(() => func(...val), 0) diff --git a/packages/core/console.js b/packages/core/console.js index 75bd9d859..00efd6ac2 100644 --- a/packages/core/console.js +++ b/packages/core/console.js @@ -2,7 +2,7 @@ import acg, { NORMAL } from 'auto-console-group' import { BOLD, LABEL, PARENT } from '../common/consts' import deprecate from '../common/deprecate' -import formatAdvise from '../common/format-advise' +import createFormatAdvise from '../common/format-advise' import { esModuleInterop, id as identity } from '../common/utils' import settings from './values/settings' @@ -76,13 +76,15 @@ const formatLogMsg = (...args) => [`${LABEL}(${iframeId})`, ...args].join(' ') +const formatAdvise = createFormatAdvise(identity) export const advise = (iframeId, ...args) => settings[iframeId] - ? settings[iframeId].console.warn(formatAdvise(identity)(...args)) - : queueMicrotask( + ? settings[iframeId].console.warn(...args.map(formatAdvise)) + : queueMicrotask(() => { + const localFormatAdvise = createFormatAdvise(formatLogMsg(iframeId)) // eslint-disable-next-line no-console - () => console?.warn(formatAdvise(formatLogMsg(iframeId))(...args)), - ) + console?.warn(...args.map(localFormatAdvise)) + }) const deprecateAdvise = deprecate(advise) export const deprecateFunction = deprecateAdvise('Function') diff --git a/packages/core/index.js b/packages/core/index.js index f5823a271..e56f2f7d7 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -42,6 +42,7 @@ import { PARENT, PARENT_INFO, PARENT_INFO_STOP, + REMOVED_NEXT_VERSION, RESET, RESET_REQUIRED_METHODS, RESIZE, @@ -62,6 +63,7 @@ import { hasOwn, isolateUserCode, once, typeAssert } from '../common/utils' import { advise, debug, + endAutoGroup, error, errorBoundary, event as consoleEvent, @@ -73,6 +75,7 @@ import { warn, } from './console' import warnOnNoResponse from './timeout' +import checkUniqueId from './unique' import defaults from './values/defaults' import page from './values/page' import settings from './values/settings' @@ -1064,7 +1067,7 @@ export default (options) => (iframe) => { iframeId, `Deprecated Method Name -The \u001B[removeListeners() method has been renamed to \u001B[disconnect(). +The removeListeners() method has been renamed to disconnect(). ${REMOVED_NEXT_VERSION} `, ) this.disconnect() @@ -1074,7 +1077,7 @@ The \u001B[removeListeners() method has been renamed to \u001B[disconnect()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) @@ -1159,7 +1162,7 @@ Use of the resize() method from the parent page is deprecated and will be iframeId, `Deprecated Option -The sizeWidth, sizeHeight and autoResize options have been replaced with new direction option which expects values of "${VERTICAL}", "${HORIZONTAL}" or "${NONE}". +The sizeWidth, sizeHeight and autoResize options have been replaced with new direction option which expects values of ${VERTICAL}, ${HORIZONTAL}, ${BOTH} or ${NONE}. `, ) } @@ -1235,7 +1238,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep if (hasOwn(settings[iframeId], oldName)) { advise( iframeId, - `Deprecated option\n\nThe ${oldName} option has been renamed to ${newName}. Use of the old name will be removed in a future version of iframe-resizer.`, + `Deprecated option\n\nThe ${oldName} option has been renamed to ${newName}. ${REMOVED_NEXT_VERSION}`, ) settings[iframeId][newName] = settings[iframeId][oldName] delete settings[iframeId][oldName] @@ -1258,15 +1261,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep : '*' } - function checkOffset(options) { - if (options?.offset) { - advise( - iframeId, - `Deprecated option\n\n The offset option has been renamed to offsetSize. Use of the old name will be removed in a future version of iframe-resizer.`, - ) - } - } - function processOptions(options) { settings[iframeId] = { ...settings[iframeId], @@ -1286,7 +1280,6 @@ The sizeWidth, sizeHeight and autoResize options have been rep consoleEvent(iframeId, 'setup') setDirection() setOffsetSize(options?.offsetSize || options?.offset) // ignore zero offset - checkOffset(options) checkWarningTimeout() getPostMessageTarget() setTargetOrigin() @@ -1299,6 +1292,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep } processOptions(options) + checkUniqueId(iframeId) log(iframeId, `src: %c${iframe.srcdoc || iframe.src}`, HIGHLIGHT) preModeCheck() setupEventListenersOnce() @@ -1307,6 +1301,7 @@ The sizeWidth, sizeHeight and autoResize options have been rep init(iframeId, createOutgoingMsg(iframeId)) setupIframeObject() log(iframeId, 'Setup complete') + endAutoGroup(iframeId) } function enableVInfo(options) { diff --git a/packages/core/timeout.js b/packages/core/timeout.js index eb1d7798e..acd9343b7 100644 --- a/packages/core/timeout.js +++ b/packages/core/timeout.js @@ -27,24 +27,24 @@ function showWarning(id, settings) { 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. +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'. +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. +The iframe has the sandbox attribute, please ensure it contains both the allow-same-origin and allow-scripts values. ` : '' } diff --git a/packages/core/unique.js b/packages/core/unique.js new file mode 100644 index 000000000..b08be45f9 --- /dev/null +++ b/packages/core/unique.js @@ -0,0 +1,32 @@ +import { NEW_LINE } from '../common/consts' +import { advise } from './console' + +const shownDuplicateIdWarning = {} + +export default function checkUniqueId(id) { + if (shownDuplicateIdWarning[id] === true) return false + + const elements = document.querySelectorAll(`iframe#${CSS.escape(id)}`) + if (elements.length <= 1) return true + + shownDuplicateIdWarning[id] = true + + const elementList = Array.from(elements).flatMap((element) => [ + NEW_LINE, + element, + NEW_LINE, + ]) + + advise( + id, + `Duplicate ID attributes detected + +The ${id} ID is not unique. Having multiple iframes on the same page with the same ID causes problems with communication between the iframe and parent page. Please ensure that the ID of each iframe has a unique value. + +Found ${elements.length} iframes with the ${id} ID:`, + ...elementList, + NEW_LINE, + ) + + return false +}