fixed nasty union bug but need test, key opt but need test, no false trigger, val unique check, tab respects instances, http header sent safety.

This commit is contained in:
Mark Nadal 2015-06-17 02:29:08 -07:00
parent 04059b8feb
commit b2af477752
6 changed files with 184 additions and 142 deletions

View File

@ -5,64 +5,61 @@
.hide { display: none; }
form .who { width: 10%; }
form .what { width: 80%; }
ul { list-style: none; padding: 0; }
</style>
<ul>
<li class="hide">
<b class="who"></b>:
<span class="what"></span>
<i class="when">0</i>
<u class="sort hide">0</u>
</li>
</ul>
<ul><li class="hide">
<b class="who"></b>:
<span class="what"></span>
<i class="hide when">0</i>
<u class="hide sort">0</u>
</li></ul>
<form>
<input class="who" placeholder="alias">
<input class="what" placeholder="message">
<button>send</button>
</form>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="../../gun.js"></script>
<script>
//Gun.log.verbose = true;
var chat = Gun(location.origin + '/gun').get('example/chat/data').not(function(){
console.log("INIT CHAT");
return this.put({1: {
who: 'sys', what: "Welcome to your chat app!", when: Gun.time.is()
}}).key('example/chat/data');
return this.put({1: {who: 'Welcome', what: "to the chat app!", when: 1}}).key('example/chat/data');
});
$('form').submit(function(e){
var msg = {};
msg.when = Gun.time.is();
msg.who = $(this).find('.who').val() || 'user' + Gun.text.random(6);
msg.what = $(this).find('.what').val() || '';
chat.path(msg.when + '_' + Gun.text.random(4)).put(msg);
$(this).find('.what').val('');
return e.preventDefault(), false;
});
var $model = $('ul li').clone().removeClass('hide');
chat.map().val(function(msg, field){
//console.log("the message:", field, msg);
if(!spam.lock && !spam.start){ spam(); }
var $last = sort(field, $('ul li').last()), $msg = $("#msg-" + field);
$msg = $msg.length? $msg : $model.clone().attr('id', 'msg-' + field).insertBefore($last);
$msg.find('.who').text(msg.who);
$msg.find('.what').text(msg.what);
$msg.find('.when').text(new Date(msg.when).toLocaleTimeString());
$msg.find('.sort').text(field);
window.scrollTo(0,document.body.scrollHeight);
var $ul = $('ul'), $last = $.sort(field, $ul.lastChild), $msg;
($msg = $("#msg-" + field) || $ul.insertBefore($.model.cloneNode(true), $last)).id = 'msg-' + field;
$('.who', $msg)[$.text] = msg.who;
$('.what', $msg)[$.text] = msg.what;
$msg.setAttribute('title', $('.when', $msg)[$.text] = new Date(msg.when).toLocaleTimeString().toLowerCase());
$('.sort', $msg)[$.text] = field;
window.scrollTo(0, document.body.scrollHeight);
});
function sort(when, elem){
return (when > (elem.find('.sort').text() || 0))? elem : sort(when, elem.prev());
}
var $ = function(s, e){ return (e || document).querySelector(s) } // make native look like jQuery.
$.text = document.body.textContent? 'textContent' : 'innerText'; // because browsers are stupid.
$.sort = function(when, e){ return (when > ($('.sort', e)[$.text] || 0))? e : $.sort(when, e.previousSibling) }
($.model = $('ul li').cloneNode(true)).removeAttribute('class');
$('.who', $('form')).value = document.cookie;
$('.what', $('form')).focus();
$('form').onsubmit = function(e){
var msg = {};
msg.when = Gun.time.is();
msg.who = document.cookie = $('.who', this).value || 'user' + Gun.text.random(6);
msg.what = $('.what', this).value || '';
chat.path(msg.when + '_' + Gun.text.random(4)).put(msg);
$('.what', this).value = '';
return (e && e.preventDefault()), false;
};
function spam(){
spam.start = true;
spam.lock = false;
if(spam.count >= 20){ return }
$('.what').val(++spam.count);
$('form').trigger('submit');
spam.start = true; spam.lock = false;
if(spam.count >= 100){ return }
var $f = $('form');
$('.what', $f).value = ++spam.count;
$f.onsubmit();
setTimeout(spam, 0);
}; spam.count = 0;
spam.lock = true;
}; spam.count = 0; spam.lock = true;
</script>
</body>
</html>

