@data-fair/frame 0.18.7 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/d-frame-content.min.js +1 -1
- package/dist/d-frame-content.min.js.map +1 -1
- package/dist/d-frame.min.js +2 -2
- package/dist/d-frame.min.js.map +1 -1
- package/dist/v-iframe-compat/d-frame-content.min.js +1 -1
- package/dist/v-iframe-compat/d-frame-content.min.js.map +1 -1
- package/lib/DFrameContent.d.ts +2 -1
- package/lib/DFrameContent.js +29 -23
- package/lib/DFrameElement.js +44 -18
- package/lib/messages.js +7 -4
- package/lib/utils/sync-params.js +10 -8
- package/lib/utils/url.d.ts +1 -0
- package/lib/utils/url.js +11 -0
- package/lib/v-iframe-compat/ui-notif.js +1 -1
- package/lib/vue/use-parent-urls.js +1 -1
- package/lib/vue-reactive/util.js +4 -5
- package/lib/vue-router/state-change-adapter.js +13 -3
- package/lib/vue-router/use-parent-url.js +9 -4
- package/package.json +9 -6
- package/lib/vue-router/router-type.d.ts +0 -17
- package/lib/vue-router/router-type.js +0 -7
- package/lib/vue-router/util.d.ts +0 -17
- package/lib/vue-router/util.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d-frame-content.min.js","sources":["../../lib/messages.js","../../lib/utils/in-iframe.js","../../lib/DFrameContent.js","../../lib/vue-reactive/util.js","../../lib/v-iframe-compat/d-frame-content.js"],"sourcesContent":["export function isMouseEventMessage(message) {\n return message[0] === 'df-global' && message[1] === 'mouse';\n}\nexport function isInitChildMessage(message) {\n return message[0] === 'df-child' && message[1] === 'init';\n}\nexport function isHeightMessage(message) {\n return message[0] === 'df-child' && message[1] === 'height';\n}\nexport function isReinitHeightMessage(message) {\n return message[0] === 'df-child' && message[1] === 'reinit-height';\n}\nexport function isStateChangeMessage(message) {\n return message[0] === 'df-child' && message[1] === 'stateChange';\n}\nexport function isCustomMessage(message) {\n return message[0] === 'df-child' && message[1] === 'custom';\n}\nexport function isNotifMessage(message) {\n return message[0] === 'df-child' && message[1] === 'notif';\n}\nexport function isAddParentUrlListenerMessage(message) {\n return message[0] === 'df-child' && message[1] === 'addParentUrlListener';\n}\nexport function isRemoveParentUrlListenerMessage(message) {\n return message[0] === 'df-child' && message[1] === 'removeParentUrlListener';\n}\nexport function isReadyMessage(message) {\n return message[0] === 'df-child' && message[1] === 'ready';\n}\nexport function isInitParentMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'init';\n}\nexport function isUpdateSrcMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'updateSrc';\n}\nexport function isParentUrlMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'parentUrl';\n}\n","const inIframe = (() => {\n try {\n return window.top !== window.self;\n }\n catch (e) {\n return true;\n }\n})();\nexport default inIframe;\n","import { isInitParentMessage, isMouseEventMessage, isUpdateSrcMessage, isParentUrlMessage } from './messages.js';\nimport inIframe from './utils/in-iframe.js';\nconst windowEventTypes = [\n 'animationstart',\n 'webkitAnimationStart',\n 'animationiteration',\n 'webkitAnimationIteration',\n 'animationend',\n 'webkitAnimationEnd',\n 'input',\n 'mouseup',\n 'mousedown',\n 'orientationchange',\n 'afterprint',\n 'beforeprint',\n 'readystatechange',\n 'touchstart',\n 'touchend',\n 'touchcancel',\n 'transitionstart',\n 'webkitTransitionStart',\n 'MSTransitionStart',\n 'oTransitionStart',\n 'otransitionstart', 'transitioniteration',\n 'webkitTransitionIteration',\n 'MSTransitionIteration',\n 'oTransitionIteration',\n 'otransitioniteration', 'transitionend',\n 'webkitTransitionEnd',\n 'MSTransitionEnd',\n 'oTransitionEnd',\n 'otransitionend', 'resize'\n];\nexport default class DFrameContent {\n initialized = false;\n options;\n pendingCheckHeight = false;\n debug = false;\n id;\n throttledCheckHeight;\n lastHeight = 0;\n parentUrlListeners = {};\n parentUrlResults = {};\n constructor(options) {\n this.options = options ?? {};\n if (typeof window === 'undefined')\n return;\n if (window.__d_frame__content) {\n throw new Error('DFrameContent was already initialized');\n }\n window.__d_frame__content = this;\n const afCallback = () => {\n this.checkHeight();\n this.pendingCheckHeight = false;\n };\n this.throttledCheckHeight = () => {\n if (this.pendingCheckHeight)\n return;\n this.pendingCheckHeight = true;\n requestAnimationFrame(afCallback);\n };\n window.addEventListener('message', (e) => this.onMessage(e));\n // simple handshake for initialization\n this.postMessageToParent(['df-child', 'init']);\n }\n log(level, ...args) {\n if (level === 'debug' && !this.debug)\n return;\n if (level === 'debug')\n console.timeLog(`d-frame:${this.id}:content`, ...args);\n if (level === 'info')\n console.info(`d-frame:${this.id}:content`, ...args);\n if (level === 'error')\n console.error(`d-frame:${this.id}:content`, ...args);\n }\n onMessage(e) {\n if (e.source === window.parent && Array.isArray(e.data)) {\n const message = e.data;\n if (isInitParentMessage(message)) {\n this.id = message[2].id;\n this.debug = !!message[2].debug;\n if (this.debug)\n console.time(`d-frame:${this.id}:content`);\n this.log('debug', 'init handshake is ok', message, this.options);\n if (message[2].src && message[2].src !== window.location.href) {\n this.log('error', `src mismatch at init, maybe the frame navigated before the initialization handshake: ${window.location.href} !== ${message[2].src}`);\n }\n if (message[2].resize !== 'no')\n this.initResize();\n if (message[2].syncParams || message[2].syncPath || message[2].stateChangeEvents)\n this.initStateChangeWatcher();\n if (message[2].mouseEvents)\n this.initMouseEvents(message[2].mouseEvents);\n this.initialized = true;\n }\n if (isUpdateSrcMessage(message)) {\n const newSrc = message[2].startsWith('/') ? window.location.origin + message[2] : message[2];\n if (newSrc === window.location.href)\n return;\n this.log('debug', `update src ${window.location.href} -> ${newSrc}`);\n if (this.options.updateSrc) {\n this.options.updateSrc(newSrc, this);\n }\n else {\n this.log('debug', 'no updateSrc method available, falling back to updating window.location.href', newSrc);\n window.location.href = newSrc;\n }\n }\n if (isMouseEventMessage(message)) {\n this.applySyncedMouseEvent(message);\n }\n if (isParentUrlMessage(message)) {\n if (!this.parentUrlListeners[message[2]]) {\n this.log('error', `received getParentUrlResponse message but no matching callback is registered: ${message[2]}`);\n }\n else {\n for (const listener of this.parentUrlListeners[message[2]]) {\n listener(message[3]);\n }\n }\n }\n }\n }\n postMessageToParent(message) {\n window.parent.postMessage(message, '*');\n }\n sendNotif(notif) {\n this.postMessageToParent(['df-child', 'notif', notif]);\n }\n sendMessage(msg) {\n this.postMessageToParent(['df-child', 'custom', msg]);\n }\n ready() {\n this.postMessageToParent(['df-child', 'ready']);\n }\n reinitHeight() {\n this.postMessageToParent(['df-child', 'reinit-height']);\n }\n // this is not a single-time callback, it can be called multiple times when parent url changes\n addParentUrlListener(partialChildHref, listener) {\n const childHref = new URL(partialChildHref, window.location.href).href;\n if (!inIframe) {\n listener(childHref);\n return;\n }\n if (this.parentUrlResults[childHref]) {\n listener(this.parentUrlResults[childHref]);\n }\n this.parentUrlListeners[childHref] = this.parentUrlListeners[childHref] ?? [];\n this.parentUrlListeners[childHref].push(listener);\n this.postMessageToParent(['df-child', 'addParentUrlListener', childHref]);\n }\n removeParentUrlListener(partialChildHref, listener) {\n const childHref = new URL(partialChildHref, window.location.href).href;\n if (this.parentUrlListeners[childHref]) {\n this.parentUrlListeners[childHref] = this.parentUrlListeners[childHref].filter(cb => cb !== listener);\n if (!this.parentUrlListeners[childHref].length) {\n delete this.parentUrlListeners[childHref];\n this.postMessageToParent(['df-child', 'removeParentUrlListener', childHref]);\n }\n }\n }\n currentSyncedMouseEvents = new Set();\n initMouseEvents(types) {\n this.log('debug', 'initMouseEvents');\n for (const eventType of types) {\n document.addEventListener(eventType, (e) => {\n if (!(e instanceof MouseEvent))\n return;\n this.transmitSyncedMouseEvent(['df-global', 'mouse', eventType, { altKey: e.altKey, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey }]);\n }, true);\n }\n }\n transmitSyncedMouseEvent(message) {\n if (this.currentSyncedMouseEvents.has(message[2])) {\n this.currentSyncedMouseEvents.delete(message[2]);\n }\n else {\n this.postMessageToParent(message);\n }\n }\n applySyncedMouseEvent(message) {\n this.currentSyncedMouseEvents.add(message[2]);\n const event = new MouseEvent(message[2], { bubbles: true, cancelable: true, view: window, ...message[3] });\n document.body.dispatchEvent(event);\n }\n initResize() {\n this.log('debug', 'initResize');\n this.checkHeight();\n this.createMutationObserver();\n this.createWindowEventListeners();\n }\n initStateChangeWatcher() {\n this.log('debug', 'initStateChangeWatcher');\n // monkey patch pushState and replaceState to send all state change info to the parent window\n // this is to compensate the lack of event emission by window.history\n const oldReplaceState = window.history.replaceState;\n const newPushState = (...args) => {\n const before = window.location.href;\n // do a replace instead of a push, the push will be done in the parent window if sync-state is activated\n const ret = oldReplaceState.apply(window.history, args);\n if (window.location.href !== before) {\n this.postMessageToParent(['df-child', 'stateChange', 'push', window.location.href]);\n }\n return ret;\n };\n window.history.pushState = newPushState;\n const newReplaceState = (...args) => {\n const before = window.location.href;\n const ret = oldReplaceState.apply(window.history, args);\n if (window.location.href !== before) {\n this.postMessageToParent(['df-child', 'stateChange', 'replace', window.location.href]);\n }\n return ret;\n };\n window.history.replaceState = newReplaceState;\n }\n createMutationObserver() {\n const body = document.querySelector('body');\n if (!body)\n throw new Error('DFrameContentManager was initialized before the HTML body');\n if (!this.throttledCheckHeight)\n throw new Error('throttledCheckHeight is not defined');\n const observer = new window.MutationObserver(this.throttledCheckHeight);\n observer.observe(body, {\n attributes: false,\n attributeOldValue: false,\n characterData: false,\n characterDataOldValue: false,\n childList: true,\n subtree: true,\n });\n }\n createWindowEventListeners() {\n if (!this.throttledCheckHeight)\n throw new Error('throttledCheckHeight is not defined');\n for (const eventType of windowEventTypes) {\n window.addEventListener(eventType, this.throttledCheckHeight, { passive: true });\n }\n }\n checkHeight() {\n const elements = document.querySelectorAll('[data-iframe-height]');\n let maxWithoutOffset = 0;\n let max = 0;\n for (const element of elements) {\n const dataAttribute = element.getAttribute('data-iframe-height');\n if (dataAttribute === 'false' || dataAttribute === 'no')\n continue;\n const bottomWithoutOffset = element.getBoundingClientRect().bottom +\n parseFloat(getComputedStyle(element).getPropertyValue('margin-bottom'));\n const bottom = dataAttribute ? (bottomWithoutOffset + parseFloat(dataAttribute)) : bottomWithoutOffset;\n if (bottomWithoutOffset > maxWithoutOffset)\n maxWithoutOffset = bottomWithoutOffset;\n if (bottom > max)\n max = bottom;\n }\n max = Math.ceil(max);\n if (maxWithoutOffset > 0 && max !== this.lastHeight) {\n this.postMessageToParent(['df-child', 'height', max]);\n this.lastHeight = max;\n }\n }\n}\n","export const applySearchParams = (reactiveParams, searchParams) => {\n const existingKeys = Object.keys(reactiveParams);\n searchParams.forEach((value, key) => {\n reactiveParams[key] = value;\n existingKeys.splice(existingKeys.indexOf(key), 1);\n });\n existingKeys.forEach(key => {\n delete reactiveParams[key];\n });\n};\n","import DFrameContent from '../DFrameContent.js';\nimport { applySearchParams } from '../vue-reactive/util.js';\nconst dFrameContent = new DFrameContent({\n updateSrc(src, instance) {\n const vIframeOptions = window.vIframeOptions;\n const reactiveParams = vIframeOptions?.reactiveParams;\n if (reactiveParams && typeof reactiveParams === 'object') {\n const srcUrl = new URL(src);\n if (srcUrl.origin === window.location.origin && srcUrl.pathname === window.location.pathname) {\n instance.log('debug', 'applying reactive params', srcUrl.searchParams);\n applySearchParams(reactiveParams, srcUrl.searchParams);\n return;\n }\n }\n const router = vIframeOptions?.router;\n if (router) {\n // @ts-ignore vue-router v2\n const base = router.options?.base ?? router.options.history?.base;\n if (base === null || base === undefined) {\n instance.log('error', 'failed to access base path in router (no router.options.base nor router.options.history.base)', router);\n }\n else {\n const urlPrefix = window.location.origin + base;\n if (!src.startsWith(urlPrefix)) {\n instance.log('info', 'src does not start with base path', src, urlPrefix);\n }\n else {\n let newRoute = src.replace(urlPrefix, '');\n if (!newRoute.startsWith('/'))\n newRoute = '/' + newRoute;\n instance.log('debug', 'applying router.replace', newRoute);\n router.replace(newRoute);\n return;\n }\n }\n }\n instance.log('info', 'falling back to updating window.location.href', src);\n window.location.href = src;\n }\n});\nwindow.dFrameContent = dFrameContent;\nexport default dFrameContent;\n"],"names":["isMouseEventMessage","message","isInitParentMessage","isUpdateSrcMessage","isParentUrlMessage","inIframe","windowEventTypes","DFrameContent","options","__publicField","afCallback","e","level","args","newSrc","listener","notif","msg","partialChildHref","childHref","cb","types","eventType","event","oldReplaceState","newPushState","before","ret","newReplaceState","body","elements","maxWithoutOffset","max","element","dataAttribute","bottomWithoutOffset","bottom","applySearchParams","reactiveParams","searchParams","existingKeys","value","key","dFrameContent","src","instance","vIframeOptions","srcUrl","router","base","_a","_b","urlPrefix","newRoute"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"d-frame-content.min.js","sources":["../../lib/utils/url.js","../../lib/messages.js","../../lib/utils/in-iframe.js","../../lib/DFrameContent.js","../../lib/vue-reactive/util.js","../../lib/v-iframe-compat/d-frame-content.ts"],"sourcesContent":["export function getPathDirectory(path) {\n if (path.endsWith('/'))\n return path;\n if (path === '')\n return '/';\n return path.slice(0, path.lastIndexOf('/') + 1);\n}\nexport function getPathWithTrailingSlash(path) {\n if (path.endsWith('/'))\n return path;\n return path + '/';\n}\nexport function getUrlRelativePath(srcUrl, currentUrl) {\n if (currentUrl.origin !== srcUrl.origin)\n return currentUrl.href;\n if (getPathWithTrailingSlash(currentUrl.pathname) === getPathWithTrailingSlash(srcUrl.pathname))\n return '';\n const srcDirectory = getPathDirectory(srcUrl.pathname);\n if (!currentUrl.pathname.startsWith(srcDirectory))\n return currentUrl.pathname;\n return currentUrl.pathname.replace(srcDirectory, '');\n}\n// a URL that can be navigated to or used as a link without running code (no javascript:, data:...)\nexport function isHttpUrl(value, base) {\n if (typeof value !== 'string')\n return false;\n try {\n return ['http:', 'https:'].includes(new URL(value, base).protocol);\n }\n catch {\n return false;\n }\n}\n","import { isHttpUrl } from './utils/url.js';\nexport function isMouseEventMessage(message) {\n return message[0] === 'df-global' && message[1] === 'mouse' && typeof message[2] === 'string';\n}\nexport function isInitChildMessage(message) {\n return message[0] === 'df-child' && message[1] === 'init';\n}\nexport function isHeightMessage(message) {\n // the height is written in the style of the wrapper, anything else than a number would inject css\n return message[0] === 'df-child' && message[1] === 'height' && typeof message[2] === 'number' && Number.isFinite(message[2]) && message[2] >= 0;\n}\nexport function isReinitHeightMessage(message) {\n return message[0] === 'df-child' && message[1] === 'reinit-height';\n}\n// the URL of the child becomes the src of the iframe when it is reloaded: a javascript: URL would run in the parent\nexport function isStateChangeMessage(message) {\n return message[0] === 'df-child' && message[1] === 'stateChange' && (message[2] === 'push' || message[2] === 'replace') && isHttpUrl(message[3]);\n}\nexport function isCustomMessage(message) {\n return message[0] === 'df-child' && message[1] === 'custom';\n}\nexport function isNotifMessage(message) {\n return message[0] === 'df-child' && message[1] === 'notif';\n}\nexport function isAddParentUrlListenerMessage(message) {\n return message[0] === 'df-child' && message[1] === 'addParentUrlListener' && isHttpUrl(message[2]);\n}\nexport function isRemoveParentUrlListenerMessage(message) {\n return message[0] === 'df-child' && message[1] === 'removeParentUrlListener';\n}\nexport function isReadyMessage(message) {\n return message[0] === 'df-child' && message[1] === 'ready';\n}\nexport function isInitParentMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'init';\n}\nexport function isUpdateSrcMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'updateSrc';\n}\nexport function isParentUrlMessage(message) {\n return message[0] === 'df-parent' && message[1] === 'parentUrl';\n}\n","const inIframe = (() => {\n try {\n return window.top !== window.self;\n }\n catch (e) {\n return true;\n }\n})();\nexport default inIframe;\n","import { isInitParentMessage, isMouseEventMessage, isUpdateSrcMessage, isParentUrlMessage } from './messages.js';\nimport inIframe from './utils/in-iframe.js';\nimport { isHttpUrl } from './utils/url.js';\n// prefixed variants are not listed: browsers do not fire them when there is an unprefixed listener\nconst windowEventTypes = [\n 'animationstart',\n 'animationiteration',\n 'animationend',\n 'input',\n 'mouseup',\n 'mousedown',\n 'orientationchange',\n 'afterprint',\n 'beforeprint',\n 'touchstart',\n 'touchend',\n 'touchcancel',\n 'transitionstart',\n 'transitionend',\n 'resize'\n];\nexport default class DFrameContent {\n initialized = false;\n options;\n pendingCheckHeight = false;\n debug = false;\n id;\n throttledCheckHeight;\n lastHeight = 0;\n parentUrlListeners = {};\n parentUrlResults = {};\n constructor(options) {\n this.options = options ?? {};\n if (typeof window === 'undefined')\n return;\n if (window.__d_frame__content) {\n throw new Error('DFrameContent was already initialized');\n }\n window.__d_frame__content = this;\n const afCallback = () => {\n this.checkHeight();\n this.pendingCheckHeight = false;\n };\n this.throttledCheckHeight = () => {\n if (this.pendingCheckHeight)\n return;\n this.pendingCheckHeight = true;\n requestAnimationFrame(afCallback);\n };\n window.addEventListener('message', (e) => this.onMessage(e));\n // simple handshake for initialization\n this.postMessageToParent(['df-child', 'init']);\n }\n log(level, ...args) {\n if (level === 'debug' && !this.debug)\n return;\n if (level === 'debug')\n console.timeLog(`d-frame:${this.id}:content`, ...args);\n if (level === 'info')\n console.info(`d-frame:${this.id}:content`, ...args);\n if (level === 'error')\n console.error(`d-frame:${this.id}:content`, ...args);\n }\n onMessage(e) {\n if (e.source === window.parent && Array.isArray(e.data)) {\n const message = e.data;\n if (isInitParentMessage(message)) {\n this.id = message[2].id;\n this.debug = !!message[2].debug;\n if (this.debug)\n console.time(`d-frame:${this.id}:content`);\n this.log('debug', 'init handshake is ok', message, this.options);\n if (message[2].src && message[2].src !== window.location.href) {\n this.log('error', `src mismatch at init, maybe the frame navigated before the initialization handshake: ${window.location.href} !== ${message[2].src}`);\n }\n if (message[2].resize !== 'no')\n this.initResize();\n if (message[2].syncParams || message[2].syncPath || message[2].stateChangeEvents)\n this.initStateChangeWatcher();\n if (message[2].mouseEvents)\n this.initMouseEvents(message[2].mouseEvents);\n this.initialized = true;\n }\n if (isUpdateSrcMessage(message)) {\n // the parent can be any page, it must not be able to run code in this window (javascript: URL)\n if (!isHttpUrl(message[2], window.location.href)) {\n this.log('error', 'refused to navigate to a src that is not a http(s) URL', message[2]);\n return;\n }\n const newSrc = new URL(message[2], window.location.href).href;\n if (newSrc === window.location.href)\n return;\n this.log('debug', `update src ${window.location.href} -> ${newSrc}`);\n if (this.options.updateSrc) {\n this.options.updateSrc(newSrc, this);\n }\n else {\n this.log('debug', 'no updateSrc method available, falling back to updating window.location.href', newSrc);\n window.location.href = newSrc;\n }\n }\n if (isMouseEventMessage(message) && this.mouseEventTypes.includes(message[2])) {\n this.applySyncedMouseEvent(message);\n }\n if (isParentUrlMessage(message)) {\n if (!isHttpUrl(message[3])) {\n // the result is used as a link, it must not be able to run code\n this.log('error', 'refused a parent URL that is not a http(s) URL', message[3]);\n }\n else if (!this.parentUrlListeners[message[2]]) {\n this.log('error', `received getParentUrlResponse message but no matching callback is registered: ${message[2]}`);\n }\n else {\n this.parentUrlResults[message[2]] = message[3];\n for (const listener of this.parentUrlListeners[message[2]]) {\n listener(message[3]);\n }\n }\n }\n }\n }\n postMessageToParent(message) {\n window.parent.postMessage(message, '*');\n }\n sendNotif(notif) {\n this.postMessageToParent(['df-child', 'notif', notif]);\n }\n sendMessage(msg) {\n this.postMessageToParent(['df-child', 'custom', msg]);\n }\n ready() {\n this.postMessageToParent(['df-child', 'ready']);\n }\n reinitHeight() {\n // the parent forgets the height, the next one must be sent even if it did not change\n this.lastHeight = 0;\n this.postMessageToParent(['df-child', 'reinit-height']);\n }\n // this is not a single-time callback, it can be called multiple times when parent url changes\n addParentUrlListener(partialChildHref, listener) {\n const childHref = new URL(partialChildHref, window.location.href).href;\n if (!inIframe) {\n listener(childHref);\n return;\n }\n if (this.parentUrlListeners[childHref]) {\n // the parent already sends the updates of this URL, the result is known or on its way\n this.parentUrlListeners[childHref].push(listener);\n if (childHref in this.parentUrlResults)\n listener(this.parentUrlResults[childHref]);\n return;\n }\n this.parentUrlListeners[childHref] = [listener];\n this.postMessageToParent(['df-child', 'addParentUrlListener', childHref]);\n }\n removeParentUrlListener(partialChildHref, listener) {\n const childHref = new URL(partialChildHref, window.location.href).href;\n if (this.parentUrlListeners[childHref]) {\n this.parentUrlListeners[childHref] = this.parentUrlListeners[childHref].filter(cb => cb !== listener);\n if (!this.parentUrlListeners[childHref].length) {\n delete this.parentUrlListeners[childHref];\n delete this.parentUrlResults[childHref];\n this.postMessageToParent(['df-child', 'removeParentUrlListener', childHref]);\n }\n }\n }\n currentSyncedMouseEvents = new Set();\n mouseEventTypes = [];\n initMouseEvents(types) {\n this.log('debug', 'initMouseEvents');\n this.mouseEventTypes = types;\n for (const eventType of types) {\n document.addEventListener(eventType, (e) => {\n if (!(e instanceof MouseEvent))\n return;\n this.transmitSyncedMouseEvent(['df-global', 'mouse', eventType, { altKey: e.altKey, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey }]);\n }, true);\n }\n }\n transmitSyncedMouseEvent(message) {\n if (this.currentSyncedMouseEvents.has(message[2])) {\n this.currentSyncedMouseEvents.delete(message[2]);\n }\n else {\n this.postMessageToParent(message);\n }\n }\n applySyncedMouseEvent(message) {\n this.currentSyncedMouseEvents.add(message[2]);\n const event = new MouseEvent(message[2], { bubbles: true, cancelable: true, view: window, ...message[3] });\n document.body.dispatchEvent(event);\n }\n initResize() {\n this.log('debug', 'initResize');\n this.checkHeight();\n this.createMutationObserver();\n this.createWindowEventListeners();\n }\n initStateChangeWatcher() {\n this.log('debug', 'initStateChangeWatcher');\n // monkey patch pushState and replaceState to send all state change info to the parent window\n // this is to compensate the lack of event emission by window.history\n const oldReplaceState = window.history.replaceState;\n const newPushState = (...args) => {\n const before = window.location.href;\n // do a replace instead of a push, the push will be done in the parent window if sync-state is activated\n const ret = oldReplaceState.apply(window.history, args);\n if (window.location.href !== before) {\n this.postMessageToParent(['df-child', 'stateChange', 'push', window.location.href]);\n }\n return ret;\n };\n window.history.pushState = newPushState;\n const newReplaceState = (...args) => {\n const before = window.location.href;\n const ret = oldReplaceState.apply(window.history, args);\n if (window.location.href !== before) {\n this.postMessageToParent(['df-child', 'stateChange', 'replace', window.location.href]);\n }\n return ret;\n };\n window.history.replaceState = newReplaceState;\n }\n createMutationObserver() {\n const body = document.querySelector('body');\n if (!body)\n throw new Error('DFrameContentManager was initialized before the HTML body');\n if (!this.throttledCheckHeight)\n throw new Error('throttledCheckHeight is not defined');\n const observer = new window.MutationObserver(this.throttledCheckHeight);\n observer.observe(body, {\n attributes: false,\n attributeOldValue: false,\n characterData: false,\n characterDataOldValue: false,\n childList: true,\n subtree: true,\n });\n }\n createWindowEventListeners() {\n if (!this.throttledCheckHeight)\n throw new Error('throttledCheckHeight is not defined');\n for (const eventType of windowEventTypes) {\n window.addEventListener(eventType, this.throttledCheckHeight, { passive: true });\n }\n }\n checkHeight() {\n const elements = document.querySelectorAll('[data-iframe-height]');\n let maxWithoutOffset = 0;\n let max = 0;\n for (const element of elements) {\n const dataAttribute = element.getAttribute('data-iframe-height');\n if (dataAttribute === 'false' || dataAttribute === 'no')\n continue;\n const bottomWithoutOffset = element.getBoundingClientRect().bottom +\n parseFloat(getComputedStyle(element).getPropertyValue('margin-bottom'));\n const bottom = dataAttribute ? (bottomWithoutOffset + parseFloat(dataAttribute)) : bottomWithoutOffset;\n if (bottomWithoutOffset > maxWithoutOffset)\n maxWithoutOffset = bottomWithoutOffset;\n if (bottom > max)\n max = bottom;\n }\n max = Math.ceil(max);\n if (maxWithoutOffset > 0 && max !== this.lastHeight) {\n this.postMessageToParent(['df-child', 'height', max]);\n this.lastHeight = max;\n }\n }\n}\n","export const applySearchParams = (reactiveParams, searchParams) => {\n searchParams.forEach((value, key) => {\n reactiveParams[key] = value;\n });\n for (const key of Object.keys(reactiveParams)) {\n if (!searchParams.has(key))\n delete reactiveParams[key];\n }\n};\n","import DFrameContent from '../DFrameContent.js'\nimport type { Router } from 'vue-router'\nimport { applySearchParams } from '../vue-reactive/util.js'\n\nexport type VIframeOptions = {\n router?: Router,\n reactiveParams?: Record<string, string> | boolean\n}\n\nconst dFrameContent = new DFrameContent({\n updateSrc (src: string, instance: DFrameContent) {\n const vIframeOptions = (window as Window & { vIframeOptions?: VIframeOptions }).vIframeOptions\n\n const reactiveParams = vIframeOptions?.reactiveParams\n if (reactiveParams && typeof reactiveParams === 'object') {\n const srcUrl = new URL(src)\n if (srcUrl.origin === window.location.origin && srcUrl.pathname === window.location.pathname) {\n instance.log('debug', 'applying reactive params', srcUrl.searchParams)\n applySearchParams(reactiveParams, srcUrl.searchParams)\n return\n }\n }\n\n const router = vIframeOptions?.router\n if (router) {\n // @ts-ignore vue-router v2\n const base = router.options?.base ?? router.options.history?.base\n if (base === null || base === undefined) {\n instance.log('error', 'failed to access base path in router (no router.options.base nor router.options.history.base)', router)\n } else {\n const urlPrefix = window.location.origin + base\n if (!src.startsWith(urlPrefix)) {\n instance.log('info', 'src does not start with base path', src, urlPrefix)\n } else {\n let newRoute = src.replace(urlPrefix, '')\n if (!newRoute.startsWith('/')) newRoute = '/' + newRoute\n instance.log('debug', 'applying router.replace', newRoute)\n router.replace(newRoute)\n return\n }\n }\n }\n\n instance.log('info', 'falling back to updating window.location.href', src)\n window.location.href = src\n }\n});\n\n(window as (Window & { dFrameContent?: DFrameContent })).dFrameContent = dFrameContent\n\nexport default dFrameContent\n"],"names":["isHttpUrl","value","base","isMouseEventMessage","message","isInitParentMessage","isUpdateSrcMessage","isParentUrlMessage","inIframe","windowEventTypes","DFrameContent","options","afCallback","e","level","args","newSrc","listener","notif","msg","partialChildHref","childHref","cb","types","eventType","event","oldReplaceState","newPushState","before","ret","newReplaceState","body","elements","maxWithoutOffset","max","element","dataAttribute","bottomWithoutOffset","bottom","applySearchParams","reactiveParams","searchParams","key","dFrameContent","src","instance","vIframeOptions","srcUrl","router","urlPrefix","newRoute"],"mappings":"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"}
|
package/lib/DFrameContent.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type DFrameContentOptions = {
|
|
|
4
4
|
};
|
|
5
5
|
declare global {
|
|
6
6
|
interface Window {
|
|
7
|
-
__d_frame__content
|
|
7
|
+
__d_frame__content?: DFrameContent;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
export default class DFrameContent {
|
|
@@ -28,6 +28,7 @@ export default class DFrameContent {
|
|
|
28
28
|
addParentUrlListener(partialChildHref: string, listener: (result: string) => any): void;
|
|
29
29
|
removeParentUrlListener(partialChildHref: string, listener: (result: string) => any): void;
|
|
30
30
|
private currentSyncedMouseEvents;
|
|
31
|
+
private mouseEventTypes;
|
|
31
32
|
private initMouseEvents;
|
|
32
33
|
private transmitSyncedMouseEvent;
|
|
33
34
|
private applySyncedMouseEvent;
|
package/lib/DFrameContent.js
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
import { isInitParentMessage, isMouseEventMessage, isUpdateSrcMessage, isParentUrlMessage } from './messages.js';
|
|
2
2
|
import inIframe from './utils/in-iframe.js';
|
|
3
|
+
import { isHttpUrl } from './utils/url.js';
|
|
4
|
+
// prefixed variants are not listed: browsers do not fire them when there is an unprefixed listener
|
|
3
5
|
const windowEventTypes = [
|
|
4
6
|
'animationstart',
|
|
5
|
-
'webkitAnimationStart',
|
|
6
7
|
'animationiteration',
|
|
7
|
-
'webkitAnimationIteration',
|
|
8
8
|
'animationend',
|
|
9
|
-
'webkitAnimationEnd',
|
|
10
9
|
'input',
|
|
11
10
|
'mouseup',
|
|
12
11
|
'mousedown',
|
|
13
12
|
'orientationchange',
|
|
14
13
|
'afterprint',
|
|
15
14
|
'beforeprint',
|
|
16
|
-
'readystatechange',
|
|
17
15
|
'touchstart',
|
|
18
16
|
'touchend',
|
|
19
17
|
'touchcancel',
|
|
20
18
|
'transitionstart',
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'oTransitionStart',
|
|
24
|
-
'otransitionstart', 'transitioniteration',
|
|
25
|
-
'webkitTransitionIteration',
|
|
26
|
-
'MSTransitionIteration',
|
|
27
|
-
'oTransitionIteration',
|
|
28
|
-
'otransitioniteration', 'transitionend',
|
|
29
|
-
'webkitTransitionEnd',
|
|
30
|
-
'MSTransitionEnd',
|
|
31
|
-
'oTransitionEnd',
|
|
32
|
-
'otransitionend', 'resize'
|
|
19
|
+
'transitionend',
|
|
20
|
+
'resize'
|
|
33
21
|
];
|
|
34
22
|
export default class DFrameContent {
|
|
35
23
|
initialized = false;
|
|
@@ -94,7 +82,12 @@ export default class DFrameContent {
|
|
|
94
82
|
this.initialized = true;
|
|
95
83
|
}
|
|
96
84
|
if (isUpdateSrcMessage(message)) {
|
|
97
|
-
|
|
85
|
+
// the parent can be any page, it must not be able to run code in this window (javascript: URL)
|
|
86
|
+
if (!isHttpUrl(message[2], window.location.href)) {
|
|
87
|
+
this.log('error', 'refused to navigate to a src that is not a http(s) URL', message[2]);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const newSrc = new URL(message[2], window.location.href).href;
|
|
98
91
|
if (newSrc === window.location.href)
|
|
99
92
|
return;
|
|
100
93
|
this.log('debug', `update src ${window.location.href} -> ${newSrc}`);
|
|
@@ -106,14 +99,19 @@ export default class DFrameContent {
|
|
|
106
99
|
window.location.href = newSrc;
|
|
107
100
|
}
|
|
108
101
|
}
|
|
109
|
-
if (isMouseEventMessage(message)) {
|
|
102
|
+
if (isMouseEventMessage(message) && this.mouseEventTypes.includes(message[2])) {
|
|
110
103
|
this.applySyncedMouseEvent(message);
|
|
111
104
|
}
|
|
112
105
|
if (isParentUrlMessage(message)) {
|
|
113
|
-
if (!
|
|
106
|
+
if (!isHttpUrl(message[3])) {
|
|
107
|
+
// the result is used as a link, it must not be able to run code
|
|
108
|
+
this.log('error', 'refused a parent URL that is not a http(s) URL', message[3]);
|
|
109
|
+
}
|
|
110
|
+
else if (!this.parentUrlListeners[message[2]]) {
|
|
114
111
|
this.log('error', `received getParentUrlResponse message but no matching callback is registered: ${message[2]}`);
|
|
115
112
|
}
|
|
116
113
|
else {
|
|
114
|
+
this.parentUrlResults[message[2]] = message[3];
|
|
117
115
|
for (const listener of this.parentUrlListeners[message[2]]) {
|
|
118
116
|
listener(message[3]);
|
|
119
117
|
}
|
|
@@ -134,6 +132,8 @@ export default class DFrameContent {
|
|
|
134
132
|
this.postMessageToParent(['df-child', 'ready']);
|
|
135
133
|
}
|
|
136
134
|
reinitHeight() {
|
|
135
|
+
// the parent forgets the height, the next one must be sent even if it did not change
|
|
136
|
+
this.lastHeight = 0;
|
|
137
137
|
this.postMessageToParent(['df-child', 'reinit-height']);
|
|
138
138
|
}
|
|
139
139
|
// this is not a single-time callback, it can be called multiple times when parent url changes
|
|
@@ -143,11 +143,14 @@ export default class DFrameContent {
|
|
|
143
143
|
listener(childHref);
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
|
-
if (this.
|
|
147
|
-
|
|
146
|
+
if (this.parentUrlListeners[childHref]) {
|
|
147
|
+
// the parent already sends the updates of this URL, the result is known or on its way
|
|
148
|
+
this.parentUrlListeners[childHref].push(listener);
|
|
149
|
+
if (childHref in this.parentUrlResults)
|
|
150
|
+
listener(this.parentUrlResults[childHref]);
|
|
151
|
+
return;
|
|
148
152
|
}
|
|
149
|
-
this.parentUrlListeners[childHref] =
|
|
150
|
-
this.parentUrlListeners[childHref].push(listener);
|
|
153
|
+
this.parentUrlListeners[childHref] = [listener];
|
|
151
154
|
this.postMessageToParent(['df-child', 'addParentUrlListener', childHref]);
|
|
152
155
|
}
|
|
153
156
|
removeParentUrlListener(partialChildHref, listener) {
|
|
@@ -156,13 +159,16 @@ export default class DFrameContent {
|
|
|
156
159
|
this.parentUrlListeners[childHref] = this.parentUrlListeners[childHref].filter(cb => cb !== listener);
|
|
157
160
|
if (!this.parentUrlListeners[childHref].length) {
|
|
158
161
|
delete this.parentUrlListeners[childHref];
|
|
162
|
+
delete this.parentUrlResults[childHref];
|
|
159
163
|
this.postMessageToParent(['df-child', 'removeParentUrlListener', childHref]);
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
}
|
|
163
167
|
currentSyncedMouseEvents = new Set();
|
|
168
|
+
mouseEventTypes = [];
|
|
164
169
|
initMouseEvents(types) {
|
|
165
170
|
this.log('debug', 'initMouseEvents');
|
|
171
|
+
this.mouseEventTypes = types;
|
|
166
172
|
for (const eventType of types) {
|
|
167
173
|
document.addEventListener(eventType, (e) => {
|
|
168
174
|
if (!(e instanceof MouseEvent))
|
package/lib/DFrameElement.js
CHANGED
|
@@ -56,7 +56,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
56
56
|
}
|
|
57
57
|
get src() { return this.getAttribute('src'); }
|
|
58
58
|
set src(value) { this.setAttribute('src', value); }
|
|
59
|
-
get fullSrc() { return this.src
|
|
59
|
+
get fullSrc() { return new URL(this.src, window.location.href).href; }
|
|
60
60
|
get scrolling() { return this.getAttribute('scrolling'); }
|
|
61
61
|
set scrolling(value) { this.setAttribute('scrolling', value); }
|
|
62
62
|
get aspectRatio() {
|
|
@@ -109,9 +109,9 @@ export class DFrameElement extends HTMLElement {
|
|
|
109
109
|
}
|
|
110
110
|
set syncPath(value) {
|
|
111
111
|
if (value !== null)
|
|
112
|
-
this.setAttribute('sync-
|
|
112
|
+
this.setAttribute('sync-path', value);
|
|
113
113
|
else
|
|
114
|
-
this.removeAttribute('sync-
|
|
114
|
+
this.removeAttribute('sync-path');
|
|
115
115
|
}
|
|
116
116
|
get stateChangeEvents() { return this.hasAttribute('state-change-events'); }
|
|
117
117
|
set stateChangeEvents(value) {
|
|
@@ -235,6 +235,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
235
235
|
src: this.currentChildSrc
|
|
236
236
|
}]);
|
|
237
237
|
this.childInitialized = true;
|
|
238
|
+
// a new document starts in the iframe, it registers its own parent URL listeners
|
|
239
|
+
this.parentUrlListeners = {};
|
|
238
240
|
}
|
|
239
241
|
if (isHeightMessage(message)) {
|
|
240
242
|
this.resizedHeight = message[2];
|
|
@@ -246,6 +248,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
246
248
|
}
|
|
247
249
|
if (isReinitHeightMessage(message) && this.reinitHeight === 'on-message') {
|
|
248
250
|
this.resizedHeight = undefined;
|
|
251
|
+
// not maintained while the height came from the child
|
|
252
|
+
this.updateAspectRatioHeight();
|
|
249
253
|
this.updateStyle();
|
|
250
254
|
}
|
|
251
255
|
if (isStateChangeMessage(message)) {
|
|
@@ -283,7 +287,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
283
287
|
this.dispatchEvent(new CustomEvent('ready'));
|
|
284
288
|
this.updateStyle();
|
|
285
289
|
}
|
|
286
|
-
if (isMouseEventMessage(message)) {
|
|
290
|
+
if (isMouseEventMessage(message) && this.mouseEvents?.includes(message[2])) {
|
|
287
291
|
this.applySyncedMouseEvent(message);
|
|
288
292
|
}
|
|
289
293
|
if (isAddParentUrlListenerMessage(message)) {
|
|
@@ -311,7 +315,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
311
315
|
this.parsedSyncParams = this.syncParams !== null ? parseSyncParams(this.syncParams || '*') : undefined;
|
|
312
316
|
}
|
|
313
317
|
updateSrc() {
|
|
314
|
-
|
|
318
|
+
// an empty src is not known yet (<d-frame :src="url">), resolved against the page it would load the parent itself
|
|
319
|
+
if (!this.connected || !this.src)
|
|
315
320
|
return;
|
|
316
321
|
for (const [childHref, existingParentUrl] of Object.entries(this.parentUrlListeners)) {
|
|
317
322
|
const parentUrl = getParentUrl(this.fullSrc, childHref, window.location.href, this.parsedSyncParams, this.syncPath);
|
|
@@ -350,7 +355,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
350
355
|
this.log('debug', 'height of iframe based on direct attribute: ' + this.height);
|
|
351
356
|
wrapperHeight = this.height;
|
|
352
357
|
}
|
|
353
|
-
else if (this.aspectRatio !== null) {
|
|
358
|
+
else if (this.aspectRatio !== null && this.aspectRatioHeight) {
|
|
354
359
|
this.log('debug', 'height of iframe based on aspect ratio: ' + this.aspectRatioHeight);
|
|
355
360
|
wrapperHeight = this.aspectRatioHeight + 'px';
|
|
356
361
|
}
|
|
@@ -465,7 +470,13 @@ export class DFrameElement extends HTMLElement {
|
|
|
465
470
|
/* standard custom element callbacks */
|
|
466
471
|
connectedCallback() {
|
|
467
472
|
if (!this.debug) {
|
|
468
|
-
|
|
473
|
+
let globalDebug = null;
|
|
474
|
+
try {
|
|
475
|
+
globalDebug = window.localStorage.getItem('debug');
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
// storage is blocked in some contexts (sandboxed parent, blocked cookies)
|
|
479
|
+
}
|
|
469
480
|
if (globalDebug) {
|
|
470
481
|
const debugParts = globalDebug.split(',');
|
|
471
482
|
if (debugParts.includes('d-frame') || debugParts.includes('d-frame:' + this.id))
|
|
@@ -481,10 +492,12 @@ export class DFrameElement extends HTMLElement {
|
|
|
481
492
|
if (this.adapter.onStateChange) {
|
|
482
493
|
this.stopOnStateChange = this.adapter.onStateChange(() => { this.updateSrc(); });
|
|
483
494
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
495
|
+
if (this.aspectRatio !== null && !this.height) {
|
|
496
|
+
// measure synchronously: the observer only fires after the next layout, and the frame
|
|
497
|
+
// would be painted at the height of its loading slot in between
|
|
498
|
+
this.width = this.getBoundingClientRect().width;
|
|
499
|
+
this.updateAspectRatioHeight();
|
|
500
|
+
}
|
|
488
501
|
this.resizeObserver.observe(this);
|
|
489
502
|
window.addEventListener('message', this.boundOnMessage);
|
|
490
503
|
this.updateParsedSyncParams();
|
|
@@ -496,6 +509,12 @@ export class DFrameElement extends HTMLElement {
|
|
|
496
509
|
}
|
|
497
510
|
disconnectedCallback() {
|
|
498
511
|
this.log('debug', 'disconnected');
|
|
512
|
+
this.connected = false;
|
|
513
|
+
// the iframe is reloaded from its src attribute when the element is connected again:
|
|
514
|
+
// it must be the state reached by the child, then the child does the handshake again
|
|
515
|
+
this.childInitialized = false;
|
|
516
|
+
if (this.currentChildSrc)
|
|
517
|
+
this.iframeElement.setAttribute('src', this.currentChildSrc);
|
|
499
518
|
this.resizeObserver.disconnect();
|
|
500
519
|
window.removeEventListener('message', this.boundOnMessage);
|
|
501
520
|
this.stopOnStateChange?.();
|
|
@@ -508,16 +527,22 @@ export class DFrameElement extends HTMLElement {
|
|
|
508
527
|
/* reflected attributes */
|
|
509
528
|
static get observedAttributes() { return ['src', 'aspect-ratio', 'height', 'sync-params', 'sync-path', 'reload']; }
|
|
510
529
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
511
|
-
if (name === 'aspect-ratio')
|
|
530
|
+
if (name === 'aspect-ratio') {
|
|
512
531
|
this.updateAspectRatioHeight();
|
|
532
|
+
// also when the attribute is removed
|
|
533
|
+
this.updateStyle();
|
|
534
|
+
}
|
|
513
535
|
// if (name.startsWith('iframe-')) this.updateIframeExtraAttrs()
|
|
514
536
|
if (!this.connected)
|
|
515
537
|
return;
|
|
516
538
|
this.log('debug', 'attribute change', name, oldValue, newValue);
|
|
517
539
|
if (name === 'src')
|
|
518
540
|
this.updateSrc();
|
|
519
|
-
if (name === 'height')
|
|
541
|
+
if (name === 'height') {
|
|
542
|
+
// the aspect ratio height is not maintained while a height is set
|
|
543
|
+
this.updateAspectRatioHeight();
|
|
520
544
|
this.updateStyle();
|
|
545
|
+
}
|
|
521
546
|
if (name === 'sync-params') {
|
|
522
547
|
this.updateParsedSyncParams();
|
|
523
548
|
this.updateSrc();
|
|
@@ -529,10 +554,6 @@ export class DFrameElement extends HTMLElement {
|
|
|
529
554
|
this.log('error', 'reload attribute changed but the iframe has no current src');
|
|
530
555
|
return;
|
|
531
556
|
}
|
|
532
|
-
if (!this.iframeElement.contentWindow?.location) {
|
|
533
|
-
this.log('debug', 'reload attribute changed but no iframe contentWindow, is this a cross domain iframe ?');
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
557
|
if (!this.iframeLoaded) {
|
|
537
558
|
this.log('debug', 'reload attribute changed before initial loading was complete, ignore it');
|
|
538
559
|
return;
|
|
@@ -540,7 +561,12 @@ export class DFrameElement extends HTMLElement {
|
|
|
540
561
|
this.log('debug', 'reload iframe with current src', this.currentChildSrc);
|
|
541
562
|
this.childInitialized = false;
|
|
542
563
|
this.iframeElement.setAttribute('src', this.currentChildSrc);
|
|
543
|
-
|
|
564
|
+
try {
|
|
565
|
+
this.iframeElement.contentWindow?.location.reload();
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
// cross-origin child: reload() is forbidden, the src attribute set above already navigates the frame
|
|
569
|
+
}
|
|
544
570
|
}
|
|
545
571
|
}
|
|
546
572
|
}
|
package/lib/messages.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { isHttpUrl } from './utils/url.js';
|
|
1
2
|
export function isMouseEventMessage(message) {
|
|
2
|
-
return message[0] === 'df-global' && message[1] === 'mouse';
|
|
3
|
+
return message[0] === 'df-global' && message[1] === 'mouse' && typeof message[2] === 'string';
|
|
3
4
|
}
|
|
4
5
|
export function isInitChildMessage(message) {
|
|
5
6
|
return message[0] === 'df-child' && message[1] === 'init';
|
|
6
7
|
}
|
|
7
8
|
export function isHeightMessage(message) {
|
|
8
|
-
|
|
9
|
+
// the height is written in the style of the wrapper, anything else than a number would inject css
|
|
10
|
+
return message[0] === 'df-child' && message[1] === 'height' && typeof message[2] === 'number' && Number.isFinite(message[2]) && message[2] >= 0;
|
|
9
11
|
}
|
|
10
12
|
export function isReinitHeightMessage(message) {
|
|
11
13
|
return message[0] === 'df-child' && message[1] === 'reinit-height';
|
|
12
14
|
}
|
|
15
|
+
// the URL of the child becomes the src of the iframe when it is reloaded: a javascript: URL would run in the parent
|
|
13
16
|
export function isStateChangeMessage(message) {
|
|
14
|
-
return message[0] === 'df-child' && message[1] === 'stateChange';
|
|
17
|
+
return message[0] === 'df-child' && message[1] === 'stateChange' && (message[2] === 'push' || message[2] === 'replace') && isHttpUrl(message[3]);
|
|
15
18
|
}
|
|
16
19
|
export function isCustomMessage(message) {
|
|
17
20
|
return message[0] === 'df-child' && message[1] === 'custom';
|
|
@@ -20,7 +23,7 @@ export function isNotifMessage(message) {
|
|
|
20
23
|
return message[0] === 'df-child' && message[1] === 'notif';
|
|
21
24
|
}
|
|
22
25
|
export function isAddParentUrlListenerMessage(message) {
|
|
23
|
-
return message[0] === 'df-child' && message[1] === 'addParentUrlListener';
|
|
26
|
+
return message[0] === 'df-child' && message[1] === 'addParentUrlListener' && isHttpUrl(message[2]);
|
|
24
27
|
}
|
|
25
28
|
export function isRemoveParentUrlListenerMessage(message) {
|
|
26
29
|
return message[0] === 'df-child' && message[1] === 'removeParentUrlListener';
|
package/lib/utils/sync-params.js
CHANGED
|
@@ -3,7 +3,7 @@ const escapeRegExp = (str) => str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
|
3
3
|
export function parseSyncParams(syncParamsStr) {
|
|
4
4
|
const syncParams = [];
|
|
5
5
|
for (const syncParam of syncParamsStr.split(',')) {
|
|
6
|
-
const [pattern = '*', append] = syncParam.split(':');
|
|
6
|
+
const [pattern = '*', append] = syncParam.trim().split(':');
|
|
7
7
|
const regexpStr = '^' + escapeRegExp(pattern).replace(/\\\*/g, '(.*)') + '$';
|
|
8
8
|
syncParams.push({
|
|
9
9
|
regexp: new RegExp(regexpStr),
|
|
@@ -60,8 +60,8 @@ export function getParentUrl(fullSrc, childHref, currentParentHref, syncParams,
|
|
|
60
60
|
const childUrl = new URL(childHref);
|
|
61
61
|
if (syncParams) {
|
|
62
62
|
// remove existing keys that were deleted from the child
|
|
63
|
-
//
|
|
64
|
-
for (const key of parentUrl.searchParams.keys()
|
|
63
|
+
// copy the keys as we mutate parentUrl in the loop (not with Iterator.toArray(), missing in Safari < 18.4)
|
|
64
|
+
for (const key of Array.from(parentUrl.searchParams.keys())) {
|
|
65
65
|
for (const syncParam of syncParams) {
|
|
66
66
|
if (syncParam.append && !key.startsWith(syncParam.append))
|
|
67
67
|
continue;
|
|
@@ -78,10 +78,12 @@ export function getParentUrl(fullSrc, childHref, currentParentHref, syncParams,
|
|
|
78
78
|
for (const syncParam of syncParams) {
|
|
79
79
|
if (!key.match(syncParam.regexp))
|
|
80
80
|
continue;
|
|
81
|
-
if (srcUrl?.searchParams.get(key) === value)
|
|
82
|
-
continue;
|
|
83
81
|
const parentKey = syncParam.append ? (syncParam.append + key) : key;
|
|
84
|
-
|
|
82
|
+
// the value of src is the default one: no need to keep it in the parent, but a previous value must go
|
|
83
|
+
if (srcUrl.searchParams.get(key) === value)
|
|
84
|
+
parentUrl.searchParams.delete(parentKey);
|
|
85
|
+
else
|
|
86
|
+
parentUrl.searchParams.set(parentKey, value);
|
|
85
87
|
break;
|
|
86
88
|
}
|
|
87
89
|
});
|
|
@@ -92,8 +94,8 @@ export function getParentUrl(fullSrc, childHref, currentParentHref, syncParams,
|
|
|
92
94
|
parentUrl.hash = path;
|
|
93
95
|
}
|
|
94
96
|
else if (syncPath.startsWith('/')) {
|
|
95
|
-
// a full path is not accepted, only a path child of the base
|
|
96
|
-
if (path.startsWith('/'))
|
|
97
|
+
// a full path or another origin is not accepted, only a path child of the base
|
|
98
|
+
if (path.startsWith('/') || childUrl.origin !== srcUrl.origin)
|
|
97
99
|
return null;
|
|
98
100
|
if (path === '')
|
|
99
101
|
parentUrl.pathname = syncPath;
|
package/lib/utils/url.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare function getPathDirectory(path: string): string;
|
|
2
2
|
export declare function getPathWithTrailingSlash(path: string): string;
|
|
3
3
|
export declare function getUrlRelativePath(srcUrl: URL, currentUrl: URL): string;
|
|
4
|
+
export declare function isHttpUrl(value: unknown, base?: string): value is string;
|
package/lib/utils/url.js
CHANGED
|
@@ -20,3 +20,14 @@ export function getUrlRelativePath(srcUrl, currentUrl) {
|
|
|
20
20
|
return currentUrl.pathname;
|
|
21
21
|
return currentUrl.pathname.replace(srcDirectory, '');
|
|
22
22
|
}
|
|
23
|
+
// a URL that can be navigated to or used as a link without running code (no javascript:, data:...)
|
|
24
|
+
export function isHttpUrl(value, base) {
|
|
25
|
+
if (typeof value !== 'string')
|
|
26
|
+
return false;
|
|
27
|
+
try {
|
|
28
|
+
return ['http:', 'https:'].includes(new URL(value, base).protocol);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function isVIframeUiNotif(message) {
|
|
2
|
-
return message
|
|
2
|
+
return message?.vIframe === true && message.uiNotification !== undefined && message.uiNotification !== null;
|
|
3
3
|
}
|
|
4
4
|
export function convertVIframeUiNotif(notif) {
|
|
5
5
|
if (typeof notif === 'string')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { onScopeDispose, toRef, reactive } from 'vue';
|
|
2
2
|
export const useDFrameParentUrls = (dFrameContent, router) => {
|
|
3
|
-
console.warn('useDFrameParentUrls is deprecated, it is replaced by useParentUrl from @data-fair/frame/vue-router/use-parent-url.js');
|
|
3
|
+
console.warn('useDFrameParentUrls is deprecated, it is replaced by useParentUrl from @data-fair/frame/lib/vue-router/use-parent-url.js');
|
|
4
4
|
let parentUrls = reactive({});
|
|
5
5
|
let listeners = {};
|
|
6
6
|
const get = (childHref) => {
|
package/lib/vue-reactive/util.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export const applySearchParams = (reactiveParams, searchParams) => {
|
|
2
|
-
const existingKeys = Object.keys(reactiveParams);
|
|
3
2
|
searchParams.forEach((value, key) => {
|
|
4
3
|
reactiveParams[key] = value;
|
|
5
|
-
existingKeys.splice(existingKeys.indexOf(key), 1);
|
|
6
|
-
});
|
|
7
|
-
existingKeys.forEach(key => {
|
|
8
|
-
delete reactiveParams[key];
|
|
9
4
|
});
|
|
5
|
+
for (const key of Object.keys(reactiveParams)) {
|
|
6
|
+
if (!searchParams.has(key))
|
|
7
|
+
delete reactiveParams[key];
|
|
8
|
+
}
|
|
10
9
|
};
|
|
@@ -17,13 +17,23 @@ class VueRouterDFrameStateChangeAdapter {
|
|
|
17
17
|
if (!newRoute.startsWith('/'))
|
|
18
18
|
newRoute = '/' + newRoute;
|
|
19
19
|
element.log('debug', 'apply state change on vue.router', action, newRoute);
|
|
20
|
+
// a guard that throws rejects the navigation without calling afterEach: the parent stays on its URL,
|
|
21
|
+
// the child is brought back to it like after an aborted navigation
|
|
22
|
+
const onError = (err) => {
|
|
23
|
+
element.log('error', 'failed to apply state change on vue.router', err);
|
|
24
|
+
element.updateSrc();
|
|
25
|
+
};
|
|
20
26
|
if (action === 'push')
|
|
21
|
-
this.router.push(newRoute);
|
|
27
|
+
this.router.push(newRoute).catch(onError);
|
|
22
28
|
if (action === 'replace')
|
|
23
|
-
this.router.replace(newRoute);
|
|
29
|
+
this.router.replace(newRoute).catch(onError);
|
|
24
30
|
}
|
|
25
31
|
onStateChange(callback) {
|
|
26
|
-
|
|
32
|
+
// a cancelled navigation is superseded by a newer one: syncing the child from the parent URL
|
|
33
|
+
// would send back stale params while the child has already moved on (fast clicks in the child).
|
|
34
|
+
// An aborted one (4 = NavigationFailureType.aborted) was refused by a guard: the child must go back to the parent URL
|
|
35
|
+
return this.router.afterEach((_to, _from, failure) => { if (!failure || failure.type === 4)
|
|
36
|
+
callback(); });
|
|
27
37
|
}
|
|
28
38
|
}
|
|
29
39
|
export default (router) => new VueRouterDFrameStateChangeAdapter(router);
|
|
@@ -17,16 +17,21 @@ export const useParentUrl = (location, router, dFrameContent) => {
|
|
|
17
17
|
let listener = null;
|
|
18
18
|
const parentUrl = ref(null);
|
|
19
19
|
watch(() => toValue(location), (to) => {
|
|
20
|
+
const href = to === undefined ? undefined : router.resolve(to).href;
|
|
21
|
+
// a getter can return a new location object for the same target: keep the listener and its result
|
|
22
|
+
if (href === resolved)
|
|
23
|
+
return;
|
|
20
24
|
if (listener && resolved)
|
|
21
25
|
dFrameContent.removeParentUrlListener(resolved, listener);
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
resolved = href;
|
|
27
|
+
listener = null;
|
|
28
|
+
if (href === undefined) {
|
|
29
|
+
parentUrl.value = undefined;
|
|
24
30
|
}
|
|
25
31
|
else {
|
|
26
32
|
parentUrl.value = null;
|
|
27
|
-
resolved = router.resolve(to).href;
|
|
28
33
|
listener = (result) => { parentUrl.value = result; };
|
|
29
|
-
dFrameContent.addParentUrlListener(
|
|
34
|
+
dFrameContent.addParentUrlListener(href, listener);
|
|
30
35
|
}
|
|
31
36
|
}, { immediate: true });
|
|
32
37
|
onScopeDispose(() => {
|