Update gun.js

This commit is contained in:
Mark Nadal 2016-12-17 12:25:36 -08:00 committed by GitHub
parent ca30236be5
commit 53d5417d4a

862
gun.js
View File

@ -1,5 +1,5 @@
//console.log("!!!!!!!!!!!!!!!! WARNING THIS IS GUN 0.5 !!!!!!!!!!!!!!!!!!!!!!");
;(function(){
//console.log("!!!!!!!!!!!!!!!! WARNING THIS IS GUN 0.5 !!!!!!!!!!!!!!!!!!!!!!");
;(function(){
/* UNBUILD */
var root;
@ -119,7 +119,7 @@
t.r[k] = v;
return;
} t.r = t.r || [];
t.r.push(k);
t.r.push(k);
};
Type.obj.map = function(l, c, _){
var u, i = 0, x, r, ll, lle, f = fn_is(c);
@ -903,40 +903,40 @@
;(function(){
Gun.chain.opt = function(opt){
opt = opt || {};
var peers = obj_is(opt) ? opt.peers : opt;
if (text_is(peers)) {
peers = [peers];
}
if (list_is(peers)) {
peers = obj_map(peers, function (url, field, m) {
m(url, {});
});
}
if (!obj_is(opt)) {
opt = {};
}
opt.peers = peers;
var gun = this, at = gun._;
at.root = at.root || gun;
Gun.chain.opt = function(opt){
opt = opt || {};
var peers = obj_is(opt) ? opt.peers : opt;
if (text_is(peers)) {
peers = [peers];
}
if (list_is(peers)) {
peers = obj_map(peers, function (url, field, m) {
m(url, {});
});
}
if (!obj_is(opt)) {
opt = {};
}
opt.peers = peers;
var gun = this, at = gun._;
at.root = at.root || gun;
at.graph = at.graph || {};
at.dedup = new Dedup();
at.opt = at.opt || {};
at.opt.peers = Gun.obj.to(at.opt.peers || {}, peers);
Gun.obj.to(opt, at.opt);
Gun.on('opt', at);
if(!at.once){
gun.on('in', input, at);
gun.on('out', output, at);
}
at.once = true;
return gun;
}
at.opt = at.opt || {};
at.opt.peers = Gun.obj.to(at.opt.peers || {}, peers);
Gun.obj.to(opt, at.opt);
Gun.on('opt', at);
if(!at.once){
gun.on('in', input, at);
gun.on('out', output, at);
}
at.once = true;
return gun;
}
function output(at){
var cat = this, gun = cat.gun, tmp;
var cat = this, gun = cat.gun, tmp;
// TODO: BUG! Outgoing `get` to read from in memory!!!
if(at.get && get(at, cat)){ return }
//if(at.put){
@ -1034,8 +1034,8 @@
var de = this;
var now = Gun.time.is();
var oldest = now;
var maxAge = 5 * 60 * 1000;
// TODO: Gun.scheduler already does this? Reuse that.
var maxAge = 5 * 60 * 1000;
// TODO: Gun.scheduler already does this? Reuse that.
Gun.obj.map(de.cache, function (time, id) {
oldest = Math.min(now, time);
@ -1043,8 +1043,8 @@
return;
}
delete de.cache[id];
});
delete de.cache[id];
});
var done = Gun.obj.empty(de.cache);
@ -1435,8 +1435,8 @@
return gun;
}
function output(at){
var cat = this, gun = cat.gun, root = gun.Back(-1), put, get, tmp;
if(!at.gun){
var cat = this, gun = cat.gun, root = gun.Back(-1), put, get, tmp;
if(!at.gun){
at.gun = gun;
}
if(at.get && !at.get[_soul]){
@ -1750,7 +1750,7 @@
via: at,
put: at.put[cat.get]
}
} else {
if(obj_has(at.put, cat.get)){ return ev.off() }
at = {
@ -2142,13 +2142,13 @@
var put = {}, soul = Gun.node.soul(node);
if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + node + '"!')}) }
gun.put(Gun.obj.put(put, soul, Gun.val.rel.ify(soul)), cb, opt);
});
}, {wait:0});
}
}());
})(require, './api');
;require(function(module){
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
var root, noop = function(){};
if(typeof window !== 'undefined'){ root = window }
@ -2177,7 +2177,7 @@
gun.Back(-1).on('in', {'@': at['#']});
}
return;
}
}
if(Gun.obj.has(lex, '.')){var tmp = data[lex['.']];data = {_: data._};if(u !== tmp){data[lex['.']] = tmp}}
//console.log('@@@@@@@@@@@@local get', data, at);
gun.Back(-1).on('in', {'@': at['#'], put: Gun.graph.node(data)});
@ -2187,391 +2187,391 @@
Gun.on('get', get);
})(require, './adapters/localStorage');
;require(function(module){
var Gun = require('./gun');
// Check for stone-age browsers.
if (typeof JSON === 'undefined') {
throw new Error(
'Gun depends on JSON. Please load it first:\n' +
'ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js'
);
}
function Client (url, options) {
if (!(this instanceof Client)) {
return new Client(url, options);
}
this.url = Client.formatURL(url);
this.socket = null;
this.queue = [];
this.sid = Gun.text.random(10);
this.on = Gun.on;
this.options = options || {};
this.resetBackoff();
}
Client.prototype = {
constructor: Client,
drainQueue: function () {
var queue = this.queue;
var client = this;
// Reset the queue.
this.queue = [];
// Send each message.
queue.forEach(function (msg) {
client.send(msg);
});
return queue.length;
},
connect: function () {
var client = this;
var socket = new Client.WebSocket(this.url);
this.socket = socket;
// Forward messages into the emitter.
socket.addEventListener('message', function (msg) {
client.on('message', msg);
});
// Reconnect on close events.
socket.addEventListener('close', function () {
client.scheduleReconnect();
});
// Send the messages in the queue.
this.ready(function () {
client.drainQueue();
});
return socket;
},
resetBackoff: function () {
var backoff = this.options;
this.backoff = {
time: backoff.time || 100,
max: backoff.max || 30000,
factor: backoff.factor || 2
};
return this.backoff;
},
nextBackoff: function () {
var backoff = this.backoff;
var next = backoff.time * backoff.factor;
var max = backoff.max;
if (next > max) {
next = max;
}
return (backoff.time = next);
},
// Try to efficiently reconnect.
scheduleReconnect: function () {
var client = this;
var time = this.backoff.time;
this.nextBackoff();
setTimeout(function () {
client.connect();
client.ready(function () {
client.resetBackoff();
});
}, time);
},
isClosed: function () {
var socket = this.socket;
if (!socket) {
return true;
}
var state = socket.readyState;
if (state === socket.CLOSING || state === socket.CLOSED) {
return true;
}
return false;
},
ready: function (callback) {
var socket = this.socket;
var state = socket.readyState;
if (state === socket.OPEN) {
callback();
return;
}
if (state === socket.CONNECTING) {
socket.addEventListener('open', callback);
}
},
send: function (msg) {
if (this.isClosed()) {
this.queue.push(msg);
// Will send once connected.
this.connect();
return false;
}
var socket = this.socket;
// Make sure the socket is open.
this.ready(function () {
socket.send(msg);
});
return true;
}
};
if (typeof window !== 'undefined') {
Client.WebSocket = window.WebSocket ||
window.webkitWebSocket ||
window.mozWebSocket ||
null;
}
Client.isSupported = Client.WebSocket !== null;
// Ensure the protocol is correct.
Client.formatURL = function (url) {
return url.replace('http', 'ws');
};
// Send a message to a group of peers.
Client.broadcast = function (urls, msg) {
var pool = Client.pool;
msg.headers = msg.headers || {};
Gun.obj.map(urls, function (options, addr) {
var url = Client.formatURL(addr);
var peer = pool[url];
var envelope = {
headers: Gun.obj.to(msg.headers, {
'gun-sid': peer.sid
}),
body: msg.body
};
var serialized = Gun.text.ify(envelope);
peer.send(serialized);
});
};
// A map of URLs to client instances.
Client.pool = {};
// Close all WebSockets when the window closes.
if (typeof window !== 'undefined') {
window.addEventListener('unload', function () {
Gun.obj.map(Client.pool, function (client) {
if (client.isClosed()) {
return;
}
client.socket.close();
});
});
}
// Define client instances as gun needs them.
// Sockets will not be opened until absolutely necessary.
Gun.on('opt', function (ctx) {
var gun = ctx.gun;
var peers = gun.Back('opt.peers') || {};
Gun.obj.map(peers, function (options, addr) {
var url = Client.formatURL(addr);
// Ignore clients we've seen before.
if (Client.pool.hasOwnProperty(url)) {
return;
}
var client = new Client(url, options.backoff);
// Add it to the pool.
Client.pool[url] = client;
// Listen to incoming messages.
client.on('message', function (msg) {
var data;
try {
data = Gun.obj.ify(msg.data);
} catch (err) {
// Invalid message, discard it.
return;
}
if (!data || !data.body) {
return;
}
gun.on('in', data.body);
});
});
});
function request (peers, ctx) {
if (Client.isSupported) {
Client.broadcast(peers, { body: ctx });
}
}
// Broadcast the messages.
Gun.on('out', function (ctx) {
var gun = ctx.gun;
var peers = gun.Back('opt.peers') || {};
// Validate.
if (Gun.obj.empty(peers)) {
return;
}
request(peers, ctx);
});
request.jsonp = function (opt, cb) {
request.jsonp.ify(opt, function (url) {
if (!url) {
return;
}
request.jsonp.send(url, function (err, reply) {
cb(err, reply);
request.jsonp.poll(opt, reply);
}, opt.jsonp);
});
};
request.jsonp.send = function (url, cb, id) {
var js = document.createElement('script');
js.src = url;
js.onerror = function () {
(window[js.id] || function () {})(null, {
err: 'JSONP failed!'
});
};
window[js.id = id] = function (res, err) {
cb(err, res);
cb.id = js.id;
js.parentNode.removeChild(js);
delete window[cb.id];
};
js.async = true;
document.getElementsByTagName('head')[0].appendChild(js);
return js;
};
request.jsonp.poll = function (opt, res) {
if (!opt || !opt.base || !res || !res.headers || !res.headers.poll) {
return;
}
var polls = request.jsonp.poll.s = request.jsonp.poll.s || {};
polls[opt.base] = polls[opt.base] || setTimeout(function () {
var msg = {
base: opt.base,
headers: { pull: 1 }
};
request.each(opt.headers, function (header, name) {
msg.headers[name] = header;
});
request.jsonp(msg, function (err, reply) {
delete polls[opt.base];
var body = reply.body || [];
while (body.length && body.shift) {
var res = reply.body.shift();
if (res && res.body) {
request.createServer.ing(res, function () {
request(opt.base, null, null, res);
});
}
}
});
}, res.headers.poll);
};
request.jsonp.ify = function (opt, cb) {
var uri = encodeURIComponent, query = '?';
if (opt.url && opt.url.pathname) {
query = opt.url.pathname + query;
}
query = opt.base + query;
request.each((opt.url || {}).query, function (value, key) {
query += (uri(key) + '=' + uri(value) + '&');
});
if (opt.headers) {
query += uri('`') + '=' + uri(
JSON.stringify(opt.headers)
) + '&';
}
if (request.jsonp.max < query.length) {
return cb();
}
var random = Math.floor(Math.random() * (0xffff + 1));
query += (uri('jsonp') + '=' + uri(opt.jsonp = 'P' + random));
if (opt.body) {
query += '&';
var w = opt.body, wls = function (w, l, s) {
return uri('%') + '=' + uri(w+'-'+(l||w)+'/'+(s||w)) + '&' + uri('$') + '=';
;require(function(module){
var Gun = require('./gun');
// Check for stone-age browsers.
if (typeof JSON === 'undefined') {
throw new Error(
'Gun depends on JSON. Please load it first:\n' +
'ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js'
);
}
function Client (url, options) {
if (!(this instanceof Client)) {
return new Client(url, options);
}
this.url = Client.formatURL(url);
this.socket = null;
this.queue = [];
this.sid = Gun.text.random(10);
this.on = Gun.on;
this.options = options || {};
this.resetBackoff();
}
Client.prototype = {
constructor: Client,
drainQueue: function () {
var queue = this.queue;
var client = this;
// Reset the queue.
this.queue = [];
// Send each message.
queue.forEach(function (msg) {
client.send(msg);
});
return queue.length;
},
connect: function () {
var client = this;
var socket = new Client.WebSocket(this.url);
this.socket = socket;
// Forward messages into the emitter.
socket.addEventListener('message', function (msg) {
client.on('message', msg);
});
// Reconnect on close events.
socket.addEventListener('close', function () {
client.scheduleReconnect();
});
// Send the messages in the queue.
this.ready(function () {
client.drainQueue();
});
return socket;
},
resetBackoff: function () {
var backoff = this.options;
this.backoff = {
time: backoff.time || 100,
max: backoff.max || 30000,
factor: backoff.factor || 2
};
return this.backoff;
},
nextBackoff: function () {
var backoff = this.backoff;
var next = backoff.time * backoff.factor;
var max = backoff.max;
if (next > max) {
next = max;
}
if (typeof w != 'string') {
w = JSON.stringify(w);
query += uri('^') + '=' + uri('json') + '&';
}
w = uri(w);
var i = 0, l = w.length
, s = request.jsonp.max - (query.length + wls(l.toString()).length);
if (s < 0){
return cb();
}
while (w) {
cb(query + wls(i, (i += s), l) + w.slice(0, i));
w = w.slice(i);
}
} else {
cb(query);
}
};
request.jsonp.max = 2000;
request.each = function (obj, cb, as) {
if (!obj || !cb) {
return;
}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
cb.call(as, obj[key], key);
}
}
};
module.exports = Client;
})(require, './polyfill/request');
}());
return (backoff.time = next);
},
// Try to efficiently reconnect.
scheduleReconnect: function () {
var client = this;
var time = this.backoff.time;
this.nextBackoff();
setTimeout(function () {
client.connect();
client.ready(function () {
client.resetBackoff();
});
}, time);
},
isClosed: function () {
var socket = this.socket;
if (!socket) {
return true;
}
var state = socket.readyState;
if (state === socket.CLOSING || state === socket.CLOSED) {
return true;
}
return false;
},
ready: function (callback) {
var socket = this.socket;
var state = socket.readyState;
if (state === socket.OPEN) {
callback();
return;
}
if (state === socket.CONNECTING) {
socket.addEventListener('open', callback);
}
},
send: function (msg) {
if (this.isClosed()) {
this.queue.push(msg);
// Will send once connected.
this.connect();
return false;
}
var socket = this.socket;
// Make sure the socket is open.
this.ready(function () {
socket.send(msg);
});
return true;
}
};
if (typeof window !== 'undefined') {
Client.WebSocket = window.WebSocket ||
window.webkitWebSocket ||
window.mozWebSocket ||
null;
}
Client.isSupported = Client.WebSocket !== null;
// Ensure the protocol is correct.
Client.formatURL = function (url) {
return url.replace('http', 'ws');
};
// Send a message to a group of peers.
Client.broadcast = function (urls, msg) {
var pool = Client.pool;
msg.headers = msg.headers || {};
Gun.obj.map(urls, function (options, addr) {
var url = Client.formatURL(addr);
var peer = pool[url];
var envelope = {
headers: Gun.obj.to(msg.headers, {
'gun-sid': peer.sid
}),
body: msg.body
};
var serialized = Gun.text.ify(envelope);
peer.send(serialized);
});
};
// A map of URLs to client instances.
Client.pool = {};
// Close all WebSockets when the window closes.
if (typeof window !== 'undefined') {
window.addEventListener('unload', function () {
Gun.obj.map(Client.pool, function (client) {
if (client.isClosed()) {
return;
}
client.socket.close();
});
});
}
// Define client instances as gun needs them.
// Sockets will not be opened until absolutely necessary.
Gun.on('opt', function (ctx) {
var gun = ctx.gun;
var peers = gun.Back('opt.peers') || {};
Gun.obj.map(peers, function (options, addr) {
var url = Client.formatURL(addr);
// Ignore clients we've seen before.
if (Client.pool.hasOwnProperty(url)) {
return;
}
var client = new Client(url, options.backoff);
// Add it to the pool.
Client.pool[url] = client;
// Listen to incoming messages.
client.on('message', function (msg) {
var data;
try {
data = Gun.obj.ify(msg.data);
} catch (err) {
// Invalid message, discard it.
return;
}
if (!data || !data.body) {
return;
}
gun.on('in', data.body);
});
});
});
function request (peers, ctx) {
if (Client.isSupported) {
Client.broadcast(peers, { body: ctx });
}
}
// Broadcast the messages.
Gun.on('out', function (ctx) {
var gun = ctx.gun;
var peers = gun.Back('opt.peers') || {};
// Validate.
if (Gun.obj.empty(peers)) {
return;
}
request(peers, ctx);
});
request.jsonp = function (opt, cb) {
request.jsonp.ify(opt, function (url) {
if (!url) {
return;
}
request.jsonp.send(url, function (err, reply) {
cb(err, reply);
request.jsonp.poll(opt, reply);
}, opt.jsonp);
});
};
request.jsonp.send = function (url, cb, id) {
var js = document.createElement('script');
js.src = url;
js.onerror = function () {
(window[js.id] || function () {})(null, {
err: 'JSONP failed!'
});
};
window[js.id = id] = function (res, err) {
cb(err, res);
cb.id = js.id;
js.parentNode.removeChild(js);
delete window[cb.id];
};
js.async = true;
document.getElementsByTagName('head')[0].appendChild(js);
return js;
};
request.jsonp.poll = function (opt, res) {
if (!opt || !opt.base || !res || !res.headers || !res.headers.poll) {
return;
}
var polls = request.jsonp.poll.s = request.jsonp.poll.s || {};
polls[opt.base] = polls[opt.base] || setTimeout(function () {
var msg = {
base: opt.base,
headers: { pull: 1 }
};
request.each(opt.headers, function (header, name) {
msg.headers[name] = header;
});
request.jsonp(msg, function (err, reply) {
delete polls[opt.base];
var body = reply.body || [];
while (body.length && body.shift) {
var res = reply.body.shift();
if (res && res.body) {
request.createServer.ing(res, function () {
request(opt.base, null, null, res);
});
}
}
});
}, res.headers.poll);
};
request.jsonp.ify = function (opt, cb) {
var uri = encodeURIComponent, query = '?';
if (opt.url && opt.url.pathname) {
query = opt.url.pathname + query;
}
query = opt.base + query;
request.each((opt.url || {}).query, function (value, key) {
query += (uri(key) + '=' + uri(value) + '&');
});
if (opt.headers) {
query += uri('`') + '=' + uri(
JSON.stringify(opt.headers)
) + '&';
}
if (request.jsonp.max < query.length) {
return cb();
}
var random = Math.floor(Math.random() * (0xffff + 1));
query += (uri('jsonp') + '=' + uri(opt.jsonp = 'P' + random));
if (opt.body) {
query += '&';
var w = opt.body, wls = function (w, l, s) {
return uri('%') + '=' + uri(w+'-'+(l||w)+'/'+(s||w)) + '&' + uri('$') + '=';
}
if (typeof w != 'string') {
w = JSON.stringify(w);
query += uri('^') + '=' + uri('json') + '&';
}
w = uri(w);
var i = 0, l = w.length
, s = request.jsonp.max - (query.length + wls(l.toString()).length);
if (s < 0){
return cb();
}
while (w) {
cb(query + wls(i, (i += s), l) + w.slice(0, i));
w = w.slice(i);
}
} else {
cb(query);
}
};
request.jsonp.max = 2000;
request.each = function (obj, cb, as) {
if (!obj || !cb) {
return;
}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
cb.call(as, obj[key], key);
}
}
};
module.exports = Client;
})(require, './polyfill/request');
}());