;(function(){
	function Gun(opt){
		var gun = this;
		if(!Gun.is(gun)){ // if this is not a GUN instance,
			return new Gun(opt); // then make it so.
		}
		gun.opt(opt);
	}
	Gun._ = { // some reserved key words, these are not the only ones.
		soul: '#'
		,meta: '_'
		,HAM: '>'
	}
	;(function(Gun){ // GUN specific utilities
		Util(Gun); // initialize standard utilities
		Gun.version = 0.2; // TODO: When Mark (or somebody) does a push/publish, dynamically update package.json
		Gun.is = function(gun){ return (gun instanceof Gun)? true : false }
		Gun.is.value = function(v){ // null, binary, number (!Infinity), text, or a rel (soul).
			if(v === null){ return true } // deletes
			if(v === Infinity){ return false } // we want this to be, but JSON does not support it, sad face.
			if(Gun.bi.is(v)
			|| Gun.num.is(v)
			|| Gun.text.is(v)){
				return true; // simple values
			}
			var id;
			if(id = Gun.is.soul(v)){
				return id;
			}
			return false;
		}
		Gun.is.value.as = function(v){
			return Gun.is.value(v)? v : null;
		}
		Gun.is.soul = function(v){
			if(Gun.obj.is(v)){
				var id;
				Gun.obj.map(v, function(soul, field){
					if(id){ return id = false } // if ID is already defined AND we're still looping through the object, it is invalid.
					if(field == Gun._.soul && Gun.text.is(soul)){
						id = soul; // we found the soul!
					} else {
						return id = false; // if there exists anything else on the object, that isn't the soul, then it is invalid.
					}
				});
				if(id){
					return id;
				}
			}
			return false;
		}
		Gun.is.soul.on = function(n){ return (n && n._ && n._[Gun._.soul]) || false }
		Gun.is.node = function(node, cb){
			var soul;
			if(!Gun.obj.is(node)){ return false }
			if(soul = Gun.is.soul.on(node)){
				return !Gun.obj.map(node, function(value, field){ // need to invert this, because the way we check for this is via a negation.
					if(field == Gun._.meta){ return } // skip this.
					if(!Gun.is.value(value)){ return true } // it is true that this is an invalid node.
					if(cb){ cb(value, field, node._, soul) }
				});
			}
			return false;
		}
		Gun.is.graph = function(graph, cb, fn){
			var exist = false;
			if(!Gun.obj.is(graph)){ return false }
			return !Gun.obj.map(graph, function(node, soul){ // need to invert this, because the way we check for this is via a negation.
				if(!node || soul !== Gun.is.soul.on(node) || !Gun.is.node(node, fn)){ return true } // it is true that this is an invalid graph.				 
				(cb || function(){})(node, soul, function(fn){
					if(fn){ Gun.is.node(node, fn) }
				});
				exist = true;
			}) && exist;
		}
		// Gun.ify // the serializer is too long for right here, it has been relocated towards the bottom.
		Gun.union = function(gun, prime, cb){
			var ctx = {count: 0, cb: function(){ cb = cb? cb() && null : null }};
			ctx.graph = gun.__.graph;
			if(!ctx.graph){ ctx.err = {err: Gun.log("No graph!") } }
			if(!prime){ ctx.err = {err: Gun.log("No data to merge!") } }
			if(ctx.soul = Gun.is.soul.on(prime)){
				ctx.tmp = {};
				ctx.tmp[ctx.soul] = prime;
				prime = ctx.tmp;
			}
			Gun.is.graph(prime, null, function(val, field, meta){
				if(!meta || !(meta = meta[Gun._.HAM]) || !Gun.num.is(meta[field])){ 
					return ctx.err = {err: Gun.log("No state on " + field + "!") } 
				}
			});
			if(ctx.err){ return ctx }
			(function union(graph, prime){
				ctx.count += 1;
				Gun.obj.map(prime, function(node, soul){
					soul = Gun.is.soul.on(node);
					if(!soul){ return }
					ctx.count += 1;
					var vertex = graph[soul];
					if(!vertex){ // disjoint // TODO: Maybe not correct? BUG, probably.
						Gun.on('union').emit(gun, graph[soul] = node);
						gun.__.on(soul).emit(node);
						ctx.count -= 1;
						return;
					}
					Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
						if(!vertex){ return }
						var change = {};
						change._ = change._ || {};
						change._[Gun._.soul] = Gun.is.soul.on(vertex);
						if(field){
							change._[Gun._.HAM] = change._[Gun._.HAM] || {};
							vertex[field] = change[field] = value;
							(vertex._[Gun._.HAM] = vertex._[Gun._.HAM] || {})[field] = change._[Gun._.HAM][field] = node._[Gun._.HAM][field];
						}
						//context.nodes[change._[Gun._.soul]] = change;
						//context('change').fire(change);
						Gun.on('union').emit(gun, change);
						gun.__.on(Gun.is.soul.on(change)).emit(change);					
					}, function(){})(function(){
						if(!(ctx.count -= 1)){ ctx.cb() }
					});
				});
				ctx.count -= 1;
			})(ctx.graph, prime);
			if(!ctx.count){ ctx.cb() }
			return ctx;
		}
		Gun.union.pseudo = function(soul, graph, vertex){
			var c = 0, s;
			((vertex = vertex || {})._ = {})[Gun._.soul] = soul;
			Gun.is.graph(graph, function(node, ss){
				c += 1; s = ss;
				Gun.HAM(vertex, node, function(){}, function(vertex, field, value){
					(vertex._[Gun._.HAM] = vertex._[Gun._.HAM] || {})[field] = node._[Gun._.HAM][field];
					vertex[field] = value;
				}, function(){});
			});
			if(1 == c){ return }
			return vertex;
		}
		Gun.HAM = function(vertex, delta, lower, now, upper){
			upper.max = -Infinity;
			now.end = true;
			Gun.obj.map(delta, function update(incoming, field){
				if(field === Gun._.meta){ return }
				now.end = false;
				var ctx = {incoming: {}, current: {}}, state;
				ctx.drift = (ctx.drift = Gun.time.is()) > (Gun.time.now.last || -Infinity)? ctx.drift : Gun.time.now.last;
				ctx.incoming.value = Gun.is.soul(incoming) || incoming;
				ctx.current.value = Gun.is.soul(vertex[field]) || vertex[field];
				ctx.incoming.state = Gun.num.is(ctx.tmp = ((delta._||{})[Gun._.HAM]||{})[field])? ctx.tmp : -Infinity;
				ctx.current.state = Gun.num.is(ctx.tmp = ((vertex._||{})[Gun._.HAM]||{})[field])? ctx.tmp : -Infinity;
				upper.max = ctx.incoming.state > upper.max? ctx.incoming.state : upper.max;
				state = HAM(ctx.drift, ctx.incoming.state, ctx.current.state, ctx.incoming.value, ctx.current.value);
				if(state.err){
					root.console.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err); // this error should never happen.
					return;
				}
				if(state.state || state.quarantineState || state.current){
					lower.call(state, vertex, field, incoming);
					return;
				}
				if(state.incoming){
					now.call(state, vertex, field, incoming);
					return;
				}
				if(state.amnesiaQuarantine){
					upper.wait = true;
					upper.call(state, vertex, field, incoming); // signals that there are still future modifications.
					Gun.schedule(ctx.incoming.state, function(){
						update(incoming, field);
						if(ctx.incoming.state === upper.max){ (upper.last || function(){})() }
					});
				}
			});
			if(now.end){ now.call({}, vertex) } // TODO: Should HAM handle empty updates?
			function HAM(machineState, incomingState, currentState, incomingValue, currentValue){ // TODO: Lester's comments on roll backs could be vulnerable to divergence, investigate!
				if(machineState < incomingState){
					// the incoming value is outside the boundary of the machine's state, it must be reprocessed in another state.
					return {amnesiaQuarantine: true};
				}
				if(incomingState < currentState){
					// the incoming value is within the boundary of the machine's state, but not within the range.
					return {quarantineState: true};
				}
				if(currentState < incomingState){
					// the incoming value is within both the boundary and the range of the machine's state.
					return {converge: true, incoming: true};
				}
				if(incomingState === currentState){
					if(incomingValue === currentValue){ // Note: while these are practically the same, the deltas could be technically different
						return {state: true};
					}
					/*
						The following is a naive implementation, but will always work.
						Never change it unless you have specific needs that absolutely require it.
						If changed, your data will diverge unless you guarantee every peer's algorithm has also been changed to be the same.
						As a result, it is highly discouraged to modify despite the fact that it is naive,
						because convergence (data integrity) is generally more important.
						Any difference in this algorithm must be given a new and different name.
					*/
					if(String(incomingValue) < String(currentValue)){ // String only works on primitive values!
						return {converge: true, current: true};
					}
					if(String(currentValue) < String(incomingValue)){ // String only works on primitive values!
						return {converge: true, incoming: true};
					}
				}
				return {err: "you have not properly handled recursion through your data or filtered it as JSON"};
			}
			return function(fn){
				upper.last = fn || function(){};
				if(!upper.wait){ upper.last() }
			}
		}
		;Gun.on = (function(){
			// events are fundamentally different, being synchronously 1 to N fan out,
			// than req/res/callback/promise flow, which are asynchronously 1 to 1 into a sink.
			function On(where){ return where? (On.event = On.event || On.create())(where) : On.create() }
			On.is = function(on){ return (On instanceof on)? true : false }
			On.create = function(){
				var chain = new On.chain();
				return chain.$ = function(where){
					chain.where = where;
					return chain;
				}
			}
			On.sort = Gun.list.sort('i');
			;On.chain=(function(){
				function Chain(){
					if(!(this instanceof Chain)){
						return new Chain();
					}
				}
				Chain.chain = Chain.prototype;
				Chain.chain.emit = function(what){
					var me = this
					,	where = me.where
					,	args = arguments
					, on = (me._ = me._ || {})[where] = me._[where] || [];
					if(!(me._[where] = Gun.list.map(on, function(hear, i, map){
						if(!hear || !hear.as){ return }
						map(hear);
						hear.as.apply(hear, args);
					}))){ Gun.obj.del(on, where) }
				}
				Chain.chain.event = function(as, i){
					if(!as){ return }
					var me = this
					,	where = me.where
					,	args = arguments
					, 	on = (me._ = me._ || {})[where] = me._[where] || []
					,	e = {as: as, i: i || 0, off: function(){ return !(e.as = false) }};
					return on.push(e), on.sort(On.sort), e;
				}
				Chain.chain.once = function(as, i){
					var me = this
					,	once = function(){
						this.off();
						as.apply(this, arguments)
					}
					return me.event(once, i)
				}
				return Chain;
			}());
			return On;
		}());
		Gun.roulette = function(l, c){
			var gun = Gun.is(this)? this : {};
			if(gun._ && gun.__.opt && gun.__.opt.uuid){
				if(Gun.fns.is(gun.__.opt.uuid)){
					return gun.__.opt.uuid(l, c);
				}
				l = l || gun.__.opt.uuid.length;
			}
			return Gun.text.random(l, c);
		}
	}(Gun));
	;(function(Chain){
		Chain.opt = function(opt, stun){ // idempotently update or put options
			var gun = this;
			gun._ = gun._ || {};
			gun.__ = gun.__ || {};
			if(opt === null){ return gun }
			opt = opt || {};
			gun.__.opt = gun.__.opt || {};
			gun.__.flag = gun.__.flag || {start: {}, end: {}};
			gun.__.key = gun.__.key || {s: {}, ed: {}};
			gun.__.graph = gun.__.graph || {};
			gun.__.on = gun.__.on || Gun.on.create();
			gun.__.meta = gun.__.meta || function(s){ return gun.__.meta[s] = gun.__.meta[s] || {} }
			if(Gun.text.is(opt)){ opt = {peers: opt} }
			if(Gun.list.is(opt)){ opt = {peers: opt} }
			if(Gun.text.is(opt.peers)){ opt.peers = [opt.peers] }
			if(Gun.list.is(opt.peers)){ opt.peers = Gun.obj.map(opt.peers, function(n,f,m){ m(n,{}) }) }
			gun.__.opt.peers = opt.peers || gun.__.opt.peers || {};
			gun.__.opt.uuid = opt.uuid || gun.__.opt.uuid || {};
			gun.__.opt.cb = gun.__.opt.cb || function(){};
			gun.__.opt.hooks = gun.__.opt.hooks || {};
			Gun.obj.map(opt.hooks, function(h, f){
				if(!Gun.fns.is(h)){ return }
				gun.__.opt.hooks[f] = h;
			});
			if(!stun){ Gun.on('opt').emit(gun, opt) }
			return gun;
		}
		Gun.chain.chain = function(from){
			var gun = Gun(null);
			from = from || this;
			gun.back = from;
			gun.__ = from.__;
			gun._ = {on: Gun.on.create()};
			gun._.at = function(e){
				var proxy = function(cb, i, chain){
					var on = gun._.on(e), at;
					if(at = ((on = gun._.on(e)).e = on.e || {})[e]){ setTimeout(function(){cb.call(on, at)},0) }
					on[chain](function(at){
						cb.call(on, at);
					}, i);
				}
				proxy.event = function(cb, i){ return proxy(cb, i, 'event') };
				proxy.once = function(cb, i){ return proxy(cb, i, 'once') };
				proxy.emit = function(at, on){//setTimeout(function(){
					((on = gun._.on(e)).e = on.e || {})[e] = at;
					gun._.on(e).emit(at);
				//},0)
				};
				return proxy;
			}
			return gun;
		}
		Chain.get = function(key, cb, opt){ // get opens up a reference to a node and loads it.
			var gun = this.chain(), ctx = {};
			if(!key){ return cb.call(gun, {err: Gun.log("No key or relation to get!") }), gun }
			ctx.key = Gun.text.is(key) && key; // if key is text, then key, else false.
			ctx.soul = Gun.is.soul(key); // if key is a soul, then the soul, else false.
			cb = cb || function(){};
			opt = opt || {};
			
			if(opt.force){ load(key) } else
			if(ctx.soul){
				if(ctx.node = gun.__.graph[ctx.soul]){ // in memory
					(ctx.graph = {})[ctx.soul] = ctx.node;
					cb.call(gun, null, Gun.obj.copy(ctx.graph));
					(ctx.graph = {})[ctx.soul] = Gun.union.pseudo(ctx.soul);
					cb.call(gun, null, ctx.graph); cb.call(gun, null, {}); // end;
				} else { load(key) } // not in memory
				ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || Gun.union.pseudo(ctx.soul);
				gun._.at('soul').emit({soul: ctx.soul, GET: 'SOUL'});
			} else 
			if(ctx.key){
				function get(soul){
					var graph = gun.__.key.s[ctx.key], end = {};
					Gun.is.graph(graph, function(node, soul){
						end[soul] = Gun.union.pseudo(soul);
						gun._.at('soul').emit({soul: soul, key: ctx.key, GET: 'SOUL'});
					});
					cb.call(gun, null, Gun.obj.copy(graph)); cb.call(gun, null, end); cb.call(gun, null, {}); // end.
				}
				if(gun.__.key.s[ctx.key]){ get() } // in memory
				else if(ctx.flag = gun.__.flag.start[ctx.key]){ // will be in memory
					ctx.flag.once(get);
				} else { load(key) } // not in memory
			} else { cb.call(gun, {err: Gun.log("No key or relation to get!")}) }
			
			function load(key){
				if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.get)){
					ctx.hook(key, function(err, data){ // will be called multiple times.
						console.log("chain.get ", key, "from hook", err, data);
						if(err){ return cb.call(gun, err, null) }
						if(!data){
							if(ctx.data){ return }
							cb.call(gun, null, null);
							if(!ctx.key || ctx.soul){ return } // TODO: Maybe want to do a .not on a soul directly?
							return gun.__.flag.end[ctx.key] = gun.__.flag.end[ctx.key] || function($){
								// TODO: cover all edge cases, uniqueness?
								delete gun.__.flag.end[ctx.key]; 
								gun._.at('soul').emit($);
							}, gun._.at('null').emit({key: ctx.key, GET: 'NULL'});
						}
						var dat = ctx.data = {};
						if(Gun.obj.empty(data)){ return cb.call(gun, null, data) }
						if(!Gun.is.graph(data, function(node, soul, map){
							if(err = Gun.union(gun, node).err){ return cb.call(gun, err, data) }
							/*dat[soul] = Gun.union.pseudo(soul); map(function(val, field){
								(dat[soul]._[Gun._.HAM] = dat[soul]._[Gun._.HAM] || {})[field] = gun.__.graph[soul]._[Gun._.HAM]
							});*/
							if(ctx.key){ (gun.__.key.s[ctx.key] = gun.__.key.s[ctx.key] || {})[soul] = gun.__.graph[soul] }
							gun._.at('soul').emit({soul: soul, key: ctx.key, GET: 'SOUL'}); // TODO: Implications?
						})){ return cb.call(gun, {err: Gun.log('Not a valid graph!') }, data) }
						cb.call(gun, null, data);
					}, opt);
				} else {
					console.Log("Warning! You have no persistence layer to get from!");
					cb.call(gun, null, null); // Technically no error, but no way we can get data.
					gun._.at('null').emit({key: ctx.key, GET: 'NULL'});
				}
			}
			return gun;
		}
		Chain.key = function(key, cb, opt){
			var gun = this, ctx = {};
			if(!key){ return cb.call(gun, {err: Gun.log('No key!')}), gun }
			if(!gun.back){ gun = gun.chain() }
			if(gun.__.key.s[key]){ console.Log("Warning! Key already used!") } // TODO! Have opt that will aggregate.
			cb = cb || function(){};
			opt = Gun.text.is(opt)? {soul: opt} : opt || {};
			opt.soul = opt.soul || opt[Gun._.soul];
			if(opt.soul){ // force inject // TODO! BUG! WRITE A TEST FOR THIS!
				if(!gun.__.graph[opt.soul]){
					((gun.__.graph[opt.soul] = {})._ = {})[Gun._.soul] = opt.soul;
				}
				(gun.__.key.s[key] = gun.__.key.s[key] || {})[opt.soul] = gun.__.graph[opt.soul];
				if(gun.__.flag.end[key]){ // TODO: Ought this be fulfilled from self as well?
					gun.__.flag.end[key]({soul: opt.soul});
				}
				index({soul: opt.soul});
			} else { // will be injected via a put
				(gun.__.flag.start[key] = gun._.at('soul')).once(function($){
					console.log("chain.key");
					(gun.__.key.s[key] = gun.__.key.s[key] || {})[$.soul] = gun.__.graph[$.soul];
					delete gun.__.flag.start[key];
				}, -1);
				gun._.at('soul').event(index);
			}
			function index($){ // TODO: once per soul in graph. (?)
				if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.key)){
					ctx.hook(key, $.soul, function(err, data){
						return cb.call(gun, err, data);
					}, opt);
				} else {
					console.Log("Warning! You have no key hook!");
					cb.call(gun, null); // This is in memory success, hardly "success" at all.
				}
			}
			return gun;
		}
		Chain.all = function(key, cb){
			var gun = this.chain();
			return gun; // TODO: BUG! We need to create all!
			cb = cb || function(){};
			gun.shot.next(function(next){
				Gun.obj.map(gun.__.key.s, function(node, key){ // TODO: BUG!! Need to handle souls too!
					if(node = Gun.is.soul.on(node)){
						(cb.vode = cb.vode || {})[key] = {};
						cb.vode[key][Gun._.soul] = node;
					} 
				});
				if(cb.vode){
					gun._.node = cb.vode; // assign it to this virtual node.
					cb.call(gun, null, Gun.obj.copy(gun._.node)), next(); // frozen copy
				} else
				if(Gun.fns.is(gun.__.opt.hooks.all)){
					gun.__.opt.hooks.all(function(err, data){ // call the hook
						// this is multiple
					});
				} else {
					console.Log("Warning! You have no all hook!");
					return cb.call(gun), next();
				}
			});
			return gun;
		}
		/*
			how many different ways can we return something? ONLY THE FIRST ONE IS SUPPORTED, the others might become plugins.
			Find via a singular path
				.path('blah').val(blah);
			Find via multiple paths with the callback getting called many times
				.path('foo', 'bar').val(fooOrBar);
			Find via multiple paths with the callback getting called once with matching arguments
				.path('foo', 'bar').val(foo, bar)
			Find via multiple paths with the result aggregated into an object of pre-given fields
				.path('foo', 'bar').val({foo: foo, bar: bar}) || .path({a: 'foo', b: 'bar'}).val({a: foo, b: bar})
			Find via multiple paths where the fields and values must match
				.path({foo: val, bar: val}).val({})
			Path ultimately should call .val each time, individually, for what it finds.
			Things that wait and merge many things together should be an abstraction ontop of path.
		*/
		Chain.path = function(path, cb){
			var gun = this.chain();
			cb = cb || function(){};
			if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun }
			// TODO: Hmmm once also? figure it out later.
			gun.back._.at('soul').event(function($){
				var ctx = {path: (Gun.text.ify(path) || '').split('.')};
				(function trace($){ // TODO: Check for field as well and merge?
					if(!ctx.path.length){ return }
					var node = gun.__.graph[$.soul], field = Gun.text.ify(ctx.path.shift()), soul, val;
					if(ctx.path.length){
						if(soul = Gun.is.soul(val = node[field])){
							gun.get(val, function(err, data){
								data = (data || {})[soul];
								if(err || !data || Gun.obj.empty(data, Gun._.meta)){ return cb.call(gun, err) }
								trace({soul: soul});
							});
						} else {
							cb.call(gun, null);
						}
					} else
					if(!Gun.obj.has(node, field)){ // TODO: THIS MAY NOT BE CORRECT BEHAVIOR!!!!
						cb.call(gun, null, null, field);
						gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); // if .put is after, makes sense. If anything else, makes sense to wait.
					} else
					if(soul = Gun.is.soul(val = node[field])){
						gun.get(val, function(err, data){
							data = (data || {})[soul];
							cb.call(gun, err, data, field); // TODO: Should we attach field here, does map?
						});
						ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || Gun.union.pseudo(soul);
						gun._.at('soul').emit({soul: soul, field: null, from: $.soul, at: field, PATH: 'SOUL'});
					} else {
						cb.call(gun, null, val, field);
						gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL'});
					}
				}($));
			});
			
			return gun;
		}
		Chain.val = (function(){
			Gun.on('union').event(function(gun, data){
				if(!Gun.obj.empty(data, Gun._.meta)){ return }
				(data = gun.__.meta(Gun.is.soul.on(data))).end = (data.end || 0) + 1;
			});
			return function(cb, opt){
				var gun = this, ctx = {};
				cb = cb || root.console.log.bind(root.console);
				opt = opt || {};
				
				gun.on(function($, delta, on){
					var node = gun.__.graph[$.soul];
					if($.key){ 
						node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node;
					}
					if($.field){
						if(ctx[$.soul + $.field]){ return }
						ctx[$.soul + $.field] = true; // TODO: unregister instead?
						return cb.call(gun, node[$.field], $.field || $.at);
					}
					if(ctx[$.soul] || ($.key && ctx[$.key]) || !gun.__.meta($.soul).end){ return } // TODO: Add opt to change number of terminations.
					ctx[$.soul] = ctx[$.key] = true; // TODO: unregister instead?
					cb.call(gun, Gun.obj.copy(node), $.field || $.at);
				}, {raw: true});
				
				return gun;
			}
		}());
		// .on(fn) gives you back the object, .on(fn, true) gives you delta pair.
		Chain.on = function(cb, opt){
			var gun = this, ctx = {};
			opt = Gun.obj.is(opt)? opt : {change: opt};
			cb = cb || function(){};
			
			gun._.at('soul').event(function($){ // TODO: once per soul on graph. (?)
				if(ctx[$.soul]){
					if(opt.raw){
						ctx[$.soul](gun.__.graph[$.soul], $); // TODO: we get duplicate ons, once here and once from HAM.
					}
				} else {
					(ctx[$.soul] = function(delta, $$){
						var $$ = $$ || $, node = gun.__.graph[$$.soul];
						if(opt.raw){ return cb.call(gun, $$, delta, this) }
						if(!opt.end && Gun.obj.empty(delta, Gun._.meta)){ return }
						if($$.key){ node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node }
						cb.call(gun, Gun.obj.copy(opt.change? delta || node : node), $$.field || $$.at);
					})(gun.__.graph[$.soul], $);
					gun.__.on($.soul).event(ctx[$.soul]);
				}
			});
			
			return gun;
		}
		/*
			ACID compliant? Unfortunately the vocabulary is vague, as such the following is an explicit definition:
			A - Atomic, if you put a full node, or nodes of nodes, if any value is in error then nothing will be put.
				If you want puts to be independent of each other, you need to put each piece of the data individually.
			C - Consistency, if you use any reserved symbols or similar, the operation will be rejected as it could lead to an invalid read and thus an invalid state.
			I - Isolation, the conflict resolution algorithm guarantees idempotent transactions, across every peer, regardless of any partition,
				including a peer acting by itself or one having been disconnected from the network.
			D - Durability, if the acknowledgement receipt is received, then the state at which the final persistence hook was called on is guaranteed to have been written.
				The live state at point of confirmation may or may not be different than when it was called.
				If this causes any application-level concern, it can compare against the live data by immediately reading it, or accessing the logs if enabled.
		*/
		Chain.put = function(val, cb, opt){ // TODO: handle case where val is a gun context!
			var gun = this.chain(), call = function(){
				gun.back._.at('soul').emit({soul: Gun.is.soul.on(val) || Gun.roulette.call(gun), empty: true, PUT: 'SOUL'});
			}, drift = Gun.time.now();
			cb = cb || function(){};
			opt = opt || {};
			if(!gun.back.back){
				gun = gun.chain();
				call();
			}
			if(gun.back.not){ gun.back.not(call) }
			
			gun.back._.at('soul').event(function($){ // TODO: maybe once per soul?
				var ctx = {}, obj = val, $ = Gun.obj.copy($);
				console.log("chain.put", val);
				if(Gun.is.value(obj)){
					if($.from && $.at){
						$.soul = $.from;
						$.field = $.at;
					} // no else!
					if(!$.field){
						return cb.call(gun, {err: Gun.log("No field exists for " + (typeof obj) + "!")});
					} else
					if(gun.__.graph[$.soul]){
						ctx.tmp = {};
						ctx.tmp[ctx.field = $.field] = obj;
						obj = ctx.tmp;
					} else {
						return cb.call(gun, {err: Gun.log("No node exists to put " + (typeof obj) + " in!")});
					}
				}
				if(Gun.obj.is(obj)){
					if($.field && !ctx.field){
						ctx.tmp = {};
						ctx.tmp[ctx.field = $.field] = obj;
						obj = ctx.tmp;
					}
					Gun.ify(obj, function(env, cb){
						var at;
						if(!env || !(at = env.at) || !env.at.node){ return }
						if(!at.node._){
							at.node._ = {};
						}
						if(!Gun.is.soul.on(at.node)){
							if(obj === at.obj){
								env.graph[at.node._[Gun._.soul] = at.soul = $.soul] = at.node;
								cb(at, at.soul);
							} else {
								function path(err, data){
									if(at.soul){ return }
									at.soul = Gun.is.soul.on(data) || Gun.is.soul.on(at.obj) || Gun.roulette.call(gun);
									env.graph[at.node._[Gun._.soul] = at.soul] = at.node;
									cb(at, at.soul);
								};
								$.empty? path() : gun.back.path(at.path.join('.'), path); // TODO: clean this up.
							}
						}
						if(!at.node._[Gun._.HAM]){
							at.node._[Gun._.HAM] = {};
						}
						if(!at.field){ return }
						at.node._[Gun._.HAM][at.field] = drift;
					})(function(err, ify){
						console.log("chain.put PUT <----", ify.graph);
						if(err || ify.err){ return cb.call(gun, err || ify.err) }
						if(err = Gun.union(gun, ify.graph).err){ return cb.call(gun, err) }
						if($.from = Gun.is.soul(ify.root[$.field])){ $.soul = $.from; $.field = null }
						Gun.obj.map(ify.graph, function(node, soul){ Gun.union(gun, Gun.union.pseudo(soul)) });
						gun._.at('soul').emit({soul: $.soul, field: $.field, key: $.key, PUT: 'SOUL', WAS: 'ON'}); // WAS ON
						if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.put)){
							ctx.hook(ify.graph, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved
								if(err){ return cb.call(gun, err) }
								return cb.call(gun, null, data);
							}, opt);
						} else {
							console.Log("Warning! You have no persistence layer to save to!");
							cb.call(gun, null); // This is in memory success, hardly "success" at all.
						}
					});
				}
			});
			return gun;
		}
		Chain.map = function(cb, opt){
			var gun = this.chain(), ctx = {};
			opt = (Gun.obj.is(opt)? opt : (opt? {node: true} : {}));
			cb = cb || function(){};
			
			gun.back.on(function(node){ // oo what if this gets TODO: BUG! retriggered?
				var soul = Gun.is.soul.on(node);
				Gun.obj.map(node, function(val, field){ // maybe filter against known fields.
					if(Gun._.meta == field){ return }
					var s = Gun.is.soul(val);
					if(s){
						gun.get(val, function(err, data){
							data = (data || {})[s]; // TODO: should map have support for `.not`? error?
							if(err || !data || Gun.obj.empty(data, Gun._.meta)){ return }
							cb.call(this, Gun.obj.copy(data), field);
						});
						gun.__.graph[s] = gun.__.graph[s] || Gun.union.pseudo(s);
						gun._.at('soul').emit({soul: s, field: null, from: soul, at: field, MAP: 'SOUL'});
					} else {
						if(opt.node){ return } // {node: true} maps over only sub nodes.
						console.log("trigger next thing", field, val);
						cb.call(gun, val, field);
						gun._.at('soul').emit({soul: soul, field: field, MAP: 'SOUL'});
					}
				});
			}, true);
			
			return gun;
		}
		Chain.set = function(val, cb, opt){
			var gun = this, ctx = {}, drift = Gun.time.now();
			cb = cb || function(){};
			opt = opt || {};
			
			if(!gun.back){ gun = gun.put({}) }
			gun = gun.not(function(next, key){ return key? this.put({}).key(key) : this.put({}) });
			if(!val && !Gun.is.value(val)){ return gun }
			var obj = {};
			obj['I' + drift + 'R' + Gun.text.random(5)] = val;
			return gun.put(obj, cb);
		}
		Chain.not = function(cb){
			var gun = this, ctx = {};
			cb = cb || function(){};
			
			gun._.at('null').once(function(key){
				if(key.soul || gun.__.key.s[key = (key || {}).key]){ return }
				// TODO! BUG? Removed a start flag check and tests passed, but is that an edge case?
				var kick = function(next){
					if(++c){ return Gun.log("Warning! Multiple `not` resumes!"); }
					next._.at('soul').once(function($){ $.N0T = 'KICK SOUL'; gun._.at('soul').emit($) });
				}, chain = gun.chain(), next = cb.call(chain, kick, key), c = -1;
				if(Gun.is(next)){ kick(next) }
				chain._.at('soul').emit({soul: Gun.roulette.call(chain), empty: true, key: key, N0T: 'SOUL', WAS: 'ON'}); // WAS ON
			});
			
			return gun;
		}
		Chain.err = function(dud){ // WARNING: dud was depreciated.
			this._.err = Gun.fns.is(dud)? dud : function(){};
			return this;
		}
	}(Gun.chain = Gun.prototype));
	;function Util(Util){
		Util.fns = {};
		Util.fns.is = function(fn){ return (fn instanceof Function)? true : false }
		Util.fns.sum = function(done){ // combine with Util.obj.map for some easy parallel async operations!
			var context = {task: {}, data: {}};
			context.end = function(e,v){ return done(e,v), done = function(){} };
			context.add = function(fn, id){
				context.task[id = id || (Gun.text.is(fn)? fn : Gun.text.random())] = false;
				var each = function(err, val){
					context.task[id] = true;
					if(err){ (context.err = context.err || {})[id] = err }
					context.data[id] = val;
					if(!Gun.obj.map(context.task, function(val){ if(!val){ return true } })){ // it is true if we are NOT done yet, then invert.
						done(context.err, context.data);
					}
				}, c = context;
				return Gun.fns.is(fn)? function(){ return fn.apply({task: c.task, data: c.data, end: c.end, done: each}, arguments) } : each;
			}
			return context;
		}
		Util.bi = {};
		Util.bi.is = function(b){ return (b instanceof Boolean || typeof b == 'boolean')? true : false }
		Util.num = {};
		Util.num.is = function(n){
			return ((n===0)? true : (!isNaN(n) && !Util.bi.is(n) && !Util.list.is(n) && !Util.text.is(n))? true : false );
		}
		Util.text = {};
		Util.text.is = function(t){ return typeof t == 'string'? true : false }
		Util.text.ify = function(t){
			if(Util.text.is(t)){ return t }
			if(JSON){ return JSON.stringify(t) }
			return (t && t.toString)? t.toString() : t;
		}
		Util.text.random = function(l, c){
			var s = '';
			l = l || 24; // you are not going to make a 0 length random number, so no need to check type
			c = c || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghiklmnopqrstuvwxyz';
			while(l > 0){ s += c.charAt(Math.floor(Math.random() * c.length)); l-- }
			return s;
		}
		Util.list = {};
		Util.list.is = function(l){ return (l instanceof Array)? true : false }
		Util.list.slit = Array.prototype.slice;
		Util.list.sort = function(k){ // creates a new sort function based off some field
			return function(A,B){
				if(!A || !B){ return 0 } A = A[k]; B = B[k];
				if(A < B){ return -1 }else if(A > B){ return 1 }
				else { return 0 }
			}
		}
		Util.list.map = function(l, c, _){ return Util.obj.map(l, c, _) }
		Util.list.index = 1; // change this to 0 if you want non-logical, non-mathematical, non-matrix, non-convenient array notation
		Util.obj = {};
		Util.obj.is = function(o){ return (o instanceof Object && !Util.list.is(o) && !Util.fns.is(o))? true : false }
		Util.obj.del = function(o, k){
			if(!o){ return }
			o[k] = null;
			delete o[k];
			return true;
		}
		Util.obj.ify = function(o){
			if(Util.obj.is(o)){ return o }
			try{o = JSON.parse(o);
			}catch(e){o={}};
			return o;
		}
		Util.obj.copy = function(o){ // because http://web.archive.org/web/20140328224025/http://jsperf.com/cloning-an-object/2
			return !o? o : JSON.parse(JSON.stringify(o)); // is shockingly faster than anything else, and our data has to be a subset of JSON anyways!
		}
		Util.obj.has = function(o, t){ return o && Object.prototype.hasOwnProperty.call(o, t) }
		Util.obj.empty = function(o, n){
			if(!o){ return true }
			return Util.obj.map(o,function(v,i){
				if(n && (i === n || (Util.obj.is(n) && Util.obj.has(n, i)))){ return }
				if(i){ return true }
			})? false : true;
		}
		Util.obj.map = function(l, c, _){
			var u, i = 0, ii = 0, x, r, rr, f = Util.fns.is(c),
			t = function(k,v){
				if(v !== u){
					rr = rr || {};
					rr[k] = v;
					return;
				} rr = rr || [];
				rr.push(k);
			};
			if(Util.list.is(l)){
				x = l.length;
				for(;i < x; i++){
					ii = (i + Util.list.index);
					if(f){
						r = _? c.call(_, l[i], ii, t) : c(l[i], ii, t);
						if(r !== u){ return r }
					} else {
						//if(Util.test.is(c,l[i])){ return ii } // should implement deep equality testing!
						if(c === l[i]){ return ii } // use this for now
					}
				}
			} else {
				for(i in l){
					if(f){
						if(Util.obj.has(l,i)){
							r = _? c.call(_, l[i], i, t) : c(l[i], i, t);
							if(r !== u){ return r }
						}
					} else {
						//if(a.test.is(c,l[i])){ return i } // should implement deep equality testing!
						if(c === l[i]){ return i } // use this for now
					}
				}
			}
			return f? rr : Util.list.index? 0 : -1;
		}
		Util.time = {};
		Util.time.is = function(t){ return t? t instanceof Date : (+new Date().getTime()) }
		Util.time.now = function(t){
			return (t=t||Util.time.is()) > (Util.time.now.last || -Infinity)? (Util.time.now.last = t) : Util.time.now(t + 1);
			return (t = Util.time.is() + Math.random()) > (Util.time.now.last || -Infinity)? 
				(Util.time.now.last = t) : Util.time.now();
		};
	};
	;(function(schedule){ // maybe use lru-cache
		schedule.waiting = [];
		schedule.soonest = Infinity;
		schedule.sort = Gun.list.sort('when');
		schedule.set = function(future){
			if(Infinity <= (schedule.soonest = future)){ return }
			var now = Gun.time.is();
			future = (future <= now)? 0 : (future - now);
			clearTimeout(schedule.id);
			schedule.id = setTimeout(schedule.check, future);
		}
		schedule.check = function(){
			var now = Gun.time.is(), soonest = Infinity;
			schedule.waiting.sort(schedule.sort);
			schedule.waiting = Gun.list.map(schedule.waiting, function(wait, i, map){
				if(!wait){ return }
				if(wait.when <= now){
					if(Gun.fns.is(wait.event)){
						setTimeout(function(){ wait.event() },0);
					}
				} else {
					soonest = (soonest < wait.when)? soonest : wait.when;
					map(wait);
				}
			}) || [];
			schedule.set(soonest);
		}
		Gun.schedule = function(state, cb){
			schedule.waiting.push({when: state, event: cb || function(){}});
			if(schedule.soonest < state){ return }
			schedule.set(state);
		}
	}({}));
	;Gun.ify=(function(Serializer){
		function ify(data, cb, opt){
			opt = opt || {};
			cb = cb || function(env, cb){ cb(env.at, Gun.roulette()) };
			var end = function(fn){
				ctx.end = fn || function(){};
				if(ctx.err){ return ctx.end(ctx.err, ctx), ctx.end = function(){} }
				unique(ctx);
			}, ctx = {};
			if(!data){ return ctx.err = Gun.log('Serializer does not have correct parameters.'), end }
			ctx.at = {};
			ctx.root = {};
			ctx.graph = {};
			ctx.queue = [];
			ctx.seen = [];
			ctx.loop = true;
			
			ctx.at.path = [];
			ctx.at.obj = data;
			ctx.at.node = ctx.root;
			while(ctx.loop && !ctx.err){
				seen(ctx, ctx.at);
				map(ctx, cb);
				if(ctx.queue.length){
					ctx.at = ctx.queue.shift();
				} else {
					ctx.loop = false;
				}
			}
			return end;
		}
		function map(ctx, cb){
			var rel = function(at, soul){
				at.soul = at.soul || soul;
				Gun.list.map(at.back, function(rel){
					rel[Gun._.soul] = at.soul;
				});
				unique(ctx); // could we remove the setTimeot?
			}, it;
			Gun.obj.map(ctx.at.obj, function(val, field){
				ctx.at.val = val;
				ctx.at.field = field;
				it = cb(ctx, rel) || true;
				if(field === Gun._.meta){
					ctx.at.node[field] = Gun.obj.copy(val); // TODO: BUG! Is this correct?
					return;
				}
				if(false && notValidField(field)){ // TODO: BUG! Do later for ACID "consistency" guarantee.
					return ctx.err = {err: Gun.log('Invalid field name on ' + ctx.at.path.join('.'))};
				}
				if(!Gun.is.value(val)){
					var at = {obj: val, node: {}, back: [], path: [field]}, tmp = {}, was;
					at.path = (ctx.at.path||[]).concat(at.path || []);
					if(!Gun.obj.is(val)){
						return ctx.err = {err: Gun.log('Invalid value at ' + at.path.join('.') + '!' )};
					}
					if(was = seen(ctx, at)){
						tmp[Gun._.soul] = Gun.is.soul.on(was.node) || null;
						(was.back = was.back || []).push(ctx.at.node[field] = tmp);
					} else {
						ctx.queue.push(at);
						tmp[Gun._.soul] = null;
						at.back.push(ctx.at.node[field] = tmp);
					}
				} else {
					ctx.at.node[field] = Gun.obj.copy(val);
				}
			});
			if(!it){ cb(ctx, rel) }
		}
		function unique(ctx){
			if(ctx.err || !Gun.list.map(ctx.seen, function(at){
				if(!at.soul){ return true }
			}) && !ctx.loop){ return ctx.end(ctx.err, ctx), ctx.end = function(){} }
		}
		function seen(ctx, at){
			return Gun.list.map(ctx.seen, function(has){
				if(at.obj === has.obj){ return has }
			}) || (ctx.seen.push(at) && false);
		}
		return ify;
	}({}));
	if(typeof window !== "undefined"){
		window.Gun = Gun;
	} else {
		module.exports = Gun;
	}
	var root = this || {}; // safe for window, global, root, and 'use strict'.
	root.console = root.console || {log: function(s){ return s }}; // safe for old browsers
	var console = {
		log: Gun.log = function(s){return (Gun.log.verbose && root.console.log.apply(root.console, arguments)), s},
		Log: function(s){return (!Gun.log.squelch && root.console.log.apply(root.console, arguments)), s}
	};
}({}));

