Merge pull request #4 from amark/master

update to amark/gun master
This commit is contained in:
sirpy 2019-12-08 17:40:24 +02:00 committed by GitHub
commit af1590cf60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 2577 additions and 2406 deletions

2
.gitignore vendored
View File

@ -10,4 +10,4 @@ yarn.lock
*.DS_store
.esm-cache
.sessionStorage
.localStorage
.localStorage

View File

@ -116,7 +116,8 @@ Thanks to:<br/>
<a href="https://github.com/dfreire">Dário Freire</a>,
<a href="http://github.com/velua">John Williamson</a>,
<a href="http://github.com/finwo">Robin Bron</a>,
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>,
<a href="http://github.com/mikestaub">Mike Staub</a>
</p>
- Join others in sponsoring code: https://www.patreon.com/gunDB !
@ -193,7 +194,7 @@ var Gun = require('gun/gun');
If you also need to install SEA for user auth and crypto, also install some of its dependencies like this:
`npm install text-encoding node-webcrypto-ossl --save`
`npm install text-encoding @peculiar/webcrypto --save`
You will need to require it too (it will be automatically added to the Gun object):

22
examples/basic/paste.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<style>html, body, textarea { width: 100%; height: 100%; padding: 0; margin: 0; }</style>
<textarea placeholder="paste here!"></textarea>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var to, paste = gun.get('test').get('paste').on(function(data){
$('textarea').val(data);
})
$('textarea').on('input change blur keyup mouseup touchend', function(){
clearTimeout(to); // debounce
to = setTimeout(function(){
paste.put($('textarea').val());
}, 100);
})
</script>

View File

@ -1,4 +1,4 @@
<html><body>
<!DOCTYPE html>
<style>
html, body {
background: rgb(245, 245, 245);
@ -136,5 +136,4 @@ $('#search').on('blur', function(e){
});
});
});
</script>
</body></html>
</script>

View File

@ -5,6 +5,7 @@
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
</head>
<body>
<h1><button id="left">&larr;</button> <span id="date"></span> Schedule <button id="right">&rarr;</button></h1>
<form id="add">

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<video id="video" width="100%"></video>
<center>
<button id="record">Record</button>
<button id="play">Play</button>
</center>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var record = {recorder: null, recording: false};
$('#record').on('click', ()=>{
if(!record.ing){ return record.stream() }
$('#record').text("Record");
if(record.ing.stop){ record.ing.stop() }
record.ing = false;
})
record.stream = function(){
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
var chunks = []; // we have a stream, we can record it
record.ing = new MediaRecorder(stream);
record.ing.ondataavailable = eve => chunks.push(eve.data);
record.ing.onstop = eve => record.save(new Blob(chunks));
record.ing.start()
$('#record').text("End");
}, err => { console.log(err) });
}
record.save = function(data){
record.file = record.file || new FileReader();
record.file.readAsDataURL(data);
record.file.onloadend = function(){
var b64 = record.file.result;
b64 = "data:video/webm" + b64.slice(b64.indexOf(';'));
gun.get('test').get('screen').put(b64);
}
}
$('#play').on('click', ()=>{
if(record.playing){
$('#play').text("Play")
$('#video').get(0).stop();
record.playing = false;
return;
}
$('#play').text("Stop");
record.playing = true;
gun.get('test').get('screen').once((data)=>{
if(!data){ return }
$('#video').get(0).src = data;
$('#video').get(0).play()
})
})
</script>

View File

@ -1,50 +0,0 @@
<h1>Search</h1>
<form id="ask">
<input id="search" placeholder="search..." autocomplete="off">
</form>
<div id="answer"></div>
<ul></ul>
<small>Note: No data is indexed by default, you need to add some!</small>
<script src="../../examples/jquery.js"></script>
<script src="../../gun.js"></script>
<script src="../../sea.js"></script>
<script src="../../lib/space.js"></script>
<script>
var gun = Gun();
var ask = {};
$('#search').on('keyup', function(e){
ask.now = (this.value||'').toLowerCase().replace(/[\W_]+/g,"");
if(ask.last === ask.now){ return }
ask.last = ask.now;
clearTimeout(ask.to);
ask.to = setTimeout(search, 20);
});
function search(){
var key = ask.now;
gun.get('Q').space(key, function(ack){
if(!ack || key !== ask.now){ return }
UI(ack)
});
}
function UI(ack){
$('#answer').text(ack.data || '');
var $ul = $('ul').empty(), tree = ack.tree;
Gun.obj.map(tree, function(v,k){
$('<li>').text(k +' - ' + v).appendTo($ul);
});
};
function load(DATA){
Gun.obj.map(DATA, function(v,k){
gun.get('Q').space(k, v);
});
}
</script>

View File

@ -1,3 +1,5 @@
<!DOCTYPE html>
<h1>Tables</h1>
<form id="sign">

View File

