From 20be1eba0d15a3bb0082fa06a98a3ad4f46feb79 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 9 Feb 2018 03:02:26 -0800 Subject: [PATCH 1/4] upload --- examples/contact/index.html | 17 ++++------------ examples/style.css | 11 ++++++++++ lib/upload.js | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 lib/upload.js diff --git a/examples/contact/index.html b/examples/contact/index.html index 5c42caa8..1807da85 100644 --- a/examples/contact/index.html +++ b/examples/contact/index.html @@ -17,15 +17,6 @@ display: inline-block; width: 15%; } - .loading { - animation: pulse 2s infinite; - } - @keyframes pulse - { - 0% {opacity: 1;} - 50% {opacity: 0.5;} - 100% {opacity: 1;} - }
Welcome,
@@ -212,10 +203,10 @@ c.tell("Passphrase needs to be longer than 9 characters."); return; } - but.addClass('loading'); + but.addClass('pulse'); data.alias = data.alias.toLowerCase(); user.create(data.alias, data.pass, function(ack){ - if(!ack.wait){ but.removeClass('loading') } + if(!ack.wait){ but.removeClass('pulse') } if(ack.err){ c.tell(ack.err); return } if(ack.pub){ gun.get('users').get(data.alias).put(gun.get('alias/'+data.alias)); @@ -237,10 +228,10 @@ return; } var but = form.find('button:first'); - but.addClass('loading'); + but.addClass('pulse'); data.alias = data.alias.toLowerCase(); user.auth(data.alias, data.pass, function(ack){ - if(!ack.wait){ but.removeClass('loading') } + if(!ack.wait){ but.removeClass('pulse') } if(ack.err){ c.tell(ack.err); return } session(data); }); diff --git a/examples/style.css b/examples/style.css index e79f4374..d967eb6d 100644 --- a/examples/style.css +++ b/examples/style.css @@ -132,6 +132,17 @@ ul, li { color: white; } +.pulse { + animation: pulse 2s infinite; +} + +@keyframes pulse +{ + 0% {opacity: 1;} + 50% {opacity: 0.5;} + 100% {opacity: 1;} +} + .hue { background: #4D79D8; -webkit-animation: hue 900s infinite; diff --git a/lib/upload.js b/lib/upload.js new file mode 100644 index 00000000..87985fd2 --- /dev/null +++ b/lib/upload.js @@ -0,0 +1,40 @@ +;(function(){ + function upload(cb, opt){ + var el = $(this); cb = cb || function(){}; opt = opt || {}; + el.on('drop', function(e){ + e.preventDefault(); + upload.drop(((e.originalEvent||{}).dataTransfer||{}).files, 0); + }).on('dragover', function(e){ + e.preventDefault(); + }); + upload.drop = function(files,i){ + if(opt.max && (files[i].fileSize > opt.max || files[i].size > opt.max)){ + cb({err: "File size is too large.", file: file[i]}, upload); + if(files[++i]){ upload.drop(files,i) } + return false; + } + reader = new FileReader(); + reader.onload = function(e){ + cb({file: files[i], event: e, id: i}, upload); + if(files[++i]){ upload.drop(files,i) } + }; + if(files[i]){ reader.readAsDataURL(files[i]) } + } + } + upload.shrink = function(e, cb, w, h){ // via stackoverflow + if(!e){ return cb && cb({err: "No file!"}) } + if(e.err){ return } + var file = (((e.event || e).target || e).result || e), img = new Image(); + img.src = file; + img.onload = function(){ + if(!h && img.width > w){ h = img.height * (w / img.width) } + var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); + canvas.width = w; + canvas.height = h; + ctx.drawImage(img, 0, 0, w, h); // draw source image to canvas. + var b64 = e.base64 = canvas.toDataURL(); // base64 the compressed image. + cb((e.base64 && e) || b64); + }; + } + $.fn.upload = upload; +}()); \ No newline at end of file From 68587763f9426c82ef38814d22fa294f14d6e5a0 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 13 Feb 2018 13:36:54 -0800 Subject: [PATCH 2/4] ++perf --hack --- gun.js | 31 +++++++++++++------------------ untitled.js => lib/untitled.js | 0 package.json | 2 +- test/common.js | 16 ++++++++-------- 4 files changed, 22 insertions(+), 27 deletions(-) rename untitled.js => lib/untitled.js (100%) diff --git a/gun.js b/gun.js index 94c3f5aa..8c49d684 100644 --- a/gun.js +++ b/gun.js @@ -288,6 +288,7 @@ } }()); Val.rel.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it. + Type.obj.has._ = '.'; var rel_ = Val.rel._, u; var bi_is = Type.bi.is; var num_is = Type.num.is; @@ -711,7 +712,10 @@ if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" } if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) } obj_map(ctx.put, merge, ctx); - if(!ctx.async){ obj_map(ctx.map, map, ctx) } + if(!ctx.async){ + at.stop = {}; // temporary fix till a better solution? + obj_map(ctx.map, map, ctx) + } if(u !== ctx.defer){ setTimeout(function(){ Gun.on.put(msg, gun); @@ -760,6 +764,7 @@ if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting. if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context. this.off(); + cat.stop = {}; // temporary fix till a better solution? obj_map(ctx.map, map, ctx); // all done, trigger chains. }); } @@ -778,10 +783,8 @@ } function map(msg, soul){ if(!msg.gun){ return } - msg.gun._.root._.stop = {}; //console.log('map ->', soul, msg.put); (msg.gun._).on('in', msg); - msg.gun._.root._.stop = {}; } Gun.on.get = function(msg, gun){ @@ -944,9 +947,7 @@ //if(u !== back.put){ back.on('in', back); } - if(back.ack){ - return; - } + if(back.ack){ return } msg.gun = back.gun; back.ack = -1; } else @@ -1002,7 +1003,7 @@ } if(u === change){ ev.to.next(msg); - if(cat.soul){ return } + if(cat.soul){ return } // TODO: BUG, I believe the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc. echo(cat, msg, ev); if(cat.has){ not(cat, msg); @@ -1017,13 +1018,6 @@ obj_map(change, map, {at: msg, cat: cat}); return; } - /*if(rel = Gun.val.rel.is(change)){ - if(tmp = (gun.back(-1).get(rel)._).put){ - change = tmp; // this will cause performance to turn to mush, maybe use `.now` check? - } - //if(tmp.put){ change = tmp.put; } - } - if(!rel || tmp){*/ if(!(rel = Gun.val.rel.is(change))){ if(Gun.val.is(change)){ if(cat.has || cat.soul){ @@ -1078,10 +1072,11 @@ // neither of these are ideal, need to be fixed without hacks, // but for now, this works for current tests. :/ if(!now){ - var stop = at.root._.stop; + return; + /*var stop = at.root._.stop; if(!stop){ return } if(stop[at.id] === rel){ return } - stop[at.id] = rel; + stop[at.id] = rel;*/ } else { if(u === now[at.id]){ return } if((now._ || (now._ = {}))[at.id] === rel){ return } @@ -1151,7 +1146,6 @@ function ask(at, soul){ var tmp = (at.root.get(soul)._); if(at.ack){ - //tmp.ack = tmp.ack || -1; tmp.on('out', {get: {'#': soul}}); if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way? } @@ -1371,13 +1365,14 @@ // and STOP is a hack to get async behavior to correctly call. // neither of these are ideal, need to be fixed without hacks, // but for now, this works for current tests. :/ - var tmp = cat.root._.now; obj.del(cat.root._, 'now'); + var tmp = cat.root._.now; obj.del(cat.root._, 'now'); cat.root._.PUT = true; var tmp2 = cat.root._.stop; (as.ref._).now = true; (as.ref._).on('out', { gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask }); obj.del((as.ref._), 'now'); + obj.del((cat.root._), 'PUT'); cat.root._.now = tmp; cat.root._.stop = tmp2; }, as); diff --git a/untitled.js b/lib/untitled.js similarity index 100% rename from untitled.js rename to lib/untitled.js diff --git a/package.json b/package.json index e9add17c..6372fd2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.93", + "version": "0.9.94", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/test/common.js b/test/common.js index 73e93438..72345f97 100644 --- a/test/common.js +++ b/test/common.js @@ -3753,18 +3753,18 @@ describe('Gun', function(){ }, 1); }); - return; - it.only('get map should not slowdown', function(done){ + + it('get map should not slowdown', function(done){ this.timeout(5000); - var gun = (window.gun = Gun()).get('g/m/no/slow'); + var gun = Gun().get('g/m/no/slow'); //console.log("---------- setup data done -----------"); - var prev, diff, max = 25, total = 2, largest = -1, gone = {}; + var prev, diff, max = 25, total = 9, largest = -1, gone = {}; //var prev, diff, max = Infinity, total = 10000, largest = -1, gone = {}; // TODO: It would be nice if we could change these numbers for different platforms/versions of javascript interpreters so we can squeeze as much out of them. gun.get('history').map().on(function(time, index){ - console.log(">>>", index, time); + //console.log(">>>", index, time); diff = Gun.time.is() - time; - return; + //return; expect(gone[index]).to.not.be.ok(); gone[index] = diff; largest = (largest < diff)? diff : largest; @@ -3783,8 +3783,8 @@ describe('Gun', function(){ prev = Gun.time.is(); var put = {}; put[turns += 1] = prev; //console.log("put", put); - console.log("------", turns, "-------"); - 2 === turns && (console.debug.i = 1); + //console.log("------", turns, "-------"); + //3 === turns && (console.debug.i = 1); console.debug(1, 'save', {history: put}); gun.put({history: put}); }, 1); From 4a273de219b0d1dc7819244c692f59ff771c863a Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 16 Feb 2018 05:38:20 -0800 Subject: [PATCH 3/4] support rooms in examples with URL hash! --- examples/chat/index.html | 98 ++++----- examples/contact/index.html | 4 +- examples/style.css | 13 +- examples/todo/index.html | 3 +- examples/{gps => where}/index.html | 14 +- gun.min.js | 2 +- src/chain.js | 19 +- src/put.js | 3 +- src/root.js | 8 +- src/val.js | 1 + test/panic/speak.js | 322 +++++++++++++++++++++++++++++ untitled.js | 50 +++++ 12 files changed, 450 insertions(+), 87 deletions(-) rename examples/{gps => where}/index.html (92%) create mode 100644 test/panic/speak.js create mode 100644 untitled.js diff --git a/examples/chat/index.html b/examples/chat/index.html index d5afd1b6..4df3d917 100644 --- a/examples/chat/index.html +++ b/examples/chat/index.html @@ -1,6 +1,7 @@ + Converse @@ -49,7 +50,7 @@ margin-bottom: 0.5em; } - .send { + .say { margin: 0 0 0.4em 0.4em; padding: 0.2em 0.5em; } @@ -75,7 +76,7 @@
-
send
+
say

@@ -98,75 +99,62 @@ - + \ No newline at end of file diff --git a/examples/contact/index.html b/examples/contact/index.html index 1807da85..9d119274 100644 --- a/examples/contact/index.html +++ b/examples/contact/index.html @@ -4,12 +4,10 @@ - - +
- + ')){ return } + file = file.replace('', + ""); + return file; + }); + }, {i: i += 1, config: config})); + }); + // NOW, this is very important: + // Do not proceed to the next test until + // every single server (in different machines/processes) + // have ALL successfully launched. + return Promise.all(tests); + }); + + it(config.browsers +" browser(s) have joined!", function(){ + console.log("PLEASE OPEN https://"+ config.IP +":"+ (config.port) +" IN "+ config.browsers +" BROWSER(S)!"); + return browsers.atLeast(config.browsers); + }); + + it("Browsers load SEA!", 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('sea.js', function(){ + test.done(); + }); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + it("Browsers initialized gun!", function(){ + var tests = [], ids = {}, i = 0; + // Let us create a list of all the browsers IDs connected. + // This will later let each browser check against every other browser. + browsers.each(function(client, id){ + ids[id] = 1; + }); + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + localStorage.clear(); + var env = window.env = test.props; + var peers = [], i = env.config.servers; + while(i--){ + // For the total number of servers listed in the configuration + // Add their URL into an array. + peers.push('https://'+ env.config.IP + ':' + (env.config.port + (i + 1)) + '/gun'); + } + var gun = window.gun = Gun(peers); + var user = window.user = gun.user(); + var go = window.go = {num: 0, total: 0, users: {}, pub: {}}; + window.ID = env.id; + go.check = Gun.obj.map(env.ids, function(v,id,t){ + // for each browser ID + // they will be saving X number of messages each. + go.users[id] = true; // set an outstanding flag to check against. + var i = env.config.each; + while(i--){ + // So add a deterministic key we can check against. + t(id + (i + 1), 1); + // And count up the total number of messages we expect for all. + go.total += 1; + } + }); + + console.log(peers, go); + }, {i: i += 1, id: id, ids: ids, config: config})); + }); + return Promise.all(tests); + }); + + it("All users created!", function(){ + var tests = [], ids = {}, i = 0; + browsers.each(function(client, id){ + ids[id] = 1; + }); + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + test.async(); + + gun.on('secure', function(at){ + /* enforce some rules about shared app level data */ + if(!at.put || !at.put.users){ return } + var no; + Gun.node.is(at.put.users, function(val, key){ + Gun.SEA.read(val, false, function(val){ + if('alias/'+key === Gun.val.rel.is(val)){ return } + no = true; + }) + if(no){ return no } + }); + if(no){ return } + this.to.next(at); + }); + + var unsafepassword = 'asdf'+ID; + console.log("sign in and up:", ID); + window.user.create(ID, unsafepassword, function(ack){ + if(ack.err || !ack.pub){ return } + window.pub = ack.pub; + gun.get('users').get(ID).put(gun.get('alias/'+ID)); + console.log("signed up", ack.pub); + console.debug.j = 1; + window.user.auth(ID, unsafepassword, function(ack){ + console.debug.j = 0; + console.log("signed in", ack); + if(ack.err || !ack.pub){ return } + test.done(); + }); + }); + + }, {i: i += 1, id: id, ids: ids, config: config})); + }); + return Promise.all(tests); + }); + + it("Start reading and sending messages!", function(){ + var tests = [], ids = {}, i = 0; + browsers.each(function(client, id){ + ids[id] = 1; + }); + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + test.async(); + + gun.get('users').map().map() + .get('who').get('said').map().on(function(msg){ + check(msg); + }); + + var said = user.get('who').get('said'); + + function run(i){ + + var what = i +"|||"+ "Hello world!" +"|||"+ pub +"|||"+ ID; + said.set({ + what: what + });/*, function(ack){ + if(ack.err){ return } + test.done(); + });*/ + + } + /* TODO: sometimes sign in hangs */ + console.log("<<<<< START >>>>>"); + var i = 0, to = setInterval(function frame(a, b){ + if(!b && 2 <= (b = env.config.burst)){ + while(--b){ + frame(i, true); + } + return; + } + if(env.config.each <= i){ + clearTimeout(to); + return; + } + run(i += 1); + }, env.config.wait || 1); + + + var col = $("
").css({width: 250, position: 'relative', float: 'left', border: 'solid 1px black'}), cols = {}; + var report = $("
").css({position: 'fixed', top: 0, right: 0, background: 'white', padding: 10}).text(" / "+ go.total +" Verified").prependTo('body'); + var reportc = $('').text(0).prependTo(report); + var last = $("
").text("Processing: ").css({border: "solid 1px black"}).appendTo("body"); + last = $("").text(" ").appendTo(last); + + function check(data){ + data = data.what.split("|||"); + var msg = {num: data[0], what: data[0] +' '+ data[1], who: data[2], id: data[3]}; + var who; + if(!go.pub[msg.who]){ + go.pub[msg.who] = msg.id; + go.users[msg.id] = false; + //who = cols[msg.id] = col.clone(true).appendTo('body'); + //who.prepend(""); + //who.prepend(""); + } + if(!go.check[msg.id + msg.num]){ + return; + } + go.check[msg.id + msg.num] = false; + clearTimeout(end.to); end.to = setTimeout(end, 9); + reportc.text(++go.num); + last.text(msg.what); + //who = cols[msg.id]; + //$("
").css({border: 'solid 1px blue'}).text(msg.what).appendTo(who); + } + + function end(){ + var wait = Gun.obj.map(go.users, function(v){ if(v){ return true }}); + if(wait){ return } + var more = Gun.obj.map(go.check, function(v){ if(v){ return true }}); + if(more){ return } + test.done(); + } + + }, {i: i += 1, id: id, ids: ids, config: config})); + }); + return Promise.all(tests); + }); + + /* MODEL TEST + it("Browsers initialized gun!", function(){ + var tests = [], ids = {}, i = 0; + browsers.each(function(client, id){ + ids[id] = 1; + }); + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + // code here + }, {i: i += 1, id: id, ids: ids, config: config})); + }); + return Promise.all(tests); + }); + */ + + it("All finished!", function(done){ + console.log("Done! Cleaning things up..."); + setTimeout(function(){ + done(); + }, 2000); + }); + + after("Everything shut down.", function(){ + browsers.run(function(){ + //location.reload(); + //setTimeout(function(){ + //}, 15 * 1000); + }); + return servers.run(function(){ + process.exit(); + }); + }); +}); \ No newline at end of file diff --git a/untitled.js b/untitled.js new file mode 100644 index 00000000..65885fb2 --- /dev/null +++ b/untitled.js @@ -0,0 +1,50 @@ +function input(msg){ + var ev = this, cat = this.as, gun = msg.gun, at = gun._, change = msg.put, rel, tmp; + // dispatch to chain listeners. + // dispatch to children. + // dispatch to echoes. + if(u === change){ + // Here are the possible options: + // 1. We think the data does not exist because peers/disk cannot find it. + // 2. We know the data does not exist because a parent is or was changed to a primitive. + // Souls can only (1) because they have no parent. + // Has can be (1) or (2). + // Gets and chains can be (1) or (2). + if(cat.soul || cat.has){ + // a soul can never become undefined. + // a soul can only not be found. + if(cat.soul && u !== cat.put){ + return; + } + // a key may sometimes might not be found. + // a key on a soul can not become undefined, + // but the chain might be on a chain that + // does not exist, and therefore can be undefined. + ev.to.next(msg); // ex, notify val and stuff. + echo(cat, msg, ev); // ex, notify a sub-object pointer like `mark.pet`! // TODO: BUG ON VAL, it will still not get called because it checks !node when it should also check ack. + if(cat.soul){ return } + obj_map(cat.next, unknown); // notify children. + } + if(cat.has){ + if() + } + return; + } + if(cat.soul){ + return; + } + if(cat.has){ + return; + } + if(cat.get){ + return; + } + ev.to.next(msg); +} + +function unknown(ref, key){ + (ref = (ref._)).put = u; + ref.on('in', {get: key, put: u, gun: ref.gun}); +} + +gun.get('users').map().map().get('who').get('say').map().on(cb); \ No newline at end of file From 9ddf13620c896439d27806e4a0de4edc3b311d42 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 16 Feb 2018 06:00:11 -0800 Subject: [PATCH 4/4] examples support URL hash rooms now! + rename / fix gps/where --- examples/where/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/where/index.html b/examples/where/index.html index 7fccac3c..0c956b3a 100644 --- a/examples/where/index.html +++ b/examples/where/index.html @@ -149,7 +149,7 @@ //$('#debug').value = 'track ' + JSON.stringify(latlng); gps.where.update(latlng); }); - $('#share').toggleClass("hide"); + $('#share').addClass("hide"); } else { document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || Gun.text.random(5)); // trick with cookies! gps.ref = gun.get('gps/' + gps.track); @@ -166,7 +166,7 @@ } gps.where = gps.where || Where(gps.opt); $('#follow').text(("where.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track); - $('#share').toggleClass("hide"); + $('#share').removeClass("hide"); $('#share').on('click', function(){ $('#link').toggleClass("hide"); });