View File

@ -35,7 +35,7 @@
}
});
var $ = function(s, elem){ return (elem || document).querySelector(s) } // make native look like jQuery.
var $ = function(s, e){ return (e || document).querySelector(s) } // make native look like jQuery.
document.onkeyup = function(e){
if(!e || !e.target){ return } // ignore if no element!
if(!e.target.attributes.contenteditable){ return } // ignore if element content isn't editable!

86
gun.js
View File

@ -87,15 +87,17 @@
});
if(ctx.err){ return ctx }
(function union(graph, prime){
ctx.count += 1;
Gun.obj.map(prime, function(node, soul){
soul = Gun.is.soul.on(node);
if(!soul){ return }
ctx.count += 1;
var vertex = graph[soul];
if(!vertex){ // disjoint // TODO: Maybe not correct? BUG, probably.
gun.__.on(soul).emit(graph[soul] = node);
ctx.count -= 1;
return;
}
ctx.count += 1;
Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
if(!vertex){ return }
var change = {};
@ -113,6 +115,7 @@
if(!(ctx.count -= 1)){ ctx.cb() }
});
});
ctx.count -= 1; // TODO!!! YOU NEED A TEST FOR THIS!!! First node was a synchronise HAM op and the second one was a disjoint op. The callback got called before the synchronise operation happened cause I was only incrementally counting HAM ops, rather than counting across the whole graph like I now am doing.
})(ctx.graph, prime);
if(!ctx.count){ ctx.cb() }
return ctx;
@ -292,7 +295,10 @@
ctx.hook(key, function(err, data){ // multiple times potentially
console.log("chain.get from load", err, data);
if(err){ return cb.call(gun, err, data) }
if(!data){ return cb.call(gun, null, null), gun._.at('null').emit() }
if(!data){
if(ctx.soul){ return }
return cb.call(gun, null, null), gun._.at('null').emit()
}
if(ctx.soul = Gun.is.soul.on(data)){
gun._.at('soul').emit({soul: ctx.soul});
} else { return cb.call(gun, {err: Gun.log('No soul on data!') }, data) }
@ -315,13 +321,24 @@
Chain.key = function(key, cb, opt){
var gun = this, ctx = {};
if(!key){ return cb.call(gun, {err: Gun.log('No key!')}), gun }
if(!gun.back){ gun = gun.chain() }
cb = cb || function(){};
opt = opt || {};
(gun.__.flag.start[key] = gun._.at('node')).once(function($){
gun.__.keys[key] = gun.__.graph[$.soul];
delete gun.__.flag.start[key];
}, -1);
gun._.at('soul').event(function($){ // TODO: once per soul in graph. (?)
opt = Gun.text.is(opt)? {soul: opt} : opt || {};
opt.soul = opt.soul || opt[Gun._.soul];
gun._.at('soul').event(index);
if(opt.soul){ // TODO! BUG! WRITE A TEST FOR THIS!
if(!gun.__.graph[opt.soul]){
gun.__.graph[opt.soul] = {_: {'#': opt.soul, '>': {}}}; // TODO! SYMBOLS SHOULD NOT BE HARD CODED!
}
gun.__.keys[key] = gun.__.graph[opt.soul];
gun._.at('soul').emit({soul: opt.soul});
} else {
(gun.__.flag.start[key] = gun._.at('node')).once(function($){
gun.__.keys[key] = gun.__.graph[$.soul];
delete gun.__.flag.start[key];
}, -1);
}
function index($){ // TODO: once per soul in graph. (?)
if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.key)){
ctx.hook(key, $.soul, function(err, data){
return cb.call(gun, err, data);
@ -330,7 +347,7 @@
console.Log("Warning! You have no key hook!");
cb.call(gun, null); // This is in memory success, hardly "success" at all.
}
});
}
return gun;
}
Chain.all = function(key, cb){
@ -377,6 +394,7 @@
Chain.path = function(path, cb){
var gun = this.chain();
cb = cb || function(){};
if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun }
// TODO: Hmmm once also? figure it out later.
gun.back._.at('node').event(function($){
var ctx = {path: (Gun.text.ify(path) || '').split('.')};
@ -424,9 +442,14 @@
gun._.at('node').event(function($){
var node = gun.__.graph[$.soul];
if($.field){ return cb.call(gun, node[$.field], $.field || $.at) }
if(!gun.__.flag.end[$.soul]){ return }
if($.field){
if(ctx[$.soul + $.field]){ return }
ctx[$.soul + $.field] = true; // TODO: unregister instead?
return cb.call(gun, node[$.field], $.field || $.at)
}
if(ctx[$.soul] || !gun.__.flag.end[$.soul]){ return }
cb.call(gun, Gun.obj.copy(node), $.field || $.at);
ctx[$.soul] = true; // TODO: unregister instead?
});
return gun;
@ -899,7 +922,7 @@
if(!this.Gun){ return }
if(!window.JSON){ throw new Error("Include JSON first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js") } // for old IE use
Gun.on('opt').event(function(gun, opt){
window.tab = tab; // for debugging purposes
var tab = gun.__.tab = gun.__.tab || {};
opt = opt || {};
tab.headers = opt.headers || {};
tab.headers['gun-sid'] = tab.headers['gun-sid'] || Gun.text.random(); // stream id
@ -997,17 +1020,19 @@
});
}
tab.peers = function(cb){
if(cb && !cb.peers){ // there are no peers! this is a local only instance
setTimeout(function(){console.log("Warning! You have no peers to connect to!");cb()},1);
}
if(cb && !cb.peers){ setTimeout(function(){
console.log("Warning! You have no peers to connect to!");
if(!cb.node){ cb() }
},1)}
}
tab.put.defer = {};
request.createServer(function(req, res){
if(!req.body){ return }
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
Gun.log("client server received request", req);
Gun.union(gun, req.body); // TODO: BUG? Interesting, this won't update localStorage because .put isn't called?
}
Gun.obj.map(gun.__.opt.peers, function(){ // only create server if peers and do it once by returning immediately.
return tab.request = tab.request || request.createServer(function(req, res){
if(!req.body){ return }
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
Gun.log("client server received request", req);
Gun.union(gun, req.body); // TODO: BUG? Interesting, this won't update localStorage because .put isn't called?
}
}) || true;
});
gun.__.opt.hooks.get = gun.__.opt.hooks.get || tab.get;
gun.__.opt.hooks.put = gun.__.opt.hooks.put || tab.put;
@ -1029,15 +1054,20 @@
if(!opt.base){ return }
r.transport(opt, cb);
}
r.createServer = function(fn){ (r.createServer = fn).on = true }
r.createServer = function(fn){ r.createServer.s.push(fn) }
r.createServer.ing = function(req, cb){
var i = r.createServer.s.length;
while(i--){ (r.createServer.s[i] || function(){})(req, cb) }
}
r.createServer.s = [];
r.transport = function(opt, cb){
//Gun.log("TRANSPORT:", opt);
if(r.ws(opt, cb)){ return }
r.jsonp(opt, cb);
}
r.ws = function(opt, cb){
var ws = window.WebSocket || window.mozWebSocket || window.webkitWebSocket;
if(!ws){ return }
var ws, WS = window.WebSocket || window.mozWebSocket || window.webkitWebSocket;
if(!WS){ return }
if(ws = r.ws.peers[opt.base]){
if(!ws.readyState){ return setTimeout(function(){ r.ws(opt, cb) },10), true }
var req = {};
@ -1053,7 +1083,7 @@
return true;
}
if(ws === false){ return }
ws = r.ws.peers[opt.base] = new WebSocket(opt.base.replace('http','ws'));
ws = r.ws.peers[opt.base] = new WS(opt.base.replace('http','ws'));
ws.onopen = function(o){ r.ws(opt, cb) };
ws.onclose = window.onbeforeunload = function(c){
if(!c){ return }
@ -1074,7 +1104,7 @@
res.headers = res.headers || {};
if(res.headers['ws-rid']){ return (r.ws.cbs[res.headers['ws-rid']]||function(){})(null, res) }
Gun.log("We have a pushed message!", res);
if(res.body){ r.createServer(res, function(){}) } // emit extra events.
if(res.body){ r.createServer.ing(res, function(){}) } // emit extra events.
};
ws.onerror = function(e){ Gun.log(e); };
return true;
@ -1119,7 +1149,7 @@
while(reply.body && reply.body.length && reply.body.shift){ // we're assuming an array rather than chunk encoding. :(
var res = reply.body.shift();
//Gun.log("-- go go go", res);
if(res && res.body){ r.createServer(res, function(){}) } // emit extra events.
if(res && res.body){ r.createServer.ing(res, function(){}) } // emit extra events.
}
});
}, res.headers.poll);