@ -1,3 +1,5 @@
<!DOCTYPE html>
<h1>User</h1>
<form id="sign">
@ -11,8 +13,8 @@
<ul></ul>
<form id="said">
<input id="say">
<input id="speak" type="submit" value="speak">
<input id="say">
<input id="speak" type="submit" value="speak">
</form>
<script src="../jquery.js"></script>
@ -27,19 +29,19 @@ $('#up').on('click', function(e){
user.create($('#alias').val(), $('#pass').val(), login);
});
function login(e){
user.auth($('#alias').val(), $('#pass').val());
user.auth($('#alias').val(), $('#pass').val());
return false; // e.preventDefault();
};
$('#sign').on('submit', login);
$('#mask').on('click', login);
gun.on('auth', function(){
$('#sign').hide();
user.get('said').map().on(UI);
$('#sign').hide();
user.get('said').map().on(UI);
});
$('#said').on('submit', function(e){
e.preventDefault();
e.preventDefault();
//if(!user.is){ return }
user.get('said').set($('#say').val());
$('#say').val("");

152
examples/docs.html Normal file
View File

@ -0,0 +1,152 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<title>Docs</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body {
font-family: "Oxygen", sans-serif;
}
[contenteditable]:focus {
outline: none;
}
</style>
<div class="hold full hue2">
<div id="page" class="max focus gap" style="padding-top: 9%;"></div>
</div>
<script src="../../gun/gun.js"></script>
<script src="../../gun/lib/monotype.js"></script>
<script src="../../gun/lib/meta.js"></script>
<script src="../../gun/lib/normalize.js"></script>
<script>
var gun = Gun('https://guntest.herokuapp.com/gun');
;(window.onhashchange = function(){
var file = (location.hash||'').slice(1);
var S = +new Date;
gun.get('test/gun/docs/'+file).get('what').map().on(function render(data, i){
if(window.LOCK){ return }
var p = $('#page').children().get(i);
if(!p){
$('#page').append('<p>');
setTimeout(function(){ render(data, i) },0);
return;
}
var r = monotype(p);
var safe = $.normalize(data);
p.outerHTML = safe;
r.restore();
});
return;
gun.get('test/gun/docs/'+file).get('what').once(function(data){
console.log(+new Date - S, 'ms load');
$('#page').html($.normalize(data));
});
})();
$("<textarea id='debug'>").css({position: 'fixed', height: '5em', width: '100%', bottom: 0, left: 0, 'font-size': '18pt'}).appendTo('body')
document.execCommand('defaultParagraphSeparator', false, 'p');
meta.edit({
name: "Edit",
combo: ['E'],
use: function(eve){
console.log('on');
}, on: function(eve){
var edit = this;
meta.flip(false);
var doc = $('#page').attr('contenteditable', 'true');
if(!doc.text()){
doc.html('<p class="loud crack"></p>');
edit.select(doc.children().first().get(0));
}
$(document).on('keydown.tmp', '[contenteditable]', function(eve){
if(eve.which != 13){ return }
eve.preventDefault();
var r = window.getSelection().getRangeAt(0);
var c = r.commonAncestorContainer;
r.deleteContents();
c.splitText(r.startOffset);
var p = $(c).parent(), n = $("<"+p.get(0).tagName+">"), f;
p.contents().each(function(){
if(this === c){ return f = true }
if(!f){ return }
n.append(this);
});
p.after(n);
edit.select(n.get(0));
// make sure we re-save & sync each changed paragraph.
edit.save(p);
p.nextAll().each(function(){
edit.save(this);
});
}).on('keyup.tmp', '[contenteditable]', function(eve){
$('#debug').val(doc.html());
var p = $(window.getSelection().anchorNode).closest('p');
var r = monotype(p);
var html = p.html() || '';
var safe = $.normalize(html);
p.html(safe);
r.restore();
edit.save(p);
});
},
up: function(){
console.log("UP");
$('[contenteditable=true]').off('.tmp');
},
save: function(p){
p = $(p);
var i = p.index();// = Array.prototype.indexOf.call(parent.children, child);
var file = (location.hash||'').slice(1);
var data = p.get(0).outerHTML;
window.LOCK = true;
gun.get('test/gun/docs/'+file).get('what').get(i).put(data);
window.LOCK = false;
},
select: function(p){
var s = window.getSelection(),
r = document.createRange();
if(p.innerHTML){
r.setStart(p, 0);
r.collapse(true);
s.removeAllRanges();
s.addRange(r);
return;
}
p.innerHTML = '\u00a0';
r.selectNodeContents(p);
s.removeAllRanges();
s.addRange(r);
document.execCommand('delete', false, null);
}
});
meta.edit({
name: "Save",
combo: ['S'], fake: -1,
on: function(eve){
return;
meta.tap.on.attr('contenteditable', 'false');
var what = $.normalize($('#page').html());
var file = (location.hash||'').slice(1);
gun.get('test/gun/docs/'+file).put({what: what});
},
up: function(){}
});
</script>
</body>
</html>

439
examples/game/furball.html Normal file
View File

@ -0,0 +1,439 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="../style.css">
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
<title>Furball</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Mali');
html, body {
font-family: "Mali", sans-serif;
}
.huef {
background: #4D79D8;
-webkit-animation: huef 9s infinite;
animation: huef 9s infinite;
} @keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
} @-webkit-keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
}
button, input {
padding: 1em;
background: transparent;
border: 1px solid white;
border-radius: 1.5em;
color: white;
margin: 0.5em;
margin-bottom: 0;
cursor: pointer;
}
button:hover, input:hover {
background: white;
color: black;
transform: scale(1.1);
}
.air { padding-top: 9%; }
.yak button { font-size: 80%; }
.wag {
-webkit-animation: wag 3s infinite;
animation: wag 3s infinite;
} @keyframes wag {
0% {transform: rotate(0deg);}
50% {transform: rotate(-1deg);}
100% {transform: rotate(0deg);}
}
@keyframes print {
0% { overflow: hidden; height: 0vh; }
99% { overflow: hidden; height: 100vh; }
100% { overflow: visible; height: auto; }
}
input {
outline: none;
}
</style>
</style>
<!-- for educational sites, consider starting with a nice full screen welcome message -->
<div class="home hold full huef center air">
<div class="focus row">
<p><i>Neon ERA presents</i></p>
<p class="shout wag">Furball Forces</p>
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
<div>
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
<a class="unit yak" href="#choose"><button>PLAY GAME</button></a>
</div>
</div>
<div class="focus center row leak">
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
<img class="unit blink" src="file:///Users/mark/Downloads/supercatdog.png" style="min-width: 10em; width: 80%;">
</div>
<script>location.hash = ''</script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
<script>;(function(){
// OPTIONAL MUSIC:
$('.home button').on('click', function(){
if(window.screen.height > window.screen.width){ return }
$('body').append("<div id='audio' onclick='$(this).remove();'><iframe width='0' height='0' src='https://www.youtube-nocookie.com/embed/LLPoZGX0qZk?autoplay=1' frameborder='0'></iframe></div>");
})
}());
</script>
<style>#audio { padding: 0.5em; position: fixed; bottom: 0; left: 0; } #audio:before { content: '\25BA'; } #audio:hover:before { content: '\25FC'; }</style>
</div>
<div id="choose" class="hold full hue4 center air">
<div class="focus row">
<p class="shout wag fur">Choose Team:</p>
<div>
<a class="unit yak" href="#automecha"><button style="background: white; color: black;">#AutoMecha</button></a>
<a class="unit yak" href="#cyberninjas"><button style="background: black; color: white; border-color: black;">#CyberNinjas</button></a>
</div>
</div>
<div class="focus center row leak">
<img class="unit blink" src="file:///Users/mark/Downloads/supercatdog.png" style="transform: scaleX(-1); filter: invert(1); min-width: 10em; width: 80%;">
</div>
</div>
<div id="cyberninjas" class="hold full black">
<style>
#cyberninjas:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Waking</p>
<p>"How long until they're online?"</p>
<p>"We're copying the soul files, almost done."</p>
<p>"Monsters are on the bridge, we do not have time!"</p>
<p>"The new body is printing now, it'll be able to outrun them all, just hold on."</p>
<p>"It won't know where to run! We're risking ruining the whole resistance, I need to talk to it now."</p>
<p>"95% done." The voice behind the glass turns to the soul in the body, "My cub, can you hear me?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas2"><button>Reply "Yes, Mom?"</button></a>
</div>
<script>
;(function(){
$('#cyberninjas a').on('click', function(){
$('#hud .life').removeClass('down');
});
}());
</script>
</div>
<div id="cyberninjas2" class="hold full red">
<style>
#cyberninjas2:target .story {
animation: print 3s steps(50, end);
}
#hud {
opacity: 0.4;
font-family: 'Audiowide', cursive;
z-index: 999999999999;
transition: all 3s;
}
#hud .life {
position: fixed;
left: 50%;
bottom: 0px;
padding: 0.25em 1em 0.1em;
border-radius: 0.5em 0.5em 0 0;
transform: translateX(-50%);
background: black;
text-shadow: 0em -0.125em 0.75em white;
}
#hud .score {
position: fixed;
left: 50%;
top: 0px;
padding: 0.1em 1em 0.25em;
border-radius: 0 0 0.5em 0.5em;
transform: translateX(-50%);
background: black;
text-shadow: 0em 0.1em 0.75em white;
}
#hud .down {
bottom: -2em !important;
}
#hud .up {
top: -2em !important;
}
</style>
<div id="hud">
<div class="score shade up">
SCORE: <span id="hudscore">0</span>%
</div>
<div class="life shade down">
LIFE: <span id="hudlife">50</span>%
</div>
</div>
<div class="story pad">
<p>A fire explodes in the room behind the glass as an AutoMecha blows the door open.</p>
<p>The floor shakes and the bed crashes through the wall, flying out of the building.</p>
<p class="center">"Mom!!!"</p>
<p>There is a total free fall from 10 levels up, water down below.</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas3"><button>Dive or Die</button></a>
</div>
<script>
;(function(){
$('#cyberninjas2 a').on('click', function(){
$('#hudlife').text($('#hudlife').data().is = 50);
});
}());
</script>
</div>
<div id="cyberninjas3" class="hold full blue">
<style>
@import url('https://fonts.googleapis.com/css?family=Audiowide');
#cyberninjas3:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>The water splashes, swelling and swirling all around.</p>
<p>...</p>
<p class="center">Rapidly tap to swim up to air:</p>
<a class="unit yak"><button>Swim</button></a>
</div>
<script>
;(function(){
var go, life = $('#hudlife').data();
$('#cyberninjas3 a').on('click', function(){
$('#hudlife').text(life.is += 5);
if(100 <= life.is){
location.hash = 'cyberninjas4';
clearInterval(go);
go = false;
return;
}
if(go){ return }
go = setInterval(function(){
if(0 >= life.is){
location.hash = 'cyberninjas2';
$('#hudlife').text(life.is = 50);
clearInterval(go);
go = false;
return;
}
$('#hudlife').text(life.is -= 5);
}, 1000); // 1 second
});
}());
</script>
</div>
<div id="cyberninjas4" class="hold full black">
<style>
#cyberninjas4:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 2: Who Am I?</p>
<p>"Grab on!" A voice calls out from the darkness.</p>
<p>A life vest hits the water and floats within arm's distance.</p>
<p>The shivering body is pulled up onto the boat.</p>
<p>"Wow, you're heavier than you look. Are you OK? What's your name?"</p>
<p>...</p>
<p class="center">Write your reply & hit enter:</p>
<form class="center">
<input class="loud" style="width: 60%;">
</form>
</div>
<script>
;(function(){
$('form').on('submit', function(eve){ eve.preventDefault() });
$('#cyberninjas4').on('submit', function(){
var name = $(this).find('input').val();
if(!name.length){ return }
$('.story-name').text(' '+(window.NAME = name));
$('#hud .score').removeClass('up');
location.hash = 'cyberninjas5';
})
}());
</script>
</div>
<div id="cyberninjas5" class="hold full green">
<style>
#cyberninjas5:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>"Well<span class="story-name"></span>, it's a miracle you did not die in the building explosion or from that fall."</p>
<p>"What is going on? What happened?"</p>
<p>"You can't remember? Your brain must be knocked up pretty hard."</p>
<p>"No, I was mid copy into this body and now my memories are glitching."</p>
<p>"Woah, you're one of those pro elite AREION revolutionaries? All flesh & blood! Dense, too. I would've assumed they were stealing AutoMecha tech for that instead."</p>
<p>"I was about to be told vital data for the resistance, but then they blew up the build--"</p>
<p>...</p>
<p>"Hey, what's the matter?"</p>
<p>"My mom. She was in there. I need to go back. Please, help me and tell me everything you know."</p>
<p>"I'm so sorry. I can only get so close with the boat, you're gonna have to jump over a lot of broken bits. You ready?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas6"><button>GO!</button></a>
</div>
</div>
<div id="cyberninjas6" class="hold full green">
<style>
#cyberninjas6:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="center">... to be continued ...</p>
<div id="player" style="position: fixed; width: 1em; height: 1em; background: white; left: 50%; top: 50%; border-radius: 100%;"></div>
<!-- jumping game ? like offline dinosaur ? -->
</div>
<script src="../../../gun/lib/meta.js"></script>
<script>
;(function(){
var p = $('#player');
p.x = 50;
p.y = 50;
meta.edit({
name: "Up",
combo: ["W"],
on: function(){
console.log("up");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
p.css({top: --p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Left",
combo: ["A"],
on: function(){
console.log("left");
this.to = this.to || setInterval(this.on, 100);
p.css({left: --p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Down",
combo: ["S"],
on: function on(){
console.log("down");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()+100 }, 100);
p.css({top: ++p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Right",
combo: ["D"],
on: function(){
console.log("right");
this.to = this.to || setInterval(this.on, 100);
p.css({left: ++p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Jump",
combo: [32],
on: function(){ console.log("jump") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Crouch",
combo: [16],
on: function(){ console.log("crouch") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Use",
combo: ["E"],
on: function(){ console.log("use") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Fire",
combo: ["F"],
on: function(){ console.log("fire") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Switch",
combo: [9],
on: function(){ console.log("Switch") },
use: function(){},
up: function(){}
});
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
window.requestAnimationFrame(function frame(){
window.requestAnimationFrame(frame, 16);
}, 16);
}());
</script>
</div>
<div id="automecha" class="hold full white blackt">
<style>
#automecha:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Training</p>
<p>...</p>
</div>
</div>
<div class="hold black center">
<div class="pad">
<div class="left">
<p class="loud">For <i>You</i>,</p>
<p>Crafted with love, <span class="redt"></span> by ERA.</p>
</div>
</div>
<div>
<img src="https://era.eco/media/world.png" class="row">
</div>
</div>
</body>
</html>

View File

@ -1,18 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
{
"name": "gun-react-examples",
"version": "1.1.0",
"private": true,
"dependencies": {
"gun": "file:../..",
"react": "^15.5.4",
"react-dom": "^15.5.4"
},
"devDependencies": {
"express": "^4.15.2",
"express-http-proxy": "^0.11.0",
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"server": "PORT=8081 node ./server.js"
}
}

View File

@ -1,21 +0,0 @@
console.log("If modules not found, run `npm install` in /example folder!");
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';
var express = require('express');
var proxy = require('express-http-proxy');
var http = require('http');
var app = express();
var server = http.createServer(app);
var Gun = require('gun');
var gun = Gun({
file: 'data.json',
web: server
});
app.use(Gun.serve);
app.use(proxy(host + ':8765'));
server.listen(port);
console.log('Server started on port ' + port + ' with /gun');

38
examples/react/todo.html Normal file
View File

@ -0,0 +1,38 @@
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script type="text/babel">
const gun = Gun();
const App = () => {
const newTodo = React.useRef()
const [todos, setTodos] = React.useState({})
React.useEffect(() => {
gun
.get("todos")
.map()
.on((todo, id) => setTodos(todos => ({...todos, [id]: todo })));
}, [])
return (
<div>
<title>TODOs</title>
<ul>{Object.values(todos).map(({title}, i) => <li key={i}>{title}</li>)}</ul>
<form onSubmit={e => {
e.preventDefault();
gun.get("todos").set({ title: newTodo.current.value });
ref.current.value = ''
}}>
<input ref={newTodo} placeholder="new todo"/>
</form>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
</script>
</body>
</html>

1104
examples/smoothie.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,161 +2,93 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<style>
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body {
font-family: "Oxygen", sans-serif;
}
svg, .ct-chart * {
overflow: visible;
}
.ct-series-a .ct-line,
.ct-series-a .ct-point {
str-oke: blue !important;
body {
font-family: helvetica;
background-color: rgb(25,25,25);
color: rgb(80,135,25) !important;
text-shadow: 1px 1px 20px rgb(80,150,25);
}
.ct-series-b .ct-line,
.ct-series-b .ct-point {
stroke: green !important;
}
.label {
position: absolute;
left: 0.5em;
top: 1.75em;
}
.tall { height: 10em; }
.input {
height: 30px;
padding:10px;
background-color: rgb(50,50,50);
color: rgb(250,50,50);
}
.tall { height: 5em; }
</style>
<input id="url" class="center none" placeholder="enter peer stats source url">
<div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours</div>
<div class="leak" style="padding: 0 2em;">
<div class="leak ct-mem ct-chart ct-perfect-fourth tall"></div>
<input id="url" class="center input crack" placeholder="enter peer stats source url">
<div class="center row charts">
</div>
<div class="center leak" style="padding: 0 2em;">
<div class="unit col leak ct-damc ct-chart tall" style="width: 49%;"></div>
<div class="unit col leak ct-damd ct-chart tall" style="width: 49%;"></div>
<div class="model none">
<div class="chart"><span class="label"></span><canvas class="tall row"></canvas></div>
</div>
<div class="center leak" style="padding: 0 2em;">
<div class="unit col leak ct-radc ct-chart tall" style="width: 49%;"></div>
<div class="unit col leak ct-radt ct-chart tall" style="width: 49%;"></div>
</div>
<div class="center"><span id="rerr"></span></div>
<div class="center leak" style="padding: 0 2em;">
<div class="leak ct-daml ct-chart tall"></div>
</div>
<div class="center leak" style="padding: 0 2em;">
<div class="leak ct-cpu ct-chart ct-perfect-fourth tall"></div>
</div>
<script src="./jquery.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/chartist.js/latest/chartist.min.css">
<script src="https://cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script>
<script src="./smoothie.js" charset="utf-8"></script>
<script>
var stats = {slide: [0,0,0,0,0], din: [0,0,0,0,0], dout: [0,0,0,0,0], dind: [0,0,0,0,0], doutd: [0,0,0,0,0], rgetc: [0,0,0,0,0], rputc: [0,0,0,0,0]};
setInterval(function(){
stats.show();
}, 1000 * 15);
stats.show = async function(data){ //$.getJSON(url.value||(location.origin+'/gun/stats.radata'), function(data){ console.log(data);
data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
console.log(data);
$('#peers').text(data.peers.count);
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
$('#nodes').text(data.node.count);
$('#hours').text((data.up.time / 60 / 60).toFixed(0));
var fetchData = async function(){
// fetch the data from server
var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
$('#peers').text(data.peers.count);
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
$('#nodes').text(data.node.count);
$('#hours').text((data.up.time / 60 / 60).toFixed(0));
$('#dinc').text(data.dam.in.count);
$('#dind').text((data.dam.in.done / 1024 / 1024).toFixed(1));
$('#doutc').text(data.dam.out.count);
$('#doutd').text((data.dam.out.done / 1024 / 1024).toFixed(1));
Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024);
stats.slide.push(data.memory.heapTotal / 1024 / 1024); stats.slide = stats.slide.slice(1);
new Chartist.Line('.ct-mem', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.slide]
}, {fullWidth: true, low: 0, axisY: {
labelInterpolationFnc: function(v) { return v+'MB' }
}});
console.log('data',data);
//fetch keys in all, these may be dynamically changing
//for each key, check if we already have created a time series, if not, create it and add it
// to the chart corredsponding to the unit of measure
$.each(data.all, function(key, arr){
var chart = Stats(key);
// get data and append to line
// get the arrays inside the key
//for each array append the data to the line
for(var i in arr) {
// append data [timestamp], [data]
chart.line.append(arr[i][0], arr[i][1]);
}
})
}
setInterval(fetchData, 15 * 1000);
fetchData();
stats.din.push(data.dam['in'].count); stats.din = stats.din.slice(1);
stats.dout.push(data.dam.out.count); stats.dout = stats.dout.slice(1);
new Chartist.Line('.ct-damc', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.dout, stats.din]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'msgs' }
}});
stats.dind.push(data.dam['in'].done / 1024 / 1024); stats.dind = stats.dind.slice(1);
stats.doutd.push(data.dam.out.done / 1024 / 1024); stats.doutd = stats.doutd.slice(1);
new Chartist.Line('.ct-damd', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.doutd, stats.dind]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'MB' }
}});
try{ $('#rerr').text(data.rad.put.err || data.rad.get.err) }catch(e){}
try{
stats.rgetc.push(data.rad.get.count); stats.rgetc = stats.rgetc.slice(1);
stats.rputc.push(data.rad.put.count); stats.rputc = stats.rputc.slice(1);
new Chartist.Line('.ct-radc', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.rputc, stats.rgetc]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'io' }
}});
}catch(e){}
try{
stats.radgt = Object.values(data.rad.get.time).map(function(n){ return n/1000 });
stats.radpt = Object.values(data.rad.put.time).map(function(n){ return n/1000 });
new Chartist.Line('.ct-radt', {
// A labels array that can contain any sort of values
//labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.radpt, stats.radgt]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'sec' }
}});
}catch(e){}
try{
stats.daml = Object.values(data.dam['in'].long).map(function(n){ return n });
new Chartist.Line('.ct-daml', {
// A labels array that can contain any sort of values
//labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.daml]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'ms' }
}});
}catch(e){}
new Chartist.Line('.ct-cpu', {
// A labels array that can contain any sort of values
labels: ['-15min', '-5min', '1min'],
// Our series array that contains series objects or in this case series data arrays
series: [data.cpu.loadavg.reverse()]
}, {fullWidth: true, low: 0, axisY: {
labelInterpolationFnc: function(v) { return v+'cpu' }
}});
//})
}
stats.show();
function Stats(key, chart){
// if we have already created, get data to append to it.
if(chart = Stats[key]){
return chart;
}
// create a new Series for this key
// add it into the map
chart = Stats[key] = new SmoothieChart({responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}});
chart.line = new TimeSeries();
chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 });
chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts');
chart.canvas.find('span').text(key);
chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000);
// check first two characters of key to determine other charts to add this in
// tbd later
return chart;
}
</script>
</body>
</html>

