(function () { var Frame = {}; var uid = function () { return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) .toString(32).replace(/\./g, ''); }; // create an invisible iframe with a given source // append it to a parent element // execute a callback when it has loaded var create = Frame.create = function (parent, src, onload, timeout) { var iframe = document.createElement('iframe'); timeout = timeout || 10000; var to = window.setTimeout(function () { onload('[timeoutError] could not load iframe at ' + src); }, timeout); iframe.setAttribute('id', 'cors-store'); iframe.onload = function (e) { onload(void 0, iframe, e); window.clearTimeout(to); }; // We must pass a unique parameter here to avoid cache problems in Firefox with // the NoScript plugin: if the iframe's content is taken from the cache, the JS // is not executed with NoScript.... iframe.setAttribute('src', src + '?t=' + new Date().getTime()); parent.appendChild(iframe); }; /* given an iframe with an rpc script loaded, create a frame object with an asynchronous 'send' method */ var open = Frame.open = function (e, A, timeout) { var win = e.contentWindow; var frame = {}; var listeners = {}; var timeouts = {}; timeout = timeout || 5000; var accepts = frame.accepts = function (o) { return A.some(function (e) { switch (typeof(e)) { case 'string': return e === o; case 'object': return e.test(o); } }); }; var changeHandlers = frame.changeHandlers = []; var change = frame.change = function (f) { if (typeof(f) !== 'function') { throw new Error('[Frame.change] expected callback'); } changeHandlers.push(f); }; var _listener = function (e) { if (!frame.accepts(e.origin)) { console.log("message from %s rejected!", e.origin); return; } var message = JSON.parse(e.data); var uid = message._uid; var error = message.error; var data = message.data; if (!uid) { console.log("No uid!"); return; } if (uid === 'change' && changeHandlers.length) { changeHandlers.forEach(function (f) { f(data); }); return; } if (timeouts[uid]) { window.clearTimeout(timeouts[uid]); } if (listeners[uid]) { listeners[uid](error, data, e); delete listeners[uid]; } }; window.addEventListener('message', _listener); var close = frame.close = function () { window.removeEventListener('message', _listener); }; /* method (string): (set|get|remove) key (string) data (string) cb (function) */ var send = frame.send = function (method, key, data, cb) { var req = { method: method, key: key, data: data, }; var id = req._uid = uid(); // uid must not equal 'change' while(id === 'change') { id = req._uid = uid(); } if (typeof(cb) === 'function') { //console.log("setting callback!"); listeners[id] = cb; //console.log("setting timeout of %sms", timeout); timeouts[id] = window.setTimeout(function () { // when the callback is executed it will clear this timeout cb('[TimeoutError] request timed out after ' + timeout + 'ms'); }, timeout); } else { console.log(typeof(cb)); } win.postMessage(JSON.stringify(req), '*'); }; var set = frame.set = function (key, val, cb) { send('set', key, val, cb); }; var batchset = frame.setBatch = function (map, cb) { send('batchset', void 0, map, cb); }; var get = frame.get = function (key, cb) { send('get', key, void 0, cb); }; var batchget = frame.getBatch = function (keys, cb) { send('batchget', void 0, keys, cb); }; var remove = frame.remove = function (key, cb) { send('remove', key, void 0, cb); }; var batchremove = frame.removeBatch = function (keys, cb) { send('batchremove', void 0, keys, cb); }; var keys = frame.keys = function (cb) { send('keys', void 0, void 0, cb); }; return frame; }; if (typeof(module) !== 'undefined' && module.exports) { module.exports = Frame; } else if (typeof(define) === 'function' && define.amd) { define(['jquery'], function ($) { return Frame; }); } else { window.Frame = Frame; } }());