🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

web-request-rpc

Package Overview
Dependencies
Maintainers
5
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

web-request-rpc - npm Package Compare versions

Comparing version
1.1.7
to
2.0.0
+41
WebAppWindowDialog.js
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
export class WebAppWindowDialog {
constructor() {
this._closeEventListeners = new Set();
}
addEventListener(name, listener) {
if(name !== 'close') {
throw new Error(`Unknown event "${name}".`);
}
if(typeof listener !== 'function') {
throw new TypeError('"listener" must be a function.');
}
this._closeEventListeners.add(listener);
}
removeEventListener(name, listener) {
if(name !== 'close') {
throw new Error(`Unknown event "${name}".`);
}
if(typeof listener !== 'function') {
throw new TypeError('"listener" must be a function.');
}
this._closeEventListeners.delete(listener);
}
show() {}
close() {
// emit event to all `close` event listeners
for(const listener of this._closeEventListeners) {
listener({});
}
}
destroy() {
this._closeEventListeners.clear();
}
}
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
import {WebAppWindowDialog} from './WebAppWindowDialog.js';
export class WebAppWindowInlineDialog extends WebAppWindowDialog {
constructor({url, handle, className}) {
super();
this.url = url;
this.handle = handle;
// create a top-level dialog overlay
this.dialog = document.createElement('dialog');
applyStyle(this.dialog, {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
'max-width': '100%',
'max-height': '100%',
display: 'none',
margin: 0,
padding: 0,
border: 'none',
background: 'transparent',
color: 'black',
'box-sizing': 'border-box',
overflow: 'hidden',
'z-index': 1000000
});
this.dialog.className = 'web-app-window';
if(typeof className === 'string') {
this.dialog.className = this.dialog.className + ' ' + className;
}
// ensure backdrop is transparent by default
const style = document.createElement('style');
style.appendChild(
document.createTextNode(`dialog.web-app-window::backdrop {
background-color: transparent;
}`));
// create flex container for iframe
this.container = document.createElement('div');
applyStyle(this.container, {
position: 'relative',
width: '100%',
height: '100%',
margin: 0,
padding: 0,
display: 'flex',
'flex-direction': 'column'
});
this.container.className = 'web-app-window-backdrop';
// create iframe
this.iframe = document.createElement('iframe');
this.iframe.src = url;
this.iframe.scrolling = 'auto';
applyStyle(this.iframe, {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 'none',
background: 'transparent',
overflow: 'hidden',
margin: 0,
padding: 0,
'flex-grow': 1
});
// assemble dialog
this.dialog.appendChild(style);
this.container.appendChild(this.iframe);
this.dialog.appendChild(this.container);
// a.document.appendChild(this.iframe);
// handle cancel (user pressed escape)
this.dialog.addEventListener('cancel', e => {
e.preventDefault();
this.hide();
});
// attach to DOM
document.body.appendChild(this.dialog);
this.handle = this.iframe.contentWindow;
}
show() {
this.dialog.style.display = 'block';
if(this.dialog.showModal) {
this.dialog.showModal();
}
}
close() {
this.dialog.style.display = 'none';
if(this.dialog.close) {
try {
this.dialog.close();
} catch(e) {
console.error(e);
}
}
super.close();
}
destroy() {
this.dialog.parentNode.removeChild(this.dialog);
super.destroy();
}
}
function applyStyle(element, style) {
for(const name in style) {
element.style[name] = style[name];
}
}
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
import {WebAppWindowDialog} from './WebAppWindowDialog.js';
export class WebAppWindowPopupDialog extends WebAppWindowDialog {
constructor({url, handle, bounds = {width: 500, height: 400}}) {
super();
this.url = url;
this.handle = handle;
this._locationChanging = false;
if(!handle) {
this._openWindow({url, name: 'web-app-window', bounds});
}
this.destroyed = false;
this._removeListeners = () => {};
}
show() {}
close() {
this.destroy();
}
destroy() {
if(this.handle && !this.destroyed) {
this.handle.close();
super.close();
this.handle = null;
this.destroyed = true;
this._removeListeners();
super.destroy();
}
}
isClosed() {
return !this.handle || this.handle.closed;
}
_openWindow({url, name, bounds}) {
const {x, y} = bounds;
let {width = 500, height = 400} = bounds;
width = Math.min(width, window.innerWidth);
height = Math.min(height, window.innerHeight);
const left = x !== undefined ?
x : window.screenX + (window.innerWidth - width) / 2;
const top = y !== undefined ?
y : window.screenY + (window.innerHeight - height) / 2;
const features =
'popup=yes,menubar=no,location=no,resizable=no,scrollbars=no,status=no,' +
`width=${width},height=${height},left=${left},top=${top}`;
this._locationChanging = true;
this.handle = window.open(url, name, features);
this._addListeners();
}
setLocation(url) {
this.url = url;
this._locationChanging = true;
this.handle.location.replace(url);
}
_addListeners() {
const destroyDialog = () => this.destroy();
// when a new URL loads in the dialog, clear the location changing flag
const loadDialog = () => {
this._locationChanging = false;
};
// when the dialog URL changes...
const unloadDialog = () => {
if(this._locationChanging) {
// a location change was expected, return
return;
}
// a location change was NOT expected, destroy the dialog
this.destroy();
};
this.handle.addEventListener('unload', unloadDialog);
this.handle.addEventListener('load', loadDialog);
// before the current window unloads, destroy the child dialog
window.addEventListener('beforeUnload', destroyDialog, {once: true});
// poll to check for closed window handle; necessary because cross domain
// windows will not emit any close-related events we can use here
const intervalId = setInterval(() => {
if(this.isClosed()) {
this.destroy();
clearInterval(intervalId);
}
}, 250);
// create listener clean up function
this._removeListeners = () => {
clearInterval(intervalId);
this.handle.removeListener('unload', unloadDialog);
this.handle.removeListener('load', loadDialog);
window.removeEventListener('beforeUnload', destroyDialog);
}
}
}
+6
-0
# web-request-rpc ChangeLog
## 2.0.0 - 2022-06-13
### Changed
- **BREAKING**: Enable the use of 1p popup dialogs or iframes when creating
new web app windows.
## 1.1.7 - 2021-01-22