43
gun.js
View File

@ -811,20 +811,16 @@
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
//var S = +new Date;
node = Gun.obj.copy(node);
//console.log(+new Date - S, 'copy node');
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
//var S = +new Date;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//console.log(+new Date - S, 'root got send');
//if(0 < tmp){ return }
root.on('get', msg);
}
@ -1317,7 +1313,6 @@
function use(msg){
var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp;
if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) }
//console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum);
//if(at.async && msg.root){ return }
//if(at.async === 1 && cat.async !== true){ return }
//if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);
@ -1545,7 +1540,7 @@
as = as.as;
if(!msg.$ || !msg.$._){ return } // TODO: Handle
if(msg.err){ // TODO: Handle
console.log("Please report this as an issue! Put.any.err");
Gun.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
@ -1554,7 +1549,7 @@
if(as.ref !== as.$){
tmp = (as.$._).get || at.get;
if(!tmp){ // TODO: Handle
console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
Gun.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);
@ -1814,7 +1809,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!");
Gun.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
@ -1920,7 +1915,6 @@
data = Gun.state.to(data, has);
}
//if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected?
//console.log("lS get", lex, data);
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$});
};
Gun.debug? setTimeout(to,1) : to();
@ -1976,15 +1970,14 @@
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
if(!msg){ return }
//console.log('hear batch length of', msg.length);
LOG && opt.log(+new Date, msg.length, 'in hear batch');
(function go(){
var S = +new Date; // STATS!
var S; LOG && (S = +new Date); // STATS!
var m, c = 100; // hardcoded for now?
while(c-- && (m = msg.shift())){
mesh.hear(m, peer);
}
//console.log(+new Date - S, 'hear batch');
(mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S);
LOG && opt.log(S, +new Date - S, 'batch heard');
if(!msg.length){ return }
puff(go, 0);
}());
@ -1995,7 +1988,7 @@
}catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return }
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) }
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
if(dup.check(id)){ return }
dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
@ -2011,9 +2004,9 @@
}
return;
}
//var S = +new Date;
var S; LOG && (S = +new Date);
root.on('in', msg);
//!msg.nts && console.log(+new Date - S, 'msg', msg['#']);
LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']);
return;
}
}
@ -2027,7 +2020,7 @@
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
//var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date;
var meta = msg._||(msg._=function(){});
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
@ -2041,15 +2034,15 @@
}
}
}
//console.log(+new Date - S, 'mesh say prep');
LOG && opt.log(S, +new Date - S, 'say prep');
dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more!
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) }
if(!peer && mesh.way){ return mesh.way(msg) }
if(!peer || !peer.id){ message = msg;
if(!Type.obj.is(peer || opt.peers)){ return false }
//var S = +new Date;
var S; LOG && (S = +new Date);
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
//console.log(+new Date - S, 'mesh say loop');
LOG && opt.log(S, +new Date - S, 'say loop');
return;
}
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
@ -2073,10 +2066,10 @@
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
//var S = +new Date;
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
//console.log(+new Date - S, 'mesh flush', tmp.length);
LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
@ -2086,14 +2079,14 @@
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
//var S = +new Date;
var S; LOG && (S = +new Date);
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
//console.log(+new Date - S, 'wire send', raw.length);
LOG && opt.log(S, +new Date - S, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
@ -2143,6 +2136,7 @@
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
@ -2223,6 +2217,7 @@
}());
var empty = {}, ok = true, u;
var LOG = console.LOG;
try{ module.exports = Mesh }catch(e){}

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,155 +1,189 @@
;(function(){
var noop = function(){}, u;
var m = window.meta = {edit:[]};
var k = m.key = {};
k.meta = {17:17, 91:17, 93:17, 224:17};
k.down = function(eve){
if(eve.repeat){ return }
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!eve.fake && key === k.last){ return } k.last = key;
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
if(k.meta[key]){ k.down.meta = key = -1 }
if(!k.down.meta){ return }
}
(k.combo || (k.combo = [])).push(key);
m.check('on', key, k.at || (k.at = m.edit));
if(k.meta[key]){
m.list(k.at.back || m.edit);
if(k.at && !k.at.back){ m.flip() }
}
}
k.up = function(eve){ var tmp;
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
/* UNBUILD */
;USE(function(module){
var noop = function(){}, u;
$.fn.or = function(s){ return this.length ? this : $(s||'body') };
var m = window.meta = {edit:[]};
var k = m.key = {};
k.meta = {17:17, 91:17, 93:17, 224:17}; // ctrl met
function withMeta(eve){ return eve.metaKey || eve.ctrlKey }
k.down = function(eve){
if(eve.repeat){ return }
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
// ADDED
if(!m.flip.is() && !k.meta[key]){ return } // cancel non-open events when closed TODO make optional
if(!k.meta[key] && withMeta(eve) && !k.at[key]) { return m.flip(false) } // cancel and close when no action and "meta key" held down (e.g. ctrl+c)
if(!eve.fake && key === k.last){ return }; k.last = key; // jussi: polyfilling eve.repeat?
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
// if(k.meta[key]){ k.down.meta = key = -1 }
// if(!k.down.meta){ return }
// prevent typing with meta open
if(meta.flip.is() && !withMeta(eve)) eve.preventDefault()
// hmmm?
// if(!k.meta[key] && !meta.flip.is()) return // aserwed
}
m.check('on', key, k.at || (k.at = m.edit));
if(k.meta[key]){
k.down.meta = null;
key = -1;
} else
if(!k.down.meta){ return }
}
k.last = null;
if($(':focus').closest('#meta').length){ return }
m.check('up', key);
if(-1 === key || 27 === eve.which){ k.wipe() }
}
m.flip = function(tmp){
var board = $('#meta .meta-menu');
((tmp === false) || (!tmp && board.is(':visible')))?
board.addClass('meta-none')
: board.removeClass('meta-none');
}
m.flip.is = function(){
return $('#meta .meta-menu').is(':visible');
}
m.flip.wait = 500;
m.check = function(how, key, at){
at = k.at || m.edit;
var edit = at[key];
if(!edit){ return }
var tmp = k.eve || noop;
if(tmp.preventDefault){ tmp.preventDefault() }
if(edit[how]){
if(tmp.fake && !edit.fake){
m.tap.edit = edit;
} else {
edit[how](m.eve);
/*if(k.at !== m.edit && 'up' === how){
if(k.down.meta){ m.list(k.at = m.edit) }
else { k.wipe() }
}*/
// m.list(k.at.back || m.edit);
// if(k.at){ m.flip() } // && !k.at.back
m.flip()
}
}
if('up' != how){ return }
if(at != edit){ edit.back = at }
m.list(edit, true);
}
m.list = function(at, opt){
if(!at){ return m.flip(false) }
var l = [];
$.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) });
if(!l.length){ return }
k.at = at;
l = l.sort(function(a,b){
a = a.combo.slice(-1)[0] || 0;
if(a.length){ a = a.toUpperCase().charCodeAt(0) }
b = b.combo.slice(-1)[0] || 0;
if(b.length){ b = b.toUpperCase().charCodeAt(0) }
return (a < b)? -1 : 1;
});
var $ul = $('#meta .meta-menu ul')
$ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch
$.each(l, function(i, k){
$ul.append($('<li>').text(k.name));
});
if(opt){ m.flip(true) }
$ul.append($('<li>').html('&larr;').one('click', function(){
m.list(k.at = at.back);
}));
}
m.ask = function(help, cb){
var $ul = $('#meta .meta-menu ul').empty();
var $put = $('<input>').attr('id', 'meta-ask').attr('placeholder', help);
var $form = $('<form>').append($put).on('submit', function(eve){
eve.preventDefault();
cb($put.val());
$li.remove();
k.wipe();
});
var $li = $('<li>').append($form);
$ul.append($li);
m.flip(true);
$put.focus();
}
k.wipe = function(opt){
k.down.meta = false;
k.combo = [];
if(!opt){ m.flip(false) }
m.list(k.at = m.edit);
};
m.tap = function(){
var on = $('.meta-on')
.or($($(document.querySelectorAll(':hover')).get().reverse()).first())
.or($(document.elementFromPoint(meta.tap.x, meta.tap.y)));
return on;
}
$(window).on('blur', k.wipe).on('focus', k.wipe);
$(document).on('mousedown mousemove mouseup', function(eve){
m.tap.eve = eve;
m.tap.x = eve.pageX||0;
m.tap.y = eve.pageY||0;
m.tap.on = $(eve.target);
}).on('mousedown touchstart', function(eve){
var tmp = m.tap.edit;
if(!tmp || !tmp.on){ return }
tmp.on(eve);
m.tap.edit = null;
});
$(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true });
$(document).on('click', '#meta .meta-menu li', function(eve){
if(m.tap.stun){ return m.tap.stun = false }
if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return }
eve.tap = true;
k.down(eve);
k.up(eve);
});
$(document).on('keydown', k.down).on('keyup', k.up);
meta.edit = function(edit){
var tmp = edit.combow = [];
$.each(edit.combo || (edit.combo = []), function(i,k){
if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return }
tmp.push(k.toUpperCase().charCodeAt(0));
});
var at = meta.edit, l = edit.combo.length;
$.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} });
edit.combow = edit.combow.join(',');
m.list(meta.edit);
}
$.fn.or = function(s){ return this.length ? this : $(s||'body') };
;(function(){try{
k.up = function(eve){ var tmp;
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!m.flip.is() && !k.meta[key]){ return } // ADDED cancel non-open events when closed TODO make optional
// if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
// if(k.meta[key]){
// k.down.meta = null;
// key = -1;
// } else
// if(!k.down.meta){ return }
// }
k.last = null;
// if($(':focus').closest('#meta').length){ return }
m.check('up', key);
if(27 === eve.which){ k.wipe() } // -1 === key ||
}
m.flip = function(tmp){
var board = $('#meta .meta-menu');
((tmp === false) || (!tmp && board.is(':visible')))?
board.addClass('meta-none')
: board.removeClass('meta-none');
}
m.flip.is = function(){
return $('#meta .meta-menu').is(':visible');
}
m.flip.wait = 500;
m.check = function(how, key, at){
at = k.at || m.edit;
var next = at[key];
if(!next){ return }
var tmp = k.eve || noop;
if(tmp.preventDefault){ tmp.preventDefault()} // prevent typing (etc) when action found
if(next[how]){
//if(tmp.fake && !next.fake){
//m.tap.next = next;
//} else {
next[how](m.eve);
meta.ui.blink()
/*if(k.at !== m.next && 'up' === how){
if(k.down.meta){ m.list(k.at = m.next) }
else { k.wipe() }
}*/
//}
}
if('up' == how){ return }
if(at != next){ next.back = at }
(k.combo || (k.combo = [])).push(key);
m.list(next, true);
}
m.list = function(at, opt){
if(!at){ return m.flip(false) }
// m.ui.depth(m.key.combo ? m.key.combo.length : 0)
var l = [];
$.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) });
if(!l.length){ return }
k.at = at;
l = l.sort(function(a,b){
a = a.combo.slice(-1)[0] || 0;
if(a.length){ a = a.toUpperCase().charCodeAt(0) }
b = b.combo.slice(-1)[0] || 0;
if(b.length){ b = b.toUpperCase().charCodeAt(0) }
return (a < b)? -1 : 1;
});
var $ul = $('#meta .meta-menu ul')
$ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch
$.each(l, function(i, k){
$ul.append($('<li>').text(k.name).data(k));
});
if(opt){ m.flip(true) }
$ul.append($('<li>').html('&larr;').on('click', function(){
// m.key.combo.pop()
m.list(at.back);
}));
}
m.ask = function(help, cb){
var $ul = $('#meta .meta-menu ul').empty();
var $put = $('<input>').attr('id', 'meta-ask').attr('placeholder', help);
var $form = $('<form>').append($put).on('submit', function(eve){
eve.preventDefault();
cb($put.val());
$li.remove();
k.wipe();
});
var $li = $('<li>').append($form);
$ul.append($li);
m.flip(true);
$put.focus();
}
k.wipe = function(opt){
// k.down.meta = false;
k.combo = [];
if(!opt){ m.flip(false) }
m.list(k.at = m.edit);
};
m.tap = function(){
var on = $('.meta-on')
.or($($(document.querySelectorAll(':hover')).get().reverse()).first())
.or($(document.elementFromPoint(meta.tap.x, meta.tap.y)));
return on;
}
meta.edit = function(edit){
var tmp = edit.combow = [];
$.each(edit.combo || (edit.combo = []), function(i,k){
if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return }
tmp.push(k.toUpperCase().charCodeAt(0));
});
var at = meta.edit, l = edit.combo.length;
$.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} });
edit.combow = edit.combow.join(',');
m.list(k.at || meta.edit);
}
})(USE, './metaCore');
;USE(function(module){
/* UI */
if(meta.css){ return }
meta.ui = {
blink: function(){ // hint visually that action has happened
$('#meta').css('transition', 'none').css('background', 'none')
setTimeout(function(){
$('#meta')[0].style.transition = null
$('#meta')[0].style.background = null
})
},
depth: function(n){
if (n) {
$('#meta').css('background', 'hsl(60, 100%,'+(85-(n*10))+'%)');
} else {
$('#meta')[0].style.background = null
}
}
}
var $m = $('<div>').attr('id', 'meta');
$m.append($('<span>').text('+').addClass('meta-start'));
$m.append($('<span>').html('&#9776;').addClass('meta-start'));
$m.append($('<div>').addClass('meta-menu meta-none').append('<ul>'));
$(document.body).append($m);
css({
@ -161,7 +195,7 @@
background: 'white',
'font-size': '18pt',
'font-family': 'Tahoma, arial',
'box-shadow': '0px 0px 1px #000044',
//'box-shadow': '0px 0px 1px #000044',
'border-radius': '1em',
'text-align': 'center',
'z-index': 999999,
@ -205,22 +239,22 @@
'float': 'right'
},
'#meta a': {color: 'black'},
'#meta:hover': {opacity: 1},
// '#meta:hover': {opacity: 1},
'#meta .meta-menu ul:before': {
content: "' '",
display: 'block',
'min-height': '15em',
height: '50vh'
},
'#meta li': {
background: 'white',
padding: '0.5em 1em',
'border-radius': '1em',
'margin-left': '0.25em',
'margin-top': '0.25em',
'float': 'right'
},
'#meta:hover .meta-menu': {display: 'block'}
// '#meta li': {
// background: 'white',
// padding: '0.5em 1em',
// 'border-radius': '1em',
// 'margin-left': '0.25em',
// 'margin-top': '0.25em',
// 'float': 'right'
// },
// '#meta:hover .meta-menu': {display: 'block'}
});
function css(css){
var tmp = '';
@ -233,10 +267,13 @@
});
var tag = document.createElement('style');
tag.innerHTML = tmp;
document.body.appendChild(tag);
$m.append(tag)
}
}catch(e){}}());
;(function(){
//}catch(e){}
})(USE, './metaUI');
;USE(function(module){
// include basic text editing by default.
var monotype = window.monotype || function(){console.log("monotype needed")};
var m = meta;
@ -256,7 +293,6 @@
return ((tmp = window.getSelection) && tmp().toString()) ||
((tmp = document.selection) && tmp.createRange().text) || '';
}
$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
m.text.editor = function(opt, as){ var tmp;
if(!opt){ return }
opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
@ -266,7 +302,7 @@
r.restore();
if(document.execCommand(cmd, null, as||null)){
if(m.text.range){ m.text.range = monotype() }
return;
return meta.flip(false); // ADDED meta.flip
}
}
if(!opt.tag){ return }
@ -379,5 +415,44 @@
on: function(eve){ meta.text.editor('fontSize', 7) },
up: function(){}
});
}());
})(USE, './metaText');
;USE(function(module){
var m = meta, k = m.key;
$(window).on('blur', k.wipe.bind(null, false)).on('focus', k.wipe.bind(null, false));
//$(document).on('mousedown mousemove mouseup', function(eve){
// m.tap.eve = eve;
// m.tap.x = eve.pageX||0;
// m.tap.y = eve.pageY||0;
// m.tap.on = $(eve.target);
//})
// Setting m.tap.edit has been commented, so should never end up here?
//.on('mousedown touchstart', function(eve){
// var tmp = m.tap.edit;
// if(!tmp || !tmp.on){ return }
// tmp.on(eve);
// m.tap.edit = null;
//});
//$(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true });
$(document).on('click', '#meta .meta-menu li', function(eve){
var combo = $(this).data().combo;
eve.fake = eve.which = combo && combo.slice(-1)[0].charCodeAt(0);
eve.tap = true;
k.down(eve); k.up(eve);
return;
// if(m.tap.stun){ return m.tap.stun = false }
// if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return }
// eve.tap = true;
// k.down(eve);
// k.up(eve);
});
$(document).on('keydown', k.down).on('keyup', k.up);
//$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
})(USE, './metaEvents');
}());