View File

@ -2,73 +2,88 @@
// modified by Mark to be part of core for convenience
// twas not designed for production use
// only simple local development.
var Gun = require('../gun'),
file = {};
var Gun = require('../gun'), file = {};
Gun.on('opt').event(function(gun, opts) {
if (opts.file === false && opts.s3 && opts.s3.key) {
return
} // don't use this plugin if S3 is being used.
Gun.on('opt').event(function(gun, opts){
if(opts.file === false && opts.s3 && opts.s3.key){ return } // don't use this plugin if S3 is being used.
opts.file = opts.file || 'data.json';
var fs = require('fs');
file.raw = file.raw || (fs.existsSync || require('path').existsSync)(opts.file) ? fs.readFileSync(opts.file).toString() : null;
var all = file.all = file.all || Gun.obj.ify(file.raw || {
nodes: {},
keys: {}
});
opts.file = opts.file || 'data.json';
var fs = require('fs');
file.raw = file.raw || (fs.existsSync||require('path').existsSync)(opts.file)? fs.readFileSync(opts.file).toString() : null;
var all = file.all = file.all || Gun.obj.ify(file.raw || {nodes: {}, keys: {}});
gun.opt({hooks: {
get: function(key, cb, options){
if(Gun.obj.is(key) && key[Gun._.soul]){
return cb(null, all.nodes[key[Gun._.soul]]);
}
cb(null, all.nodes[all.keys[key]]);
}
,put: function(graph, cb){
all.nodes = gun.__.graph;
/*for(n in all.nodes){ // this causes some divergence problems, so removed for now till later when it can be fixed.
for(k in all.nodes[n]){
if(all.nodes[n][k] === null){
delete all.nodes[n][k];
gun.opt({
hooks: {
get: function(key, cb, options) {
if (Gun.obj.is(key) && key[Gun._.soul]) {
return cb(null, all.nodes[key[Gun._.soul]]);
}
cb(null, all.nodes[all.keys[key]]);
},
put: function(graph, cb) {
all.nodes = gun.__.graph;
/*for(n in all.nodes){ // this causes some divergence problems, so removed for now till later when it can be fixed.
for(k in all.nodes[n]){
if(all.nodes[n][k] === null){
delete all.nodes[n][k];
}
}
}
}*/
fs.writeFile(opts.file, Gun.text.ify(all), cb);
}
,key: function(key, soul, cb){
all.keys = all.keys || {};
all.keys[key] = soul;
fs.writeFile(opts.file, Gun.text.ify(all), cb);
}
,all: function(list, opt, cb){
opt = opt || {};
opt.from = opt.from || '';
opt.start = opt.from + (opt.start || '');
if(opt.end){ opt.end = opt.from + opt.end }
var match = {};
cb = cb || function(){};
Gun.obj.map(list, function(soul, key){
var end = opt.end || key;
if(key.indexOf(opt.from) === 0 && opt.start <= key && (key <= end || key.indexOf(end) === 0)){
if(opt.upto){
if(key.slice(opt.from.length).indexOf(opt.upto) === -1){
yes(soul, key);
}
} else {
yes(soul, key);
}
}
});
function yes(soul, key){
cb(key);
match[key] = {};
match[key][Gun._.soul] = soul;
}
return match;
}
}}, true);
gun.all = gun.all || function(url, cb){
url = require('url').parse(url, true);
var r = gun.__.opt.hooks.all(all.keys, {from: url.pathname, upto: url.query['*'], start: url.query['*>'], end: url.query['*<']});
console.log("All please", url.pathname, url.query['*'], r);
cb = cb || function(){};
cb(null, r);
}
}*/
fs.writeFile(opts.file, Gun.text.ify(all), cb);
},
key: function(key, soul, cb) {
all.keys = all.keys || {};
all.keys[key] = soul;
fs.writeFile(opts.file, Gun.text.ify(all), cb);
},
all: function(list, opt, cb) {
opt = opt || {};
opt.from = opt.from || '';
opt.start = opt.from + (opt.start || '');
if (opt.end) {
opt.end = opt.from + opt.end
}
var match = {};
cb = cb || function() {};
Gun.obj.map(list, function(soul, key) {
var end = opt.end || key;
if (key.indexOf(opt.from) === 0 && opt.start <= key && (key <= end || key.indexOf(end) === 0)) {
if (opt.upto) {
if (key.slice(opt.from.length).indexOf(opt.upto) === -1) {
yes(soul, key);
}
} else {
yes(soul, key);
}
}
});
});
function yes(soul, key) {
cb(key);
match[key] = {};
match[key][Gun._.soul] = soul;
}
return match;
}
}
}, true);
gun.all = gun.all || function(url, cb) {
url = require('url').parse(url, true);
var r = gun.__.opt.hooks.all(all.keys, {
from: url.pathname,
upto: url.query['*'],
start: url.query['*>'],
end: url.query['*<']
});
console.log("All please", url.pathname, url.query['*'], r);
cb = cb || function() {};
cb(null, r);
}
});

