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 } var cmds = { read: 'r', readEnd: 'R', next: 'n', nextEnd: 'N', } module.exports = function (win, origin) { var _cb, _msg if(origin && origin[0] === '/') { origin = location.origin + origin } function post(cmd, data) { var msg = cmd + data if (!win) _msg = msg else win.postMessage(msg, 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 switch (e.data[0]) { case cmds.read: return onRead(null) case cmds.readEnd: return onRead(JSON.parse(e.data.substr(1))) case cmds.next: return onNext(null, e.data.substr(1)) case cmds.nextEnd: return onNext(JSON.parse(e.data.substr(1))) } } } function onRead(end) { read(end, function (end, data) { if (end) post(cmds.nextEnd, JSON.stringify(end, fixJSON)) else post(cmds.next, 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 if (end) post(cmds.readEnd, JSON.stringify(end, fixJSON)) else post(cmds.read) } } }