View File

@ -1,7 +1,8 @@
(function(){
$.normalize = function(html, customOpt){
var html, root$, wrapped, opt;
html = html || '';
var root$, wrapped, opt;
opt = html.opt || (customOpt ? prepareOptTags($.extend(true, baseOpt, customOpt))
: defaultOpt);
if(!html.opt){

View File

@ -11,6 +11,10 @@
* Author: Jachen Duschletta / 2019
*/
// Get window or node Gun instance
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
/*
* Function promOnce
* @param limit - due to promises resolving too fast if we do not set a timer

View File

@ -19,7 +19,7 @@
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
var map = Gun.obj.map;
var LOG = false;
var LOG = console.LOG;
if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@ -46,7 +46,7 @@
cb = val;
var S; LOG && (S = +new Date);
val = r.batch(key);
LOG && console.log(+new Date - S, 'rad mem');
LOG && opt.log(S, +new Date - S, 'rad mem');
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ return }
@ -77,6 +77,7 @@
r.thrash = function(){
var thrash = r.thrash;
if(thrash.ing){ return thrash.more = true }
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
thrash.more = false;
thrash.ing = true;
var batch = thrash.at = r.batch, i = 0;
@ -174,9 +175,9 @@
}
f.write = function(){
var tmp = ename(file);
var start; LOG && (start = +new Date); // comment this out!
var S; LOG && (S = +new Date);
opt.store.put(tmp, f.text, function(err){
LOG && console.log("wrote to disk in", (+new Date) - start, tmp); // comment this out!
LOG && opt.log(S, +new Date - S, "wrote disk", tmp);
if(err){ return cb(err) }
r.list.add(tmp, cb);
});
@ -205,10 +206,10 @@
r.write.jsonify = function(f, file, rad, cb, o){
var raw;
var start; LOG && (start = +new Date); // comment this out!
var S; LOG && (S = +new Date);
try{raw = JSON.stringify(rad.$);
}catch(e){ return cb("Record too big!") }
LOG && console.log("stringified JSON in", +new Date - start); // comment this out!
LOG && opt.log(S, +new Date - S, "rad stringified JSON");
if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return }
}
@ -234,7 +235,7 @@
if(RAD && !o.next){ // cache
var S; LOG && (S = +new Date);
var val = RAD(key);
LOG && console.log(+new Date - S, 'rad cached');
LOG && opt.log(S, +new Date - S, 'rad cached');
//if(u !== val){
//cb(u, val, o);
if(atomic(val)){ cb(u, val, o); return }
@ -247,7 +248,7 @@
file = (u === file)? u : decodeURIComponent(file);
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
if(!file || (o.reverse? file < tmp : file > tmp)){
LOG && console.log(+new Date - S, 'rad read lex'); S = +new Date;
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date;
if(o.next || o.reverse){ g.file = file }
if(tmp = Q[g.file]){
tmp.push({key: key, ack: cb, file: g.file, opt: o});
@ -268,9 +269,9 @@
g.info = info;
if(disk){ RAD = g.disk = disk }
disk = Q[g.file]; delete Q[g.file];
LOG && console.log(+new Date - S, 'rad read it in, now ack to:', disk.length); S = +new Date;
LOG && opt.log(S, +new Date - S, 'rad read in, ack', disk.length); S = +new Date;
map(disk, g.ack);
LOG && console.log(+new Date - S, 'rad read acked');
LOG && opt.log(S, +new Date - S, 'rad read acked');
}
g.ack = function(as){
if(!as.ack){ return }
@ -315,7 +316,7 @@
var p = function Parse(){}, info = {};
p.disk = Radix();
p.read = function(err, data){ var tmp;
LOG && console.log('read disk in', +new Date - S, ename(file)); // keep this commented out in
LOG && opt.log(S, +new Date - S, 'read disk', ename(file));
delete Q[file];
if((p.err = err) || (p.not = !data)){
return map(q, p.ack);
@ -332,12 +333,12 @@
}
info.parsed = data.length;
LOG && (S = +new Date); // keep this commented out in production!
LOG && (S = +new Date);
if(opt.jsonify || '{' === data[0]){ // temporary testing idea
try{
var json = JSON.parse(data);
p.disk.$ = json;
LOG && console.log('parsed JSON in', +new Date - S); // keep this commented out in production!
LOG && opt.log(S, +new Date - S, 'rad parsed JSON');
map(q, p.ack);
return;
}catch(e){ tmp = e }
@ -346,7 +347,7 @@
return map(q, p.ack);
}
}
LOG && (S = +new Date); // keep this commented out in production!
LOG && (S = +new Date);
var tmp = p.split(data), pre = [], i, k, v;
if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! ";
@ -369,7 +370,7 @@
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]);
}
LOG && console.log('parsed RAD in', +new Date - S); // keep this commented out in production!
LOG && opt.log(S, +new Date - S, 'parsed RAD');
//cb(err, p.disk);
map(q, p.ack);
};
@ -389,7 +390,7 @@
if(p.err || p.not){ return cb(p.err, u, info) }
cb(u, p.disk, info);
}
var S; LOG && (S = +new Date); // keep this commented out in production!
var S; LOG && (S = +new Date);
if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read);
}

View File

@ -6,6 +6,7 @@
if(u === root.opt.super){
root.opt.super = true;
}
root.opt.log = root.opt.log || Gun.log;
this.to.next(root);
})
require('../nts');

View File

@ -5,6 +5,7 @@ Gun.on('opt', function(root){
if(root.once){ return }
if(typeof process === 'undefined'){ return }
if(typeof require === 'undefined'){ return }
if(false === root.opt.stats){ return }
var noop = function(){};
var os = require('os') || {};
var fs = require('fs') || {};
@ -39,6 +40,7 @@ Gun.on('opt', function(root){
stats.peers.count = Object.keys(root.opt.peers||{}).length;
stats.node = {};
stats.node.count = Object.keys(root.graph||{}).length;
stats.all = all;
var dam = root.opt.mesh;
if(dam){
stats.dam = {'in': {count: dam.hear.c, done: dam.hear.d, long: dam.hear.long}, 'out': {count: dam.say.c, done: dam.say.d}};
@ -57,3 +59,14 @@ Gun.on('opt', function(root){
}, 1000 * 15);
Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) }
});
var log = Gun.log, all = {}, max = 1000;
Gun.log = function(a,b,c,d){
if('number' == typeof a && 'number' == typeof b && 'string' == typeof c){
var tmp = (all[c] || (all[c] = []));
if(max < tmp.push([a,b])){ all[c] = [] } // reset
return;
}
return log.apply(Gun, arguments);
}

View File

@ -1,5 +1,5 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('create', function(root){
if(Gun.TESTING){ root.opt.file = 'radatatest' }
this.to.next(root);
@ -7,6 +7,7 @@ Gun.on('create', function(root){
if(false === opt.radisk){ return }
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
var Radix = Radisk.Radix;
var LOG = console.LOG;
opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
var rad = Radisk(opt), esc = String.fromCharCode(27);
@ -30,12 +31,12 @@ Gun.on('create', function(root){
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
rad(soul+esc+key, val, (track? ack : u));
});
//console.log(+new Date - S, 'put loop');
LOG && Gun.log(S, +new Date - S, 'put loop');
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
try{opt.store.stats.put.err = err}catch(e){} // STATS!
//Gun.log(); //try{opt.store.stats.put.err = err}catch(e){} // STATS!
root.on('in', {'@': id, err: err});
return;
}
@ -45,7 +46,7 @@ Gun.on('create', function(root){
}catch(e){} // STATS!
//console.log(+new Date - S, 'put'); S = +new Date;
root.on('in', {'@': id, ok: 1});
//console.log(+new Date - S, 'put sent');
//console.log(S, +new Date - S, 'put sent');
}
});
@ -77,10 +78,14 @@ Gun.on('create', function(root){
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
}
if(has['-'] || (soul||{})['-']){ o.reverse = true }
if(tmp = (root.next||empty)[soul]){
if(tmp && tmp.rad){ return }
if(o.atom){ tmp = (tmp.next||empty)[o.atom] }
if(tmp && tmp.rad){ return }
if((tmp = (root.next||empty)[soul]) && tmp.put){
if(o.atom){
tmp = (tmp.next||empty)[o.atom] ;
if(tmp && tmp.rad){ return }
} else
if(tmp && tmp.rad){
return;
}
}
var S = (+new Date); // STATS!
rad(key||'', function(err, data, o){
@ -89,7 +94,7 @@ Gun.on('create', function(root){
if(err){ opt.store.stats.get.err = err }
}catch(e){} // STATS!
//if(u === data && o.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail.
//console.log(+new Date - S, 'got'); S = +new Date;
LOG && Gun.log(S, +new Date - S, 'got'); S = +new Date; // MARK RETURN HERE!!!! Gun.log will always log unless off :/ switch to something like LOG && whatever?
if(data){
if(typeof data !== 'string'){
if(o.atom){
@ -100,11 +105,10 @@ Gun.on('create', function(root){
}
if(!graph && data){ each(data, '') }
}
//console.log(+new Date - S, 'got prep'); S = +new Date;
LOG && Gun.log(S, +new Date - S, 'got prep');
root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: each});
//console.log(+new Date - S, 'got sent');
}, o);
//console.log(+new Date - S, 'get call');
LOG && Gun.log(S, +new Date - S, 'get call');
function each(val, has, a,b){
if(!val){ return }
has = (key+has).split(esc);
@ -118,6 +122,7 @@ Gun.on('create', function(root){
if(o.limit && o.limit <= o.count){ return true }
}
each.rad = get;
LOG = console.LOG;
});
opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS!
var statg = 0, statp = 0; // STATS!

View File

@ -1,272 +0,0 @@
(function(){
if(!this.Gun){ return }
function Test(o){
var test = this;
if(!(test instanceof Test)){ return new Test(o) }
test._ = {};
test._.stack = [];
return test;
}
Test.chain = Test.prototype;
Test.chain.run = function(fn){
var test = this;
var e = test._.i, i = 0;
var stack = test._.stack;
stack.push(fn);
//var to = setInterval(function(){ if(++i >= e){ return clearTimeout(to) }
var start = Gun.time.is();
while(++i <= e){
Gun.list.map(stack, function(fn){ (fn||function(){})(i) })
}
console.log((Gun.time.is() - start) / 1000);
//},0);
return test;
}
Test.chain.it = function(i){
var test = this;
test._.i = i || 1000;
return test;
}
Test.chain.gen = function(fn){
var test = this;
test._.stack.push(fn);
return test;
}
var gun = window.gun = Gun();
window.SPAM = function(read){ // TODO: BUG? gun-sid in transport layer not correct?
//localStorage.clear();
var start = Gun.time.is();
/*var mark = {
name: "Mark Nadal"
};
var amber = {
name: "Amber Nadal",
spouse: mark
}
mark.spouse = amber;
amber.pet = {
name: "Fluffy",
slave: mark
}
Test().it(read).gen(function(i){
Gun.ify(mark);
}).run();return;*/
Test().it(read).gen(function(i){
gun.get('users').path(i).path('where').put({lat: Math.random(), lng: Math.random(),i:i});
}).run();return;
Test().it(read === true? 1 : read || 1000).gen(function(i){
if(read === true){
gun.get('users').map().path('where').on(function(node){
console.log("node:", node);
if(node.i === (1000)){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});
return;
}
// PUT, GET, PATH, ON
var now = Gun.time.is();
/*var obj = {'i': i, 'v': Gun.text.random(100)};
gun.put(obj, function(err, ok){
//console.log("put done", i, 'in', Gun.time.is() - now);//, i % 1000);
if(i % (1000) === 0){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});return;*/
gun.get('users').path(i).path('where').put({
lat: Math.random(),lng: Math.random(),i: i
//lol: i / 2
}, function(err, ok){
console.log("put done", i, 'in', Gun.time.is() - now);//, i % 1000);
if(i % (1000) === 0){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});return;
}).run(function(){});
}
//window.SPAM(1000000);
}());
/* EXTRA GUN UTILITY FUNCTIONS I MIGHT WANT TO KEEP
(function(){
Gun().get('chat').path('messages').since(Gun().get('me').path('last')).map().val(function(msg){
});
Gun().get('chat').path('messages').last(100).map().val(function(msg){
});
var peers = [
peer1,
peer2
];
Gun.on('put').event(function(graph, cb, opt){
Gun.is.graph(graph, function(node, soul){
localStorage[soul] = node;
});
});
Gun.on('put').event(function(graph, cb, opt){
Peers(opt.peers).send({
id: MsgID,
value: data,
from: myPeerID
}, cb);
});
Gun.on('get').event(function(lex, cb, opt){
Peers(opt.peers || peers).send({
'#': MsgID,
'$': lex,
'~': myPeerID
}, cb);
});
Peers.server(function(req, res){
if(Msg.IDs[req.id]){ return } // throttle
// auth
Peers(peers).send(req); // relay
// auth
if(req.rid){ return } // ignore
if(req.put && opt.everything || graph[for soul in req.body]){ // process
Gun.put(gun, req.body, REPLY);
}
});
// TODO: MARK / JESSE need to solve infinite circular loop on get flushing and put flushing.
GUN = {'#': 'soul', '.': 'field', '=': 'value', '>': 'state'}
MSG = {'#': 'id', '$': 'body', '@': 'to'}
Gun.wire = function(data){
}
Gun.get.wire = function(lex, cb, opt){ return Gun.text.is(lex)? Gun.get.wire.from(lex, cb, opt) : Gun.get.wire.to(lex, cb, opt) }
Gun.get.wire.to = function(lex, cb, opt){
var t = '';
Gun.obj.map(lex, function(v,f){
if(!v){ return }
Gun.list.map(Gun.list.is(v)? v : [v], function(v){
t += f + "'" + Gun.put.wire.ify(v) + "'";
});
});
return t + '?';
}
Gun.get.wire.from = function(t, cb, opt){
if(!t){ return null }
var a = Gun.put.wire.from.parse(t), lex = {};
Gun.list.map([Gun._.soul, Gun._.field, Gun._.value, Gun._.state], function(sym, i){
if(!(i = a.indexOf(sym) + 1)){ return }
lex[sym] = Gun.put.wire.type(a[i]);
});
return lex;
}
// #soul.field
// "#soul.field=value>state"
// #messages>>1234567890 //{soul: 'messages', state: {'>': 1234567890}}
// #id$"msg"~who@to
Gun.put.wire = function(n, cb, opt){ return Gun.text.is(n)? Gun.put.wire.from(n, cb, opt) : Gun.put.wire.to(n, cb, opt) }
Gun.put.wire.ify = function(s){ var tmp;
if(Infinity === s || -Infinity === s){ return s }
if(tmp = Gun.is.rel(s)){ return '#' + JSON.stringify(tmp) }
return JSON.stringify(s)
}
Gun.put.wire.type = function(s){ var tmp;
if(Gun._.soul === s.charAt(0)){ return Gun.is.rel.ify(JSON.parse(s.slice(1))) }
if(String(Infinity) === s){ return Infinity }
if(String(-Infinity) === s){ return -Infinity }
return JSON.parse(s)
}
Gun.put.wire.to = function(n, cb, opt){ var t, b;
if(!n || !(t = Gun.is.node.soul(n))){ return null }
cb = cb || function(){};
t = (b = "#'" + Gun.put.wire.ify(t) + "'");
var val = function(v,f, nv,nf){
var w = '', s = Gun.is.node.state(n,f), sw = '';
if(!s){ return }
w += ".'" + Gun.put.wire.ify(f) + "'";
console.log("yeah value?", v, Gun.put.wire.ify(v));
w += "='" + Gun.put.wire.ify(v) + "'";
if(s !== Gun.is.node.state(n,nf)){
w += ">'" + Gun.put.wire.ify(s) + "'";
} else {
sw = ">'" + Gun.put.wire.ify(s) + "'";
}
t += w;
w = b + w + sw;
cb(null, w);
}
var next = function(v,f){ // TODO: BUG! Missing adding meta data.
if(Gun._.meta === f){ return }
if(next.f){
val(next.v, next.f, v,f);
}
next.f = f;
next.v = v;
}
Gun.obj.map(n, next);
next();
return t;
}
Gun.put.wire.from = function(t, cb, opt){
if(!t){ return null }
var a = Gun.put.wire.from.parse(t);
Gun.list.map(a, function(v, i){
if(Gun._.soul === v){
Gun.is.node.soul.ify(n, Gun.put.wire.type(a[i]));
return;
}
if(Gun._.field === v){
var val = a.indexOf(Gun._.value,i), state = a.indexOf(Gun._.state,i);
Gun.is.node.state.ify([n], Gun.put.wire.type(a[i]), Gun.put.wire.type(a[val+1]), Gun.put.wire.type(a[state+1]));
return;
}
})
return n;
}
Gun.put.wire.from.parse = function(t){
var a = [], s = -1, e = 0, end = 1, n = {};
while((e = t.indexOf("'", s + 1)) >= 0){
if(s === e || '\\' === t.charAt(e-1)){}else{
a.push(t.slice(s + 1,e));
s = e;
}
}
return a;
}
}());
*/
;(function(){ // make as separate module!
function SQL(){}
SQL.select = function(sel){
this._.sql.select = sel;
return this;
}
SQL.from = function(from){
this._.sql.from = from;
//this.get(from).map();
return this;
}
SQL.where = function(where){
this._.sql.where = where;
return this;
}
Gun.chain.sql = function(sql){
var gun = this;//.chain();
sql = gun._.sql = sql || {};
gun.select = SQL.select;
gun.from = SQL.from;
gun.where = SQL.where;
return gun;
}
Gun.on('chain').event(function(gun, at){
console.log("sql stuff?", gun._, at.node);
var query = gun._.sql;
if(!query){ return }
var node = at.node;
});
}());

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.2019.930",
"version": "0.2019.1120",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"main": "index.js",
"browser": "gun.js",
@ -14,7 +14,8 @@
"e2e": "mocha e2e/distributed.js",
"docker": "hooks/build",
"minify": "uglifyjs gun.js -o gun.min.js -c -m",
"unbuild": "node lib/unbuild.js & npm run minify"
"unbuild": "node lib/unbuild.js & npm run minify",
"unbuildMeta": "node lib/unbuild.js lib/meta"
},
"repository": {
"type": "git",

17
sea.js
View File

@ -964,6 +964,7 @@
}
// If authenticated user wants to delete his/her account, let's support it!
User.prototype.delete = async function(alias, pass, cb){
console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
var gun = this, root = gun.back(-1), user = gun.back('user');
try {
user.auth(alias, pass, function(ack){
@ -1005,6 +1006,7 @@
return gun;
}
User.prototype.alive = async function(){
console.log("user.alive() IS DEPRECATED!!!");
const gunRoot = this.back(-1)
try {
// All is good. Should we do something more with actual recalled data?
@ -1024,25 +1026,32 @@
console.log(ctx, ev)
})
}
user.get('trust').get(path).put(theirPubkey);
// do a lookup on this gun chain directly (that gets bob's copy of the data)
// do a lookup on the metadata trust table for this path (that gets all the pubkeys allowed to write on this path)
// do a lookup on each of those pubKeys ON the path (to get the collab data "layers")
// THEN you perform Jachen's mix operation
// and return the result of that to...
}
User.prototype.grant = function(to, cb){
console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
var gun = this, user = gun.back(-1).user(), pair = user._.sea, path = '';
gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
(async function(){
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
var enc, sec = await user.get('grant').get(pair.pub).get(path).then();
sec = await SEA.decrypt(sec, pair);
if(!sec){
sec = SEA.random(16).toString();
enc = await SEA.encrypt(sec, pair);
user.get('trust').get(pair.pub).get(path).put(enc);
user.get('grant').get(pair.pub).get(path).put(enc);
}
var pub = to.get('pub').then();
var epub = to.get('epub').then();
pub = await pub; epub = await epub;
var dh = await SEA.secret(epub, pair);
enc = await SEA.encrypt(sec, dh);
user.get('trust').get(pub).get(path).put(enc, cb);
user.get('grant').get(pub).get(path).put(enc, cb);
}());
return gun;
}

View File

@ -4,7 +4,7 @@ if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only.
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!");
Gun.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
@ -110,7 +110,6 @@ Gun.on('create', function(root){
data = Gun.state.to(data, has);
}
//if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected?
//console.log("lS get", lex, data);
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$});
};
Gun.debug? setTimeout(to,1) : to();

View File

@ -19,15 +19,14 @@ function Mesh(root){
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
if(!msg){ return }
//console.log('hear batch length of', msg.length);
LOG && opt.log(msg.length, 'in hear batch');
(function go(){
var S = +new Date; // STATS!
var S; LOG && (S = +new Date); // STATS!
var m, c = 100; // hardcoded for now?
while(c-- && (m = msg.shift())){
mesh.hear(m, peer);
}
//console.log(+new Date - S, 'hear batch');
(mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S);
LOG && opt.log(+new Date - S, 'batch heard');
if(!msg.length){ return }
puff(go, 0);
}());
@ -38,7 +37,7 @@ function Mesh(root){
}catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return }
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) }
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
if(dup.check(id)){ return }
dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
@ -54,9 +53,9 @@ function Mesh(root){
}
return;
}
//var S = +new Date;
var S; LOG && (S = +new Date);
root.on('in', msg);
//!msg.nts && console.log(+new Date - S, 'msg', msg['#']);
LOG && !msg.nts && opt.log(+new Date - S, 'msg', msg['#']);
return;
}
}
@ -70,7 +69,7 @@ function Mesh(root){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
//var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date;
var meta = msg._||(msg._=function(){});
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
@ -84,15 +83,15 @@ function Mesh(root){
}
}
}
//console.log(+new Date - S, 'mesh say prep');
LOG && opt.log(+new Date - S, 'say prep');
dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more!
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) }
if(!peer && mesh.way){ return mesh.way(msg) }
if(!peer || !peer.id){ message = msg;
if(!Type.obj.is(peer || opt.peers)){ return false }
//var S = +new Date;
var S; LOG && (S = +new Date);
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
//console.log(+new Date - S, 'mesh say loop');
LOG && opt.log(+new Date - S, 'say loop');
return;
}
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
@ -116,10 +115,10 @@ function Mesh(root){
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
//var S = +new Date;
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
//console.log(+new Date - S, 'mesh flush', tmp.length);
LOG && opt.log(+new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
@ -129,14 +128,14 @@ function Mesh(root){
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
//var S = +new Date;
var S; LOG && (S = +new Date);
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
//console.log(+new Date - S, 'wire send', raw.length);
LOG && opt.log(+new Date - S, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
@ -186,6 +185,7 @@ function Mesh(root){
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
@ -266,6 +266,7 @@ function Mesh(root){
}());
var empty = {}, ok = true, u;
var LOG = console.LOG;
try{ module.exports = Mesh }catch(e){}

View File

@ -89,7 +89,6 @@ function soul(gun, cb, opt, as){
function use(msg){
var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp;
if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) }
//console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum);
//if(at.async && msg.root){ return }
//if(at.async === 1 && cat.async !== true){ return }
//if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);

View File

@ -177,7 +177,7 @@ function any(soul, as, msg, eve){
as = as.as;
if(!msg.$ || !msg.$._){ return } // TODO: Handle
if(msg.err){ // TODO: Handle
console.log("Please report this as an issue! Put.any.err");
Gun.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
@ -186,7 +186,7 @@ function any(soul, as, msg, eve){
if(as.ref !== as.$){
tmp = (as.$._).get || at.get;
if(!tmp){ // TODO: Handle
console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
Gun.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);

View File

@ -162,20 +162,16 @@ Gun.dup = require('./dup');
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
//var S = +new Date;
node = Gun.obj.copy(node);
//console.log(+new Date - S, 'copy node');
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
//var S = +new Date;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//console.log(+new Date - S, 'root got send');
//if(0 < tmp){ return }
root.on('get', msg);
}

136
test/panic/4dht.js Normal file
View File

@ -0,0 +1,136 @@
/*
This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 2,
browsers: 2,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var bob = servers.pluck(1);
var carl = servers.excluding(bob).pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var dave = browsers.excluding(alice).pluck(1);
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 1;
browsers.each(function(client, id){
tests.push(client.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.gun = gun;
window.ref = gun.get('test');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Alice", function(){
return alice.run(function(test){
console.log("I AM ALICE");
//test.async();
console.log(gun._.opt.peers);
});
});
it("Dave", function(){
return dave.run(function(test){
console.log("I AM DAVE");
//test.async();
console.log(gun._.opt.peers);
});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
//},1000);
},1000 * 60);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});

192
test/panic/livestream.js Normal file
View File

@ -0,0 +1,192 @@
/*
This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
1. (this file) Is a browser write is confirmed as save by multiple peers even if by daisy chain.
2.
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 1,
browsers: 2,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js',
'/livestream.html': __dirname + '/livestream.html',
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var bob = servers.pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var others = browsers.excluding(alice);
describe("Broadcast Video", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers load QVDev's streaming!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
test.async();
console.log("load?");
function load(src, cb){
var script = document.createElement('script');
script.onload = cb; script.src = src;
document.head.appendChild(script);
}
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunRecorder.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunStreamer.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunViewer.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/mediabuffer.js', function(){
test.done();
});
});
});
});
$('body').append('<video id="video" width="100%" autoplay></video>');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.gun = gun;
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Stream", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
var stream = window.stream = new GunStreamer({
url: "https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/parser_worker.js",
gun: gun,
streamId: 'livestream',
dbRecord: 'streams'
});
var record = window.record = new GunRecorder({
mimeType: 'video/webm; codecs="opus,vp8"',
//audioBitsPerSecond: 6000,//Audio bits per second this is the lowest quality
//videoBitsPerSecond: 100000,//Video bits per second this is the lowest quality
cameraOptions: {video:{width: 1280, height: 720, facingMode: "environment", frameRate: 60}, audio: false},
video_id: "video",
recordInterval: 1000, // how long each chunk?
onRecordStateChange: function(state){ /* change play/pause buttons */ },
onDataAvailable: function(data){ stream.onDataAvailable(data) } // pass recorded data to streamer
});
record.startCamera();
$('#video').on('playing', function(eve){
record.record();
test.done();
});
console.log("start recording!");
}, {acks: config.servers});
});
it("View", function(){
return others.run(function(test){
console.log("I AM A VIEWER!");
test.async();
var view = new GunViewer({
//mimeType: 'video/webm; codecs="opus,vp8"', // NEED THIS ONE FOR AUDIO+VIDEO
mimeType: 'video/webm; codecs="vp8"',
streamerId: "video",
debug: true,//For debug logs
});
gun.get('livestream').on(function(data){
window.data = data;
view.onStreamerData(data);
});
}, {acks: config.servers});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});