View File

@ -21,7 +21,7 @@ module.exports = function(req, res, next){
res.statusCode = reply.statusCode || reply.status;
}
if(reply.headers){
if(!res._headerSent){ // TODO: BUG? There was an edge case where this was not safe.
if(!(res.headersSent || res.headerSent || res._headerSent || res._headersSent)){
Gun.obj.map(reply.headers, function(val, field){
res.setHeader(field, val);
});

View File

@ -114,7 +114,7 @@
key = {};
key[Gun._.soul] = req.url.query[Gun._.soul];
}
//console.log("transport.getting key ->", key);
console.log("tran.get", key);
gun.get(key, function(err, node){
//tran.sub.scribe(req.tab, node._[Gun._.soul]);
cb({headers: reply.headers, chunk: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
@ -172,12 +172,12 @@
}
tran.put.key = function(req, cb){ // key hook!
if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return }
var get = {}, index = req.url.key, soul;
soul = get[Gun._.soul] = Gun.is.soul(req.body);
gun.get(get).key(index, function(err, reply){
var index = req.url.key, soul = Gun.is.soul(req.body);
console.log("tran.key", index, req.body);
gun.key(index, function(err, reply){
if(err){ return cb({headers: {'Content-Type': tran.json}, body: {err: err}}) }
cb({headers: {'Content-Type': tran.json}, body: reply}); // TODO: Fix so we know what the reply is.
});
}, soul);
return true;
}
gun.server.on('network').event(function(req){