mirror of
https://github.com/amark/gun.git
synced 2025-06-06 14:16:44 +00:00
This commit is contained in:
commit
51b98ba370
@ -112,7 +112,8 @@ Thanks to:<br/>
|
||||
<a href="http://github.com/ctrlplusb">Sean Matheson</a>,
|
||||
<a href="http://github.com/alanmimms">Alan Mimms</a>,
|
||||
<a href="https://github.com/dfreire">Dário Freire</a>,
|
||||
<a href="http://github.com/velua">John Williamson</a>
|
||||
<a href="http://github.com/velua">John Williamson</a>,
|
||||
<a href="http://github.com/finwo">Robin Bron</a>
|
||||
</p>
|
||||
|
||||
- Join others in sponsoring code: https://www.patreon.com/gunDB !
|
||||
@ -166,7 +167,7 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
|
||||
|
||||
This would not be possible without **community contributors**, big shout out to:
|
||||
|
||||
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
|
||||
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
|
||||
|
||||
I am missing many others, apologies, will be adding them soon!
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<a id="share" class="hide"><div class="stick button">Share</div></a>
|
||||
<a id="share" class="hide"><div class="stick button">Request Ride</div></a>
|
||||
<div id="link" class="hide">
|
||||
<p>Copy and Paste this URL to your friends to share your location:</p>
|
||||
<center>
|
||||
@ -123,6 +123,10 @@
|
||||
});
|
||||
return;
|
||||
}
|
||||
var z = 12;
|
||||
var x = lng2tile(latlng.lng, z);
|
||||
var y = lat2tile(latlng.lat, z);
|
||||
console.log(x, y); //
|
||||
where.map.setView(latlng, where.opt.zoom.level, {animate: true});
|
||||
where.marker = where.marker || L.marker().setLatLng(latlng).addTo(where.map);
|
||||
where.marker.setLatLng(latlng).update();
|
||||
@ -145,6 +149,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
function lng2tile(lng,z) { return (Math.floor((lng+180)/360*Math.pow(2,z))) }
|
||||
function lat2tile(lat,z) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,z))) }
|
||||
|
||||
return where;
|
||||
}
|
||||
</script>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Party by Neon ERA</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic" rel="stylesheet" type="text/css">
|
||||
@ -68,7 +69,7 @@
|
||||
width: 3em;
|
||||
}
|
||||
</style>
|
||||
<div class="loud write shout rim">A social network you own & control.</div>
|
||||
<div class="loud write shout rim">Party by NEON ERA.</div>
|
||||
<div id="faces" class="flush faces">
|
||||
<div class="right" style="max-width: 20em;">
|
||||
<input id="halias" class="write jot sap" placeholder="username">
|
||||
@ -508,6 +509,8 @@
|
||||
}
|
||||
#draft .what {
|
||||
}
|
||||
#draft .spoke {
|
||||
}
|
||||
</style>
|
||||
<p class="pad">Welcome!</p>
|
||||
<!--
|
||||
@ -520,7 +523,7 @@
|
||||
- take
|
||||
-->
|
||||
<form id="speak" class="mid row col">
|
||||
<div id="d0" class="draft gap tint sap" contenteditable="true" style="min-height: 1em;" data-text="Express yourself..."></div>
|
||||
<div id="d0" class="draft spoke gap tint sap editable" contenteditable="true" style="min-height: 1em;" data-text="Express yourself..."></div>
|
||||
<input type="submit" class="sap hide" value="speak!">
|
||||
</form>
|
||||
<ul class="mid row col">
|
||||
@ -544,6 +547,9 @@
|
||||
});
|
||||
window.user = S.user;
|
||||
$('#speak').on('submit', (e) => {
|
||||
/*var say = normalize($('#speak .draft'));
|
||||
console.log(1, say.html());
|
||||
return;*/
|
||||
var say = $('#speak .draft').text();
|
||||
if(!say){ return }
|
||||
var ref = S.user.get('who').get('all').set({what: say});
|
||||
@ -669,7 +675,6 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$.as.route.page('person', () => {
|
||||
if(!user.is){ return $.as.route('hi') }
|
||||
var pub = location.hash.split('/').slice(-1)[0];
|
||||
@ -678,7 +683,6 @@
|
||||
});
|
||||
|
||||
$(document).upload(function resize(e, up){
|
||||
console.log("WOAH");
|
||||
if(e.err){ return }
|
||||
var m = $($("#d"+e.id)[0] || $('#d0').clone(true,true).attr('id', 'd'+e.id).css('backgroundImage', '').appendTo('#draft')).addClass('pulse');
|
||||
if(up){ return up.shrink(e, resize, 1000) }
|
||||
@ -690,17 +694,8 @@
|
||||
backgroundSize: 'cover'
|
||||
});
|
||||
});
|
||||
|
||||
window.fun = function fun(e){ setTimeout(() => {
|
||||
e = e || {}; var $img = $('<div class="joy"></div>')
|
||||
.css({position: 'fixed', width: 100,
|
||||
top: (e.y || e.clientY || (Math.random() * $(window).height()))-50,
|
||||
left: (e.x || e.clientX || e.pageX || (Math.random() * $(window).width()))-50,
|
||||
transform: 'rotate('+(Math.random() * 360)+'deg)'
|
||||
}).appendTo('body');
|
||||
setTimeout(() => { $img.remove() },800);
|
||||
},10)};
|
||||
$(document).on('keyup', fun).on('touchstart', fun).on('mousedown', fun);
|
||||
</script>
|
||||
<script async src="../../gun/lib/fun.js"></script>
|
||||
<script async src="../../gun/lib/normalize.js"></script>
|
||||
</body>
|
||||
</html>
|
22
gun.js
22
gun.js
@ -400,7 +400,7 @@
|
||||
return n;
|
||||
}
|
||||
State.to = function(from, k, to){
|
||||
var val = from[k]; // BUGGY!
|
||||
var val = (from||{})[k];
|
||||
if(obj_is(val)){
|
||||
val = obj_copy(val);
|
||||
}
|
||||
@ -1287,13 +1287,16 @@
|
||||
return at;
|
||||
}
|
||||
function soul(gun, cb, opt, as){
|
||||
var cat = gun._, tmp;
|
||||
var cat = gun._, acks = 0, tmp;
|
||||
if(tmp = cat.soul){ return cb(tmp, as, cat), gun }
|
||||
if(tmp = cat.link){ return cb(tmp, as, cat), gun }
|
||||
gun.get(function(msg, ev){ // TODO: Bug! Needs once semantics?
|
||||
gun.get(function(msg, ev){
|
||||
if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && acks++ <= tmp){
|
||||
return;
|
||||
}
|
||||
ev.rid(msg);
|
||||
var at = ((at = msg.$) && at._) || {};
|
||||
tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put);
|
||||
tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub;
|
||||
cb(tmp, as, msg, ev);
|
||||
}, {out: {get: {'.':true}}});
|
||||
return gun;
|
||||
@ -1319,9 +1322,10 @@
|
||||
msg = obj_to(msg, {put: data = tmp.put});
|
||||
}
|
||||
}
|
||||
if((tmp = root.mum) && at.id){
|
||||
if(tmp[at.id]){ return }
|
||||
if(u !== data && !rel.is(data)){ tmp[at.id] = true; }
|
||||
if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now?
|
||||
var id = at.id + (eve.id || (eve.id = Gun.text.random(9)));
|
||||
if(tmp[id]){ return }
|
||||
if(u !== data && !rel.is(data)){ tmp[id] = true; }
|
||||
}
|
||||
as.use(msg, eve);
|
||||
if(eve.stun){
|
||||
@ -1343,7 +1347,7 @@
|
||||
//obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
|
||||
return;
|
||||
}
|
||||
var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to;
|
||||
var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to;
|
||||
var num_is = Gun.num.is;
|
||||
var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._;
|
||||
var empty = {}, u;
|
||||
@ -1778,7 +1782,7 @@
|
||||
try{store = (Gun.window||noop).localStorage}catch(e){}
|
||||
if(!store){
|
||||
console.log("Warning: No localStorage exists to persist data to!");
|
||||
store = {setItem: noop, removeItem: noop, getItem: noop};
|
||||
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
|
||||
}
|
||||
/*
|
||||
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
|
||||
|
4
gun.min.js
vendored
4
gun.min.js
vendored
File diff suppressed because one or more lines are too long
10
lib/fun.js
Normal file
10
lib/fun.js
Normal file
@ -0,0 +1,10 @@
|
||||
window.fun = function fun(e){ setTimeout(() => {
|
||||
e = e || {}; var $img = $('<div class="joy"></div>')
|
||||
.css({position: 'fixed', width: 100,
|
||||
top: (e.y || e.clientY || (Math.random() * $(window).height()))-50,
|
||||
left: (e.x || e.clientX || e.pageX || (Math.random() * $(window).width()))-50,
|
||||
transform: 'rotate('+(Math.random() * 360)+'deg)'
|
||||
}).appendTo('body');
|
||||
setTimeout(() => { $img.remove() },800);
|
||||
},10)};
|
||||
$(document).on('keyup', fun).on('touchstart', fun).on('mousedown', fun);
|
38
lib/metae.js
Normal file
38
lib/metae.js
Normal file
@ -0,0 +1,38 @@
|
||||
$(function(){
|
||||
var m = window.meta = {edit:[], os:{}}, ua = '';
|
||||
try{ua = navigator.userAgent.toLowerCase()}catch(e){}
|
||||
m.os.is = {
|
||||
win: (ua.search("win") >= 0)? "windows":false,
|
||||
lin: (ua.search("linux") >= 0)? "linux":false,
|
||||
mac: (ua.search("mac") >= 0)? "macintosh":false,
|
||||
and: (ua.search("android") >= 0)? "android":false,
|
||||
ios: (ua.search('ipod') >= 0
|
||||
|| ua.search('iphone') >= 0
|
||||
|| ua.search('ipad') >= 0)? "ios":false
|
||||
}
|
||||
m.key = {ctrl: 17, cmd: 91};
|
||||
m.key.meta = (m.os.is.win||m.os.is.lin||m.os.is.and)? m.key.ctrl : m.key.cmd;
|
||||
m.key.on = {};
|
||||
$(document).on('keydown', function(e){
|
||||
m.e = e;
|
||||
console.log('keydown', e.keyCode);
|
||||
m.key.on[e.code = e.keyCode] = !0;
|
||||
}).on('keyup', function(e){
|
||||
m.e = e;
|
||||
delete m.key.on[e.code = e.keyCode];
|
||||
}).on('keydown', '[contenteditable=true]', function(e){
|
||||
return;
|
||||
var r = monotype();
|
||||
console.log("keys down", Gun.obj.copy(m.key.on));
|
||||
$.each(m.edit, function(i,edit){ var tmp = true;
|
||||
$.each(edit.keys||[''], function(i,k){
|
||||
if(!m.key.on[k.length? k.charCodeAt(0) : k]){ tmp = false }
|
||||
});
|
||||
console.log(tmp, edit);
|
||||
})
|
||||
r.restore();
|
||||
});
|
||||
m.edit.push({keys: ['B'], on: function(){
|
||||
console.log('hi!');
|
||||
}})
|
||||
});
|
230
lib/monotype.js
Normal file
230
lib/monotype.js
Normal file
@ -0,0 +1,230 @@
|
||||
;var monotype = monotype || (function(monotype){
|
||||
monotype.range = function(n){
|
||||
var R, s, t, n = n || 0, win = monotype.win || window, doc = win.document;
|
||||
if(!arguments.length) return doc.createRange();
|
||||
if(!(win.Range && R instanceof Range)){
|
||||
s = win.getSelection? win.getSelection() : {};
|
||||
if(s.rangeCount){
|
||||
R = s.getRangeAt(n);
|
||||
} else {
|
||||
if(doc.createRange){
|
||||
R = doc.createRange();
|
||||
R.setStart(doc.body, 0);
|
||||
} else
|
||||
if (doc.selection){ // <IE9
|
||||
R = doc.selection.createRange();
|
||||
R = R.getBookmark();
|
||||
}
|
||||
}
|
||||
s.end = (s.extentNode || s.focusNode || R.startContainer);
|
||||
if(s.anchorNode === s.end){
|
||||
R.direction = s.anchorOffset <= (s.extentOffset || s.focusOffset || 0)? 1 : -1;
|
||||
} else
|
||||
if($.contains(s.anchorNode||{}, s.end||{})){
|
||||
s.end = $(s.anchorNode).contents().filter(s.end).length? s.end : $(s.end).parentsUntil(s.anchorNode).last()[0];
|
||||
R.direction = s.anchorOffset < $(s.anchorNode).contents().index(s.end)? 1 : -1; // Compare immediate descendants to see which comes first.
|
||||
} else {
|
||||
R.direction = s.anchorNode === R.endContainer? -1 : 1; // Checking against startContainer fails going backward.
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
monotype.restore = function(R){
|
||||
var win = monotype.win, doc = win.document;
|
||||
if(R.R && R.restore){
|
||||
R.restore();
|
||||
return;
|
||||
}
|
||||
if(win.getSelection){
|
||||
var s = win.getSelection();
|
||||
s.removeAllRanges();
|
||||
if(s.extend && R.direction < 0){
|
||||
R.esC = R.startContainer;
|
||||
R.esO = R.startOffset;
|
||||
R.setStart(R.endContainer, R.endOffset);
|
||||
}
|
||||
s.addRange(R);
|
||||
R.esC && s.extend(R.esC, R.esO);
|
||||
} else {
|
||||
if(doc.body.createTextRange) { // <IE9
|
||||
var ier = doc.body.createTextRange();
|
||||
ier.moveToBookmark(R);
|
||||
ier.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
monotype.text = function(n){
|
||||
return !n? false : (n.nodeType == 3 || n.nodeType == Node.TEXT_NODE);
|
||||
}
|
||||
monotype.prev = function(n,c,d){
|
||||
return !n? null : n === c? null
|
||||
: n[(d?'next':'previous')+'Sibling']?
|
||||
monotype.deep(n[(d?'next':'previous')+'Sibling'],d?-1:Infinity).container
|
||||
: monotype.prev($(n).parent()[0],c,d);
|
||||
}; monotype.next = function(n,c){ return monotype.prev(n,c,1) }
|
||||
monotype.deep = function(n, o, c, i){
|
||||
return i = (o === Infinity? $(n).contents().length-1 : o),
|
||||
i = (i === -1? 0 : i),
|
||||
(c = $(n).contents()).length?
|
||||
monotype.deep(c = c[i < c.length? i : c.length - 1], monotype.text(c)? 0 : o)
|
||||
: {
|
||||
container: n
|
||||
,offset: $(n).text() && o !== -1? (o === Infinity? $(n).text().length : o) : 0
|
||||
};
|
||||
}
|
||||
monotype.count = function(n, o, c){
|
||||
var g = monotype.deep(n, o)
|
||||
, m = g.container
|
||||
, i = g.offset || 0;
|
||||
while(m = monotype.prev(m,c)){
|
||||
i += $(m).text().length;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
monotype.hint = function(n, o, c){
|
||||
var g = monotype.deep(n, o)
|
||||
, m = g.container
|
||||
, i = g.offset || 0
|
||||
, h = [], t;
|
||||
while(m){
|
||||
h.push({
|
||||
t: t = $(m).text()
|
||||
,n: t? 'TEXT' : m.nodeName
|
||||
});
|
||||
m = t? null : monotype.prev(m,c);
|
||||
}
|
||||
if(h.length == 1 && h[0].t){
|
||||
return [];
|
||||
}
|
||||
if((t = $(n).contents()).length && o == t.length){
|
||||
h.push(1); // Indicate that the selection is after the last element.
|
||||
}
|
||||
return h;
|
||||
}
|
||||
monotype.reach = function(i, c, o){
|
||||
o = o || {};
|
||||
o.i = o.i || o.offset || 0;
|
||||
o.$ = o.$? o.$.jquery? o.$ : $(o.$)
|
||||
: o.container? $(o.container) : $(c);
|
||||
var n = monotype.deep(o.$[0], -1).container, t;
|
||||
while(n){
|
||||
t = $(n).text().length;
|
||||
if(i <= o.i + t){
|
||||
o.$ = $(n);
|
||||
o.i = i - o.i;
|
||||
n = null;
|
||||
} else {
|
||||
o.i += t;
|
||||
}
|
||||
n = monotype.next(n,c);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
return monotype;
|
||||
})(function(r,opt){
|
||||
r = r || {};
|
||||
opt = opt || {};
|
||||
monotype.win = opt.win || window;
|
||||
r = r.jquery || monotype.text(r)? {root: $(r)} : r;
|
||||
r.root = $(r.root || monotype.win.document.body);
|
||||
var t, m = monotype;
|
||||
//console.log('_______________________');
|
||||
r.R = m.range(0);
|
||||
r.H = {};
|
||||
r.H.R = $.extend({}, r.R);
|
||||
r.d = r.R.direction || 1;
|
||||
r.t = r.R.toString();
|
||||
r.H.s = m.hint(r.R.startContainer, r.R.startOffset, r.root[0]);
|
||||
r.s = m.count(r.R.startContainer, r.R.startOffset, r.root[0]);
|
||||
t = m.deep(r.R.startContainer, r.R.startOffset);
|
||||
(!t.offset && !r.H.s.length) && (r.s += 0.1); // At the beginning of a text, not at the end of a text.
|
||||
r.H.e = m.hint(r.R.endContainer, r.R.endOffset, r.root[0]);
|
||||
r.e = (function(n, o, c, t){
|
||||
if(r.R.collapsed
|
||||
|| (o === r.R.startOffset
|
||||
&& n === r.R.startContainer)){
|
||||
return r.s;
|
||||
} c = m.count(n, o, r.root[0]);
|
||||
t = m.deep(n, o);
|
||||
(!t.offset && !r.H.e.length) && (c += 0.1); // Same as above.
|
||||
return c;
|
||||
})(r.R.endContainer, r.R.endOffset);
|
||||
//console.log(r.s, r.R.startOffset, r.H.s, 'M',r.d,'E', r.H.e, r.R.endOffset, r.e);
|
||||
t = r.root.text();
|
||||
r.L = t.length;
|
||||
r.T = {
|
||||
s: t.slice(r.s - 9, r.s)
|
||||
,e: t.slice(r.e, r.e + 9)
|
||||
,t: function(){ return r.T.s + r.T.e }
|
||||
}
|
||||
r.range = function(){
|
||||
//console.log('----');
|
||||
r.H = r.H || {};
|
||||
var s = m.reach(r.s, r.root[0])
|
||||
, st = s.$.text()
|
||||
, e = m.reach(r.e, r.root[0])
|
||||
, et = e.$.text()
|
||||
, R = m.range()
|
||||
, p = function(g, c){ // TODO: BUG! Backtracking in non-Chrome and non-IE9+ browsers. IE9 doesn't like end selections.
|
||||
if(!c || !c.length){
|
||||
return g;
|
||||
}
|
||||
var n = g.$[0], f = [], i = 0, t;
|
||||
while((n = m.next(n,r.root[0])) && ++i < c.length){
|
||||
t = $(n).text();
|
||||
if(t){
|
||||
n = null;
|
||||
} else {
|
||||
f.push(n);
|
||||
}
|
||||
}
|
||||
n = $(f[f.length-1] || g.$);
|
||||
t = n.parent();
|
||||
if(c[c.length-1] === 1 || (i && f.length === i
|
||||
&& (f.length < c.length-1))){ // tests pass with this condition, yet failed without
|
||||
return {
|
||||
i: t.contents().length
|
||||
,$: t
|
||||
}
|
||||
}
|
||||
if(f.length < c.length - 1){ // despite above's addition, this still gets activated.
|
||||
f = t.contents().slice(n = t.contents().index(n));
|
||||
i = f.map(function(j){ return $(this).text()? (n+j+1) : null})[0] || t.contents().length;
|
||||
f = f.slice(0, i - n);
|
||||
n = f.last()[0];
|
||||
if(g.$[0] === n){
|
||||
return g;
|
||||
}
|
||||
return {
|
||||
$: t
|
||||
,i: t.contents().index(n)
|
||||
}
|
||||
}
|
||||
return {
|
||||
i: 0
|
||||
,$: n
|
||||
};
|
||||
}
|
||||
s = p(s, r.H.s);
|
||||
e = p(e, r.H.e);
|
||||
//console.log("START", parseInt(s.i), 'in """',(s.$[0]),'""" with hint of', r.H.s, 'from original', r.s);
|
||||
//console.log("END", parseInt(e.i), 'in """',(e.$[0]),'""" hint clue of', r.H.e, 'from original', r.e);
|
||||
R.setStart(s.$[0], parseInt(s.i));
|
||||
R.setEnd(e.$[0], parseInt(e.i));
|
||||
return R;
|
||||
}
|
||||
r.restore = function(R){
|
||||
if(r.R.startOffset !== r.H.R.startOffset
|
||||
|| r.R.endOffset !== r.H.R.endOffset
|
||||
|| r.R.startContainer !== r.H.R.startContainer
|
||||
|| r.R.endContainer !== r.H.R.endContainer){
|
||||
r.R = R = r.range();
|
||||
} else {
|
||||
R = r.R;
|
||||
}
|
||||
R.direction = r.d;
|
||||
m.restore(R);
|
||||
return r;
|
||||
}
|
||||
return r;
|
||||
});
|
@ -9,6 +9,7 @@
|
||||
fn && fn(a);
|
||||
});
|
||||
})
|
||||
return el;
|
||||
};
|
||||
var n = normalize, u;
|
||||
n.get = function(o, p){
|
||||
@ -97,31 +98,29 @@
|
||||
,function(a, tmp){ // convert
|
||||
if(!(tmp = n.get(a.opt,'convert.' + a.tag))){ return }
|
||||
a.attr = a.attr || n.attrs(a.$);
|
||||
a.$.replaceWith(a.$ = $('<'+ (a.tag = t.toLowerCase()) +'>').append(a.$.contents()));
|
||||
a.$.replaceWith(a.$ = $('<'+ (a.tag = tmp.toLowerCase()) +'>').append(a.$.contents()));
|
||||
h.attr(a.$, a.attr, a.attrs);
|
||||
}
|
||||
,function(a, tmp){ // lookahead
|
||||
if((tmp = n.joint(a.$,1)) && (t = t.contents()).length === 1 && a.tag === n.tag(t = t.first())){
|
||||
a.$.append(t.parent()); // no need to unwrap the child, since the recursion will do it for us
|
||||
if((tmp = n.joint(a.$,1)) && (tmp = tmp.contents()).length === 1 && a.tag === n.tag(tmp = tmp.first())){
|
||||
a.$.append(tmp.parent()); // no need to unwrap the child, since the recursion will do it for us
|
||||
}
|
||||
}
|
||||
,function(a){ // recurse
|
||||
// this needs to precede the exclusion and empty.
|
||||
normalize(a);
|
||||
}
|
||||
,function(a){ // exclude
|
||||
var t;
|
||||
,function(a, tmp){ // exclude
|
||||
if(!n.get(a.opt,'tags.' + a.tag)
|
||||
|| ((t = n.get(a.opt,'tags.'+ a.tag +'.exclude'))
|
||||
&& a.$.parents($.map(t,function(i,v){return v})+' ').length)
|
||||
|| ((tmp = n.get(a.opt,'tags.'+ a.tag +'.exclude'))
|
||||
&& a.$.parents($.map(tmp,function(i,v){return v})+' ').length)
|
||||
){
|
||||
a.$.replaceWith(a.$.contents());
|
||||
}
|
||||
}
|
||||
,function(a){ // prior
|
||||
var t;
|
||||
if((t = n.joint(a.$)).length && a.tag === n.tag(t)){
|
||||
t.append(a.$.contents());
|
||||
,function(a, tmp){ // prior
|
||||
if((tmp = n.joint(a.$)).length && a.tag === n.tag(tmp)){
|
||||
tmp.append(a.$.contents());
|
||||
}
|
||||
}
|
||||
,function(a){ // empty
|
||||
|
@ -77,7 +77,7 @@ function Mem(opt){
|
||||
cb(null, tmp);
|
||||
}, 1);
|
||||
};
|
||||
store.list = function(cb, match){
|
||||
store.list = function(cb, match){ // supporting this is no longer needed! Optional.
|
||||
setTimeout(function(){
|
||||
Gun.obj.map(Object.keys(storage), cb) || cb();
|
||||
}, 1);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.9.99999",
|
||||
"version": "0.9.999994",
|
||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||
"main": "index.js",
|
||||
"browser": "gun.min.js",
|
||||
|
116
sea.js
116
sea.js
@ -282,7 +282,7 @@
|
||||
}
|
||||
salt = salt || shim.random(9);
|
||||
if('SHA-256' === opt.name){
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString(opt.encode || 'base64')
|
||||
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||
return rsha;
|
||||
}
|
||||
@ -296,7 +296,7 @@
|
||||
hash: opt.hash || S.pbkdf2.hash,
|
||||
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
const r = shim.Buffer.from(result, 'binary').toString(opt.encode || 'base64')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
@ -392,15 +392,6 @@
|
||||
var u;
|
||||
|
||||
SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
|
||||
if(data && data.slice
|
||||
&& 'SEA{' === data.slice(0,4)
|
||||
&& '"m":' === data.slice(4,8)){
|
||||
// TODO: This would prevent pair2 signing pair1's signature.
|
||||
// So we may want to change this in the future.
|
||||
// but for now, we want to prevent duplicate double signature.
|
||||
if(cb){ try{ cb(data) }catch(e){console.log(e)} }
|
||||
return data;
|
||||
}
|
||||
opt = opt || {};
|
||||
if(!(pair||opt).priv){
|
||||
pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
|
||||
@ -408,11 +399,10 @@
|
||||
const pub = pair.pub
|
||||
const priv = pair.priv
|
||||
const jwk = S.jwk(pub, priv)
|
||||
const msg = JSON.stringify(data)
|
||||
const hash = await sha256hash(msg)
|
||||
const hash = await sha256hash(JSON.stringify(data))
|
||||
const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')});
|
||||
const r = 'SEA'+JSON.stringify({m: data, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')});
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
@ -451,9 +441,17 @@
|
||||
const jwk = S.jwk(pub)
|
||||
const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify'])
|
||||
const hash = await sha256hash(json.m)
|
||||
const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8'))
|
||||
const check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
var buf; var sig; var check; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
const r = check? parse(json.m) : u;
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
@ -503,9 +501,9 @@
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
||||
}, aes, new shim.TextEncoder().encode(msg)))
|
||||
const r = 'SEA'+JSON.stringify({
|
||||
ct: shim.Buffer.from(ct, 'binary').toString('utf8'),
|
||||
iv: rand.iv.toString('utf8'),
|
||||
s: rand.s.toString('utf8')
|
||||
ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'),
|
||||
iv: rand.iv.toString(opt.encode || 'base64'),
|
||||
s: rand.s.toString(opt.encode || 'base64')
|
||||
});
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
@ -535,10 +533,18 @@
|
||||
key = pair.epriv || pair;
|
||||
}
|
||||
const json = parse(data)
|
||||
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
||||
|
||||
var buf; try{buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){buf = shim.Buffer.from(json.s, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
var bufiv; try{bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){bufiv = shim.Buffer.from(json.iv, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
var bufct; try{bufct = shim.Buffer.from(json.ct, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){bufct = shim.Buffer.from(json.ct, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
|
||||
const ct = await aeskey(key, buf, opt)
|
||||
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv)
|
||||
}, aes, new Uint8Array(bufct)))
|
||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
@ -743,26 +749,30 @@
|
||||
act.proof = proof;
|
||||
SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account.
|
||||
}
|
||||
act.c = function(pair){
|
||||
act.c = function(pair){ var tmp;
|
||||
act.pair = pair || {};
|
||||
// the user's public key doesn't need to be signed. But everything else needs to be signed with it!
|
||||
if(tmp = cat.root.user){
|
||||
tmp._.sea = pair;
|
||||
tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||
}
|
||||
// the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now!
|
||||
act.data = {pub: pair.pub};
|
||||
SEA.sign(alias, pair, act.d);
|
||||
}
|
||||
act.d = function(alias){
|
||||
act.data.alias = alias;
|
||||
act.d = function(sig){
|
||||
act.data.alias = alias || sig;
|
||||
SEA.sign(act.pair.epub, act.pair, act.e);
|
||||
}
|
||||
act.e = function(epub){
|
||||
act.data.epub = epub;
|
||||
act.data.epub = act.pair.epub || epub;
|
||||
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f); // to keep the private key safe, we AES encrypt it with the proof of work!
|
||||
}
|
||||
act.f = function(auth){
|
||||
act.data.auth = auth;
|
||||
act.data.auth = JSON.stringify({ek: auth, s: act.salt});
|
||||
SEA.sign({ek: auth, s: act.salt}, act.pair, act.g);
|
||||
}
|
||||
act.g = function(auth){ var tmp;
|
||||
act.data.auth = auth;
|
||||
act.data.auth = act.data.auth || auth;
|
||||
root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
|
||||
root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
|
||||
setTimeout(function(){ // we should be able to delete this now, right?
|
||||
@ -806,14 +816,20 @@
|
||||
}
|
||||
act.c = function(auth){
|
||||
if(u === auth){ return act.b() }
|
||||
SEA.work(pass, (act.auth = auth).s, act.d); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||
if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // new format
|
||||
SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||
}
|
||||
act.d = function(proof){
|
||||
if(u === proof){ return act.b() }
|
||||
SEA.decrypt(act.auth.ek, proof, act.e);
|
||||
SEA.decrypt(act.auth.ek, proof, act.e, act.enc);
|
||||
}
|
||||
act.e = function(half){
|
||||
if(u === half){ return act.b() }
|
||||
if(u === half){
|
||||
if(!act.enc){ // try old format
|
||||
act.enc = {encode: 'utf8'};
|
||||
return act.c(act.auth);
|
||||
} act.enc = null; // end backwards
|
||||
return act.b();
|
||||
}
|
||||
act.half = half;
|
||||
act.f(act.data);
|
||||
}
|
||||
@ -833,6 +849,7 @@
|
||||
user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||
at.sea = act.pair;
|
||||
cat.ing = false;
|
||||
if(pass && !Gun.text.is(act.data.auth)){ opt.shuffle = opt.change = pass; } // migrate UTF8 + Shuffle! Test against NAB alias test_sea_shuffle + passw0rd
|
||||
opt.change? act.z() : cb(at);
|
||||
if(SEA.window && ((gun.back('user')._).opt||opt).remember){
|
||||
// TODO: this needs to be modular.
|
||||
@ -859,9 +876,17 @@
|
||||
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x);
|
||||
}
|
||||
act.x = function(auth){
|
||||
SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
|
||||
act.w(JSON.stringify({ek: auth, s: act.salt}));
|
||||
//SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
|
||||
}
|
||||
act.w = function(auth){
|
||||
if(opt.shuffle){ // delete in future!
|
||||
var tmp = Gun.obj.to(act.data);
|
||||
Gun.obj.del(tmp, '_');
|
||||
tmp.auth = auth;
|
||||
console.log('migrate core account from UTF8 & shuffle', tmp);
|
||||
root.get('~'+act.pair.pub).put(tmp);
|
||||
} // end delete
|
||||
root.get('~'+act.pair.pub).get('auth').put(auth, cb);
|
||||
}
|
||||
act.err = function(e){
|
||||
@ -1052,6 +1077,8 @@
|
||||
Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node
|
||||
// TODO: consider async/await use here...
|
||||
SEA.verify(val, false, function(data){ c--; // false just extracts the plain data.
|
||||
var tmp = data;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
node[key] = val = data; // transform to plain value.
|
||||
if(d && !c && (c = -1)){ to.next(msg) }
|
||||
});
|
||||
@ -1131,7 +1158,7 @@
|
||||
check['user'+soul+key] = 1;
|
||||
if(user && user.is && pub === user.is.pub){
|
||||
//var id = Gun.text.random(3);
|
||||
SEA.sign(val, (user._).sea, function(data){ var rel;
|
||||
SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){ var rel;
|
||||
if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
|
||||
if(rel = Gun.val.link.is(val)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
@ -1144,6 +1171,7 @@
|
||||
return;
|
||||
}
|
||||
SEA.verify(val, pub, function(data){ var rel, tmp;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
if(u === data){ // make sure the signature matches the account it claims to be on.
|
||||
return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account.
|
||||
}
|
||||
@ -1168,6 +1196,7 @@
|
||||
if(tmp = relpub(soul)){
|
||||
check['any'+soul+key] = 1;
|
||||
SEA.verify(val, pub = tmp, function(data){ var rel;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
|
||||
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
@ -1205,7 +1234,7 @@
|
||||
//});
|
||||
return;
|
||||
}
|
||||
if((pub = tmp) !== (user.is||noop).pub){
|
||||
if(!msg.I || (pub = tmp) !== (user.is||noop).pub){
|
||||
each.any(val, key, node, soul);
|
||||
return;
|
||||
}
|
||||
@ -1217,7 +1246,7 @@
|
||||
return;
|
||||
}*/
|
||||
check['any'+soul+key] = 1;
|
||||
SEA.sign(val, (user._).sea, function(data){
|
||||
SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){
|
||||
if(u === data){ return each.end({err: 'My signature fail.'}) }
|
||||
node[key] = data;
|
||||
check['any'+soul+key] = 0;
|
||||
@ -1242,7 +1271,18 @@
|
||||
}
|
||||
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
|
||||
}
|
||||
var noop = {};
|
||||
SEA.opt.unpack = function(data, key, node){
|
||||
if(u === data){ return }
|
||||
var tmp = data, soul = Gun.node.soul(node), s = Gun.state.is(node, key);
|
||||
if(tmp && 4 === tmp.length && soul === tmp[0] && key === tmp[1] && s === tmp[3]){
|
||||
return tmp[2];
|
||||
}
|
||||
if(s < SEA.opt.shuffle_attack){
|
||||
return data;
|
||||
}
|
||||
}
|
||||
SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
|
||||
var noop = {}, u;
|
||||
|
||||
})(USE, './index');
|
||||
}());
|
||||
|
@ -37,26 +37,30 @@
|
||||
act.proof = proof;
|
||||
SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account.
|
||||
}
|
||||
act.c = function(pair){
|
||||
act.c = function(pair){ var tmp;
|
||||
act.pair = pair || {};
|
||||
// the user's public key doesn't need to be signed. But everything else needs to be signed with it!
|
||||
if(tmp = cat.root.user){
|
||||
tmp._.sea = pair;
|
||||
tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||
}
|
||||
// the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now!
|
||||
act.data = {pub: pair.pub};
|
||||
SEA.sign(alias, pair, act.d);
|
||||
}
|
||||
act.d = function(alias){
|
||||
act.data.alias = alias;
|
||||
act.d = function(sig){
|
||||
act.data.alias = alias || sig;
|
||||
SEA.sign(act.pair.epub, act.pair, act.e);
|
||||
}
|
||||
act.e = function(epub){
|
||||
act.data.epub = epub;
|
||||
act.data.epub = act.pair.epub || epub;
|
||||
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f); // to keep the private key safe, we AES encrypt it with the proof of work!
|
||||
}
|
||||
act.f = function(auth){
|
||||
act.data.auth = auth;
|
||||
act.data.auth = JSON.stringify({ek: auth, s: act.salt});
|
||||
SEA.sign({ek: auth, s: act.salt}, act.pair, act.g);
|
||||
}
|
||||
act.g = function(auth){ var tmp;
|
||||
act.data.auth = auth;
|
||||
act.data.auth = act.data.auth || auth;
|
||||
root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
|
||||
root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
|
||||
setTimeout(function(){ // we should be able to delete this now, right?
|
||||
@ -78,7 +82,7 @@
|
||||
}
|
||||
cat.ing = true;
|
||||
opt = opt || {};
|
||||
var pair = (alias.pub || alias.epub)? alias : (pass.pub || pass.epub)? pass : null;
|
||||
var pair = (alias && (alias.pub || alias.epub))? alias : (pass && (pass.pub || pass.epub))? pass : null;
|
||||
var act = {}, u;
|
||||
act.a = function(data){
|
||||
if(!data){ return act.b() }
|
||||
@ -100,14 +104,20 @@
|
||||
}
|
||||
act.c = function(auth){
|
||||
if(u === auth){ return act.b() }
|
||||
SEA.work(pass, (act.auth = auth).s, act.d); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||
if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // new format
|
||||
SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||
}
|
||||
act.d = function(proof){
|
||||
if(u === proof){ return act.b() }
|
||||
SEA.decrypt(act.auth.ek, proof, act.e);
|
||||
SEA.decrypt(act.auth.ek, proof, act.e, act.enc);
|
||||
}
|
||||
act.e = function(half){
|
||||
if(u === half){ return act.b() }
|
||||
if(u === half){
|
||||
if(!act.enc){ // try old format
|
||||
act.enc = {encode: 'utf8'};
|
||||
return act.c(act.auth);
|
||||
} act.enc = null; // end backwards
|
||||
return act.b();
|
||||
}
|
||||
act.half = half;
|
||||
act.f(act.data);
|
||||
}
|
||||
@ -127,13 +137,16 @@
|
||||
user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||
at.sea = act.pair;
|
||||
cat.ing = false;
|
||||
if(pass && !Gun.text.is(act.data.auth)){ opt.shuffle = opt.change = pass; } // migrate UTF8 + Shuffle! Test against NAB alias test_sea_shuffle + passw0rd
|
||||
opt.change? act.z() : cb(at);
|
||||
if(SEA.window && ((gun.back('user')._).opt||opt).remember){
|
||||
// TODO: this needs to be modular.
|
||||
var sS = {}; try{sS = window.sessionStorage}catch(e){}
|
||||
try{var sS = {};
|
||||
sS = window.sessionStorage;
|
||||
sS.recall = true;
|
||||
sS.alias = alias;
|
||||
sS.tmp = pass;
|
||||
}catch(e){}
|
||||
}
|
||||
try{
|
||||
(root._).on('auth', at) // TODO: Deprecate this, emit on user instead! Update docs when you do.
|
||||
@ -151,9 +164,17 @@
|
||||
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x);
|
||||
}
|
||||
act.x = function(auth){
|
||||
SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
|
||||
act.w(JSON.stringify({ek: auth, s: act.salt}));
|
||||
//SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
|
||||
}
|
||||
act.w = function(auth){
|
||||
if(opt.shuffle){ // delete in future!
|
||||
var tmp = Gun.obj.to(act.data);
|
||||
Gun.obj.del(tmp, '_');
|
||||
tmp.auth = auth;
|
||||
console.log('migrate core account from UTF8 & shuffle', tmp);
|
||||
root.get('~'+act.pair.pub).put(tmp);
|
||||
} // end delete
|
||||
root.get('~'+act.pair.pub).get('auth').put(auth, cb);
|
||||
}
|
||||
act.err = function(e){
|
||||
@ -195,10 +216,12 @@
|
||||
delete user._.sea;
|
||||
}
|
||||
if(SEA.window){
|
||||
var sS = {}; try{sS = window.sessionStorage}catch(e){};
|
||||
try{var sS = {};
|
||||
sS = window.sessionStorage;
|
||||
delete sS.alias;
|
||||
delete sS.tmp;
|
||||
delete sS.recall;
|
||||
}catch(e){};
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
@ -224,7 +247,8 @@
|
||||
opt = opt || {};
|
||||
if(opt && opt.sessionStorage){
|
||||
if(SEA.window){
|
||||
var sS = {}; try{sS = window.sessionStorage}catch(e){}
|
||||
try{var sS = {};
|
||||
sS = window.sessionStorage;
|
||||
if(sS){
|
||||
(root._).opt.remember = true;
|
||||
((gun.back('user')._).opt||opt).remember = true;
|
||||
@ -232,6 +256,7 @@
|
||||
root.user().auth(sS.alias, sS.tmp, cb);
|
||||
}
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
|
@ -13,15 +13,24 @@
|
||||
key = pair.epriv || pair;
|
||||
}
|
||||
const json = parse(data)
|
||||
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
||||
|
||||
var buf; try{buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){buf = shim.Buffer.from(json.s, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
var bufiv; try{bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){bufiv = shim.Buffer.from(json.iv, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
var bufct; try{bufct = shim.Buffer.from(json.ct, opt.encode || 'base64') // NEW DEFAULT!
|
||||
}catch(e){bufct = shim.Buffer.from(json.ct, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
|
||||
|
||||
const ct = await aeskey(key, buf, opt)
|
||||
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv)
|
||||
}, aes, new Uint8Array(bufct)))
|
||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
@ -18,15 +18,16 @@
|
||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
||||
}, aes, new shim.TextEncoder().encode(msg)))
|
||||
const r = 'SEA'+JSON.stringify({
|
||||
ct: shim.Buffer.from(ct, 'binary').toString('utf8'),
|
||||
iv: rand.iv.toString('utf8'),
|
||||
s: rand.s.toString('utf8')
|
||||
ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'),
|
||||
iv: rand.iv.toString(opt.encode || 'base64'),
|
||||
s: rand.s.toString(opt.encode || 'base64')
|
||||
});
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
var SEA = require('./root');
|
||||
if(SEA.window){
|
||||
try{ if(SEA.window){
|
||||
if(location.protocol.indexOf('s') < 0
|
||||
&& location.host.indexOf('localhost') < 0
|
||||
&& location.protocol.indexOf('file:') < 0){
|
||||
location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS!
|
||||
}
|
||||
}
|
||||
} }catch(e){}
|
||||
|
25
sea/index.js
25
sea/index.js
@ -34,6 +34,8 @@
|
||||
Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node
|
||||
// TODO: consider async/await use here...
|
||||
SEA.verify(val, false, function(data){ c--; // false just extracts the plain data.
|
||||
var tmp = data;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
node[key] = val = data; // transform to plain value.
|
||||
if(d && !c && (c = -1)){ to.next(msg) }
|
||||
});
|
||||
@ -77,7 +79,7 @@
|
||||
// potentially parallel async operations!!!
|
||||
var check = {}, each = {}, u;
|
||||
each.node = function(node, soul){
|
||||
//if(Gun.obj.empty(node, '_')){ return check['node'+soul] = 0 } // ignore empty updates, don't reject them. @amark, removing this line solves issue #616. 2018-10-02
|
||||
if(Gun.obj.empty(node, '_')){ return check['node'+soul] = 0 } // ignore empty updates, don't reject them.
|
||||
Gun.obj.map(node, each.way, {soul: soul, node: node});
|
||||
};
|
||||
each.way = function(val, key){
|
||||
@ -113,7 +115,7 @@
|
||||
check['user'+soul+key] = 1;
|
||||
if(user && user.is && pub === user.is.pub){
|
||||
//var id = Gun.text.random(3);
|
||||
SEA.sign(val, (user._).sea, function(data){ var rel;
|
||||
SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){ var rel;
|
||||
if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
|
||||
if(rel = Gun.val.link.is(val)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
@ -126,6 +128,7 @@
|
||||
return;
|
||||
}
|
||||
SEA.verify(val, pub, function(data){ var rel, tmp;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
if(u === data){ // make sure the signature matches the account it claims to be on.
|
||||
return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account.
|
||||
}
|
||||
@ -150,6 +153,7 @@
|
||||
if(tmp = relpub(soul)){
|
||||
check['any'+soul+key] = 1;
|
||||
SEA.verify(val, pub = tmp, function(data){ var rel;
|
||||
data = SEA.opt.unpack(data, key, node);
|
||||
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
|
||||
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
@ -187,7 +191,7 @@
|
||||
//});
|
||||
return;
|
||||
}
|
||||
if((pub = tmp) !== (user.is||noop).pub){
|
||||
if(!msg.I || (pub = tmp) !== (user.is||noop).pub){
|
||||
each.any(val, key, node, soul);
|
||||
return;
|
||||
}
|
||||
@ -199,7 +203,7 @@
|
||||
return;
|
||||
}*/
|
||||
check['any'+soul+key] = 1;
|
||||
SEA.sign(val, (user._).sea, function(data){
|
||||
SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){
|
||||
if(u === data){ return each.end({err: 'My signature fail.'}) }
|
||||
node[key] = data;
|
||||
check['any'+soul+key] = 0;
|
||||
@ -224,6 +228,17 @@
|
||||
}
|
||||
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
|
||||
}
|
||||
var noop = {};
|
||||
SEA.opt.unpack = function(data, key, node){
|
||||
if(u === data){ return }
|
||||
var tmp = data, soul = Gun.node.soul(node), s = Gun.state.is(node, key);
|
||||
if(tmp && 4 === tmp.length && soul === tmp[0] && key === tmp[1] && s === tmp[3]){
|
||||
return tmp[2];
|
||||
}
|
||||
if(s < SEA.opt.shuffle_attack){
|
||||
return data;
|
||||
}
|
||||
}
|
||||
SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
|
||||
var noop = {}, u;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
@ -64,6 +65,7 @@
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
34
sea/sea.js
34
sea/sea.js
@ -1,44 +1,18 @@
|
||||
|
||||
// Old Code...
|
||||
try{
|
||||
const __gky10 = require('./shim')
|
||||
const crypto = __gky10.crypto
|
||||
const subtle = __gky10.subtle
|
||||
const ossl = __gky10.ossl
|
||||
const TextEncoder = __gky10.TextEncoder
|
||||
const TextDecoder = __gky10.TextDecoder
|
||||
const getRandomBytes = __gky10.random
|
||||
const EasyIndexedDB = require('./indexed')
|
||||
const Buffer = require('./buffer')
|
||||
var settings = require('./settings');
|
||||
const __gky11 = require('./settings')
|
||||
const pbKdf2 = __gky11.pbkdf2
|
||||
const ecdsaKeyProps = __gky11.ecdsa.pair
|
||||
const ecdsaSignProps = __gky11.ecdsa.sign
|
||||
const ecdhKeyProps = __gky11.ecdh
|
||||
const keysToEcdsaJwk = __gky11.jwk
|
||||
const sha1hash = require('./sha1')
|
||||
const sha256hash = require('./sha256')
|
||||
const parseProps = require('./parse')
|
||||
}catch(e){}
|
||||
|
||||
var shim = require('./shim');
|
||||
// Practical examples about usage found from ./test/common.js
|
||||
const SEA = require('./root');
|
||||
var SEA = require('./root');
|
||||
SEA.work = require('./work');
|
||||
SEA.sign = require('./sign');
|
||||
SEA.verify = require('./verify');
|
||||
SEA.encrypt = require('./encrypt');
|
||||
SEA.decrypt = require('./decrypt');
|
||||
|
||||
SEA.random = SEA.random || getRandomBytes;
|
||||
|
||||
// This is easy way to use IndexedDB, all methods are Promises
|
||||
// Note: Not all SEA interfaces have to support this.
|
||||
SEA.EasyIndexedDB = EasyIndexedDB;
|
||||
SEA.random = SEA.random || shim.random;
|
||||
|
||||
// This is Buffer used in SEA and usable from Gun/SEA application also.
|
||||
// For documentation see https://nodejs.org/api/buffer.html
|
||||
SEA.Buffer = SEA.Buffer || Buffer;
|
||||
SEA.Buffer = SEA.Buffer || require('./buffer');
|
||||
|
||||
// These SEA functions support now ony Promises or
|
||||
// async/await (compatible) code, use those like Promises.
|
||||
|
@ -29,6 +29,7 @@
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
16
sea/sign.js
16
sea/sign.js
@ -3,17 +3,9 @@
|
||||
var shim = require('./shim');
|
||||
var S = require('./settings');
|
||||
var sha256hash = require('./sha256');
|
||||
var u;
|
||||
|
||||
SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
|
||||
if(data && data.slice
|
||||
&& 'SEA{' === data.slice(0,4)
|
||||
&& '"m":' === data.slice(4,8)){
|
||||
// TODO: This would prevent pair2 signing pair1's signature.
|
||||
// So we may want to change this in the future.
|
||||
// but for now, we want to prevent duplicate double signature.
|
||||
if(cb){ try{ cb(data) }catch(e){console.log(e)} }
|
||||
return data;
|
||||
}
|
||||
opt = opt || {};
|
||||
if(!(pair||opt).priv){
|
||||
pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
|
||||
@ -21,17 +13,17 @@
|
||||
const pub = pair.pub
|
||||
const priv = pair.priv
|
||||
const jwk = S.jwk(pub, priv)
|
||||
const msg = JSON.stringify(data)
|
||||
const hash = await sha256hash(msg)
|
||||
const hash = await sha256hash(JSON.stringify(data))
|
||||
const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')});
|
||||
const r = 'SEA'+JSON.stringify({m: data, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')});
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
@ -22,9 +22,17 @@
|
||||
const jwk = S.jwk(pub)
|
||||
const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify'])
|
||||
const hash = await sha256hash(json.m)
|
||||
const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8'))
|
||||
const check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
var buf; var sig; var check; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
const r = check? parse(json.m) : u;
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
@ -32,6 +40,7 @@
|
||||
} catch(e) {
|
||||
console.log(e); // mismatched owner FOR MARTTI
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
salt = salt || shim.random(9);
|
||||
if('SHA-256' === opt.name){
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString(opt.encode || 'base64')
|
||||
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||
return rsha;
|
||||
}
|
||||
@ -28,11 +28,12 @@
|
||||
hash: opt.hash || S.pbkdf2.hash,
|
||||
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
const r = shim.Buffer.from(result, 'binary').toString(opt.encode || 'base64')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
@ -5,7 +5,7 @@ var root, noop = function(){}, store, u;
|
||||
try{store = (Gun.window||noop).localStorage}catch(e){}
|
||||
if(!store){
|
||||
console.log("Warning: No localStorage exists to persist data to!");
|
||||
store = {setItem: noop, removeItem: noop, getItem: noop};
|
||||
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
|
||||
}
|
||||
/*
|
||||
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
|
||||
|
@ -20,7 +20,7 @@ function Mesh(ctx){
|
||||
return;
|
||||
}
|
||||
// add hook for AXE?
|
||||
if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; }
|
||||
//if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; } // rogowski
|
||||
mesh.say(msg);
|
||||
}
|
||||
|
||||
@ -229,3 +229,5 @@ Mesh.hash = function(s){ // via SO
|
||||
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
|
||||
|
||||
try{ module.exports = Mesh }catch(e){}
|
||||
|
||||
|
@ -93,9 +93,10 @@ function use(msg){
|
||||
msg = obj_to(msg, {put: data = tmp.put});
|
||||
}
|
||||
}
|
||||
if((tmp = root.mum) && at.id){
|
||||
if(tmp[at.id]){ return }
|
||||
if(u !== data && !rel.is(data)){ tmp[at.id] = true; }
|
||||
if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now?
|
||||
var id = at.id + (eve.id || (eve.id = Gun.text.random(9)));
|
||||
if(tmp[id]){ return }
|
||||
if(u !== data && !rel.is(data)){ tmp[id] = true; }
|
||||
}
|
||||
as.use(msg, eve);
|
||||
if(eve.stun){
|
||||
|
@ -6,7 +6,7 @@ describe('Gun', function(){
|
||||
if(typeof global !== 'undefined'){ env = global }
|
||||
if(typeof window !== 'undefined'){ env = window }
|
||||
root = env.window? env.window : global;
|
||||
env.window && root.localStorage && root.localStorage.clear();
|
||||
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
|
||||
try{ require('fs').unlinkSync('data.json') }catch(e){}
|
||||
//root.Gun = root.Gun || require('../gun');
|
||||
if(root.Gun){
|
||||
@ -3703,6 +3703,29 @@ describe('Gun', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Multiple subscribes should trigger', function(done){
|
||||
// thanks to @ivkan for reporting and providing test.
|
||||
var gun = Gun();
|
||||
var check = {};
|
||||
gun.get('m/s/key').put({property: 'value'});
|
||||
|
||||
gun.get('m/s/key').on(function(data, key){
|
||||
check['a'+data.property] = 1;
|
||||
});
|
||||
|
||||
gun.get('m/s/key').on(function(data, key){
|
||||
check['b'+data.property] = 1;
|
||||
if(check.avalue && check.bvalue && check.anewValue && check.bnewValue){
|
||||
if(done.c){ return } done.c = true;
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function(){
|
||||
gun.get('m/s/key').put({property: 'newValue'});
|
||||
}, 1000);
|
||||
});
|
||||
return;
|
||||
it('Nested listener should be called', function(done){
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user