function fixJSON(key, value) { if (value instanceof Error) return { message: value.message, stack: value.stack, name: value.name, code: value.code, } return value } function isOriginCompatible(a, b) { return !a || a === '*' || a.slice(0, b.length) === b } module.exports = function (win, origin) { var _cb, _msg if(origin && origin[0] === '/') { origin = location.origin + origin } function post(msg) { if (!win) _msg = msg else win.postMessage(JSON.stringify(msg, fixJSON), origin || '*') } function gotWin(_win) { win = _win if (_msg) { var msg = _msg _msg = null post(msg) } } return function (read) { function onMessage(e) { if(isOriginCompatible(origin, e.origin)) { if(!win) gotWin(e.source) if(!origin) origin = e.origin var msg = JSON.parse(e.data) switch (msg[0]) { case 'read': return onRead(msg[1]) case 'next': return onNext(msg[1], msg[2]) } } } function onRead(end) { read(end, function (end, data) { post(['next', end, data]) }) } function onNext(end, data) { if (end) window.removeEventListener('message', onMessage, false) if (!_cb) return console.error('unexpected data', end, data) var cb = _cb _cb = null return cb(end, data) } window.addEventListener('message', onMessage, false) return function (end, cb) { if(_cb) return cb(new Error('read too soon')) _cb = cb post(['read', end]) } } }