Skip to content

Instantly share code, notes, and snippets.

@javimosch
Created July 17, 2018 13:56
Show Gist options
  • Save javimosch/e66a112442ba1f4a90b5ad876d52d004 to your computer and use it in GitHub Desktop.
Save javimosch/e66a112442ba1f4a90b5ad876d52d004 to your computer and use it in GitHub Desktop.
Cross Window Communication With sessionStorage (Case of use: Cordova's InAppBrowser)
window.sessionStorageChannel = (function() {
const LISTEN_HANDLERS_INTERVAL = 1000
const LISTEN_PENDINGS_INTERVAL = 200
const LISTEN_RESOLVED_INTERVAL = 200
const set = (n, v, d) => window.sessionStorage.setItem(n, v || d || '')
const get = (n, d) => window.sessionStorage.getItem(n) || d || ''
const getJson = (n, d) => JSON.parse(get(n, (d && JSON.stringify(d)) || '{}'))
const setJson = (n, json) => set(n, JSON.stringify(json || {}), "{}")
const getHandlersFrom = (nsp) => getJson(nsp + '_handlers')
const setHandlersFrom = (nsp, json) => setJson(nsp + '_handlers', json)
const getPendingFrom = (nsp, d) => getJson(nsp + '_pendings', d)
const setPendingFrom = (nsp, json) => setJson(nsp + '_pendings', json)
const getResolvedFrom = (nsp, d) => getJson(nsp + '_resolved', d)
const setResolvedFrom = (nsp, json) => setJson(nsp + '_resolved', json)
const ID = () => '_' + Math.random().toString(36).substr(2, 9)
var scope = {
handlers: {},
listenToHandlersFromInterval: null,
listenToPendingsFromInterval: null,
listenToResolvedFromInterval: null,
resolversQueue: {},
resolvedDefaults: {
list: []
},
pendingsDefaults: {
list: []
}
}
function listenToResolvedFrom(namespace) {
listenToResolvedFromInterval = setInterval(function() {
var resolvedList = getResolvedFrom(namespace, scope.pendingsDefaults)
var toDelete = []
resolvedList.list.forEach(resolvedItem => {
if (!!scope.resolversQueue[resolvedItem.id]) {
if (!!resolvedItem.err) {
scope.resolversQueue[resolvedItem.id].reject(resolvedItem.err)
} else {
scope.resolversQueue[resolvedItem.id].resolve(resolvedItem.result)
}
delete scope.resolversQueue[resolvedItem.id];
toDelete.push(resolvedItem.id)
} else {
console.warn('no resolvers found for', resolvedItem.name, resolvedItem.id)
toDelete.push(resolvedItem.id)
}
})
resolvedList.list = resolvedList.list.filter(item => !toDelete.find(i => i.toString() == item.id.toString()))
setResolvedFrom(namespace, resolvedList)
}, LISTEN_RESOLVED_INTERVAL)
}
function listenToPendingsFrom(namespace) {
listenToPendingsFromInterval = setInterval(function() {
var pendings = getPendingFrom(namespace, scope.pendingsDefaults)
var toDelete = []
pendings.list.forEach(pending => {
if (!!scope.handlers[pending.name]) {
try {
var res = scope.handlers[pending.name].apply({}, pending.args)
if (res instanceof Promise) {
res.then(r => handleHandlerResolve(r, pending, namespace)).catch(err => handleHandlerReject(err, pending, namespace))
} else {
handleHandlerResolve(res, pending, namespace)
}
} catch (err) {
handleHandlerReject(err, pending, namespace)
}
toDelete.push(pending.id)
} else {
console.warn('no handler for pending', pending.name, pending.id)
}
})
pendings.list = pendings.list.filter(item => !toDelete.find(i => i.toString() == item.id.toString()))
setPendingFrom(namespace, pendings)
}, LISTEN_PENDINGS_INTERVAL)
}
function handleHandlerResolve(result, item, namespace) {
console.log(namespace, name, 'resolve', result)
var json = getResolvedFrom(namespace, scope.resolvedDefaults)
if (!json.list.find(i => i.id == item.id)) {
json.list.push({
name: item.name,
id: item.id,
result: result
})
}
setResolvedFrom(namespace, json)
}
function handleHandlerReject(err, item, namespace) {
console.log(namespace, name, 'reject', err.stack)
var json = getResolvedFrom(namespace, scope.resolvedDefaults)
if (!json.list.find(i => i.id == item.id)) {
json.list.push({
name: item.name,
id: item.id,
err: err.stack
})
}
setResolvedFrom(namespace, json)
}
function setHandler(name, handler, namespace, scope) {
scope.handlers[name] = handler
var json = getHandlersFrom(namespace)
json[name] = true
setHandlersFrom(namespace, json)
}
function listenToHandlersFrom(namespace, self) {
listenToHandlersFromInterval = setInterval(function() {
var handlers = getHandlersFrom(namespace)
Object.keys(handlers).forEach(key => {
if (!self[key]) {
self[key] = function() {
return new Promise((resolve, reject) => {
if (!!scope.resolversQueue[key]) {
return reject('Already in queue...')
}
var uniqueId = ID()
var payload = {
name: key,
id: uniqueId,
args: Array.prototype.slice.call(arguments)
}
var pendings = getPendingFrom(namespace, scope.pendingsDefaults)
pendings.list.push(payload)
setPendingFrom(namespace, pendings)
scope.resolversQueue[uniqueId] = {
resolve: resolve,
reject: reject
}
})
}
}
})
}, LISTEN_HANDLERS_INTERVAL)
}
return {
clean: function() {
setPendingFrom('cordova', scope.pendingsDefaults)
},
cordova: function() {
var self = {
$set: (name, handler) => setHandler(name, handler, 'cordova', scope)
}
listenToHandlersFrom('website', self)
listenToPendingsFrom('website')
listenToResolvedFrom('website')
return self;
},
website: function() {
var self = {
$set: (name, handler) => setHandler(name, handler, 'website', scope)
}
listenToHandlersFrom('cordova', self)
listenToPendingsFrom('cordova')
listenToResolvedFrom('cordova')
return self;
}
}
})()
/*
sessionStorageChannelTest()
function sessionStorageChannelTest() {
var comm = window.comm = sessionStorageChannel.cordova()
comm.$set('openExternalLink', function(params) {
console.log('openExternalLink implentation')
//window.open(params.url, '_system', 'location=yes');
})
var webComm = window.webComm = sessionStorageChannel.website()
webComm.$set('fixCordovaExternalLinks', function() {
console.log('fixCordovaExternalLinks implementation')
})
sessionStorageChannel.clean()
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment