diff --git a/axe.js b/axe.js index bb7a2008..825e5e90 100644 --- a/axe.js +++ b/axe.js @@ -60,6 +60,20 @@ // with one common superpeer (with ready failovers) // in case the p2p linear latency is high. // Or there could be plenty of other better options. + + /* + AXE should have a couple of threshold items... + let's pretend there is a variable max peers connected + mob = 10000 + if we get more peers than that... + we should start sending those peers a remote command + that they should connect to this or that other peer + and then once they (or before they do?) drop them from us. + sake of the test... gonna set that peer number to 1. + The mob threshold might be determined by other factors, + like how much RAM or CPU stress we have. + */ + opt.mob = opt.mob || Infinity; var mesh = opt.mesh = opt.mesh || Gun.Mesh(at); console.log("AXE enabled."); @@ -256,7 +270,7 @@ this.to.next(peer); if(!peer.url){ return } axe.up[peer.id] = peer; - }) + }); at.on('bye', function(peer){ this.to.next(peer); if(peer.url){ delete axe.up[peer.id] } Gun.obj.map(peer.routes, function(route, hash){ @@ -266,6 +280,42 @@ } }); }); + + // handle rebalancing a mob of peers: + at.on('hi', function(peer){ + this.to.next(peer); + if(peer.url){ return } // I am assuming that if we are wanting to make an outbound connection to them, that we don't ever want to drop them unless our actual config settings change. + var count = Object.keys(opt.peers).length; + if(opt.mob >= count){ return } // TODO: Make dynamic based on RAM/CPU also. Or possibly even weird stuff like opt.mob / axe.up length? + var peers = Object.keys(axe.up); + if(!peers.length){ return } + mesh.say({dam: 'mob', mob: count, peers: peers}, peer); + //setTimeout(function(){ mesh.bye(peer) }, 9); // something with better perf? // UNCOMMENT WHEN WE ACTIVATE THIS FEATURE + }); + at.on('bye', function(peer){ + this.to.next(peer); + }); + + at.on('hi', function(peer){ + this.to.next(peer); + // this code handles disconnecting from self & duplicates + setTimeout(function(){ // must wait + if(peer.pid !== opt.pid){ + // this extra logic checks for duplicate connections between 2 peers. + if(!Gun.obj.map(axe.up, function(p){ + if(peer.pid === p.pid && peer !== p){ + return yes = true; + } + })){ return } + } + mesh.say({dam: '-'}, peer); + delete at.dup.s[peer.last]; + }, Math.random() * 100); + }); + mesh.hear['-'] = function(msg, peer){ + mesh.bye(peer); + peer.url = ''; + } } function joindht(dht, soul, pids) { diff --git a/examples/docs.html b/examples/docs.html index 1adf9b2b..1d23ae98 100644 --- a/examples/docs.html +++ b/examples/docs.html @@ -31,12 +31,18 @@ + + + + + \ No newline at end of file diff --git a/examples/react/todo.html b/examples/react/todo.html index 5cf513a6..ad2b72f7 100644 --- a/examples/react/todo.html +++ b/examples/react/todo.html @@ -1,3 +1,4 @@ +
@@ -35,4 +36,4 @@ ReactDOM.render(, document.getElementById("app")); - \ No newline at end of file + diff --git a/examples/stats.html b/examples/stats.html index 8d1d72df..e6e53e67 100644 --- a/examples/stats.html +++ b/examples/stats.html @@ -53,6 +53,10 @@ $('#hours').text((data.up.time / 60 / 60).toFixed(0)); Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024); + try{ Stats('dam # in').line.append(+new Date, data.dam.in.count); }catch(e){} + try{ Stats('dam in MB').line.append(+new Date, data.dam.in.done / 1024 / 1024); }catch(e){} + try{ Stats('dam # out').line.append(+new Date, data.dam.out.count); }catch(e){} + try{ Stats('dam out MB').line.append(+new Date, data.dam.out.done / 1024 / 1024); }catch(e){} console.log('data',data); //fetch keys in all, these may be dynamically changing diff --git a/gun.js b/gun.js index 45156557..868e042b 100644 --- a/gun.js +++ b/gun.js @@ -961,7 +961,7 @@ if(obj_has(back, 'put')){ back.on('in', back); } - if(tmp){ return } + if(tmp && u !== back.put){ return } //if(tmp){ return } msg.$ = back.$; } else if(obj_has(back.put, get)){ // TODO: support #LEX ! @@ -1174,7 +1174,7 @@ if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ - neat.ack = -1; // TODO: BUG? Should this be 0? + neat.ack = -1; // Shouldn't this be reset to 0? If we do that, SEA test `set user ref should be found` fails, odd. } neat.on('in', { get: key, @@ -2122,7 +2122,8 @@ opt.peers[peer.url || peer.id] = peer; } else { tmp = peer.id = peer.id || Type.text.random(9); - mesh.say({dam: '?'}, opt.peers[tmp] = peer); + mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer); + delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self } peer.met = peer.met || +(new Date); if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) } @@ -2140,18 +2141,12 @@ } mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['?'] = function(msg, peer){ - if(!msg.pid){ - mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); - // @rogowski I want to re-enable this AXE logic with some fix/merge later. - /* var tmp = peer.queue; peer.queue = []; - Type.obj.map(tmp, function(msg){ - mesh.say(msg, peer); - }); */ - // @rogowski 2: I think with my PID fix we can delete this and use the original. - return; + if(msg.pid){ + if(!peer.pid){ peer.pid = msg.pid } + if(msg['@']){ return } } - if(peer.pid){ return } - peer.pid = msg.pid; + mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); + delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self } root.on('create', function(root){ diff --git a/gun.min.js b/gun.min.js index 1eb09d40..41456ee7 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var e=(t=t||{}).console||{log:function(){}};function j(e,t){return t?require(e):e.slice?j[o(e)]:function(t,n){e(t={exports:{}}),j[o(n)]=t.exports};function o(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var $=module;j(function(t){var n,c,l={};function e(t,n){v(this,n)&&void 0!==this[n]||(this[n]=t)}function o(t,n){var e=this.n;if(!e||!(n===e||g(e)&&v(e,n)))return!!n||void 0}function p(t,n){2!==arguments.length?(p.r=p.r||[]).push(t):(p.r=p.r||{})[t]=n}l.fn={is:function(t){return!!t&&"function"==typeof t}},l.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},l.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},l.text={is:function(t){return"string"==typeof t}},l.text.ify=function(t){return l.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},l.text.random=function(t,n){var e="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0"]||n["<"])||o===n["="]&&(e=n["*"]||n[">"]||n["<"],t.slice(0,(e||"").length)===e||o===n["*"]&&(o!==n[">"]&&o!==n["<"]?t>=n[">"]&&t<=n["<"]:o!==n[">"]&&t>=n[">"]||o!==n["<"]&&t<=n["<"])))},l.list={is:function(t){return t instanceof Array}},l.list.slit=Array.prototype.slice,l.list.sort=function(e){return function(t,n){return t&&n?(t=t[e])<(n=n[e])?-1:n",u.drift=0,u.is=function(t,n,e){var o=n&&t&&t[k]&&t[k][u._]||e;if(o)return v(o=o[n])?o:-1/0},u.lex=function(){return u().toString(36).replace(".","")},u.ify=function(t,n,e,o,i){if(!t||!t[k]){if(!i)return;t=a.soul.ify(t,i)}var r=l(t[k],u._);return void 0!==n&&n!==k&&(v(e)&&(r[n]=e),void 0!==o&&(t[n]=o)),t},u.to=function(t,n,e){var o=(t||{})[n];return h(o)&&(o=g(o)),u.ify(e,n,u.is(t,n),o,a.soul(t))},u.map=function(i,r,a){var t=h(t=i||r)?t:null;return i=m(i=i||r)?i:null,t&&!i?(r=v(r)?r:u(),t[k]=t[k]||{},d(t,f,{o:t,s:r}),t):(a=a||h(r)?r:void 0,r=v(r)?r:u(),function(t,n,e,o){if(!i)return f.call({o:e,s:r},t,n),t;i.call(a||this||{},t,n,e,o),p(e,n)&&void 0===e[n]||f.call({o:e,s:r},t,n)})};var c=n.obj,l=c.as,p=c.has,h=c.is,d=c.map,g=c.copy,v=n.num.is,m=n.fn.is,k=a._;t.exports=u})(j,"./state"),j(function(t){var a=j("./type"),u=j("./val"),s=j("./node"),i={};function r(t,n){if(!t||n!==s.soul(t)||!s.is(t,this.fn,this.as))return!0;this.cb&&(e.n=t,e.as=this.as,this.cb.call(e.as,t,n,e))}function e(t){t&&s.is(e.n,t,e.as)}function f(t,n){var e;return(e=function(t,n){var e,o=t.seen,i=o.length;for(;i--;)if(e=o[i],n.obj===e.obj)return e;o.push(n)}(t,n))?e:(n.env=t,n.soul=c,s.ify(n.obj,o,n)&&(n.link=n.link||u.link.ify(s.soul(n.node)),n.obj!==t.shell&&(t.graph[u.link.is(n.link)]=n.node)),n)}function o(t,n,e){var o,i,r=this,a=r.env;if(s._===n&&g(t,u.link._))return e._;if(o=l(t,n,e,r,a)){if(n||(r.node=r.node||e||{},g(t,s._)&&s.soul(t)&&(r.node._=b(t._)),r.node=s.soul.ify(r.node,u.link.is(r.link)),r.link=r.link||u.link.ify(s.soul(r.node))),(i=a.map)&&(i.call(a.as||{},t,n,e,r),g(e,n))){if(void 0===(t=e[n]))return void d(e,n);if(!(o=l(t,n,e,r,a)))return}if(!n)return r.node;if(!0===o)return t;if((i=f(a,{obj:t,path:r.path.concat(n)})).node)return i.link}}function c(t){var n=this,e=u.link.is(n.link),o=n.env.graph;n.link=n.link||u.link.ify(t),n.link[u.link._]=t,n.node&&n.node[s._]&&(n.node[s._][u.link._]=t),g(o,e)&&(o[t]=o[e],d(o,e))}function l(t,n,e,o,i){var r;return!!u.is(t)||(h(t)?1:(r=i.invalid)?l(t=r.call(i.as||{},t,n,e),n,e,o,i):(i.err="Invalid value at '"+o.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(i.err+=" Use `.set(item)` instead of an Array."))))}function p(t,n){var e,o;if(s._!==n)(e=u.link.is(t))?(o=this.opt.seen[e])?this.obj[n]=o:this.obj[n]=this.opt.seen[e]=i.to(this.graph,e,this.opt):this.obj[n]=t;else{if(v(t,u.link._))return;this.obj[n]=b(t)}}i.is=function(t,n,e,o){return!(!t||!h(t)||v(t)||k(t,r,{cb:n,fn:e,as:o}))},i.ify=function(t,n,e){var o={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(o.link=u.link.ify(n.soul)),n.shell=(e||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||e,f(n,o),n.root=o.node,n.graph},i.node=function(t){var n=s.soul(t);if(n)return m({},n,t)},i.to=function(t,n,e){if(t){var o={};return e=e||{seen:{}},k(t[n],p,{obj:o,graph:t,opt:e}),o}};a.fn.is;var n=a.obj,h=n.is,d=n.del,g=n.has,v=n.empty,m=n.put,k=n.map,b=n.copy;t.exports=i})(j,"./graph"),j(function(t){j("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var e=t["#"]||t,o=(this.tag||empty)[e];if(!o)return;return o=this.on(e,n),clearTimeout(o.err),!0}e=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return e;var i=this.on(e,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),e}}})(j,"./ask"),j(function(t){var r=j("./type");var a=r.time.is;t.exports=function(o){var i={s:{}};return o=o||{max:1e3,age:9e3},i.check=function(t){var n;return!!(n=i.s[t])&&(n.pass?n.pass=!1:i.track(t))},i.track=function(t,n){var e=i.s[t]||(i.s[t]={});return e.was=a(),n&&(e.pass=!0),i.to||(i.to=setTimeout(function(){var e=a();r.obj.map(i.s,function(t,n){t&&o.age>e-t.was||r.obj.del(i.s,n)}),i.to=null},o.age+9)),e},i}})(j,"./dup"),j(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=j("./type");function a(t){var n,e,o=this.as,i=o.at||o,r=i.$;(e=t["#"])||(e=t["#"]=h(9)),(n=i.dup).check(e)?o.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(e),i.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),o.out||(t.out=a,i.on("out",t)))}function i(t,n,e,o){var i=this,r=c.state.is(e,n);if(!r)return i.err="Error: No state on '"+n+"' in node '"+o+"'!";var a=i.graph[o]||x,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(i.machine,r,u,t,s);f.incoming?(i.put[o]=c.state.to(e,n,i.put[o]),(i.diff||(i.diff={}))[o]=c.state.to(e,n,i.diff[o]),i.souls[o]=!0):f.defer&&(i.defer=r<(i.defer||1/0)?r:i.defer)}function r(t,n){var e=this,o=e.$._,i=(o.next||x)[n];if(!i){if(!(o.opt||x).super)return void(e.souls[n]=!1);i=e.$.get(n)._}var r=e.map[n]={put:t,get:n,$:i.$},a={ctx:e,msg:r};e.async=!!o.tag.node,e.ack&&(r["@"]=e.ack),k(t,u,a),e.async&&(e.and||o.on("node",function(t){this.to.next(t),t===e.map[t.get]&&(e.souls[t.get]=!1,k(t.put,s,t),k(e.souls,function(t){if(t)return t})||e.c||(e.c=1,this.off(),k(e.map,f,e)))}),e.and=!0,o.on("node",r))}function u(t,n){var e=this.ctx,o=e.graph,i=this.msg,r=i.get,a=i.put,u=i.$._;o[r]=c.state.to(a,n,o[r]),e.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var e=this.put,o=this.$._;o.put=c.state.to(e,n,o.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}n.obj.to(n,c),c.HAM=j("./HAM"),c.val=j("./val"),c.node=j("./node"),c.state=j("./state"),c.graph=j("./graph"),c.on=j("./onto"),c.ask=j("./ask"),c.dup=j("./dup"),c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n},c.on.put=function(t,n){var e=n._,o={$:n,graph:e.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:e,stop:{}};if(c.graph.is(t.put,null,i,o)||(o.err="Error: Invalid graph!"),o.err)return e.on("in",{"@":t["#"],err:c.log(o.err)});k(o.put,r,o),o.async||k(o.map,f,o),void 0!==o.defer&&setTimeout(function(){c.on.put(t,n)},o.defer-o.machine),o.diff&&e.on("put",m(t,{put:o.diff}))},c.on.get=function(t,n){var e=n._,o=t.get,i=o[y],r=e.graph[i],a=o[_],u=(e.next||(e.next={}))[i];if(!r)return e.on("get",t);if(a){if("string"!=typeof a||!v(r,a))return e.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||x).ack,e.on("in",{"@":t["#"],how:"mem",put:r,$:n}),e.on("get",t)},c.chain.opt=function(t){t=t||{};var n=this._,e=t.peers||t;return g(t)||(t={}),g(n.opt)||(n.opt=t),p(e)&&(e=[e]),o(e)&&(e=k(e,function(t,n,e){(n={}).id=n.url=t,e(t,n)}),g(n.opt.peers)||(n.opt.peers={}),n.opt.peers=m(e,n.opt.peers)),n.opt.peers=n.opt.peers||{},k(t,function t(n,e){!v(this,e)||l.is(n)||d.empty(n)?this[e]=n:n&&n.constructor!==Object&&!o(n)||k(n,t,this[e])},n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return b()+h(12)},this};var o=c.list.is,l=c.text,p=l.is,h=l.random,d=c.obj,g=d.is,v=d.has,m=d.to,k=d.map,b=(d.copy,c.state.lex),y=c.val.link._,_=".",x=(c.node._,c.val.link.is,{});e.only=function(t,n){return e.only.i&&t===e.only.i&&e.only.i++&&(e.log.apply(e,arguments)||n)},(c.log=function(){return c.log.off||e.log.apply(e,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,e){return(e=c.log.once)[t]=e[t]||0,e[t]++||c.log(n)},c.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&((window.GUN=window.Gun=c).window=window);try{void 0!==$&&($.exports=c)}catch(t){}t.exports=c})(j,"./root"),j(function(t){var u=j("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var e=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var o=0,i=t.length,r=e;o(r.acks||0)&&this.off(),r.ack&&r.ack(t,this)},r.opt),e=0,o=n.root.now;h.del(n.root,"now");var i=n.root.mum;n.root.mum={},r.ref._.on("out",{$:r.ref,put:r.out=r.env.graph,opt:r.opt,"#":t}),n.root.mum=i?h.to(i,n.root.mum):i,n.root.now=o},r),r.res&&r.res())}function n(t,n){if(t)return!0}function c(r,t,n,a){var u=this,s=f.is(r);!t&&a.path.length&&(u.res||o)(function(){for(var t=a.path,n=u.ref,e=(u.opt,0),o=t.length;e .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var e=this,o=e._,i=o.put;if(0=(o.batch||1e3))return f();i=i||setTimeout(f,o.wait||1)}),a.on("get",function(n){this.to.next(n);var e,o,i,r=n.get;function t(){if(r&&(e=r["#"])){var t=r["."];(o=s[e]||i)&&t&&(o=Gun.state.to(o,t)),a.on("in",{"@":n["#"],put:Gun.graph.node(o),how:"lS",lS:n.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,e,o){s[o]=Gun.state.to(e,n,s[o])},f=function(t){var e;u=0,clearTimeout(i),i=!1;var n=r;r={},t&&(s=t);try{p.setItem(o.prefix,JSON.stringify(s))}catch(t){Gun.log(e=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install"),a.on("localStorage:error",{err:e,file:o.prefix,flush:s,retry:f})}(e||Gun.obj.empty(o.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:e,ok:0})})}}})}})(j,"./adapters/localStorage"),j(function(t){var v=j("../type"),m="undefined"!=typeof setImmediate?setImmediate:setTimeout;!function(){v.text.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var e=0,o=t.length;e<"])&&(r._.to=v.obj.map(o.split(","),p)),r.dam?void((o=s.hear[r.dam])&&o(r,i,u)):(k&&(a=+new Date),u.on("in",r),void(k&&!r.nts&&f.log(+new Date-a,"msg",r["#"])));var a}}};var l,p=function(t,n,e){e(t,!0)};function h(t){s.say(l,t)}function d(t){var n=t.batch;if(t.batch=t.tail=null,n&&n.length){var e;k&&(e=+new Date);try{n=1===n.length?n[0]:JSON.stringify(n)}catch(t){return f.log("DAM JSON stringify error",t)}k&&f.log(+new Date-e,"say stringify",n.length),n&&g(n,t)}}function g(n,e){try{var t,o=e.wire;k&&(t=+new Date),e.say?e.say(n):o.send&&o.send(n),k&&f.log(+new Date-t,"wire send",n.length),s.say.d+=n.length||0,++s.say.c}catch(t){(e.queue=e.queue||[]).push(n)}}s.hear.c=s.hear.d=0,s.say=function(t,n){if(this.to&&this.to.next(t),!t)return!1;var e,o,i,r;k&&(a=+new Date);var a,u=t._||(t._=function(){});if((e=t["#"])||(e=t["#"]=v.text.random(9)),(o=t["##"])||void 0===t.put||(o=t["##"]=v.obj.hash(t.put)),!(r=u.raw)&&(r=u.raw=s.raw(t),o&&(i=t["@"])&&(c.track(i+o).it=t,i=(c.s[i]||!0).it))){if(o===i["##"])return!1;i["##"]=o}if(k&&f.log(+new Date-a,"say prep"),c.track(e).it=t,!(n=n||(i=c.s[t["@"]])&&(i=i.it)&&(i=i._)&&(i=i.via))&&s.way)return s.way(t);if(!n||!n.id)return l=t,!!v.obj.is(n||f.peers)&&(k&&(a=+new Date),v.obj.map(n||f.peers,h),void(k&&f.log(+new Date-a,"say loop")));if(!n.wire&&s.wire&&s.wire(n),e!==n.last){if(n.last=e,n===u.via)return!1;if((i=u.to)&&(i[n.url]||i[n.pid]||i[n.id]))return!1;if(n.batch){if(n.tail=(i=n.tail||0)+r.length,n.tail<=f.pack)return void n.batch.push(r);d(n)}n.batch=[],setTimeout(function(){d(n)},f.gap),g(r,n)}},s.say.c=s.say.d=0,function(){s.raw=function(t){if(!t)return"";var n,e=t._||{};if(n=e.raw)return n;if("string"==typeof t)return t;if(!t.dam){var o=0,i=[];v.obj.map(f.peers,function(t){if(i.push(t.url||t.pid||t.id),9<++o)return!0}),1<"]=i.join())}var r=a(t);return e&&(e.raw=r),r};var a=JSON.stringify}(),s.hi=function(n){var t=n.wire||{};n.id?f.peers[n.url||n.id]=n:(t=n.id=n.id||v.text.random(9),s.say({dam:"?"},f.peers[t]=n)),n.met=n.met||+new Date,t.hied||u.on(t.hied="hi",n),t=n.queue,n.queue=[],v.obj.map(t,function(t){g(t,n)})},s.bye=function(t){u.on("bye",t);var n=+new Date;n-=t.met||n,s.bye.time=((s.bye.time||n)+n)/2,k=e.LOG},s.hear["!"]=function(t,n){f.log("Error:",t.err)},s.hear["?"]=function(t,n){t.pid?n.pid||(n.pid=t.pid):s.say({dam:"?",pid:f.pid,"@":t["#"]},n)},u.on("create",function(t){t.opt.pid=t.opt.pid||v.text.random(9),this.to.next(t),t.on("out",s.say)}),u.on("bye",function(t,n){t=f.peers[t.id||t]||t,this.to.next(t),t.bye?t.bye():(n=t.wire)&&n.close&&n.close(),v.obj.del(f.peers,t.id),t.wire=null});var i={};return u.on("bye",function(t,n){this.to.next(t),(n=t.url)&&(i[n]=!0,setTimeout(function(){delete i[n]},f.lack||9e3))}),u.on("hi",function(e,o){this.to.next(e),(o=e.url)&&i[o]&&(delete i[o],v.obj.map(u.next,function(t,n){(o={})[n]=u.graph[n],s.say({"##":v.obj.hash(o),get:{"#":n}},e)}))}),s}}catch(t){}})(j,"./adapters/mesh"),j(function(t){var f=j("../index");f.Mesh=j("./mesh"),f.on("opt",function(t){this.to.next(t);var o=t.opt;if(!t.once&&!1!==o.WebSocket){var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var e=o.WebSocket||n.WebSocket||n.webkitWebSocket||n.mozWebSocket;if(e){o.WebSocket=e;var i=o.mesh=o.mesh||f.Mesh(t);i.wire||o.wire;i.wire=o.wire=u,setTimeout(function(){t.on("out",{dam:"hi"})},1);var r=2e3,a="undefined"!=typeof document&&document}}function u(n){try{if(!n||!n.url)return e&&e(n);var t=n.url.replace("http","ws"),e=n.wire=new o.WebSocket(t);return e.onclose=function(){o.mesh.bye(n),s(n)},e.onerror=function(t){s(n)},e.onopen=function(){o.mesh.hi(n)},e.onmessage=function(t){t&&o.mesh.hear(t.data||t,n)},e}catch(t){}}function s(n){clearTimeout(n.defer),a&&n.retry<=0||(n.retry=(n.retry||o.retry||60)-1,n.defer=setTimeout(function t(){if(a&&a.hidden)return setTimeout(t,r);u(n)},r))}})})(j,"./adapters/websocket")}(); \ No newline at end of file +!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var e=(t=t||{}).console||{log:function(){}};function _(e,t){return t?require(e):e.slice?_[o(e)]:function(t,n){e(t={exports:{}}),_[o(n)]=t.exports};function o(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var m=module;_(function(t){var p={fn:{is:function(t){return!!t&&"function"==typeof t}}};p.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},p.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},p.text={is:function(t){return"string"==typeof t}},p.text.ify=function(t){return p.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},p.text.random=function(t,n){var e="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0"]||n["<"])||o===n["="]&&(e=n["*"]||n[">"]||n["<"],t.slice(0,(e||"").length)===e||o===n["*"]&&(o!==n[">"]&&o!==n["<"]?t>=n[">"]&&t<=n["<"]:o!==n[">"]&&t>=n[">"]||o!==n["<"]&&t<=n["<"])))},p.list={is:function(t){return t instanceof Array}},p.list.slit=Array.prototype.slice,p.list.sort=function(e){return function(t,n){return t&&n?(t=t[e])<(n=n[e])?-1:n",s.drift=0,s.is=function(t,n,e){var o=n&&t&&t[m]&&t[m][s._]||e;if(o)return g(o=o[n])?o:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,e,o,i){if(!t||!t[m]){if(!i)return;t=a.soul.ify(t,i)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(e)&&(r[n]=e),void 0!==o&&(t[n]=o)),t},s.to=function(t,n,e){var o=(t||{})[n];return p(o)&&(o=d(o)),s.ify(e,n,s.is(t,n),o,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(i,r,a){var t=p(t=i||r)?t:null;return i=v(i=i||r)?i:null,t&&!i?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,e,o){if(!i)return u.call({o:e,s:r},t,n),t;i.call(a||this||{},t,n,e,o),l(e,n)&&void 0===e[n]||u.call({o:e,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function i(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(e.n=t,e.as=this.as,this.cb.call(e.as,t,n,e))}function e(t){t&&c.is(e.n,t,e.as)}r.is=function(t,n,e,o){return!(!t||!l(t)||u(t))&&!s(t,i,{cb:n,fn:e,as:o})}}(),function(){function u(t,n){var e;return(e=function(t,n){var e,o=t.seen,i=o.length;for(;i--;)if(e=o[i],n.obj===e.obj)return e;o.push(n)}(t,n))?e:(n.env=t,n.soul=i,c.ify(n.obj,o,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function o(t,n,e){var o,i,r=this,a=r.env;if(c._===n&&h(t,f.link._))return e._;if(o=s(t,n,e,r,a)){if(n||(r.node=r.node||e||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(i=a.map)&&(i.call(a.as||{},t,n,e,r),h(e,n))){if(void 0===(t=e[n]))return void p(e,n);if(!(o=s(t,n,e,r,a)))return}if(!n)return r.node;if(!0===o)return t;if((i=u(a,{obj:t,path:r.path.concat(n)})).node)return i.link}}function i(t){var n=this,e=f.link.is(n.link),o=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(o,e)&&(o[t]=o[e],p(o,e))}function s(t,n,e,o,i){var r;return!!f.is(t)||(l(t)?1:(r=i.invalid)?s(t=r.call(i.as||{},t,n,e),n,e,o,i):(i.err="Invalid value at '"+o.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(i.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,e){var o={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(o.link=f.link.ify(n.soul)),n.shell=(e||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||e,u(n,o),n.root=o.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return e({},n,t)},function(){function i(t,n){var e,o;if(c._!==n)(e=f.link.is(t))?(o=this.opt.seen[e])?this.obj[n]=o:this.obj[n]=this.opt.seen[e]=r.to(this.graph,e,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,e){if(t){var o={};return e=e||{seen:{}},s(t[n],i,{obj:o,graph:t,opt:e}),o}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,e=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var e=t["#"]||t,o=(this.tag||empty)[e];if(!o)return;return o=this.on(e,n),clearTimeout(o.err),!0}e=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return e;var i=this.on(e,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),e}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(o){var i={s:{}};return o=o||{max:1e3,age:9e3},i.check=function(t){var n;return!!(n=i.s[t])&&(n.pass?n.pass=!1:i.track(t))},i.track=function(t,n){var e=i.s[t]||(i.s[t]={});return e.was=a(),n&&(e.pass=!0),i.to||(i.to=setTimeout(function(){var e=a();r.obj.map(i.s,function(t,n){t&&o.age>e-t.was||r.obj.del(i.s,n)}),i.to=null},o.age+9)),e},i}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,e,o=this.as,i=o.at||o,r=i.$;(e=t["#"])||(e=t["#"]=u(9)),(n=i.dup).check(e)?o.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(e),i.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),o.out||(t.out=a,i.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function i(t,n,e,o){var i=this,r=c.state.is(e,n);if(!r)return i.err="Error: No state on '"+n+"' in node '"+o+"'!";var a=i.graph[o]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(i.machine,r,u,t,s);f.incoming?(i.put[o]=c.state.to(e,n,i.put[o]),(i.diff||(i.diff={}))[o]=c.state.to(e,n,i.diff[o]),i.souls[o]=!0):f.defer&&(i.defer=r<(i.defer||1/0)?r:i.defer)}function r(t,n){var e=this,o=e.$._,i=(o.next||v)[n];if(!i){if(!(o.opt||v).super)return void(e.souls[n]=!1);i=e.$.get(n)._}var r=e.map[n]={put:t,get:n,$:i.$},a={ctx:e,msg:r};e.async=!!o.tag.node,e.ack&&(r["@"]=e.ack),h(t,u,a),e.async&&(e.and||o.on("node",function(t){this.to.next(t),t===e.map[t.get]&&(e.souls[t.get]=!1,h(t.put,s,t),h(e.souls,function(t){if(t)return t})||e.c||(e.c=1,this.off(),h(e.map,f,e)))}),e.and=!0,o.on("node",r))}function u(t,n){var e=this.ctx,o=e.graph,i=this.msg,r=i.get,a=i.put,u=i.$._;o[r]=c.state.to(a,n,o[r]),e.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var e=this.put,o=this.$._;o.put=c.state.to(e,n,o.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var e=n._,o={$:n,graph:e.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:e,stop:{}};if(c.graph.is(t.put,null,i,o)||(o.err="Error: Invalid graph!"),o.err)return e.on("in",{"@":t["#"],err:c.log(o.err)});h(o.put,r,o),o.async||h(o.map,f,o),void 0!==o.defer&&setTimeout(function(){c.on.put(t,n)},o.defer-o.machine),o.diff&&e.on("put",p(t,{put:o.diff}))},c.on.get=function(t,n){var e=n._,o=t.get,i=o[d],r=e.graph[i],a=o[g],u=(e.next||(e.next={}))[i];if(!r)return e.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return e.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,e.on("in",{"@":t["#"],how:"mem",put:r,$:n}),e.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,e=t.peers||t;return s(t)||(t={}),s(n.opt)||(n.opt=t),r(e)&&(e=[e]),o(e)&&(e=h(e,function(t,n,e){(n={}).id=n.url=t,e(t,n)}),s(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(e,n.opt.peers)),n.opt.peers=n.opt.peers||{},h(t,function t(n,e){!l(this,e)||i.is(n)||a.empty(n)?this[e]=n:n&&n.constructor!==Object&&!o(n)||h(n,t,this[e])},n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return f()+u(12)},this};var o=c.list.is,i=c.text,r=i.is,u=i.random,a=c.obj,s=a.is,l=a.has,p=a.to,h=a.map,f=(a.copy,c.state.lex),d=c.val.link._,g=".",v=(c.node._,c.val.link.is,{});e.only=function(t,n){return e.only.i&&t===e.only.i&&e.only.i++&&(e.log.apply(e,arguments)||n)},(c.log=function(){return!c.log.off&&e.log.apply(e,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,e){return(e=c.log.once)[t]=e[t]||0,e[t]++||c.log(n)},c.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&((window.GUN=window.Gun=c).window=window);try{void 0!==m&&(m.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var e=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var o=0,i=t.length,r=e;o(r.acks||0)&&this.off(),r.ack&&r.ack(t,this)},r.opt),e=0,o=n.root.now;h.del(n.root,"now");var i=n.root.mum;n.root.mum={},r.ref._.on("out",{$:r.ref,put:r.out=r.env.graph,opt:r.opt,"#":t}),n.root.mum=i?h.to(i,n.root.mum):i,n.root.now=o},r),r.res&&r.res())}function n(t,n){if(t)return!0}function c(r,t,n,a){var u=this,s=f.is(r);!t&&a.path.length&&(u.res||o)(function(){for(var t=a.path,n=u.ref,e=(u.opt,0),o=t.length;e .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var e=this,o=e._,i=o.put;if(0=(o.batch||1e3))return f();i||(i=setTimeout(f,o.wait||1))}),r.on("get",function(n){this.to.next(n);var e,o,i=n.get;function t(){if(i&&(e=i["#"])){var t=i["."];(o=s[e]||void 0)&&t&&(o=Gun.state.to(o,t)),r.on("in",{"@":n["#"],put:Gun.graph.node(o),how:"lS",lS:n.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,e,o){s[o]=Gun.state.to(e,n,s[o])},f=function(t){var e;u=0,clearTimeout(i),i=!1;var n=a;a={},t&&(s=t);try{p.setItem(o.prefix,JSON.stringify(s))}catch(t){Gun.log(e=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install"),r.on("localStorage:error",{err:e,file:o.prefix,flush:s,retry:f})}(e||Gun.obj.empty(o.peers))&&Gun.obj.map(n,function(t,n){r.on("in",{"@":n,err:e,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var g=_("../type"),f="undefined"!=typeof setImmediate?setImmediate:setTimeout;!function(){g.text.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var e=0,o=t.length;e<"])&&(r._.to=g.obj.map(o.split(","),s)),r.dam?void((o=l.hear[r.dam])&&o(r,i,u)):(v&&(a=+new Date),u.on("in",r),void(v&&!r.nts&&p.log(a,+new Date-a,"msg",r["#"])));var a}}};var s=function(t,n,e){e(t,!0)};function d(n,e){try{var t,o=e.wire;v&&(t=+new Date),e.say?e.say(n):o.send&&o.send(n),v&&p.log(t,+new Date-t,"wire send",n.length),l.say.d+=n.length||0,++l.say.c}catch(t){(e.queue=e.queue||[]).push(n)}}l.hear.c=l.hear.d=0,function(){var s;function f(t){l.say(s,t)}function c(t){var n=t.batch;if(t.batch=t.tail=null,n&&n.length){var e;v&&(e=+new Date);try{n=1===n.length?n[0]:JSON.stringify(n)}catch(t){return p.log("DAM JSON stringify error",t)}v&&p.log(e,+new Date-e,"say stringify",n.length),n&&d(n,t)}}l.say=function(t,n){if(this.to&&this.to.next(t),!t)return!1;var e,o,i,r;v&&(a=+new Date);var a,u=t._||(t._=function(){});if((e=t["#"])||(e=t["#"]=g.text.random(9)),(o=t["##"])||void 0===t.put||(o=t["##"]=g.obj.hash(t.put)),!(r=u.raw)&&(r=u.raw=l.raw(t),o&&(i=t["@"])&&(h.track(i+o).it=t,i=(h.s[i]||!0).it))){if(o===i["##"])return!1;i["##"]=o}if(v&&p.log(a,+new Date-a,"say prep"),h.track(e).it=t,n||(n=(i=h.s[t["@"]])&&(i=i.it)&&(i=i._)&&(i=i.via)),!n&&l.way)return l.way(t);if(!n||!n.id)return s=t,!!g.obj.is(n||p.peers)&&(v&&(a=+new Date),g.obj.map(n||p.peers,f),void(v&&p.log(a,+new Date-a,"say loop")));if(!n.wire&&l.wire&&l.wire(n),e!==n.last){if(n.last=e,n===u.via)return!1;if((i=u.to)&&(i[n.url]||i[n.pid]||i[n.id]))return!1;if(n.batch){if(n.tail=(i=n.tail||0)+r.length,n.tail<=p.pack)return void n.batch.push(r);c(n)}n.batch=[],setTimeout(function(){c(n)},p.gap),d(r,n)}},l.say.c=l.say.d=0}(),function(){l.raw=function(t){if(!t)return"";var n,e=t._||{};if(n=e.raw)return n;if("string"==typeof t)return t;if(!t.dam){var o=0,i=[];g.obj.map(p.peers,function(t){if(i.push(t.url||t.pid||t.id),9<++o)return!0}),1<"]=i.join())}var r=a(t);return e&&(e.raw=r),r};var a=JSON.stringify}(),l.hi=function(n){var t=n.wire||{};n.id?p.peers[n.url||n.id]=n:(t=n.id=n.id||g.text.random(9),l.say({dam:"?",pid:u.opt.pid},p.peers[t]=n),delete h.s[n.last]),n.met=n.met||+new Date,t.hied||u.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){d(t,n)})},l.bye=function(t){u.on("bye",t);var n=+new Date;n-=t.met||n,l.bye.time=((l.bye.time||n)+n)/2,v=e.LOG},l.hear["!"]=function(t,n){p.log("Error:",t.err)},l.hear["?"]=function(t,n){t.pid&&(n.pid||(n.pid=t.pid),t["@"])||(l.say({dam:"?",pid:p.pid,"@":t["#"]},n),delete h.s[n.last])},u.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),t.on("out",l.say)}),u.on("bye",function(t,n){t=p.peers[t.id||t]||t,this.to.next(t),t.bye?t.bye():(n=t.wire)&&n.close&&n.close(),g.obj.del(p.peers,t.id),t.wire=null});var i={};return u.on("bye",function(t,n){this.to.next(t),(n=t.url)&&(i[n]=!0,setTimeout(function(){delete i[n]},p.lack||9e3))}),u.on("hi",function(e,o){this.to.next(e),(o=e.url)&&i[o]&&(delete i[o],g.obj.map(u.next,function(t,n){(o={})[n]=u.graph[n],l.say({"##":g.obj.hash(o),get:{"#":n}},e)}))}),l}}catch(t){}})(_,"./adapters/mesh"),_(function(t){var f=_("../index");f.Mesh=_("./mesh"),f.on("opt",function(t){this.to.next(t);var o=t.opt;if(!t.once&&!1!==o.WebSocket){var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var e=o.WebSocket||n.WebSocket||n.webkitWebSocket||n.mozWebSocket;if(e){o.WebSocket=e;var i=o.mesh=o.mesh||f.Mesh(t);i.wire||o.wire;i.wire=o.wire=u,setTimeout(function(){t.on("out",{dam:"hi"})},1);var r=2e3,a="undefined"!=typeof document&&document}}function u(n){try{if(!n||!n.url)return e&&e(n);var t=n.url.replace("http","ws"),e=n.wire=new o.WebSocket(t);return e.onclose=function(){o.mesh.bye(n),s(n)},e.onerror=function(t){s(n)},e.onopen=function(){o.mesh.hi(n)},e.onmessage=function(t){t&&o.mesh.hear(t.data||t,n)},e}catch(t){}}function s(n){clearTimeout(n.defer),a&&n.retry<=0||(n.retry=(n.retry||o.retry||60)-1,n.defer=setTimeout(function t(){if(a&&a.hidden)return setTimeout(t,r);u(n)},r))}})})(_,"./adapters/websocket")}(); \ No newline at end of file diff --git a/lib/meta.js b/lib/meta.js index 7e2c0a11..0ff75cc4 100644 --- a/lib/meta.js +++ b/lib/meta.js @@ -17,6 +17,7 @@ /* UNBUILD */ ;USE(function(module){ + var noop = function(){}, u; $.fn.or = function(s){ return this.length ? this : $(s||'body') }; var m = window.meta = {edit:[]}; @@ -30,19 +31,18 @@ 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(!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() - + if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length && !$(eve.target).closest('#meta').length){ + if(!meta.flip.is() && !withMeta(eve)){ // cancel meta/hud during text input UNLESS hud is open OR cmd key is held down. + return; + } + //if(k.meta[key]){ k.down.meta = key = -1 } + //if(!k.down.meta){ return } // hmmm? - // if(!k.meta[key] && !meta.flip.is()) return // aserwed + //if(!k.meta[key] && !meta.flip.is()) return // aserwed } m.check('on', key, k.at || (k.at = m.edit)); if(k.meta[key]){ @@ -53,7 +53,7 @@ } 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(!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; @@ -131,7 +131,8 @@ eve.preventDefault(); cb($put.val()); $li.remove(); - k.wipe(); + //k.wipe(); + m.list(k.at); }); var $li = $('
  • ').append($form); $ul.append($li); @@ -147,7 +148,7 @@ m.tap = function(){ var on = $('.meta-on') .or($($(document.querySelectorAll(':hover')).get().reverse()).first()) - .or($(document.elementFromPoint(meta.tap.x, meta.tap.y))); + .or($(document.elementFromPoint(meta.tap.x||0, meta.tap.y||0))); return on; } meta.edit = function(edit){ @@ -167,18 +168,18 @@ ;USE(function(module){ /* UI */ meta.ui = { - blink: function(){ // hint visually that action has happened + 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 + $('#meta')[0].style.transition = null + $('#meta')[0].style.background = null }) }, depth: function(n){ if (n) { - $('#meta').css('background', 'hsl(60, 100%,'+(85-(n*10))+'%)'); + $('#meta').css('background', 'hsl(60, 100%,'+(85-(n*10))+'%)'); } else { - $('#meta')[0].style.background = null + $('#meta')[0].style.background = null } } } @@ -236,7 +237,8 @@ 'border-radius': '1em', 'margin-left': '0.25em', 'margin-top': '0.25em', - 'float': 'right' + 'float': 'right', + 'cursor': 'pointer' }, '#meta a': {color: 'black'}, // '#meta:hover': {opacity: 1}, @@ -274,6 +276,7 @@ })(USE, './metaUI'); ;USE(function(module){ + // include basic text editing by default. var monotype = window.monotype || function(){console.log("monotype needed")}; var m = meta; @@ -420,13 +423,13 @@ })(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); - //}) + $(window).on('focus', k.wipe.bind(null, false)); // .on('blur', 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; @@ -437,12 +440,17 @@ //$(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); + var [start, end] = 'ontouchstart' in window + ? ['touchstart', 'touchend'] + : ['mousedown', 'mouseup'] + + $(document).on(start, '#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; + k.down(eve); + $(document).one(end, () => 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; @@ -451,6 +459,11 @@ }); $(document).on('keydown', k.down).on('keyup', k.up); + $('#meta').on('click', function(ev) { + if (ev.target.tagName == 'LI' || ev.target.tagName == 'UL') return + meta.flip() + }) + //$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on); diff --git a/lib/multicast.js b/lib/multicast.js index 94f0e39b..e4c5e4ad 100644 --- a/lib/multicast.js +++ b/lib/multicast.js @@ -22,7 +22,7 @@ Gun.on('create', function(root){ socket.bind({port: udp.port, exclusive: true}, function(){ socket.setBroadcast(true); socket.setMulticastTTL(128); - socket.addMembership(udp.address); + try{ socket.addMembership(udp.address); }catch(e){} }); socket.on("listening", function(){ diff --git a/lib/open.js b/lib/open.js index a9b5a8a6..2233d7e5 100644 --- a/lib/open.js +++ b/lib/open.js @@ -5,6 +5,7 @@ Gun.chain.open = function(cb, opt, at){ opt.doc = opt.doc || {}; opt.ids = opt.ids || {}; opt.any = opt.any || cb; + opt.meta = opt.meta || false; opt.ev = opt.ev || {off: function(){ Gun.obj.map(opt.ev.s, function(e){ if(e){ e.off() } @@ -12,7 +13,9 @@ Gun.chain.open = function(cb, opt, at){ opt.ev.s = {}; }, s:{}} return this.on(function(data, key, ctx, ev){ - delete ((data = Gun.obj.copy(data))||{})._; + if(opt.meta !== true){ + delete ((data = Gun.obj.copy(data))||{})._; + } clearTimeout(opt.to); opt.to = setTimeout(function(){ if(!opt.any){ return } diff --git a/lib/server.js b/lib/server.js index d2f763f4..71ab9fc8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -2,6 +2,7 @@ var Gun = require('../gun'), u; Gun.serve = require('./serve'); //process.env.GUN_ENV = process.env.GUN_ENV || 'debug'; + //console.LOG = true; // only temporarily, REVERT THIS IN FUTURE! Gun.on('opt', function(root){ if(u === root.opt.super){ root.opt.super = true; diff --git a/lib/stats.js b/lib/stats.js index 6ccf8105..90be7c59 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -66,7 +66,7 @@ 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; } return log.apply(Gun, arguments); } \ No newline at end of file diff --git a/lib/wave.js b/lib/wave.js new file mode 100644 index 00000000..589f3c40 --- /dev/null +++ b/lib/wave.js @@ -0,0 +1,1242 @@ +var global = typeof global !== 'undefined' ? global : window; +global.edide = {}; + +"use strict";edide.global = (function _global (edide) { +return typeof global !== 'undefined' ? global : window; +return this; +}).call({}, edide); +"use strict";edide.editorModule = (function _editorModule (edide) { +var ref; +return typeof file !== "undefined" && file !== null ? (ref = file.modules) != null ? ref.all : void 0 : void 0; +return this; +}).call({}, edide); +"use strict";edide.logProd = (function _logProd (edide) { +return (...args) => { + var console, ref; + ({console} = (ref = edide.editorModule) != null ? ref : window); + return console.log(...args); +}; +return this; +}).call({}, edide); +"use strict";edide.var = (function _var (edide) { +var currentReact, debugging, dependees, dependsOn, depsRequired, inInitCall, infinityCheck, initSetter, newVar, parent, setLinks, setters, updateVar, values; +values = new Map; // //edide.keep ? +setters = new Map; // varName => setter:func +dependees = new Map; // varName => deps: +setLinks = new Map; // for reactiveGraph, show setters inside reactive/setter:s +debugging = false; //edide.editorModule? +depsRequired = new Map; // varName : dependsOn +inInitCall = false; // TODO use dependsOn? instead +dependsOn = new Set(); // remove +initSetter = (name, setter) => { + var debugName, err, parent, ref, val; + debugName = (ref = setter.type) != null ? ref : name; + if ((setters.get(name)) != null) { + throw Error(`Reactive for '${debugName}' already exists`); + } + setters.set(name, setter); + if (inInitCall) { + throw Error(`can't create reactive setter (for '${debugName}') inside reactive context`); + } + inInitCall = name; + dependsOn.clear(); // TODO clear => new Set + try { + val = setter(); // TODO: some day add revar and unvar as params; helps with multiple reactives to keep the separated + } catch (error) { + err = error; + inInitCall = false; + err.message = `Reactive initialization of '${debugName}' failed: ${err.message}`; + throw err; + } + parent = null; + dependsOn.forEach((depName) => { + var deps; + if ((deps = dependees.get(depName)) == null) { + dependees.set(depName, deps = new Set); + } + return deps.add(name); + }); + inInitCall = false; + return val; +}; +infinityCheck = new Set; //edide.keep +parent = null; +updateVar = function(name, val) { + var ref, ref1, type; + if (arguments.length === 1) { + val = setters.get(name)(); + if (debugging && (type = setters.get(name).type)) { + edide.logProd(`running ${(setters.get(name).type)}`); + } + } + if (typeof name !== 'string') { // symbol ~ react function + return; + } + if (values.get(name) === val) { // can't reset same value + return; + } + if (infinityCheck.has(name)) { + infinityCheck.forEach((k) => { + return edide.logProd(k); + }); + edide.logProd(name); + if ((ref = edide.editorModule) != null) { + if (typeof ref.reactiveGraph === "function") { + ref.reactiveGraph(); + } + } + throw Error("Inifinite loop in \:var dependencies"); + } + if (debugging) { + edide.logProd(`updating ${name}`); + } + values.set(name, val); + if (!inInitCall) { + infinityCheck.add(name); + if ((ref1 = dependees.get(name)) != null) { + ref1.forEach((depName) => { + return updateVar(depName); + }); + } + infinityCheck.delete(name); + } + return val; +}; +currentReact = []; +newVar = function(name, setter) { + var context, contextSet, err; + if (arguments.length === 1) { + if (typeof name === 'string') { + if (inInitCall) { + dependsOn.add(name); + } + return values.get(name); + } else { + setter = name; + name = Symbol(); + values.set(name, name); // for debugging (showing react/dom funcs in graph) + } + } + if (currentReact.length) { // and debugging + context = currentReact[currentReact.length - 1]; + if (!(contextSet = setLinks.get(context))) { + setLinks.set(context, contextSet = new Set); + } + contextSet.add(name); + } + currentReact.push(name); + if (typeof setter === 'function') { + setter = initSetter(name, setter); // setter becomes value + } + if (typeof name === 'string') { + try { + updateVar(name, setter); + } catch (error) { + err = error; + infinityCheck.clear(); + throw err; + } + } + currentReact.pop(); + return setter; +}; +Object.assign(newVar, {dependees, values, setters, setLinks}); +return newVar; +return this; +}).call({}, edide); +"use strict";edide.strRandom = (function _strRandom (edide) { +return (limit = 20) => { + return (Math.random() + '').slice(2, +(limit + 1) + 1 || 9e9); +}; +return this; +}).call({}, edide); +"use strict";edide.reactive = (function _reactive (edide) { +return (initialVars = {}) => { + var handler, id, key, react, revar, todoMap, unvar, val; + id = edide.strRandom(); + handler = { + get: (map, prop) => { + var ref; + if ((ref = edide.editorModule) != null ? ref.editor_inspector.inspectingNow : void 0) { + console.log('IN inspector? Find out is it possible to end up here form inside setter'); + return edide.var.values.get(`${id}.${prop}`); + } + return edide.var(`${id}.${prop}`); + }, + set: (map, prop, value) => { + edide.var(`${id}.${prop}`, value); + return true; // Proxy set must return true if set is successfull; In the future use Reflect.set, which returns true automatically? + } + }; + revar = new Proxy((todoMap = new Map), handler); // NOTE: map is not used yet + unvar = new Proxy(todoMap, { + get: (map, prop) => { + return edide.var.values.get(`${id}.${prop}`); + }, + set: (map, prop, value) => { + return edide.var.values.set(`${id}.${prop}`, value); + } + }); + for (key in initialVars) { + val = initialVars[key]; + revar[key] = val; + } + react = (nameOrFunc, func) => { + if (func != null) { + func.type = nameOrFunc; + } else { + func = nameOrFunc; + func.type = 'react'; // for debugging + } + return edide.var(func); + }; + return { + react, + revar, + unvar, + un: unvar, + re: revar // , dom + }; +}; +return this; +}).call({}, edide); +"use strict";edide.mmState = (function _mmState (edide) {Object.defineProperty(this, 'module_name', {value:'mmState'}); +this.defaults = { + playing: false, + recorderOn: false, + sheet: '', + diffText: '', + note: null, // currently playing note + bpm: 400, // -> beatDelay + blur: 0, + itch: 0, + instrument: 'guitar-electric', + scale: 'pentatonicMinor', + highlight: null, + keyboardInd: 0 // TODO --> keyboard +}; +this.react = null; +this.init = (startingProps = {}) => { + if (this.react != null) { + return this; + } + ({react: this.react, revar: this.revar, unvar: this.unvar} = edide.reactive(Object.assign({}, this.defaults, startingProps))); + return this; +}; +return this; +}).call({}, edide); +"use strict";edide.mmEffects = (function _mmEffects (edide) {Object.defineProperty(this, 'module_name', {value:'mmEffects'}); +({revar: this.revar} = edide.mmState.init()); +this.maxLowpass = 10000; +this.maxDistortion = 3; +this.revar.lowpass = () => { + var blur; + ({blur} = this.revar); + return this.maxLowpass - blur * (this.maxLowpass - 200); +}; +this.revar.distortion = () => { + var itch; + ({itch} = this.revar); + return this.maxDistortion * itch; +}; +return this; +}).call({}, edide); +"use strict";edide.musicScales = (function _musicScales (edide) {Object.defineProperty(this, 'module_name', {value:'musicScales'}); +this.full = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; +this.scaleSteps = { + pentatonicMinor: [ + 0, + 3, + 2, + 2, + 3 // hidden 2 + ], + naturalMajor: [ + 0, + 2, + 2, + 1, + 2, + 2, + 2 // hidden to beginning: 1 + ], + naturalMinor: [ + 0, + 2, + 1, + 2, + 2, + 1, + 2 // hidden to beginning: 2 + ] +}; +this.triadSteps = { + major: [ + 0, + 4, + 3 // positive valence + ], + minor: [0, 3, 4] +}; +this.triadCombinations = { + protagonism: 'M2M', + outerSpace: 'M6M', + fantastical: 'M8M', + sadness: 'M4m', + romantic: 'M5m', // and middle eastern + wonder: 'm5M', // and trancendence + mystery: 'm2M', // and dark comedy + dramatic: 'm11M', + antagonismDanger: 'm6m', // less character based + antagonismEvil: 'm8m' // cahracter based +}; +return this; +}).call({}, edide); +"use strict";edide.noteFreq = (function _noteFreq (edide) {Object.defineProperty(this, 'module_name', {value:'noteFreq'}); +var a4; +this.notes = [['C0', 16.35, 2109.89], ['C#0', 17.32, 1991.47], ['D0', 18.35, 1879.69], ['D#0', 19.45, 1774.20], ['E0', 20.60, 1674.62], ['F0', 21.83, 1580.63], ['F#0', 23.12, 1491.91], ['G0', 24.50, 1408.18], ['G#0', 25.96, 1329.14], ['A0', 27.50, 1254.55], ['A#0', 29.14, 1184.13], ['B0', 30.87, 1117.67], ['C1', 32.70, 1054.94], ['C#1', 34.65, 995.73], ['D1', 36.71, 939.85], ['D#1', 38.89, 887.10], ['E1', 41.20, 837.31], ['F1', 43.65, 790.31], ['F#1', 46.25, 745.96], ['G1', 49.00, 704.09], ['G#1', 51.91, 664.57], ['A1', 55.00, 627.27], ['A#1', 58.27, 592.07], ['B1', 61.74, 558.84], ['C2', 65.41, 527.47], ['C#2', 69.30, 497.87], ['D2', 73.42, 469.92], ['D#2', 77.78, 443.55], ['E2', 82.41, 418.65], ['F2', 87.31, 395.16], ['F#2', 92.50, 372.98], ['G2', 98.00, 352.04], ['G#2', 103.83, 332.29], ['A2', 110.00, 313.64], ['A#2', 116.54, 296.03], ['B2', 123.47, 279.42], ['C3', 130.81, 263.74], ['C#3', 138.59, 248.93], ['D3', 146.83, 234.96], ['D#3', 155.56, 221.77], ['E3', 164.81, 209.33], ['F3', 174.61, 197.58], ['F#3', 185.00, 186.49], ['G3', 196.00, 176.02], ['G#3', 207.65, 166.14], ['A3', 220.00, 156.82], ['A#3', 233.08, 148.02], ['B3', 246.94, 139.71], ['C4', 261.63, 131.87], ['C#4', 277.18, 124.47], ['D4', 293.66, 117.48], ['D#4', 311.13, 110.89], ['E4', 329.63, 104.66], ['F4', 349.23, 98.79], ['F#4', 369.99, 93.24], ['G4', 392.00, 88.01], ['G#4', 415.30, 83.07], ['A4', 440.00, 78.41], ['A#4', 466.16, 74.01], ['B4', 493.88, 69.85], ['C5', 523.25, 65.93], ['C#5', 554.37, 62.23], ['D5', 587.33, 58.74], ['D#5', 622.25, 55.44], ['E5', 659.25, 52.33], ['F5', 698.46, 49.39], ['F#5', 739.99, 46.62], ['G5', 783.99, 44.01], ['G#5', 830.61, 41.54], ['A5', 880.00, 39.20], ['A#5', 932.33, 37.00], ['B5', 987.77, 34.93], ['C6', 1046.50, 32.97], ['C#6', 1108.73, 31.12], ['D6', 1174.66, 29.37], ['D#6', 1244.51, 27.72], ['E6', 1318.51, 26.17], ['F6', 1396.91, 24.70], ['F#6', 1479.98, 23.31], ['G6', 1567.98, 22.00], ['G#6', 1661.22, 20.77], ['A6', 1760.00, 19.60], ['A#6', 1864.66, 18.50], ['B6', 1975.53, 17.46], ['C7', 2093.00, 16.48], ['C#7', 2217.46, 15.56], ['D7', 2349.32, 14.69], ['D#7', 2489.02, 13.86], ['E7', 2637.02, 13.08], ['F7', 2793.83, 12.35], ['F#7', 2959.96, 11.66], ['G7', 3135.96, 11.00], ['G#7', 3322.44, 10.38], ['A7', 3520.00, 9.80], ['A#7', 3729.31, 9.25], ['B7', 3951.07, 8.73], ['C8', 4186.01, 8.24], ['C#8', 4434.92, 7.78], ['D8', 4698.63, 7.34], ['D#8', 4978.03, 6.93], ['E8', 5274.04, 6.54], ['F8', 5587.65, 6.17], ['F#8', 5919.91, 5.83], ['G8', 6271.93, 5.50], ['G#8', 6644.88, 5.19], ['A8', 7040.00, 4.90], ['A#8', 7458.62, 4.63], ['B8', 7902.13, 4.37]]; +this.findNote = (name) => { + var ind; + ind = this.notes.findIndex((arr) => { + return arr[0] === name; + }); + if (ind === -1) { + return edide.logProd(`can't find: ${name}`); + } + return [ind, name, this.notes[ind][1]]; +}; +a4 = this.findNote('A4'); +this.diffToA4 = (name) => { + var note; + note = this.findNote(name); + return note[0] - a4[0]; +}; +this.diff = (n1, n2) => { + return this.findNote(n2)[0] - this.findNote(n1)[0]; +}; +return this; +}).call({}, edide); +"use strict";edide.mathOp = (function _mathOp (edide) {Object.defineProperty(this, 'module_name', {value:'mathOp'}); +this.sum = function(a, b) { + return a + b; +}; +this.multiply = function(a, b) { + return a * b; +}; +return this; +}).call({}, edide); +"use strict";edide.mmKeyboard = (function _mmKeyboard (edide) {Object.defineProperty(this, 'module_name', {value:'mmKeyboard'}); +var A, a, char, chari, i, j, len, len1, noncaps, qwertyChar, qwertyRows, revar, row, rowi, space, unvar; +({revar, unvar} = edide.mmState.init()); +revar.scaleSteps = () => { + return edide.musicScales.scaleSteps[revar.scale]; +}; +this.special = { + rest: '.', + long: '=', + comment: '#', + var: ':', + confStart: '{', + confEnd: '}' +}; +this.specialKeyCodes = new Set(Object.values(this.special).map((s) => { + return s.charCodeAt(0); +})); +this.specialChars = new Set(Object.values(this.special)); +this.isPauseKey = (keyCode) => { + return this.specialKeyCodes.has(keyCode); +}; +this.isSpecialChar = (char) => { + return this.specialChars.has(char); +}; +this.isPauseChar = this.isSpecialChar; // not really... +this.keyboards = ['qwerty', 'abc']; +a = 'a'.charCodeAt(0); +A = 'A'.charCodeAt(0); +space = ' '.charCodeAt(0); +this.isCaps = (key) => { + return A <= key && key < a; +}; +this.capsDiff = a - A; +noncaps = (key) => { + if (this.isCaps(key)) { + return key + this.capsDiff; + } else { + return key; + } +}; +this.getNoteInd = (key) => { + var maxInstrumentNoteInd, noteBaseInd, noteInd, notes, startNote, step; + ({startNote, notes, step} = revar.instrumentConf); + noteBaseInd = edide.noteFreq.findNote(startNote)[0]; + noteInd = this[this.keyboards[revar.keyboardInd]](noncaps(key), noteBaseInd); + maxInstrumentNoteInd = noteBaseInd + notes * step; + while (noteInd > maxInstrumentNoteInd) { + noteInd -= 2 * 12; + } + return noteInd; +}; +this.abc = (key, baseInd) => { + var fromLowest, noteInd, stepsFromClosestOctave; + fromLowest = key - a; + stepsFromClosestOctave = fromLowest % revar.scaleSteps.length; + noteInd = 0; + noteInd += revar.scaleSteps.slice(0, +stepsFromClosestOctave + 1 || 9e9).reduce(edide.mathOp.sum); + noteInd += 12 * Math.floor(fromLowest / revar.scaleSteps.length); + return baseInd + noteInd; +}; +qwertyRows = ['1234567890', 'qwertyuiop', 'asdfghjkl', 'zxcvbnm']; +qwertyChar = {}; +for (rowi = i = 0, len = qwertyRows.length; i < len; rowi = ++i) { + row = qwertyRows[rowi]; + for (chari = j = 0, len1 = row.length; j < len1; chari = ++j) { + char = row[chari]; + qwertyChar[char.charCodeAt(0)] = [rowi, chari]; + } +} +this.qwerty = (key, baseInd) => { + var charSteps, halfSteps, noteInd, octave, rowchar; + if (!(rowchar = qwertyChar[key])) { + return; + } + [row, char] = rowchar; + octave = qwertyRows.length - 1 - row + Math.floor(char / unvar.scaleSteps.length); + char = char % revar.scaleSteps.length; + charSteps = revar.scaleSteps.slice(0, +char + 1 || 9e9).reduce((a, b) => { + return a + b; + }); + halfSteps = 12 * octave + charSteps; + return noteInd = baseInd + halfSteps; +}; +return this; +}).call({}, edide); +"use strict";edide.mmNote = (function _mmNote (edide) {Object.defineProperty(this, 'module_name', {value:'mmNote'}); +this.fromChar = (char) => { + return this.fromKeyCode(char.charCodeAt(0)); +}; +this.fromKeyCode = (key) => { + var noteInd, ref; + if (typeof (noteInd = edide.mmKeyboard.getNoteInd(key)) === 'number') { + return (ref = edide.noteFreq.notes[noteInd]) != null ? ref[0] : void 0; + } +}; +return this; +}).call({}, edide); +"use strict";edide.clone = (function _clone (edide) { +return function(...objects) { + if (Array.isArray(objects[0])) { + return Object.assign([], ...objects); + } else { + return Object.assign({}, ...objects); + } +}; +return this; +}).call({}, edide); +"use strict";edide.strParsesToNumber = (function _strParsesToNumber (edide) { +return (str) => { + return !Number.isNaN(parseInt(str)); +}; +return this; +}).call({}, edide); +"use strict";edide.mmConfigs = (function _mmConfigs (edide) {Object.defineProperty(this, 'module_name', {value:'mmConfigs'}); +var defaultVars, mutatingProp, mutatingVars, vars; +vars = {}; // varName: config object +mutatingVars = {}; +this.hasVar = (varName) => { + var ref; + return !!((ref = vars[varName]) != null ? ref : mutatingVars[varName]); +}; +this.activateVar = (name, value) => { + var configs; + if (value != null) { + configs = edide.clone(mutatingVars[name]); + if (edide.strParsesToNumber(value)) { + value = parseFloat(value); + } + configs[mutatingProp(configs)] = value; + return this.activate(configs); + } else { + return this.activate(vars[name]); + } +}; +mutatingProp = (configs) => { + var name, val; + for (name in configs) { + val = configs[name]; + if (val === '*') { + return name; + } + } + return false; +}; +this.activate = (conf) => { + if (conf.name != null) { + return; // throw warning? + } + return Object.assign(edide.mmState.revar, conf); +}; +this.addVar = (varName, config) => { // , activate=true + if (mutatingProp(config)) { + return mutatingVars[varName] = config; + } else { + return vars[varName] = config; + } +}; +defaultVars = { + bass: { + "instrument": "bass-electric" + }, + cello: { + "instrument": "cello" + }, + guitar: { + "instrument": "guitar-acoustic" + }, + eguitar: { + "instrument": "guitar-electric" + }, + piano: { + "instrument": "piano" + }, + synth: { + "instrument": "synth-simple" + }, + xylophone: { + "instrument": "xylophone" + }, + pentatonic: { + "scale": "pentatonicMinor" + }, + major: { + "scale": "naturalMajor" + }, + minor: { + "scale": "naturalMinor" + } +}; +this.init = () => { + var conf, name, results; + vars = {}; + results = []; + for (name in defaultVars) { + conf = defaultVars[name]; + results.push(this.addVar(name, conf, false)); + } + return results; +}; +this.init(); +return this; +}).call({}, edide); +"use strict";edide.inEditor = (function _inEditor (edide) { +return typeof window === 'object' && + typeof window.serverModule === 'object' +return this; +}).call({}, edide); +"use strict";edide.edideNamespace = (function _edideNamespace (edide) { +return 'edide' +return this; +}).call({}, edide); +"use strict";edide.showError = (function _showError (edide) { +var showError; +showError = (err) => { + if (edide.inEditor) { + return edide.editorModule.editor_error.show(err); + } else if (typeof edide.global.require === 'object' && (edide.global[edide.edideNamespace].prodErrorPrinter != null)) { + return edide.global[edide.edideNamespace].prodErrorPrinter.showError(err); + } else { + return console.error(err); + } +}; +return (err) => { + var error; + if (err != null ? err.stack : void 0) { + return showError(err); // create error to capture stack trace + } else { + try { + throw Error(err); + } catch (error1) { + error = error1; + return showError(error); + } + } +}; +return this; +}).call({}, edide); +"use strict";edide.mmParserSpecial = (function _mmParserSpecial (edide) {Object.defineProperty(this, 'module_name', {value:'mmParserSpecial'}); +this.config = (trackStr, ts = {}) => { + var endInd, err, name; + endInd = trackStr.search('}') || trackStr.length; + try { + ts.conf = JSON.parse(trackStr.slice(0, +endInd + 1 || 9e9)); + if (ts.conf.name != null) { + ({name} = ts.conf); + delete ts.conf.name; + edide.mmConfigs.addVar(name, ts.conf); + } else { + edide.mmConfigs.activate(ts.conf); // add variable if exists + } + } catch (error) { + err = error; + ts.skip = true; // could be used to show error highlighting in editor + edide.showError(err); // remove this if error highlighting implemented + } + return endInd; +}; +this.var = (track, trackState) => { + var i, value, varLength, varName, varStr; + ({i} = trackState); + varLength = track.slice(i + 1).indexOf(edide.mmKeyboard.special.var); + if (varLength === -1) { + return false; + } + varStr = track.slice(i + 1, +(i + varLength) + 1 || 9e9); + [varName, value] = varStr.split(' '); + if (!edide.mmConfigs.hasVar(varName)) { + return false; + } + edide.mmConfigs.activateVar(varName, value); + trackState.i += varLength + 2; + return true; +}; +return this; +}).call({}, edide); +"use strict";edide.onUnload = (function _onUnload (edide) { +var ref, ref1; +return (ref = (ref1 = edide.editorModule) != null ? ref1.unload.add : void 0) != null ? ref : () => {}; +return this; +}).call({}, edide); +"use strict";edide.mmPipe = (function _mmPipe (edide) {Object.defineProperty(this, 'module_name', {value:'mmPipe'}); +var dist, lowpass, react, revar, reverber, unvar; +({revar, unvar, react} = edide.mmState.init()); +dist = lowpass = reverber = null; +this.initPipe = () => { + lowpass = new Tone.Filter(unvar.lowpass, 'lowpass', -12); + dist = new Tone.Distortion(unvar.distortion); + this.output = lowpass; + lowpass.toMaster(); + dist.connect(lowpass); + revar.pipeReady = true; + return edide.onUnload(() => { + if (dist != null) { + if (typeof dist.dipose === "function") { + dist.dipose(); + } + } + return lowpass != null ? typeof lowpass.dispose === "function" ? lowpass.dispose() : void 0 : void 0; + }); +}; +this.initInstrument = (instrument) => { + instrument.connect(dist); + edide.onUnload(() => { + return instrument != null ? instrument.disconnect() : void 0; + }); + return instrument; +}; +react('pipe distortion', () => { + var ref; + revar.distortion; + revar.lowpass; + if (dist != null) { + dist.distortion = revar.distortion; + } + return lowpass != null ? (ref = lowpass.frequency) != null ? ref.linearRampTo(revar.lowpass, 0) : void 0 : void 0; +}); +return this; +}).call({}, edide); +"use strict";edide.moduleGate = (function _moduleGate (edide) {Object.defineProperty(this, 'module_name', {value:'moduleGate'}); +if (!edide.inEditor) { + return this; +} +this.all = typeof serverModule !== "undefined" && serverModule !== null ? serverModule.modules.all : void 0; // why not just use \:modules.all ? +this.root; // module that is edited in root +this.rootName; +this.active; // module that is currently being edited (can be root or in window) +this.activeName; +this.executing; +this.executingName; +return this; +}).call({}, edide); +"use strict";edide.rejectIfRecompiled = (function _rejectIfRecompiled (edide) { +if (!edide.inEditor) { + return; +} +return (promise) => { + var recompiled, rootModuleFunc, rootName; + ({rootName} = edide.moduleGate); + recompiled = false; + edide.editorModule.editor_events.on('before_recompile', () => { + return recompiled = true; + }); + rootModuleFunc = edide.editorModule.modules.moduleFunc[rootName]; + return promise.then(function(arg) { + if (recompiled || rootName !== edide.moduleGate.rootName) { // root module changed + return Promise.reject(); // quiet rejection; no need to show error + } else { + return arg; // arg get wrapped in promise + } + }); +}; +return this; +}).call({}, edide); +"use strict";edide.promise = (function _promise (edide) {Object.defineProperty(this, 'module_name', {value:'promise'}); +this.new = function(cb) { + if (edide.inEditor) { + return edide.rejectIfRecompiled(new Promise(cb)); // don't fire cb if code has been re-executed in the meantime + } else { + return new Promise(cb); //, edide.editorModule?.editor_error.show + } +}; +this.all = function(cbArray) { + if (edide.inEditor) { // check that after resolved, still editing same module + return edide.rejectIfRecompiled(Promise.all(cbArray)); + } else { + return Promise.all(cbArray); + } +}; +this.resolve = Promise.resolve.bind(Promise); +this.reject = Promise.reject.bind(Promise); +return this; +}).call({}, edide); +"use strict";edide.qs = (function _qs (edide) { +return (selector) => { + return document.querySelector(selector); +}; +return this; +}).call({}, edide); +"use strict";edide.scriptContainer = (function _scriptContainer (edide) { +var createContainer, ref; +createContainer = () => { + var s; + s = document.createElement('div'); + s.id = 'scripts'; + return s; +}; +return (ref = edide.qs('#scripts')) != null ? ref : document.body.appendChild(createContainer()); +return this; +}).call({}, edide); +"use strict";edide.requireScript = (function _requireScript (edide) { +var base; +if ((base = edide.global).requireScriptPromises == null) { + base.requireScriptPromises = new Map; +} +return (scriptSrc) => { + var promise; + if (promise = requireScriptPromises.get(scriptSrc)) { + return promise; + } else { + requireScriptPromises.set(scriptSrc, promise = edide.promise.new((resolve) => { + var el; + console.log('adding promised', scriptSrc); + el = document.createElement('script'); + edide.scriptContainer.appendChild(el); + el.onload = resolve; //load_ready + el.type = 'application/javascript'; + return el.src = scriptSrc; + })); + return promise.catch((err) => { + edide.showError(err); + return requireScriptPromises.delete(scriptSrc); + }); + } +}; +return this; +}).call({}, edide); +"use strict";edide.set = (function _set (edide) { +return (...args) => new Set(args) +return this; +}).call({}, edide); +"use strict";edide.membrameSynth = (function _membrameSynth (edide) {Object.defineProperty(this, 'module_name', {value:'membrameSynth'}); +this.startNote = 'A0'; +this.init = () => { + var bd, compressor, distortion, gain, reverb; + distortion = new Tone.Distortion({ + distortion: 0.1, + oversample: "4x" // none, 2x, 4x + }); + reverb = new Tone.Freeverb(0.75, 1000); + gain = new Tone.Gain(0.5); + compressor = new Tone.Compressor({ + ratio: 12, + threshold: -24, + release: 0.05, + attack: 0.003, + knee: 1 + }); + bd = new Tone.MembraneSynth({ + pitchDecay: 0.05, + octaves: 4, + envelope: { + attack: 0.01, + decay: 0.74, + sustain: 0.71, + release: 0.05, + attackCurve: "exponential" + } + }); + bd.chain(gain, distortion, reverb, compressor); + return [bd, compressor]; +}; +return this; +}).call({}, edide); +"use strict";edide.toneSynth = (function _toneSynth (edide) {Object.defineProperty(this, 'module_name', {value:'toneSynth'}); +this.startNote = 'C3'; +this.init = () => { + var ss; + return ss = new Tone.PolySynth(12, Tone.Synth, { + oscillator: { + type: 'sine' + }, + envelope: { + attack: 0.005, + decay: 0.1, + sustain: 0.3, + release: 1 + } + }); +}; +return this; +}).call({}, edide); +"use strict";edide.instrumentConfigs = (function _instrumentConfigs (edide) {Object.defineProperty(this, 'module_name', {value:'instrumentConfigs'}); +var defaultUrl = 'https://nbrosowsky.github.io/tonejs-instruments/samples/' +var { revar, unvar, react } = edide.mmState +var defaultInstrument = 'electric-guitar' +this.initInstrument = async (resolve) => { + await edide.requireScript('https://cdnjs.cloudflare.com/ajax/libs/tone/13.8.9/Tone.js') + edide.mmPipe.initPipe() +} +this.createNew = () => { + var {name} = unvar.instrumentConf + var instrument, endOfPipe + var instrumentConf = this.instruments[name] + if (instrumentConf.module_name) { + var res = instrumentConf.init() + if (Array.isArray(res)) { + instrument = res[0] + endOfPipe = res[1] + } else { + instrument = endOfPipe = res + } + } else { + var noteFiles = buildNotes(instrumentConf) + var inst = new Tone.Sampler(noteFiles, { //edide.instrumentConfigssAll[name] + "release" : 1, + "baseUrl" : instrumentConf.url || defaultUrl + name + '/' + }) + inst.soundFontInstrument = true + instrument = endOfPipe = inst //[inst, inst] + } + edide.mmPipe.initInstrument(endOfPipe) + return instrument +} +this.isReady = () => { + var { current } = this + return current && (typeof current.loaded == 'undefined' || current.loaded === true) +} +function buildNotes({startNote, notes, step, skipNotes}) { + var [startInd] = edide.noteFreq.findNote(startNote) + var noteFiles = {} + if (Array.isArray(notes)) { + notes.forEach(note => { + noteFiles[note] = note.replace('#','s') + '.[mp3|ogg]' + }) + } else { + for (let i=0; i < notes*step; i+=step) { + let note = edide.noteFreq.notes[startInd + i][0]; + if (skipNotes && skipNotes.has(note)) + continue + noteFiles[note] = note.replace('#','s') + '.[mp3|ogg]' + } + } + return noteFiles +} +this.instruments = { + 'bass-electric': { + startNote: 'C#2', + notes: 8, // 16 + step: 3 + }, + 'cello': { + startNote: 'C2', + notes: 11, + step: 2, + skipNotes: edide.set('F#2', 'C4') + }, + 'drum-electric': edide.membrameSynth, + 'guitar-acoustic': { + startNote: 'D1', + notes: 26, // 36 + step: 2, + skipNotes: edide.set('E4', 'F#4', 'G#4', 'A#4', 'C5', 'D5', 'E5') + }, // umm, would be simpler to just add all notes + 'guitar-electric': { + startNote: 'F#2', + notes: 15, // 15 + step: 3, + }, + 'piano': { + startNote: 'A1', + notes: 30, // 29 + step: 2, + volume: -6, // TODO implement this + baseUrl: "https://tonejs.github.io/examples/audio/salamander/" + }, + 'synth-simple': edide.toneSynth, + 'xylophone': { + startNote: 'G3', + notes: ['G3', 'C4', 'G4', 'C5', 'G5', 'C6', 'G6', 'C7'] + } +} +for (let inst in this.instruments) { + this.instruments[inst].name = inst +} +this.instrumentList = Object.values(this.instruments) +react('active instrument config', () => { + revar.instrumentConf = this.instruments[revar.instrument] // this.instrumentList[revar.instrumentInd] +}) +return this; +}).call({}, edide); +"use strict";edide.cloneSibling = (function _cloneSibling (edide) { +return (srcObj) => { + var o; + o = Object.create(Object.getPrototypeOf(srcObj)); + Object.assign(o, srcObj); + return o; +}; +return this; +}).call({}, edide); +"use strict";edide.times = (function _times (edide) { +return (timesNum, action) => { + while (timesNum-- > 0) { + action(timesNum + 1); + } +}; +return this; +}).call({}, edide); +"use strict";edide.setTimeout = (function _setTimeout (edide) { +return function(arg1, arg2) { + var fun, id, num; + [fun, num] = typeof arg2 === 'function' ? [arg2, arg1] : [arg1, arg2]; + id = setTimeout(fun, num); + edide.onUnload(() => { + return clearTimeout(id); + }); + return id; +}; +return this; +}).call({}, edide); +"use strict";edide.sleep = (function _sleep (edide) { +var sleep; +sleep = (ms) => { + var resolve, timeout; + timeout = resolve = null; + return edide.promise.new((res) => { + resolve = res; + return timeout = edide.setTimeout(ms, res); + }); +}; +return sleep; +return this; +}).call({}, edide); +"use strict";edide.mmInstrument = (function _mmInstrument (edide) {Object.defineProperty(this, 'module_name', {value:'mmInstrument'}); +var cloneOrCreateInstrument, getInstruments, instruments, play, react, revar, unvar, updateVolAndTune; +instruments = {}; // name: [instrument instances...] +({unvar, revar, react} = edide.mmState); +react('delay from bmp', () => { + return revar.beatDelay = (1 / revar.bpm) * 60 * 1000; +}); +react(() => {}); +updateVolAndTune = (inst) => { + var detune, volume; + ({volume, detune} = unvar); + if ((detune != null) && (inst.detune != null)) { + inst.set("detune", detune); + } + if ((volume != null) && (inst.volume != null)) { + return inst.set("volume", volume); + } +}; +react(() => { + var inst, insts, j, len, name; + revar.volume; + revar.detune; + for (name in instruments) { + insts = instruments[name]; + for (j = 0, len = insts.length; j < len; j++) { + inst = insts[j]; + updateVolAndTune(inst); + } + } +}); +react('create first instrument', () => { + var name, ref; + if (!(name = (ref = revar.instrumentConf) != null ? ref.name : void 0)) { // e.g. illegal instrument name + return edide.showError(`Unknown instrument: ${unvar.instrument}`); + } + if (revar.pipeReady && !instruments[name]) { + 'create initial instrument'; + return instruments[name] = [edide.instrumentConfigs.createNew()]; + } +}); +cloneOrCreateInstrument = (name) => { + var inst; + inst = instruments[name][0].soundFontInstrument ? (inst = edide.cloneSibling(instruments[name][0]), inst.isPlaying = false, inst) : edide.instrumentConfigs.createNew(); + updateVolAndTune(inst); + return inst; +}; +getInstruments = (n) => { + var all, free, name; + ({name} = unvar.instrumentConf); + all = instruments[name]; + free = all.filter((i) => { + return !i.isPlaying; + }); + edide.times(n - free.length, () => { + var inst; + free.push(inst = cloneOrCreateInstrument(name)); + return all.push(inst); + }); + return free.slice(0, n); +}; +this.playChord = (chord, noteLength = 1) => {}; +this.playNote = (chord, noteLength = 1) => { + var ind, inst, insts, j, len, ref; + if (!Array.isArray(chord)) { + chord = [chord]; + } + ref = insts = getInstruments(chord.length); + for (ind = j = 0, len = ref.length; j < len; ind = ++j) { + inst = ref[ind]; + play(inst, chord[ind], noteLength); + } +}; +play = async(instrument, note, length) => { + var err; + instrument.isPlaying = note; // true + try { + instrument.triggerAttackRelease(note, (length * unvar.beatDelay) / 1000); + } catch (error) { + err = error; + err.message = `Error in playing note ${note}, '${unvar.instrumentConf.name}' probably not loaded yet`; + edide.showError(err); + } + revar.note = note; + await edide.sleep(unvar.nextDelay); + return instrument.isPlaying = false; +}; +return this; +}).call({}, edide); +"use strict";edide.mmParser = (function _mmParser (edide) {Object.defineProperty(this, 'module_name', {value:'mmParser'}); +var getNoteLength, processTrack, react, revar, unvar; +({revar, unvar, react} = edide.mmState); +getNoteLength = (row, noteInd) => { + var char, length; + length = 1; + while (char = row[++noteInd]) { + if (char === edide.mmKeyboard.special.long) { + length++; + } else { + break; + } + } + return length; +}; +processTrack = (track, ccs, ts) => { + var chord, keyCode, note; + if (!track[ts.i]) { + return; + } + if (ts.skip) { // comment or erroneous chars + return; + } + switch (track[ts.i]) { + case '{': + return ts.i += edide.mmParserSpecial.config(track.slice(ts.i), ts); // parseConfigs + case '[': + ccs.chord = []; + ts.i++; + return processTrack(track, ccs, ts); + case ']': + ({chord} = ccs); + ccs.chord = null; + ts.i++; + if (chord.length) { + edide.mmInstrument.playNote(chord, getNoteLength(track, ts.i)); + ccs.played = true; + } else { + processTrack(track, ccs, ts); + } + return; + case edide.mmKeyboard.special.var: + if (edide.mmParserSpecial.var(track, ts)) { + processTrack(track, ccs, ts); // recursive, since next char could be another var, e.g. \:bass::minor: + } else { + ts.skip = true; // parse error + } + return; + case edide.mmKeyboard.special.comment: + return ts.skip = true; + } + if (!(keyCode = track.charCodeAt(ts.i))) { + return; + } + ts.i++; + if (note = edide.mmNote.fromKeyCode(keyCode)) { + if (ccs.chord) { + ccs.chord.push(note); + processTrack(track, ccs, ts); + } else { + edide.mmInstrument.playNote(note, getNoteLength(track, ts.i)); + ccs.played = true; + } + } else if (edide.mmKeyboard.isPauseKey(keyCode)) { + ccs.played = true; // "play silence" + } else { + processTrack(track, ccs, ts); + } +}; +this.splitSections = (str) => { + var i, j, prevInd, ref, row, section, sections; + str = str.replace('&\n', ''); // TODO fix row tracking with &\n + sections = []; + section = null; + row = 0; + prevInd = 0; + for (i = j = 0, ref = str.length; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) { + if (!(str[i] === '\n' || i === str.length)) { + continue; + } + if (str[i - 1] === '\n') { + if (section != null) { + sections.push(section); + } + section = null; + } + if (i > prevInd) { + if (section == null) { + section = { + row: row, + tracks: [] + }; + } + section.tracks.push(str.slice(prevInd, i)); + } + row++; + prevInd = i + 1; + } + if (section != null) { + sections.push(section); + } + sections.row = 0; + return sections; +}; +this.play = (song, sectionInd, trackStates) => { + var ccs, section; + if (!unvar.playing) { + return; + } + revar.highlight = null; + sectionInd = sectionInd || 0; + if (typeof song === 'string') { + song = this.splitSections(song); + } + section = song[sectionInd]; + if (!section) { // song finished + return revar.playing = false; + } + if (!Array.isArray(trackStates)) { + trackStates = section.tracks.map(() => { + return { + i: 0 // initi track indices on first call to section + }; + }); + } + ccs = {}; // chord, played # TODO change back to chord: [C3, D3] and [].played = true + section.tracks.forEach((track, tInd) => { + var conf, i, iStart; + if (conf = trackStates[tInd].conf) { + return edide.mmConfigs.activate(conf); + } else { + trackStates[tInd].iStart = trackStates[tInd].i; + processTrack(track, ccs, trackStates[tInd]); + ({iStart, i} = trackStates[tInd]); + if (i > iStart) { + return revar.highlight = [song.row + section.row + tInd, iStart, i - 1]; + } + } + }); + if (ccs.played) { + edide.setTimeout(() => { + return this.play(song, sectionInd, trackStates); + }, unvar.beatDelay); + } else { + edide.setTimeout(() => { + return this.play(song, sectionInd + 1); + }); + } +}; +return this; +}).call({}, edide); +"use strict";edide.music = (function _music (edide) {Object.defineProperty(this, 'module_name', {value:'music'}); +edide.global.music = this +edide.mmEffects +var { revar, unvar, react } = edide.mmState +var playQueue = [] +this.play = (str) => { + if (!unvar.pipeReady) return playQueue.push(str) + revar.playing = true + edide.mmParser.play(str) +} +this.stop = () => revar.playing = false +edide.instrumentConfigs.initInstrument() +var initialSettings = { + "instrument": "synth-simple", //"piano" +} +react(() => { + if(!revar.pipeReady) return + edide.mmConfigs.activate(initialSettings) + this.play(playQueue.join("\n\n")) // JSON.stringify(initialSettings) +}) // TEMP init instrument +return this; +}).call({}, edide); +"use strict";edide.wave = (function _wave (edide) { +var parent = { + play: function(){ + edide.music.play(this.sheet) + return this + }, + stop: function(){ + edide.music.stop(this.sheet) + return this + }, + blur: function(val){ + edide.mmConfigs.activate({'blur': val}) + return this + }, + itch: function(val){ + edide.mmConfigs.activate({'itch': val}) + return this + }, + long: function(val){ // note delay in seconds + edide.mmConfigs.activate({'beatDelay': val*1000}) + return this + }, + pace: function(val) { // note delay in beats per minute + edide.mmConfigs.activate({'bpm': val}) + return this + }, + vary: function(val){ + edide.mmConfigs.activate({'detune': val}) + return this + }, + loud: function(val){ // increase (>0) or decrease (<0) amplitude in decibels + edide.mmConfigs.activate({'volume': val}) + return this + }, +} +return edide.global.wave = (str) => { + var obj = Object.create(parent) + obj.sheet = str + return obj +} +return this; +}).call({}, edide); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b3c251cc..aff7ee5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.2019.915", + "version": "0.2019.1211", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -40,7 +40,8 @@ "@types/node": { "version": "10.14.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.18.tgz", - "integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==" + "integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==", + "optional": true }, "@unimodules/core": { "version": "3.0.2", @@ -120,6 +121,7 @@ "version": "2.0.26", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz", "integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==", + "optional": true, "requires": { "pvutils": "^1.0.17" } @@ -1347,6 +1349,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.6.tgz", "integrity": "sha512-0yNrOdJyLE7FZzmeEHTKanwBr5XbmDAd020cKa4ZiTYuGMBYBZmq7vHOhcOqhVllh6gghDBbaz1lnVdOqiB7cw==", + "optional": true, "requires": { "@types/node": "^10.14.17", "tslib": "^1.10.0" @@ -1355,7 +1358,8 @@ "pvutils": { "version": "1.0.17", "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz", - "integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==" + "integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==", + "optional": true }, "querystring": { "version": "0.2.0", @@ -1664,7 +1668,8 @@ "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "optional": true }, "uglify-js": { "version": "3.6.0", diff --git a/package.json b/package.json index 1719dc64..3a0755f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.2019.1120", + "version": "0.2019.1228", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "browser.js", diff --git a/sea.js b/sea.js index 03fe9f34..f6a4b45a 100644 --- a/sea.js +++ b/sea.js @@ -191,8 +191,8 @@ const isocrypto = require('isomorphic-webcrypto'); api.ossl = api.subtle = isocrypto.subtle; }catch(e){ - console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!"); - OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED; + console.log("text-encoding and @peculiar/webcrypto may not be included by default, please add it to your package.json!"); + TEXT_ENCODING_OR_PECULIAR_WEBCRYPTO_NOT_INSTALLED; }} module.exports = api diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index 0c4ca13e..9e52f427 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -19,14 +19,14 @@ function Mesh(root){ if('[' === tmp){ try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} if(!msg){ return } - LOG && opt.log(msg.length, 'in hear batch'); + LOG && opt.log(+new Date, msg.length, 'in hear batch'); (function go(){ var S; LOG && (S = +new Date); // STATS! var m, c = 100; // hardcoded for now? while(c-- && (m = msg.shift())){ mesh.hear(m, peer); } - LOG && opt.log(+new Date - S, 'batch heard'); + LOG && opt.log(S, +new Date - S, 'batch heard'); if(!msg.length){ return } puff(go, 0); }()); @@ -55,7 +55,7 @@ function Mesh(root){ } var S; LOG && (S = +new Date); root.on('in', msg); - LOG && !msg.nts && opt.log(+new Date - S, 'msg', msg['#']); + LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']); return; } } @@ -83,7 +83,7 @@ function Mesh(root){ } } } - LOG && opt.log(+new Date - S, '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) } @@ -91,7 +91,7 @@ function Mesh(root){ if(!Type.obj.is(peer || opt.peers)){ return false } var S; LOG && (S = +new Date); Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. - LOG && opt.log(+new Date - S, 'say loop'); + LOG && opt.log(S, +new Date - S, 'say loop'); return; } if(!peer.wire && mesh.wire){ mesh.wire(peer) } @@ -118,7 +118,7 @@ function Mesh(root){ 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)} - LOG && opt.log(+new Date - S, 'say stringify', tmp.length); + LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length); if(!tmp){ return } send(tmp, peer); } @@ -135,7 +135,7 @@ function Mesh(root){ if(wire.send){ wire.send(raw); } - LOG && opt.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); @@ -171,7 +171,8 @@ function Mesh(root){ opt.peers[peer.url || peer.id] = peer; } else { tmp = peer.id = peer.id || Type.text.random(9); - mesh.say({dam: '?'}, opt.peers[tmp] = peer); + mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer); + delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self } peer.met = peer.met || +(new Date); if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) } @@ -189,18 +190,12 @@ function Mesh(root){ } mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['?'] = function(msg, peer){ - if(!msg.pid){ - mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); - // @rogowski I want to re-enable this AXE logic with some fix/merge later. - /* var tmp = peer.queue; peer.queue = []; - Type.obj.map(tmp, function(msg){ - mesh.say(msg, peer); - }); */ - // @rogowski 2: I think with my PID fix we can delete this and use the original. - return; + if(msg.pid){ + if(!peer.pid){ peer.pid = msg.pid } + if(msg['@']){ return } } - if(peer.pid){ return } - peer.pid = msg.pid; + mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); + delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self } root.on('create', function(root){ diff --git a/src/chain.js b/src/chain.js index 68be3de3..834f13c6 100644 --- a/src/chain.js +++ b/src/chain.js @@ -34,7 +34,7 @@ function output(msg){ if(obj_has(back, 'put')){ back.on('in', back); } - if(tmp){ return } + if(tmp && u !== back.put){ return } //if(tmp){ return } msg.$ = back.$; } else if(obj_has(back.put, get)){ // TODO: support #LEX ! @@ -247,7 +247,7 @@ function not(at, msg){ if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ - neat.ack = -1; // TODO: BUG? Should this be 0? + neat.ack = -1; // Shouldn't this be reset to 0? If we do that, SEA test `set user ref should be found` fails, odd. } neat.on('in', { get: key, diff --git a/test/common.js b/test/common.js index 8bf55968..91f6615b 100644 --- a/test/common.js +++ b/test/common.js @@ -3774,6 +3774,24 @@ describe('Gun', function(){ done(); }) }); + + /*describe('talk to live server tests', function(){ + this.timeout(1000 * 9); + it.only('Second once on undefined should call', function(done){ // this test is passing when it fails by hand? + var gun = Gun('https://gunjs.herokuapp.com/gun'); + gun.get('~@O8H2BJa4pNfecWamWN7efd888Pg1@hackernoon').once(function(data){ + console.log(1, data); + expect(data).to.not.be.ok(); + setTimeout(function(){ + gun.get('~@O8H2BJa4pNfecWamWN7efd888Pg1@hackernoon').once(function(data){ + console.log(2, data); + expect(data).to.not.be.ok(); + done(); + }); + }, 3000); + }); + }); + });*/ return; it('Nested listener should be called', function(done){ diff --git a/test/panic/4dht.js b/test/panic/4dht.js index 996ec04c..8ba31eab 100644 --- a/test/panic/4dht.js +++ b/test/panic/4dht.js @@ -2,13 +2,51 @@ 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. + +Alice: [Bob] +Bob: [Carl] +Carl: [Bob] +Dave: [Carl] + +Ed: [?] + +100,000 browsers +1 relay peer + +50,000 browsers on Bob +50,000 browsers on Carl + +//var gun = Gun(['https://gunjs.herokuapp.com/gun', 'https://guntest.herokuapp.com/gun']); + +pretend we have 3TB of data. +300K browsers. + +suppose we have 3 nodejs peers that are shards + +var superpeer1 = Gun(AXE({shard: 'a~m'})); +var superpeer2 = Gun(AXE({shard: 'n~r'})); +var superpeer3 = Gun(AXE({shard: 's~z'})); + +300K browsers join a popular app and they have to do this +via the browser, so they all go to superpeer1.com +then 2/3 of them should get sharded to superpeer2 & superpeer3 + +first all connect to: +..s1-----s2--s3 +./\.\. +b1.b2.b3 + +then be load balanced to: +..s1--s2--s3 +./....|....\. +b1....b2....b3 */ var config = { IP: require('ip').address(), port: 8765, - servers: 2, - browsers: 2, + servers: 3, + browsers: 3, route: { '/': __dirname + '/index.html', '/gun.js': __dirname + '/../../gun.js', @@ -35,11 +73,15 @@ manager.start({ }); var servers = clients.filter('Node.js'); -var bob = servers.pluck(1); -var carl = servers.excluding(bob).pluck(1); +var s1 = servers.pluck(1); +var s2 = servers.excluding(s1).pluck(1); +var s3 = servers.excluding([s1,s2]).pluck(1); + var browsers = clients.excluding(servers); -var alice = browsers.pluck(1); -var dave = browsers.excluding(alice).pluck(1); +var b1 = browsers.pluck(1); +var b2 = servers.excluding(b1).pluck(1); +var b3 = servers.excluding([b1,b2]).pluck(1); + describe("Put ACK", function(){ //this.timeout(5 * 60 * 1000); @@ -65,12 +107,13 @@ describe("Put ACK", function(){ var peers = [], i = env.config.servers; while(i--){ var tmp = (env.config.port + (i + 1)); - if(port != tmp){ // ignore ourselves + //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}); + var gun = Gun({file: env.i+'data', peers: peers, web: server, mob: 4}); + global.gun = gun; server.listen(port, function(){ test.done(); }); @@ -85,13 +128,29 @@ describe("Put ACK", function(){ }); it("Browsers initialized gun!", function(){ - var tests = [], i = 1; - browsers.each(function(client, id){ - tests.push(client.run(function(test){ + var tests = [], i = 0; + browsers.each(function(browser, id){ + tests.push(browser.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'); + + var mesh = gun.back('opt.mesh'); + mesh.hear['mob'] = function(msg, peer){ + // TODO: NOTE, code AXE DHT to aggressively drop new peers AFTER superpeer sends this rebalance/disconnect message that contains some other superpeers. + if(!msg.peers){ return } + var one = msg.peers[Math.floor(Math.random()*msg.peers.length)]; + console.log("CONNECT TO THIS PEER:", one); + gun.opt(one); + mesh.bye(peer); + + if(test.c){ return } + test.c = 1; + test.done(); + } + + console.log("connected to who superpeer(s)?", gun._.opt.peers); window.gun = gun; window.ref = gun.get('test'); }, {i: i += 1, config: config})); @@ -99,20 +158,16 @@ describe("Put ACK", function(){ 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("Got Load Balanced to Different Peer", function(){ + var tests = [], i = 0; + browsers.each(function(browser, id){ + tests.push(browser.run(function(test){ - it("Dave", function(){ - return dave.run(function(test){ - console.log("I AM DAVE"); - //test.async(); - console.log(gun._.opt.peers); + console.log("...", gun._.opt.peers); + + }, {i: i += 1, config: config})); }); + return Promise.all(tests); }); it("All finished!", function(done){ diff --git a/test/panic/bulkimport.js b/test/panic/bulkimport.js new file mode 100644 index 00000000..734dd901 --- /dev/null +++ b/test/panic/bulkimport.js @@ -0,0 +1,137 @@ +/* +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, + puts: 1000, + 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 browsers = clients.excluding(servers); +var alice = browsers.pluck(1); +var carl = 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 = 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.ref = gun.get('test'); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + it("Puts", function(){ + return alice.run(function(test){ + console.log("I AM ALICE"); + test.async(); + var i = test.props.puts, d = 0; + while(i--){ go(i) } + function go(i){ + ref.get(i).put({hello: 'world'}, function(ack){ + if(ack.err){ put_failed } + if(++d !== test.props.puts){ return } + console.log("all success", d); + test.done(); + }); + } + }, {puts: config.puts}); + }); + + 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(); + }); + }); +}); \ No newline at end of file