@@ -4,0 +10,0 @@

+2
-2

@@ -30,3 +30,3 @@ /*!

* a handle) to send messages to
* (defaults to `window.parent || window.opener`).
* (defaults to `window.opener || window.parent`).
*

@@ -45,3 +45,3 @@ * @return a Promise that resolves to an RPC injector once connected.

self.origin = utils.parseUrl(origin).origin;
self._handle = options.handle || window.parent || window.opener;
self._handle = options.handle || window.opener || window.parent;

@@ -48,0 +48,0 @@ const pending = self._pending;

{
"name": "web-request-rpc",
"version": "1.1.7",
"version": "2.0.0",
"description": "Web Request RPC",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -53,3 +53,3 @@ /*!

* a handle) to listen for messages from
* (defaults to `window.parent || window.opener`).
* (defaults to `window.opener || window.parent`).
* [ignoreUnknownApi] `true` to ignore unknown API messages.

@@ -67,3 +67,3 @@ */

self.origin = utils.parseUrl(origin).origin;
self._handle = options.handle || window.parent || window.opener;
self._handle = options.handle || window.opener || window.parent;

@@ -70,0 +70,0 @@ const ignoreUnknownApi = (options.ignoreUnknownApi === 'true') || false;

/*!
* Copyright (c) 2017 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2017-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import {Client} from './Client.js';

@@ -44,2 +42,4 @@ import {Server} from './Server.js';

* loads the window after its construction.
* [bounds] a bounding rectangle (top, left, width, height) to
* use when creating a popup window.
*

@@ -53,6 +53,10 @@ * @return a Promise that resolves to an RPC injector once the window is

iframe,
dialog = null,
popup = false,
handle,
windowControl,
className,
customize
customize,
// top, left, width, height
bounds
} = {}) {

@@ -68,9 +72,15 @@ // disallow loading the same WebAppContext more than once

timeout,
dialog,
iframe,
popup,
handle,
windowControl,
className,
customize
customize,
bounds
});
// if the local window closes, close the control window as well
window.addEventListener('pagehide', () => this.close(), {once: true});
// define control class; this enables the WebApp that is running in the

@@ -77,0 +87,0 @@ // WebAppContext to control its UI or close itself down

/*!
* Copyright (c) 2017-2018 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2017-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import {WebAppWindowInlineDialog} from './WebAppWindowInlineDialog.js';
import {WebAppWindowPopupDialog} from './WebAppWindowPopupDialog.js';

@@ -18,13 +19,16 @@ // default timeout is 60 seconds

timeout = LOAD_WINDOW_TIMEOUT,
dialog = null,
handle,
iframe,
windowControl,
popup = false,
className = null,
customize = null
customize = null,
// top, left, width, height
bounds
} = {}) {
this.visible = false;
this.dialog = null;
this.iframe = null;
this.dialog = dialog;
this.handle = null;
this.popup = popup;
this.windowControl = null;
this._destroyed = false;
this._ready = false;

@@ -34,2 +38,5 @@ this._private = {};

if(handle && handle._dialog) {
this.dialog = dialog = handle._dialog;
}
// private to allow caller to track readiness

@@ -64,40 +71,9 @@ this._private._readyPromise = new Promise((resolve, reject) => {

}
if(this.dialog) {
this.dialog.parentNode.removeChild(this.dialog);
if(!this._destroyed) {
this.dialog.destroy();
this.dialog = null;
this._destroyed = true;
}
};
if(iframe) {
// TODO: validate `iframe` option as much as possible
if(!(typeof iframe === 'object' && iframe.contentWindow)) {
throw new TypeError('`options.iframe` must be an iframe element.');
}
this.windowControl = {
handle: iframe.contentWindow,
show() {
iframe.style.visibility = 'visible';
},
hide() {
iframe.style.visibility = 'hidden';
}
};
this.iframe = iframe;
this.handle = this.iframe.contentWindow;
return;
}
if(windowControl) {
// TODO: validate `windowControl`
this.windowControl = windowControl;
this.handle = this.windowControl.handle;
return;
}
if(handle) {
// TODO: validate `handle`
this.handle = handle;
return;
}
if(customize) {

@@ -109,86 +85,28 @@ if(!typeof customize === 'function') {

// create a top-level dialog overlay
this.dialog = document.createElement('dialog');
applyStyle(this.dialog, {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
'max-width': '100%',
'max-height': '100%',
display: 'none',
margin: 0,
padding: 0,
border: 'none',
background: 'transparent',
color: 'black',
'box-sizing': 'border-box',
overflow: 'hidden',
'z-index': 1000000
});
this.dialog.className = 'web-app-window';
if(typeof className === 'string') {
this.dialog.className = this.dialog.className + ' ' + className;
if(!this.dialog) {
if(this.popup) {
this.dialog = new WebAppWindowPopupDialog({url, handle, bounds});
} else {
this.dialog = new WebAppWindowInlineDialog({url, handle, className});
}
} else if(this.popup && bounds) {
// resize / re-position popup window as requested
if(bounds) {
const {top: y, left: x, width, height} = bounds;
if(x !== undefined && y !== undefined) {
this.dialog.handle.moveTo(x, y);
}
if(width !== undefined && height !== undefined) {
this.dialog.handle.resizeTo(width, height);
}
}
}
// ensure backdrop is transparent by default
const style = document.createElement('style');
style.appendChild(
document.createTextNode(`dialog.web-app-window::backdrop {
background-color: transparent;
}`));
// create flex container for iframe
this.container = document.createElement('div');
applyStyle(this.container, {
position: 'relative',
width: '100%',
height: '100%',
margin: 0,
padding: 0,
display: 'flex',
'flex-direction': 'column'
});
this.container.className = 'web-app-window-backdrop';
// create iframe
this.iframe = document.createElement('iframe');
this.iframe.src = url;
this.iframe.scrolling = 'auto';
applyStyle(this.iframe, {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 'none',
background: 'transparent',
overflow: 'hidden',
margin: 0,
padding: 0,
'flex-grow': 1
});
// assemble dialog
this.dialog.appendChild(style);
this.container.appendChild(this.iframe);
this.dialog.appendChild(this.container);
// handle cancel (user pressed escape)
this.dialog.addEventListener('cancel', e => {
e.preventDefault();
this.hide();
});
// attach to DOM
document.body.appendChild(this.dialog);
this.handle = this.iframe.contentWindow;
this.handle = this.dialog.handle;
if(customize) {
try {
customize({
dialog: this.dialog,
container: this.container,
iframe: this.iframe,
dialog: this.dialog.dialog,
container: this.dialog.container,
iframe: this.dialog.iframe,
webAppWindow: this

@@ -220,7 +138,4 @@ });

body.style.overflow = 'hidden';
if(this.dialog) {
this.dialog.style.display = 'block';
if(this.dialog.showModal) {
this.dialog.showModal();
}
if(!this._destroyed) {
this.dialog.show();
} else if(this.windowControl.show) {

@@ -245,11 +160,4 @@ this.windowControl.show();

}
if(this.dialog) {
this.dialog.style.display = 'none';
if(this.dialog.close) {
try {
this.dialog.close();
} catch(e) {
console.error(e);
}
}
if(!this._destroyed) {
this.dialog.close();
} else if(this.windowControl.hide) {

@@ -261,7 +169,1 @@ this.windowControl.hide();

}
function applyStyle(element, style) {
for(const name in style) {
element.style[name] = style[name];
}
}