Comparing version 1.3.5 to 1.4.0
{ | ||
"name": "sysend", | ||
"version": "1.3.5", | ||
"version": "1.4.0", | ||
"description": "Send messages to other tabs/windows in the same browser", | ||
"main": "sysend.js", | ||
"typings": "sysend.d.ts", | ||
"scripts": { | ||
@@ -7,0 +8,0 @@ }, |
@@ -1,9 +0,9 @@ | ||
[![Sysend.js Logo](https://github.com/jcubic/sysend.js/blob/master/assets/logo.svg?raw=true)](https://github.com/jcubic/sysend.js) | ||
![Sysend.js Logo](https://github.com/jcubic/sysend.js/blob/master/assets/logo.svg?raw=true) | ||
[![npm](https://img.shields.io/badge/npm-1.3.5-blue.svg)](https://www.npmjs.com/package/sysend) | ||
![bower](https://img.shields.io/badge/bower-1.3.5-yellow.svg) | ||
[![npm](https://img.shields.io/badge/npm-1.4.0-blue.svg)](https://www.npmjs.com/package/sysend) | ||
![bower](https://img.shields.io/badge/bower-1.4.0-yellow.svg) | ||
![downloads](https://img.shields.io/npm/dt/sysend.svg) | ||
[![jsdelivr](https://img.shields.io/jsdelivr/npm/hm/sysend)](https://www.jsdelivr.com/package/npm/sysend) | ||
# Send messages between browser tabs | ||
# [Send messages between browser tabs](https://github.com/jcubic/sysend.js/) | ||
@@ -14,3 +14,3 @@ sysend.js is a small library that allows to send messages between pages that are | ||
If your browser don't support BroadcastChannel (see [Can I Use](https://caniuse.com/#feat=broadcastchannel)) | ||
then you can send any object that can be serialized to JSON with BroadcastChannel you can send any object | ||
then you can send any object that can be serialized to JSON. With BroadcastChannel you can send any object | ||
(it will not be serialized to string but the values are limited to the ones that can be copied by | ||
@@ -20,3 +20,2 @@ the [structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone)). | ||
Tested on: | ||
@@ -28,2 +27,10 @@ | ||
## Note about Safari 7+ and Cross-Domain communication | ||
All cross-domain communication is disabled by default with Safari 7+. | ||
Because of a feature that block 3rd party tracking for iframe, and any | ||
iframe used for cross-domain communication runs in sandboxed environment. | ||
That's why this library like any other solution for cross-domain comunication, | ||
don't work on Safari. | ||
## Installation | ||
@@ -61,3 +68,2 @@ | ||
```javascript | ||
window.onload = function() { | ||
@@ -84,5 +90,16 @@ sysend.on('foo', function(message) { | ||
if you want to send custom data you can use serializer (new in 1.4.0). | ||
Example serializer can be [json-dry](https://github.com/11ways/json-dry). | ||
```javascript | ||
sysend.serializer(function(data) { | ||
return Dry.stringify(data); | ||
}, function(string) { | ||
return Dry.parse(string); | ||
}); | ||
```` | ||
## Demo | ||
Open this [demo page](http://jcubic.pl/sysend.php) in two tabs/windows. | ||
Open this [demo page](http://jcubic.pl/sysend.php) in two tabs/windows (there is also link to other domain). | ||
@@ -93,6 +110,7 @@ ## API | ||
* on(name, callback) - callback(object, name) - add event of specified name | ||
* off(name [, callback]) - remove callback | ||
* broadcast(name [, object]) - send object and fire all events with specified name (in different pages that register callback using on). You can also just send notification without object | ||
* proxy(url) - create iframe proxy for different domain, the targer domain/url should have [proxy.html](https://github.com/jcubic/sysend.js/blob/master/proxy.html) file. If url domain is the same as page domain, it's ignored. So you can put both proxy calls on both domains (new in 1.3.0) | ||
* `on(name, callback)` - `callback(object, name)` - add event of specified name | ||
* `off(name [, callback])` - remove callback | ||
* `broadcast(name [, object])` - send object and fire all events with specified name (in different pages that register callback using on). You can also just send notification without object | ||
* `proxy(url)` - create iframe proxy for different domain, the targer domain/url should have [proxy.html](https://github.com/jcubic/sysend.js/blob/master/proxy.html) file. If url domain is the same as page domain, it's ignored. So you can put both proxy calls on both domains (new in 1.3.0) | ||
* `serializer(to_string, from_string)` - add serializer and deserializer functions (new in 1.4.0) | ||
@@ -99,0 +117,0 @@ ## License |
310
sysend.js
/**@license | ||
* sysend.js - send messages between browser windows/tabs version 1.3.5 | ||
* sysend.js - send messages between browser windows/tabs version 1.4.0 | ||
* | ||
@@ -25,58 +25,3 @@ * Copyright (C) 2014-2021 Jakub T. Jankiewicz <https://jcubic.pl/me> | ||
var random_value = Math.random(); | ||
// we use id because storage event is not executed if message was not | ||
// changed, and we want it if user send same object twice (before it will | ||
// be removed) | ||
var id = 0; | ||
// we need to clean up localStorage if broadcast on unload | ||
// because setTimeout will never fire, even setTimeout 0 | ||
var re = new RegExp('^' + uniq_prefix); | ||
for(var key in localStorage) { | ||
if (key.match(re)) { | ||
localStorage.removeItem(key); | ||
} | ||
} | ||
function get(key) { | ||
return localStorage.getItem(uniq_prefix + key); | ||
} | ||
function set(key, value) { | ||
// storage event is not fired when value is set first time | ||
if (id == 0) { | ||
localStorage.setItem(uniq_prefix + key, random_value); | ||
} | ||
localStorage.setItem(uniq_prefix + key, value); | ||
} | ||
function remove(key) { | ||
localStorage.removeItem(uniq_prefix + key); | ||
} | ||
function to_json(input) { | ||
// save random_value in storage to fix issue in IE that storage event | ||
// is fired on same page where setItem was called | ||
var obj = [id++, random_value]; | ||
// undefined in array get stringified as null | ||
if (typeof input != 'undefined') { | ||
obj.push(input); | ||
} | ||
return JSON.stringify(obj); | ||
} | ||
function from_json(json) { | ||
return JSON.parse(json); | ||
} | ||
var host = (function() { | ||
var a = document.createElement('a'); | ||
return function(url) { | ||
a.href = url; | ||
return a.host; | ||
}; | ||
})(); | ||
function send_to_iframes(key, data) { | ||
// propagate events to iframes | ||
iframes.forEach(function(iframe) { | ||
var payload = { | ||
name: uniq_prefix, | ||
key: key, | ||
data: data | ||
}; | ||
iframe.window.postMessage(JSON.stringify(payload), "*"); | ||
}); | ||
} | ||
var serializer = {}; | ||
// object with user events as keys and values arrays of callback functions | ||
@@ -87,61 +32,16 @@ var callbacks = {}; | ||
var channel; | ||
if (typeof window.BroadcastChannel === 'function') { | ||
channel = new window.BroadcastChannel(uniq_prefix); | ||
channel.addEventListener('message', function(event) { | ||
if (event.target.name === uniq_prefix) { | ||
var key = event.data && event.data.name; | ||
if (callbacks[key]) { | ||
callbacks[key].forEach(function(fn) { | ||
fn(event.data.data, key); | ||
}); | ||
} | ||
} | ||
}); | ||
} else { | ||
window.addEventListener('storage', function(e) { | ||
// prevent event to be executed on remove in IE | ||
if (e.key.match(re) && index++ % 2 === 0) { | ||
var key = e.key.replace(re, ''); | ||
if (callbacks[key]) { | ||
var value = e.newValue || get(key); | ||
if (value && value != random_value) { | ||
var obj = JSON.parse(value); | ||
if (obj && obj[1] != random_value) { | ||
// don't call on remove | ||
callbacks[key].forEach(function(fn) { | ||
fn(obj[2], key); | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}, false); | ||
} | ||
// ref: https://stackoverflow.com/a/326076/387194 | ||
function is_iframe() { | ||
try { | ||
return window.self !== window.top; | ||
} catch (e) { | ||
return true; | ||
} | ||
} | ||
if (is_iframe()) { | ||
window.addEventListener('message', function(e) { | ||
if (typeof e.data === 'string' && e.data.match(prefix_re)) { | ||
try { | ||
var payload = JSON.parse(e.data); | ||
if (payload && payload.name === uniq_prefix) { | ||
sysend.broadcast(payload.key, payload.data); | ||
} | ||
} catch(e) { | ||
// ignore wrong JSON, the message don't came from Sysend | ||
// even that the string have unix_prefix, this is just in case | ||
} | ||
} | ||
}); | ||
} | ||
return { | ||
// we use id because storage event is not executed if message was not | ||
// changed, and we want it if user send same object twice (before it will | ||
// be removed) | ||
var id = 0; | ||
// ------------------------------------------------------------------------- | ||
init(); | ||
// ------------------------------------------------------------------------- | ||
var serialize = make_process(serializer, 'to'); | ||
var unserialize = make_process(serializer, 'from'); | ||
// ------------------------------------------------------------------------- | ||
var sysend = { | ||
broadcast: function(event, message) { | ||
if (channel) { | ||
channel.postMessage({name: event, data: message}); | ||
channel.postMessage({name: event, data: serialize(message)}); | ||
} else { | ||
@@ -156,2 +56,10 @@ set(event, to_json(message)); | ||
}, | ||
serializer: function(to, from) { | ||
if (typeof to !== 'function' || typeof from !== 'function') { | ||
throw new Error('sysend::serializer: Invalid argument, expecting' + | ||
' function'); | ||
} | ||
serializer.to = to; | ||
serializer.from = from; | ||
}, | ||
proxy: function(url) { | ||
@@ -168,3 +76,4 @@ if (typeof url === 'string' && host(url) !== window.location.host) { | ||
setTimeout(function() { | ||
throw new Error('html proxy file not found on "' + url + '" url'); | ||
throw new Error('html proxy file not found on "' + url + | ||
'" url'); | ||
}, 0); | ||
@@ -175,2 +84,4 @@ iframe.removeEventListener('error', handler); | ||
var win; | ||
// fix for Safari | ||
// https://stackoverflow.com/q/42632188/387194 | ||
try { | ||
@@ -208,2 +119,171 @@ win = iframe.contentWindow; | ||
}; | ||
// ------------------------------------------------------------------------- | ||
function get(key) { | ||
return localStorage.getItem(uniq_prefix + key); | ||
} | ||
// ------------------------------------------------------------------------- | ||
function set(key, value) { | ||
// storage event is not fired when value is set first time | ||
if (id == 0) { | ||
localStorage.setItem(uniq_prefix + key, random_value); | ||
} | ||
localStorage.setItem(uniq_prefix + key, value); | ||
} | ||
// ------------------------------------------------------------------------- | ||
function remove(key) { | ||
localStorage.removeItem(uniq_prefix + key); | ||
} | ||
// ------------------------------------------------------------------------- | ||
function make_process(object, prop) { | ||
var labels = { | ||
from: 'Unserialize', | ||
to: 'Serialize' | ||
}; | ||
var prefix_message = labels[prop] + ' Error: '; | ||
return function(data) { | ||
var fn = object[prop]; | ||
try { | ||
if (fn) { | ||
return fn(data); | ||
} | ||
return data; | ||
} catch (e) { | ||
console.warn(prefix_message + e.message); | ||
} | ||
}; | ||
} | ||
// ------------------------------------------------------------------------- | ||
// ref: https://stackoverflow.com/a/326076/387194 | ||
// ------------------------------------------------------------------------- | ||
function is_iframe() { | ||
try { | ||
return window.self !== window.top; | ||
} catch (e) { | ||
return true; | ||
} | ||
} | ||
// ------------------------------------------------------------------------- | ||
function send_to_iframes(key, data) { | ||
// propagate events to iframes | ||
iframes.forEach(function(iframe) { | ||
var payload = { | ||
name: uniq_prefix, | ||
key: key, | ||
data: data | ||
}; | ||
iframe.window.postMessage(JSON.stringify(payload), "*"); | ||
}); | ||
} | ||
// ------------------------------------------------------------------------- | ||
function to_json(input) { | ||
// save random_value in storage to fix issue in IE that storage event | ||
// is fired on same page where setItem was called | ||
var obj = [id++, random_value]; | ||
// undefined in array get stringified as null | ||
if (typeof input != 'undefined') { | ||
obj.push(input); | ||
} | ||
var data = serialize(obj); | ||
if (data === obj) { | ||
return JSON.stringify(obj); | ||
} | ||
return data; | ||
} | ||
// ------------------------------------------------------------------------- | ||
function from_json(json) { | ||
var result = unserialize(json); | ||
if (result === json) { | ||
return JSON.parse(json); | ||
} | ||
return result; | ||
} | ||
// ------------------------------------------------------------------------- | ||
var host = (function() { | ||
if (typeof URL !== 'undefined') { | ||
return function(url) { | ||
url = new URL(url); | ||
return url.host; | ||
}; | ||
} | ||
var a = document.createElement('a'); | ||
return function(url) { | ||
a.href = url; | ||
return a.host; | ||
}; | ||
})(); | ||
// ------------------------------------------------------------------------- | ||
function invoke(key, data) { | ||
callbacks[key].forEach(function(fn) { | ||
fn(data, key); | ||
}); | ||
} | ||
// ------------------------------------------------------------------------- | ||
function is_private_mode() { | ||
try { | ||
localStorage.setItem(uniq_prefix, 1); | ||
localStorage.removeItem(uniq_prefix); | ||
return false; | ||
} catch (e) { | ||
return true; | ||
} | ||
} | ||
// ------------------------------------------------------------------------- | ||
function init() { | ||
// we need to clean up localStorage if broadcast called on unload | ||
// because setTimeout will never fire, even setTimeout 0 | ||
var re = new RegExp('^' + uniq_prefix); | ||
for(var key in localStorage) { | ||
if (key.match(re)) { | ||
localStorage.removeItem(key); | ||
} | ||
} | ||
if (typeof window.BroadcastChannel === 'function') { | ||
channel = new window.BroadcastChannel(uniq_prefix); | ||
channel.addEventListener('message', function(event) { | ||
if (event.target.name === uniq_prefix) { | ||
var key = event.data && event.data.name; | ||
if (callbacks[key]) { | ||
invoke(key, unserialize(event.data.data)); | ||
} | ||
} | ||
}); | ||
} else if (is_private_mode()) { | ||
console.warn('Your browser don\'t support localStorgage. ' + | ||
'In Safari this is most of the time because ' + | ||
'of "Private Browsing Mode"'); | ||
} else { | ||
window.addEventListener('storage', function(e) { | ||
// prevent event to be executed on remove in IE | ||
if (e.key.match(re) && index++ % 2 === 0) { | ||
var key = e.key.replace(re, ''); | ||
if (callbacks[key]) { | ||
var value = e.newValue || get(key); | ||
if (value && value != random_value) { | ||
var obj = from_json(value); | ||
// don't call on remove | ||
if (obj && obj[1] != random_value) { | ||
invoke(key, obj[2]); | ||
} | ||
} | ||
} | ||
} | ||
}, false); | ||
} | ||
if (is_iframe()) { | ||
window.addEventListener('message', function(e) { | ||
if (typeof e.data === 'string' && e.data.match(prefix_re)) { | ||
try { | ||
var payload = JSON.parse(e.data); | ||
if (payload && payload.name === uniq_prefix) { | ||
sysend.broadcast(payload.key, unserialize(payload.data)); | ||
} | ||
} catch(e) { | ||
// ignore wrong JSON, the message don't came from Sysend | ||
// even that the string have unix_prefix, this is just in case | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
return sysend; | ||
}); |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
17863
6
300
118
1