;(function(tab){
	if(!this.Gun){ return }
	if(!window.JSON){ throw new Error("Include JSON first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js") } // for old IE use
	Gun.on('opt').event(function(gun, opt){
		opt = opt || {};
		var tab = gun.__.tab = gun.__.tab || {}; 
		tab.headers = opt.headers || {};
		tab.headers['gun-sid'] = tab.headers['gun-sid'] || Gun.text.random(); // stream id
		tab.prefix = tab.prefix || opt.prefix || 'gun/';
		tab.prekey = tab.prekey || opt.prekey || '';
		tab.prenode = tab.prenode || opt.prenode || '_/nodes/';
		window.tab = tab; window.store = store;
		tab.get = tab.get || function(key, cb, opt){
			if(!key){ return }
			cb = cb || function(){};
			cb.GET = true;
			(opt = opt || {}).url = opt.url || {};
			opt.headers = Gun.obj.copy(tab.headers);
			if(Gun.is.soul(key)){
				opt.url.query = key;
			} else {
				opt.url.pathname = '/' + key;
			}
			Gun.log("tab get --->", key);
			(function local(key, cb){
				var path = (path = Gun.is.soul(key))? tab.prefix + tab.prenode + path
					: tab.prefix + tab.prekey + key, node = store.get(path), graph, soul;
				if(Gun.is.node(node)){
					(cb.graph = cb.graph || {}
					)[soul = Gun.is.soul.on(node)] = (graph = {})[soul] = cb.node = node;
					cb(null, graph); 
					(graph = {})[soul] = Gun.union.pseudo(soul); // end.
					return cb(null, graph);
				} else 
				if(Gun.obj.is(node)){
					Gun.obj.map(node, function(rel){ if(Gun.is.soul(rel)){ local(rel, cb) } });
					cb(null, {});
				}
			}(key, cb));
			if(!(cb.local = opt.local)){
				Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){ var p = {};
					request(url, null, tab.error(cb, "Error: Get failed through " + url, function(reply){
						if(!p.graph && !Gun.obj.empty(cb.graph)){ // if we have local data
							tab.put(p.graph = cb.graph, function(e,r){ // then sync it if we haven't already
								Gun.log("Stateless handshake sync:", e, r);
							}, {peers: tab.peers(url)}); // to the peer. // TODO: This forces local to flush again, not necessary.
							// TODO: What about syncing our keys up?
						}
						Gun.is.graph(reply.body, function(node, soul){ // make sure for each received node
							if(!Gun.is.soul(key)){ tab.key(key, soul, function(){}, {local: true}) } // that the key points to it.
						});
						setTimeout(function(){ tab.put(reply.body, function(){}, {local: true}) },1); // and flush the in memory nodes of this graph to localStorage after we've had a chance to union on it.
					}), opt);
					cb.peers = true;
				});
			} tab.peers(cb);
		}
		tab.put = tab.put || function(graph, cb, opt){
			cb = cb || function(){};
			opt = opt || {};
			Gun.is.graph(graph, function(node, soul){
				if(!opt.local){ gun.__.on(soul).emit(node) } // TODO: Should this be in core?
				if(!gun.__.graph[soul]){ return }
				store.put(tab.prefix + tab.prenode + soul, gun.__.graph[soul]);
			});
			if(!(cb.local = opt.local)){
				Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){
					request(url, graph, tab.error(cb, "Error: Put failed on " + url), {headers: tab.headers});
					cb.peers = true;
				});
			} tab.peers(cb);
		}
		tab.key = tab.key || function(key, soul, cb, opt){
			var meta = {};
			opt = opt || {};
			cb = cb || function(){};
			meta[Gun._.soul] = soul = Gun.is.soul(soul) || soul;
			if(!soul){ return cb({err: Gun.log("No soul!")}) }
			(function(souls){
				(souls = store.get(tab.prefix + tab.prekey + key) || {})[soul] = meta;
				store.put(tab.prefix + tab.prekey + key, souls);
			}());
			if(!(cb.local = opt.local || opt.soul)){
				Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){
					request(url, meta, tab.error(cb, "Error: Key failed to be made on " + url), {url: {pathname: '/' + key }, headers: tab.headers});
					cb.peers = true;
				}); 
			} tab.peers(cb);
		}
		tab.error = function(cb, error, fn){
			return function(err, reply){
				reply.body = reply.body || reply.chunk || reply.end || reply.write;
				if(err || !reply || (err = reply.body && reply.body.err)){
					return cb({err: Gun.log(err || error) });
				}
				if(fn){ fn(reply) }
				cb(null, reply.body);
			}
		}
		tab.peers = function(cb, o){
			if(Gun.text.is(cb)){ return (o = {})[cb] = {}, o }
			if(cb && !cb.peers){ setTimeout(function(){
				if(!cb.local){ console.log("Warning! You have no peers to connect to!") }
				if(!(cb.graph || cb.node)){ cb() }
			},1)}
		}
		tab.server = tab.server || function(req, res){
			if(!req || !res || !req.url || !req.method){ return }
			req.url = req.url.href? req.url : document.createElement('a');
			req.url.href = req.url.href || req.url;
			req.url.key = (req.url.pathname||'').replace(tab.server.regex,'').replace(/^\//i,'') || '';
			req.method = req.body? 'put' : 'get';
			if('get' == req.method){ return tab.server.get(req, res) }
			if('put' == req.method || 'post' == req.method){ return tab.server.put(req, res) }
		}
		tab.server.json = 'application/json';
		tab.server.regex = gun.__.opt.route = gun.__.opt.route || opt.route || /^\/gun/i;
		tab.server.get = function(){}
		tab.server.put = function(req, cb){
			var reply = {headers: {'Content-Type': tab.server.json}};
			if(!req.body){ return cb({headers: reply.headers, body: {err: "No body"}}) }
			// TODO: Re-emit message to other peers if we have any non-overlaping ones.
			if(tab.server.put.key(req, cb)){ return }
			if(Gun.is.node(req.body) || Gun.is.graph(req.body, function(node, soul){
				gun.__.flag.end[soul] = true; // TODO! Put this in CORE not in TAB driver?
			})){
				//console.log("tran.put", req.body);					
				if(req.err = Gun.union(gun, req.body, function(err, ctx){
					if(err){ return cb({headers: reply.headers, body: {err: err || "Union failed."}}) }
					var ctx = ctx || {}; ctx.graph = {};
					Gun.is.graph(req.body, function(node, soul){ ctx.graph[soul] = gun.__.graph[soul] });
					gun.__.opt.hooks.put(ctx.graph, function(err, ok){
						if(err){ return cb({headers: reply.headers, body: {err: err || "Failed."}}) }
						cb({headers: reply.headers, body: {ok: ok || "Persisted."}});
					}, {local: true});
				}).err){ cb({headers: reply.headers, body: {err: req.err || "Union failed."}}) }
			}
		}
		tab.server.put.key = function(req, cb){
			if(!req || !req.url || !req.url.key || !Gun.obj.has(req.body, Gun._.soul)){ return }
			var index = req.url.key, soul = Gun.is.soul(req.body);
			//console.log("tran.key", index, req.body);
			gun.key(index, function(err, reply){
				if(err){ return cb({headers: {'Content-Type': tab.server.json}, body: {err: err}}) }
				cb({headers: {'Content-Type': tab.server.json}, body: reply}); // TODO: Fix so we know what the reply is.
			}, soul);
			return true;
		}
		Gun.obj.map(gun.__.opt.peers, function(){ // only create server if peers and do it once by returning immediately.
			return tab.request = tab.request || request.createServer(tab.server) || true;
		});
		gun.__.opt.hooks.get = gun.__.opt.hooks.get || tab.get;
		gun.__.opt.hooks.put = gun.__.opt.hooks.put || tab.put;
		gun.__.opt.hooks.key = gun.__.opt.hooks.key || tab.key;
	});
	var store = (function(){
		function s(){}
		var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}};
		s.put = function(key, val){ return store.setItem(key, Gun.text.ify(val)) }
		s.get = function(key){ return Gun.obj.ify(store.getItem(key) || null) }
		s.del = function(key){ return store.removeItem(key) }
		return s;
	}());
	var request = (function(){
		function r(base, body, cb, opt){
			opt = opt || (base.length? {base: base} : base);
			opt.base = opt.base || base;
			opt.body = opt.body || body;
			if(!opt.base){ return }
			r.transport(opt, cb);
		}
		r.createServer = function(fn){ r.createServer.s.push(fn) }
		r.createServer.ing = function(req, cb){
			var i = r.createServer.s.length;
			while(i--){ (r.createServer.s[i] || function(){})(req, cb) }
		}
		r.createServer.s = [];
		r.transport = function(opt, cb){
			//Gun.log("TRANSPORT:", opt);
			if(r.ws(opt, cb)){ return }
			r.jsonp(opt, cb);
		}
		r.ws = function(opt, cb){
			var ws, WS = window.WebSocket || window.mozWebSocket || window.webkitWebSocket;
			if(!WS){ return }
			if(ws = r.ws.peers[opt.base]){
				if(!ws.readyState){ return setTimeout(function(){ r.ws(opt, cb) },10), true }
				var req = {};
				if(opt.headers){ req.headers = opt.headers }
				if(opt.body){ req.body = opt.body }
				if(opt.url){ req.url = opt.url }
				req.headers = req.headers || {};
				r.ws.cbs[req.headers['ws-rid'] = 'WS' + (+ new Date()) + '.' + Math.floor((Math.random()*65535)+1)] = function(err,res){
					if(res.body || res.end){ delete r.ws.cbs[req.headers['ws-rid']] }
					cb(err,res);
				}
				ws.send(JSON.stringify(req));
				return true;
			}
			if(ws === false){ return }
			ws = r.ws.peers[opt.base] = new WS(opt.base.replace('http','ws'));
			ws.onopen = function(o){ r.ws(opt, cb) };
			ws.onclose = window.onbeforeunload = function(c){
				if(!c){ return }
				if(ws && ws.close instanceof Function){ ws.close() }
				if(1006 === c.code){ // websockets cannot be used
					ws = r.ws.peers[opt.base] = false;
					r.transport(opt, cb);
					return;
				}
				ws = r.ws.peers[opt.base] = null; // this will make the next request try to reconnect
			};
			ws.onmessage = function(m){
				if(!m || !m.data){ return }
				var res;
				try{res = JSON.parse(m.data);
				}catch(e){ return }
				if(!res){ return }
				res.headers = res.headers || {};
				if(res.headers['ws-rid']){ return (r.ws.cbs[res.headers['ws-rid']]||function(){})(null, res) }
				Gun.log("We have a pushed message!", res);
				if(res.body){ r.createServer.ing(res, function(){}) } // emit extra events.
			};
			ws.onerror = function(e){ Gun.log(e); };
			return true;
		}
		r.ws.peers = {};
		r.ws.cbs = {};
		r.jsonp = function(opt, cb){
			//Gun.log("jsonp send", opt);
			r.jsonp.ify(opt, function(url){
				//Gun.log(url);
				if(!url){ return }
				r.jsonp.send(url, function(reply){
					//Gun.log("jsonp reply", reply);
					cb(null, reply);
					r.jsonp.poll(opt, reply);
				}, opt.jsonp);
			});
		}
		r.jsonp.send = function(url, cb, id){
			var js = document.createElement('script');
			js.src = url;
			window[js.id = id] = function(res){
				cb(res);
				cb.id = js.id;
				js.parentNode.removeChild(js);
				window[cb.id] = null; // TODO! BUG: This needs to handle chunking!
				try{delete window[cb.id];
				}catch(e){}
			}
			js.async = true;
			document.getElementsByTagName('head')[0].appendChild(js);
			return js;
		}
		r.jsonp.poll = function(opt, res){
			if(!opt || !opt.base || !res || !res.headers || !res.headers.poll){ return }
			(r.jsonp.poll.s = r.jsonp.poll.s || {})[opt.base] = r.jsonp.poll.s[opt.base] || setTimeout(function(){ // TODO: Need to optimize for Chrome's 6 req limit?
				//Gun.log("polling again");
				var o = {base: opt.base, headers: {pull: 1}};
				r.each(opt.headers, function(v,i){ o.headers[i] = v })
				r.jsonp(o, function(err, reply){
					delete r.jsonp.poll.s[opt.base];
					while(reply.body && reply.body.length && reply.body.shift){ // we're assuming an array rather than chunk encoding. :(
						var res = reply.body.shift();
						//Gun.log("-- go go go", res);
						if(res && res.body){ r.createServer.ing(res, function(){}) } // emit extra events.
					}
				});
			}, res.headers.poll);
		}
		r.jsonp.ify = function(opt, cb){
			var uri = encodeURIComponent, q = '?';
			if(opt.url && opt.url.pathname){ q = opt.url.pathname + q; }
			q = opt.base + q;
			r.each((opt.url||{}).query, function(v, i){ q += uri(i) + '=' + uri(v) + '&' });
			if(opt.headers){ q += uri('`') + '=' + uri(JSON.stringify(opt.headers)) + '&' }
			if(r.jsonp.max < q.length){ return cb() }
			q += uri('jsonp') + '=' + uri(opt.jsonp = 'P'+Math.floor((Math.random()*65535)+1));
			if(opt.body){
				q += '&';
				var w = opt.body, wls = function(w,l,s){
					return uri('%') + '=' + uri(w+'-'+(l||w)+'/'+(s||w))  + '&' + uri('$') + '=';
				}
				if(typeof w != 'string'){
					w = JSON.stringify(w);
					q += uri('^') + '=' + uri('json') + '&';
				}
				w = uri(w);
				var i = 0, l = w.length
				, s = r.jsonp.max - (q.length + wls(l.toString()).length);
				if(s < 0){ return cb() }
				while(w){
					cb(q + wls(i, (i = i + s), l) + w.slice(0, i));
					w = w.slice(i);
				}
			} else {
				cb(q);
			}
		}
		r.jsonp.max = 2000;
		r.each = function(obj, cb){
			if(!obj || !cb){ return }
			for(var i in obj){
				if(obj.hasOwnProperty(i)){
					cb(obj[i], i);
				}
			}
		}
		return r;
	}());
}({}));