@data-fair/frame 0.18.6 → 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.d.ts +2 -1
- package/lib/DFrameElement.js +49 -15
- 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/state-change-adapter.d.ts +1 -1
- package/lib/vue-reactive/state-change-adapter.js +1 -1
- package/lib/vue-reactive/util.js +4 -5
- package/lib/vue-router/state-change-adapter.d.ts +1 -1
- 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.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ParentMessage } from './messages.js';
|
|
2
2
|
export interface StateChangeAdapter {
|
|
3
3
|
stateChange(action: 'push' | 'replace', newUrl: URL, element: DFrameElement): void;
|
|
4
|
-
onStateChange(callback: () => void): void;
|
|
4
|
+
onStateChange(callback: () => void): () => void;
|
|
5
5
|
}
|
|
6
6
|
export declare class DFrameElement extends HTMLElement {
|
|
7
7
|
get id(): string;
|
|
@@ -54,6 +54,7 @@ export declare class DFrameElement extends HTMLElement {
|
|
|
54
54
|
private resizeObserver;
|
|
55
55
|
private boundOnMessage;
|
|
56
56
|
private mouseEventListeners;
|
|
57
|
+
private stopOnStateChange;
|
|
57
58
|
private parentUrlListeners;
|
|
58
59
|
get actualAspectRatio(): number;
|
|
59
60
|
constructor();
|
package/lib/DFrameElement.js
CHANGED
|
@@ -17,6 +17,7 @@ class WindowStateChangeAdapter {
|
|
|
17
17
|
// the idea being that the child makes itself wholly compatible with d-frame and changes its navigation logic to fit it
|
|
18
18
|
// but the parent should be left as untouched as possible and uses d-frame as a black box (custom element)
|
|
19
19
|
window.addEventListener('popstate', callback);
|
|
20
|
+
return () => { window.removeEventListener('popstate', callback); };
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
// we try to adapt to some of the conventions found out there
|
|
@@ -55,7 +56,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
55
56
|
}
|
|
56
57
|
get src() { return this.getAttribute('src'); }
|
|
57
58
|
set src(value) { this.setAttribute('src', value); }
|
|
58
|
-
get fullSrc() { return this.src
|
|
59
|
+
get fullSrc() { return new URL(this.src, window.location.href).href; }
|
|
59
60
|
get scrolling() { return this.getAttribute('scrolling'); }
|
|
60
61
|
set scrolling(value) { this.setAttribute('scrolling', value); }
|
|
61
62
|
get aspectRatio() {
|
|
@@ -108,9 +109,9 @@ export class DFrameElement extends HTMLElement {
|
|
|
108
109
|
}
|
|
109
110
|
set syncPath(value) {
|
|
110
111
|
if (value !== null)
|
|
111
|
-
this.setAttribute('sync-
|
|
112
|
+
this.setAttribute('sync-path', value);
|
|
112
113
|
else
|
|
113
|
-
this.removeAttribute('sync-
|
|
114
|
+
this.removeAttribute('sync-path');
|
|
114
115
|
}
|
|
115
116
|
get stateChangeEvents() { return this.hasAttribute('state-change-events'); }
|
|
116
117
|
set stateChangeEvents(value) {
|
|
@@ -169,6 +170,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
169
170
|
resizeObserver;
|
|
170
171
|
boundOnMessage = (e) => this.onMessage(e);
|
|
171
172
|
mouseEventListeners = {};
|
|
173
|
+
stopOnStateChange;
|
|
172
174
|
parentUrlListeners = {};
|
|
173
175
|
get actualAspectRatio() {
|
|
174
176
|
if (this.aspectRatio !== 'auto')
|
|
@@ -233,6 +235,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
233
235
|
src: this.currentChildSrc
|
|
234
236
|
}]);
|
|
235
237
|
this.childInitialized = true;
|
|
238
|
+
// a new document starts in the iframe, it registers its own parent URL listeners
|
|
239
|
+
this.parentUrlListeners = {};
|
|
236
240
|
}
|
|
237
241
|
if (isHeightMessage(message)) {
|
|
238
242
|
this.resizedHeight = message[2];
|
|
@@ -244,6 +248,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
244
248
|
}
|
|
245
249
|
if (isReinitHeightMessage(message) && this.reinitHeight === 'on-message') {
|
|
246
250
|
this.resizedHeight = undefined;
|
|
251
|
+
// not maintained while the height came from the child
|
|
252
|
+
this.updateAspectRatioHeight();
|
|
247
253
|
this.updateStyle();
|
|
248
254
|
}
|
|
249
255
|
if (isStateChangeMessage(message)) {
|
|
@@ -281,7 +287,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
281
287
|
this.dispatchEvent(new CustomEvent('ready'));
|
|
282
288
|
this.updateStyle();
|
|
283
289
|
}
|
|
284
|
-
if (isMouseEventMessage(message)) {
|
|
290
|
+
if (isMouseEventMessage(message) && this.mouseEvents?.includes(message[2])) {
|
|
285
291
|
this.applySyncedMouseEvent(message);
|
|
286
292
|
}
|
|
287
293
|
if (isAddParentUrlListenerMessage(message)) {
|
|
@@ -309,7 +315,8 @@ export class DFrameElement extends HTMLElement {
|
|
|
309
315
|
this.parsedSyncParams = this.syncParams !== null ? parseSyncParams(this.syncParams || '*') : undefined;
|
|
310
316
|
}
|
|
311
317
|
updateSrc() {
|
|
312
|
-
|
|
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)
|
|
313
320
|
return;
|
|
314
321
|
for (const [childHref, existingParentUrl] of Object.entries(this.parentUrlListeners)) {
|
|
315
322
|
const parentUrl = getParentUrl(this.fullSrc, childHref, window.location.href, this.parsedSyncParams, this.syncPath);
|
|
@@ -348,7 +355,7 @@ export class DFrameElement extends HTMLElement {
|
|
|
348
355
|
this.log('debug', 'height of iframe based on direct attribute: ' + this.height);
|
|
349
356
|
wrapperHeight = this.height;
|
|
350
357
|
}
|
|
351
|
-
else if (this.aspectRatio !== null) {
|
|
358
|
+
else if (this.aspectRatio !== null && this.aspectRatioHeight) {
|
|
352
359
|
this.log('debug', 'height of iframe based on aspect ratio: ' + this.aspectRatioHeight);
|
|
353
360
|
wrapperHeight = this.aspectRatioHeight + 'px';
|
|
354
361
|
}
|
|
@@ -463,7 +470,13 @@ export class DFrameElement extends HTMLElement {
|
|
|
463
470
|
/* standard custom element callbacks */
|
|
464
471
|
connectedCallback() {
|
|
465
472
|
if (!this.debug) {
|
|
466
|
-
|
|
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
|
+
}
|
|
467
480
|
if (globalDebug) {
|
|
468
481
|
const debugParts = globalDebug.split(',');
|
|
469
482
|
if (debugParts.includes('d-frame') || debugParts.includes('d-frame:' + this.id))
|
|
@@ -477,7 +490,13 @@ export class DFrameElement extends HTMLElement {
|
|
|
477
490
|
this.log('debug', 'connected');
|
|
478
491
|
this.connected = true;
|
|
479
492
|
if (this.adapter.onStateChange) {
|
|
480
|
-
this.adapter.onStateChange(() => { this.updateSrc(); });
|
|
493
|
+
this.stopOnStateChange = this.adapter.onStateChange(() => { this.updateSrc(); });
|
|
494
|
+
}
|
|
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();
|
|
481
500
|
}
|
|
482
501
|
this.resizeObserver.observe(this);
|
|
483
502
|
window.addEventListener('message', this.boundOnMessage);
|
|
@@ -490,8 +509,16 @@ export class DFrameElement extends HTMLElement {
|
|
|
490
509
|
}
|
|
491
510
|
disconnectedCallback() {
|
|
492
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);
|
|
493
518
|
this.resizeObserver.disconnect();
|
|
494
519
|
window.removeEventListener('message', this.boundOnMessage);
|
|
520
|
+
this.stopOnStateChange?.();
|
|
521
|
+
this.stopOnStateChange = undefined;
|
|
495
522
|
for (const [eventType, listener] of Object.entries(this.mouseEventListeners)) {
|
|
496
523
|
document.removeEventListener(eventType, listener, true);
|
|
497
524
|
delete this.mouseEventListeners[eventType];
|
|
@@ -500,16 +527,22 @@ export class DFrameElement extends HTMLElement {
|
|
|
500
527
|
/* reflected attributes */
|
|
501
528
|
static get observedAttributes() { return ['src', 'aspect-ratio', 'height', 'sync-params', 'sync-path', 'reload']; }
|
|
502
529
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
503
|
-
if (name === 'aspect-ratio')
|
|
530
|
+
if (name === 'aspect-ratio') {
|
|
504
531
|
this.updateAspectRatioHeight();
|
|
532
|
+
// also when the attribute is removed
|
|
533
|
+
this.updateStyle();
|
|
534
|
+
}
|
|
505
535
|
// if (name.startsWith('iframe-')) this.updateIframeExtraAttrs()
|
|
506
536
|
if (!this.connected)
|
|
507
537
|
return;
|
|
508
538
|
this.log('debug', 'attribute change', name, oldValue, newValue);
|
|
509
539
|
if (name === 'src')
|
|
510
540
|
this.updateSrc();
|
|
511
|
-
if (name === 'height')
|
|
541
|
+
if (name === 'height') {
|
|
542
|
+
// the aspect ratio height is not maintained while a height is set
|
|
543
|
+
this.updateAspectRatioHeight();
|
|
512
544
|
this.updateStyle();
|
|
545
|
+
}
|
|
513
546
|
if (name === 'sync-params') {
|
|
514
547
|
this.updateParsedSyncParams();
|
|
515
548
|
this.updateSrc();
|
|
@@ -521,10 +554,6 @@ export class DFrameElement extends HTMLElement {
|
|
|
521
554
|
this.log('error', 'reload attribute changed but the iframe has no current src');
|
|
522
555
|
return;
|
|
523
556
|
}
|
|
524
|
-
if (!this.iframeElement.contentWindow?.location) {
|
|
525
|
-
this.log('debug', 'reload attribute changed but no iframe contentWindow, is this a cross domain iframe ?');
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
557
|
if (!this.iframeLoaded) {
|
|
529
558
|
this.log('debug', 'reload attribute changed before initial loading was complete, ignore it');
|
|
530
559
|
return;
|
|
@@ -532,7 +561,12 @@ export class DFrameElement extends HTMLElement {
|
|
|
532
561
|
this.log('debug', 'reload iframe with current src', this.currentChildSrc);
|
|
533
562
|
this.childInitialized = false;
|
|
534
563
|
this.iframeElement.setAttribute('src', this.currentChildSrc);
|
|
535
|
-
|
|
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
|
+
}
|
|
536
570
|
}
|
|
537
571
|
}
|
|
538
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) => {
|
|
@@ -4,7 +4,7 @@ declare class VueReactiveDFrameStateChangeAdapter implements StateChangeAdapter
|
|
|
4
4
|
reactiveParams: Reactive<Record<string, string>>;
|
|
5
5
|
constructor(reactiveParams: Reactive<Record<string, string>>);
|
|
6
6
|
stateChange(action: 'push' | 'replace', newUrl: URL): void;
|
|
7
|
-
onStateChange(callback: () => void): void;
|
|
7
|
+
onStateChange(callback: () => void): () => void;
|
|
8
8
|
}
|
|
9
9
|
declare const _default: (reactiveParams: Reactive<Record<string, string>>) => VueReactiveDFrameStateChangeAdapter;
|
|
10
10
|
export default _default;
|
|
@@ -9,7 +9,7 @@ class VueReactiveDFrameStateChangeAdapter {
|
|
|
9
9
|
applySearchParams(this.reactiveParams, newUrl.searchParams);
|
|
10
10
|
}
|
|
11
11
|
onStateChange(callback) {
|
|
12
|
-
watch(this.reactiveParams, callback);
|
|
12
|
+
return watch(this.reactiveParams, callback);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
export default (reactiveParams) => new VueReactiveDFrameStateChangeAdapter(reactiveParams);
|
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
|
};
|
|
@@ -4,7 +4,7 @@ declare class VueRouterDFrameStateChangeAdapter implements StateChangeAdapter {
|
|
|
4
4
|
router: Router;
|
|
5
5
|
constructor(router: Router);
|
|
6
6
|
stateChange(action: 'push' | 'replace', newUrl: URL, element: DFrameElement): void;
|
|
7
|
-
onStateChange(callback: () => void): void;
|
|
7
|
+
onStateChange(callback: () => void): () => void;
|
|
8
8
|
}
|
|
9
9
|
declare const _default: (router: Router) => VueRouterDFrameStateChangeAdapter;
|
|
10
10
|
export default _default;
|