function isOriginCompatible(a, b) { return !a || a === '*' || a.slice(0, b.length) === b } module.exports = function (win, origin) { var cbs = [], queue = [], _read if(origin && origin[0] === '/') { origin = location.origin + origin } function gotWin(_win) { win = _win if(_read) doRead() } function onMessage(e) { if(isOriginCompatible(origin, e.origin)) { if(!win) gotWin(e.source) if(!origin) origin = e.origin if(cbs.length) cbs.shift()(null, e.data) else queue.push(e.data) } } var sinkEnded, sourceEnded window.addEventListener('message', onMessage, false) return { source: function (end, cb) { if(sourceEnded = end) { window.removeEventListener('message', onMessage, false) return cb(null) } if(queue.length) cb(null, queue.shift()) else cbs.push(cb) }, sink: function (read) { _read = read if(win) doRead() } } function doRead() { _read(null, function next(end, data) { if(sinkEnded = end) return win.postMessage(data, origin || '*') _read(null, next) }) } }