Comparing version 1.14.3 to 1.15.0
{ | ||
"name": "sysend", | ||
"version": "1.14.3", | ||
"version": "1.15.0", | ||
"description": "Web application synchronization between different tabs", | ||
@@ -5,0 +5,0 @@ "main": "sysend.js", |
@@ -5,4 +5,4 @@ <p align="center"> | ||
[![npm](https://img.shields.io/badge/npm-1.14.3-blue.svg)](https://www.npmjs.com/package/sysend) | ||
![bower](https://img.shields.io/badge/bower-1.14.3-yellow.svg) | ||
[![npm](https://img.shields.io/badge/npm-1.15.0-blue.svg)](https://www.npmjs.com/package/sysend) | ||
![bower](https://img.shields.io/badge/bower-1.15.0-yellow.svg) | ||
![downloads](https://img.shields.io/npm/dt/sysend.svg) | ||
@@ -78,2 +78,51 @@ [![jsdelivr](https://img.shields.io/jsdelivr/npm/hm/sysend)](https://www.jsdelivr.com/package/npm/sysend) | ||
### Windows/tabs tracking | ||
Tracking is high level API build on top of `on()` and `broadcast()`, that allows to manage windows/tabs. You can sent message directly to other windows/tabs: | ||
```javascript | ||
sysend.track('message', ({data, origin}) => { | ||
console.log(`${origin} send message "${data}"`); | ||
}); | ||
sysend.post('<ID>', 'Hello other window/tab'); | ||
``` | ||
and listen to events like: | ||
```javascript | ||
sysend.track('open', (data) => { | ||
console.log(`${data.id} window/tab just opened`); | ||
}); | ||
``` | ||
Other tracking events includes: close/primary/secondary executed when window/tab is closed or become primary or secondary. Track method was added in version 1.6.0. Another required event is `ready` (added in 1.10.0) that should be used when you want to get list of windows/tabs: | ||
```javascript | ||
sysend.track('ready', () => { | ||
sysend.list().then(tabs => { | ||
console.log(tabs); | ||
}); | ||
}); | ||
``` | ||
with `list()` method and `open`/`close` events you can implement dynamic list of windows/tab. That will change when new window/tab is open or close. | ||
In version 1.15.0 new API was added called `rpc()` (build on top of tracking mechanism) that allow to use RPC (Remote Procedure Call) between open windows/tabs. | ||
```javascript | ||
const rpc = sysend.rpc({ | ||
get_message() { | ||
return document.querySelector('input').value; | ||
} | ||
}); | ||
button.addEventListener('click', () => { | ||
rpc.get_message('<ID>').then(message => { | ||
console.log(`Message from other tab is "${message}"`); | ||
}).catch(e => { | ||
console.log(`get_message (ERROR) ${e.message}`); | ||
}); | ||
}); | ||
``` | ||
### Cross-Domain communication | ||
@@ -131,6 +180,2 @@ | ||
## Articles | ||
* [CSRF Protection Problem and How to Fix it](https://www.freecodecamp.org/news/csrf-protection-problem-and-how-to-fix-it/) | ||
* [Synchronizacja stanu aplikacji www między zakładkami](https://bulldogjob.pl/news/1804-synchronizacja-stanu-aplikacji-www-miedzy-zakladkami) | ||
## API | ||
@@ -151,8 +196,8 @@ | ||
| `track(event, callback)` | track inter window communication events | event - any of the strings: `"open"`, `"close"`, `"primary"`, <br>`"secondary"`, `"message"`<br>callback - different function depend on the event:<br>* `"message"` - `{data, origin}` - where data is anything the `post()` sends, and origin is `id` of the sender.<br>* `"open"` - `{count, primary, id}` when new window/tab is opened<br>* `"close"` - `{count, primary, id, self}` when window/tab is closed<br>* `"primary"` and `"secondary"` function has no arguments and is called when window/tab become secondary or primary.<br>* `"ready"` - event when tracking is ready. | 1.6.0 except `ready` - 1.10.0 | | ||
| `untrack(event [,callback])` | remove sigle event listener all all listeners for a given event | event - any of the strings `'open'`, `'close'`, `'primary'`, `'secondary'`, or `'message'`. | 1.6.0 | | ||
| `untrack(event [,callback])` | remove single event listener all listeners for a given event | event - any of the strings `'open'`, `'close'`, `'primary'`, `'secondary'`, or `'message'`. | 1.6.0 | | ||
| `isPrimary()` | function returns true if window is primary (first open or last that remain) | NA | 1.6.0 | | ||
| `channel()` | function restrict cross domain communication to only allowed domains. You need to call this function on proxy iframe to limit number of domains (origins) that can listen and send events. | any number of origins (e.g. 'http://localhost:8080' or 'https://jcubic.github.io') you can also use valid URL. | 1.10.0 | | ||
| `useLocalStorage([toggle])` | Function set or toggle localStorage mode. | argument is optional and can be `true` or `false`. | 1.14.0 | | ||
| `rpc(object): Promise<fn(id, ...args): Promise>` | Function accept an object with methods and return a Promise that resolve to object with same methods but async. The result function accept first additional argument that is ID of window/tab that it should sent request to. The other window/tab call the function and return value resolve original promise. | 1.15.0 | | ||
To see details of using the API, see [demo.html source code](https://github.com/jcubic/sysend.js/blob/master/demo.html) or [TypeScript definition file](https://github.com/jcubic/sysend.js/blob/master/sysend.d.ts). | ||
@@ -164,7 +209,18 @@ | ||
And the name of the library is just random word "sy" and "send" suffix. But it can be an backronym for **Synchronizing Send** as in sychronizing application between browser tabs. | ||
And the name of the library is just random word "sy" and "send" suffix. But it can be an backronym for **Synchronizing Send** as in synchronizing application between browser tabs. | ||
## Articles | ||
* [CSRF Protection Problem and How to Fix it](https://www.freecodecamp.org/news/csrf-protection-problem-and-how-to-fix-it/) | ||
* [Synchronizacja stanu aplikacji www między zakładkami](https://bulldogjob.pl/news/1804-synchronizacja-stanu-aplikacji-www-miedzy-zakladkami) | ||
## Press | ||
The library was featured in: | ||
* [Web Tools Weekly](https://webtoolsweekly.com/archives/issue-378/) | ||
* [JavaScript Weekly](https://javascriptweekly.com/issues/581) | ||
* [Impressive Webs](https://www.impressivewebs.com/most-interesting-front-end-developer-tools-2021/) | ||
* [Front-end Architecture](https://frontend-architecture.com/2022/03/30/messaging-between-browser-tabs/) | ||
## License | ||
Copyright (C) 2014-2022 [Jakub T. Jankiewicz](https://jcubic.pl/me)<br/> | ||
Copyright (C) 2014-2023 [Jakub T. Jankiewicz](https://jcubic.pl/me)<br/> | ||
Released under the [MIT license](https://opensource.org/licenses/MIT) | ||
@@ -171,0 +227,0 @@ |
/**@license | ||
* sysend.js - send messages between browser windows/tabs version 1.14.3 | ||
* sysend.js - send messages between browser windows/tabs version 1.15.0 | ||
* | ||
* Copyright (C) 2014-2022 Jakub T. Jankiewicz <https://jcubic.pl/me> | ||
* Copyright (C) 2014-2023 Jakub T. Jankiewicz <https://jcubic.pl/me> | ||
* Released under the MIT license | ||
@@ -28,6 +28,12 @@ * | ||
isPrimary(): boolean; | ||
rpc<T extends Array<unknown>, U>(object: Record<string, (...args: T) => U>): Promise<Record<string, (id: string, ...args: T) => Promise<U>>> | ||
} | ||
//Promise<Record<string, (id: string, ...args: T) => Promise<U>>; | ||
//type RPC<args extend Array | ||
declare const sysend: Sysend; | ||
export default sysend; |
108
sysend.js
/**@license | ||
* sysend.js - send messages between browser windows/tabs version 1.14.3 | ||
* sysend.js - send messages between browser windows/tabs version 1.15.0 | ||
* | ||
* Copyright (C) 2014-2022 Jakub T. Jankiewicz <https://jcubic.pl/me> | ||
* Copyright (C) 2014-2023 Jakub T. Jankiewicz <https://jcubic.pl/me> | ||
* Released under the MIT license | ||
@@ -46,2 +46,3 @@ * | ||
var target_count = 1; | ||
var rpc_count = 0; | ||
var domains; | ||
@@ -92,8 +93,8 @@ | ||
}, | ||
proxy: function() { | ||
[].slice.call(arguments).forEach(function(url) { | ||
proxy: function(...args) { | ||
args.forEach(function(url) { | ||
if (typeof url === 'string' && host(url) !== window.location.host) { | ||
domains = domains || []; | ||
domains.push(origin(url)); | ||
var iframe = document.createElement('iframe'); | ||
const iframe = document.createElement('iframe'); | ||
iframe.style.width = iframe.style.height = 0; | ||
@@ -103,3 +104,3 @@ iframe.style.position = 'absolute'; | ||
iframe.style.border = 'none'; | ||
var proxy_url = url; | ||
let proxy_url = url; | ||
if (!url.match(/\.html$/)) { | ||
@@ -116,3 +117,3 @@ proxy_url = url.replace(/\/$/, '') + '/proxy.html'; | ||
iframe.addEventListener('load', function handler() { | ||
var win; | ||
let win; | ||
// fix for Safari | ||
@@ -184,7 +185,7 @@ // https://stackoverflow.com/q/42632188/387194 | ||
list: function() { | ||
var id = list_id++; | ||
var marker = { target: target_id, id: id }; | ||
var timer = delay(sysend.timeout); | ||
const id = list_id++; | ||
const marker = { target: target_id, id: id }; | ||
const timer = delay(sysend.timeout); | ||
return new Promise(function(resolve) { | ||
var ids = []; | ||
const ids = []; | ||
sysend.on('__window_ack__', function(data) { | ||
@@ -204,4 +205,4 @@ if (data.origin.target === target_id && data.origin.id === id) { | ||
}, | ||
channel: function() { | ||
domains = [].slice.apply(arguments).map(origin); | ||
channel: function(...args) { | ||
domains = args.map(origin); | ||
return sysend; | ||
@@ -219,2 +220,62 @@ }, | ||
}, | ||
rpc: function(object) { | ||
const prefix = ++rpc_count; | ||
const req = `__${prefix}_rpc_request__`; | ||
const res = `__${prefix}_rpc_response__`; | ||
let request_index = 0; | ||
const timeout = 1000; | ||
function request(id, method, args = []) { | ||
const req_id = ++request_index; | ||
return new Promise((resolve, reject) => { | ||
sysend.track('message', function handler({data, origin}) { | ||
if (data.type === res) { | ||
const { result, error, id: res_id } = data; | ||
if (origin === id && req_id === res_id) { | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(result); | ||
} | ||
clearTimeout(timer); | ||
sysend.untrack('message', handler); | ||
} | ||
} | ||
}); | ||
sysend.post(id, { method, id: req_id, type: req, args }); | ||
const timer = setTimeout(() => { | ||
reject(new Error('Timeout error')); | ||
}, timeout); | ||
}); | ||
} | ||
sysend.track('message', async function handler({ data, origin }) { | ||
if (data.type == req) { | ||
const { method, args, id } = data; | ||
const type = res; | ||
if (Object.hasOwn(object, method)) { | ||
try { | ||
unpromise(object[method](...args), function(result) { | ||
sysend.post(origin, { result, id, type }); | ||
}, function(error) { | ||
sysend.post(origin, { error: error.message, id, type }); | ||
}); | ||
} catch(e) { | ||
sysend.post(origin, { error: e.message, id, type }); | ||
} | ||
} else { | ||
sysend.post(origin, { error: 'Method not found', id, type }); | ||
} | ||
} | ||
}); | ||
const error_msg = 'You need to specify the target window/tab'; | ||
return Object.fromEntries(Object.keys(object).map(name => { | ||
return [name, (id, ...args) => { | ||
if (!id) { | ||
return Promise.reject(new Error(error_msg)); | ||
} | ||
return request(id, name, args); | ||
}]; | ||
})); | ||
} | ||
}; | ||
@@ -254,2 +315,20 @@ // ------------------------------------------------------------------------- | ||
// ------------------------------------------------------------------------- | ||
function is_promise(obj) { | ||
return obj && typeof object == 'object' && | ||
typeof object.then === 'function'; | ||
} | ||
// ------------------------------------------------------------------------- | ||
function unpromise(obj, callback, error = null) { | ||
if (is_promise(obj)) { | ||
const ret = obj.then(callback); | ||
if (error === null) { | ||
return ret; | ||
} else { | ||
return ret.catch(error); | ||
} | ||
} else { | ||
return callback(obj); | ||
} | ||
} | ||
// ------------------------------------------------------------------------- | ||
function delay(time) { | ||
@@ -356,4 +435,3 @@ return function() { | ||
// ------------------------------------------------------------------------- | ||
function trigger(arr) { | ||
var args = [].slice.call(arguments, 1); | ||
function trigger(arr, ...args) { | ||
arr.forEach(function(fn) { | ||
@@ -360,0 +438,0 @@ fn.apply(null, args); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43639
760
225